From 16d31ef77572fcd8e5b754ff702355e56bb8acfe Mon Sep 17 00:00:00 2001 From: da-woods Date: Thu, 16 Nov 2023 08:01:33 +0000 Subject: [PATCH 001/286] Disable failing Windows CyCache test (#5826) See #5825 for a description of the failure. --- Cython/Build/Tests/TestCyCache.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Cython/Build/Tests/TestCyCache.py b/Cython/Build/Tests/TestCyCache.py index 7a44d89e999..e5108ab9ea5 100644 --- a/Cython/Build/Tests/TestCyCache.py +++ b/Cython/Build/Tests/TestCyCache.py @@ -2,7 +2,9 @@ import glob import gzip import os +import sys import tempfile +import unittest import Cython.Build.Dependencies import Cython.Utils @@ -63,6 +65,9 @@ def test_cycache_switch(self): msg='\n'.join(list(difflib.unified_diff( a_contents.split('\n'), a_contents1.split('\n')))[:10])) + @unittest.skipIf(sys.version_info[:2] == (3, 12) and sys.platform == "win32", + "This test is mysteriously broken on Windows on the CI only " + "(https://github.com/cython/cython/issues/5825)") def test_cycache_uses_cache(self): a_pyx = os.path.join(self.src_dir, 'a.pyx') a_c = a_pyx[:-4] + '.c' From a4bb6dcf1ee6b4ee23ae17cf25cbddf4ae9898a2 Mon Sep 17 00:00:00 2001 From: da-woods Date: Sat, 18 Nov 2023 14:11:46 +0000 Subject: [PATCH 002/286] Add missing unicode C API functions and Py_UNICODE warning (#5836) * Add missing unicode C API functions and Py_UNICODE warning 1. Add missing C API functions for unicode to wchar_t (since these are what should be used to replace Py_UNICODE*). 2. Add an explicit warning for users converting to Py_UNICODE*. * Add extra warnings to test --- Cython/Compiler/ExprNodes.py | 7 +++++++ Cython/Includes/cpython/unicode.pxd | 32 +++++++++++++++++++++++------ tests/errors/charptr_from_temp.pyx | 3 +++ 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 54ce1688446..6b5b2b75a3f 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -14144,6 +14144,13 @@ def __init__(self, result_type, arg, env): warning(arg.pos, "Obtaining '%s' from externally modifiable global Python value" % result_type, level=1) + if self.type.is_pyunicode_ptr: + warning(arg.pos, + "Py_UNICODE* has been removed in Python 3.12. This conversion to a " + "Py_UNICODE* will no longer compile in the latest Python versions. " + "Use Python C API functions like PyUnicode_AsWideCharString if you " + "need to obtain a wchar_t* on Windows (and free the string manually after use).", + level=1) def analyse_types(self, env): # The arg is always already analysed diff --git a/Cython/Includes/cpython/unicode.pxd b/Cython/Includes/cpython/unicode.pxd index 6452d892e2d..a7d24e42823 100644 --- a/Cython/Includes/cpython/unicode.pxd +++ b/Cython/Includes/cpython/unicode.pxd @@ -1,3 +1,4 @@ +from libc.stddef cimport wchar_t cdef extern from *: ctypedef unsigned char Py_UCS1 # uint8_t @@ -180,14 +181,33 @@ cdef extern from *: # following functions. Support is optimized if Python's own # Py_UNICODE type is identical to the system's wchar_t. - #ctypedef int wchar_t - # Create a Unicode object from the wchar_t buffer w of the given # size. Return NULL on failure. - #PyObject* PyUnicode_FromWideChar(wchar_t *w, Py_ssize_t size) - - #Py_ssize_t PyUnicode_AsWideChar(object o, wchar_t *w, Py_ssize_t size) - + object PyUnicode_FromWideChar(wchar_t *w, Py_ssize_t size) + + # Copy the Unicode object contents into the wchar_t buffer w. + # At most size wchar_t characters are copied (excluding a possibly + # trailing null termination character). Return the number of wchar_t + # characters copied or -1 in case of an error. Note that the + # esulting wchar_t* string may or may not be null-terminated. + # It is the responsibility of the caller to make sure that the wchar_t* + # string is null-terminated in case this is required by the application. + # Also, note that the wchar_t* string might contain null characters, + # which would cause the string to be truncated when used with most C functions. + Py_ssize_t PyUnicode_AsWideChar(object o, wchar_t *w, Py_ssize_t size) + + # Convert the Unicode object to a wide character string. The output + # string always ends with a null character. If size is not NULL, + # write the number of wide characters (excluding the trailing null + # termination character) into *size. Note that the resulting wchar_t + # string might contain null characters, which would cause the string + # to be truncated when used with most C functions. If size is NULL and + # the wchar_t* string contains null characters a ValueError is raised. + + # Returns a buffer allocated by PyMem_New (use PyMem_Free() to free it) + # on success. On error, returns NULL and *size is undefined. Raises a + # MemoryError if memory allocation is failed. + wchar_t *PyUnicode_AsWideCharString(object o, Py_ssize_t *size) # Unicode Methods diff --git a/tests/errors/charptr_from_temp.pyx b/tests/errors/charptr_from_temp.pyx index 3e6760e3194..6620b46cb09 100644 --- a/tests/errors/charptr_from_temp.pyx +++ b/tests/errors/charptr_from_temp.pyx @@ -59,8 +59,11 @@ _ERRORS = """ #23:5: Storing unsafe C derivative of temporary Python reference #23:15: Casting temporary Python object to non-numeric non-Python type 26:8: Storing unsafe C derivative of temporary Python reference +38:8: Py_UNICODE* has been removed in Python 3.12. This conversion to a Py_UNICODE* will no longer compile in the latest Python versions. Use Python C API functions like PyUnicode_AsWideCharString if you need to obtain a wchar_t* on Windows (and free the string manually after use). 41:8: Obtaining 'Py_UNICODE *' from externally modifiable global Python value +41:8: Py_UNICODE* has been removed in Python 3.12. This conversion to a Py_UNICODE* will no longer compile in the latest Python versions. Use Python C API functions like PyUnicode_AsWideCharString if you need to obtain a wchar_t* on Windows (and free the string manually after use). 44:10: Storing unsafe C derivative of temporary Python reference +44:10: Py_UNICODE* has been removed in Python 3.12. This conversion to a Py_UNICODE* will no longer compile in the latest Python versions. Use Python C API functions like PyUnicode_AsWideCharString if you need to obtain a wchar_t* on Windows (and free the string manually after use). 52:7: Storing unsafe C derivative of temporary Python reference 52:7: Unsafe C derivative of temporary Python reference used in conditional expression """ From df2bffaa8d30deacbf05b7e4aa2b9ec20b3167da Mon Sep 17 00:00:00 2001 From: da-woods Date: Sun, 19 Nov 2023 10:54:30 +0000 Subject: [PATCH 003/286] Partially disable trashcan test in PyPy (#5832) The test is requiring a derministic order of destruction which PyPy doesn't give reliably and thus we're getting occasional CI failures. Therefore just treat is as compile and don't crash test. --- tests/run/trashcan.pyx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/run/trashcan.pyx b/tests/run/trashcan.pyx index 29331fdf6c9..a3bbd7944f2 100644 --- a/tests/run/trashcan.pyx +++ b/tests/run/trashcan.pyx @@ -2,10 +2,18 @@ cimport cython +import sys + +# The tests here are to do with a deterministic order of destructors which +# isn't reliable for PyPy. Therefore, on PyPy we treat the test as +# "compiles and doesn't crash" +IS_PYPY = hasattr(sys, 'pypy_version_info') # Count number of times an object was deallocated twice. This should remain 0. cdef int double_deallocations = 0 def assert_no_double_deallocations(): + if IS_PYPY: + return global double_deallocations err = double_deallocations double_deallocations = 0 @@ -98,6 +106,8 @@ cdef class RecurseList(list): cdef int base_deallocated = 0 cdef int trashcan_used = 0 def assert_no_trashcan_used(): + if IS_PYPY: + return global base_deallocated, trashcan_used err = trashcan_used trashcan_used = base_deallocated = 0 From f258b674d3117141ceae53226e7fe8530f51385c Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 20 Nov 2023 18:02:04 +0200 Subject: [PATCH 004/286] PEP 703: Accept new `Py_GIL_DISABLED` macro in addition to `PY_NOGIL` (GH-5852) See https://github.com/python/cpython/issues/111863 See https://github.com/python/cpython/pull/111864 --- Cython/Utility/ModuleSetupCode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 270ff80061e..9fa0a7bc615 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -248,7 +248,7 @@ #define CYTHON_UPDATE_DESCRIPTOR_DOC 0 #endif -#elif defined(PY_NOGIL) +#elif defined(Py_GIL_DISABLED) || defined(Py_NOGIL) #define CYTHON_COMPILING_IN_PYPY 0 #define CYTHON_COMPILING_IN_CPYTHON 0 #define CYTHON_COMPILING_IN_LIMITED_API 0 From 7f6577a36f2ce8c31e8b548e6948f6c1e9fae6d4 Mon Sep 17 00:00:00 2001 From: da-woods Date: Tue, 21 Nov 2023 21:04:46 +0000 Subject: [PATCH 005/286] Fix dataclass comparison operators and enable tests (#5857) They hadn't been following proper tuple comparison semantics so had been giving the wrong result. --- Cython/Compiler/Dataclass.py | 22 +++++----- Tools/make_dataclass_tests.py | 2 - tests/run/test_dataclasses.pyx | 73 ++++++++++++++++++++++++++++++++-- 3 files changed, 82 insertions(+), 15 deletions(-) diff --git a/Cython/Compiler/Dataclass.py b/Cython/Compiler/Dataclass.py index ac25cf7ee92..1b41bf9e6a0 100644 --- a/Cython/Compiler/Dataclass.py +++ b/Cython/Compiler/Dataclass.py @@ -529,7 +529,7 @@ def generate_cmp_code(code, op, funcname, node, fields): code.add_code_lines([ "def %s(self, other):" % funcname, - " if not isinstance(other, %s):" % node.class_name, + " if other.__class__ is not self.__class__:" " return NotImplemented", # " cdef %s other_cast" % node.class_name, @@ -547,17 +547,19 @@ def generate_cmp_code(code, op, funcname, node, fields): # generating the code. Plus, do we want to convert C structs to dicts and # compare them that way (I think not, but it might be in demand)? checks = [] - for name in names: - checks.append("(self.%s %s other_cast.%s)" % ( - name, op, name)) + op_without_equals = op.replace('=', '') - if checks: - code.add_code_line(" return " + " and ".join(checks)) + for name in names: + if op != '==': + # tuple comparison rules - early elements take precedence + code.add_code_line(" if self.%s %s other_cast.%s: return True" % ( + name, op_without_equals, name)) + code.add_code_line(" if self.%s != other_cast.%s: return False" % ( + name, name)) + if "=" in op: + code.add_code_line(" return True") # "() == ()" is True else: - if "=" in op: - code.add_code_line(" return True") # "() == ()" is True - else: - code.add_code_line(" return False") + code.add_code_line(" return False") def generate_eq_code(code, eq, node, fields): diff --git a/Tools/make_dataclass_tests.py b/Tools/make_dataclass_tests.py index dc38eee70f5..22649845ac3 100644 --- a/Tools/make_dataclass_tests.py +++ b/Tools/make_dataclass_tests.py @@ -149,8 +149,6 @@ "TestCase", "test_dataclasses_qualnames", ), # doesn't define __setattr__ and just relies on Cython to enforce readonly properties - ("TestCase", "test_compare_subclasses"), # wrong comparison - ("TestCase", "test_simple_compare"), # wrong comparison ( "TestCase", "test_field_named_self", diff --git a/tests/run/test_dataclasses.pyx b/tests/run/test_dataclasses.pyx index 4daf62cf8f5..67f2371b5c5 100644 --- a/tests/run/test_dataclasses.pyx +++ b/tests/run/test_dataclasses.pyx @@ -64,6 +64,34 @@ class C1_TestCase_test_1_field_compare: class C_TestCase_test_1_field_compare: x: int +@dataclass +@cclass +class C0_TestCase_test_simple_compare: + x: int + y: int + +@dataclass(order=False) +@cclass +class C1_TestCase_test_simple_compare: + x: int + y: int + +@dataclass(order=True) +@cclass +class C_TestCase_test_simple_compare: + x: int + y: int + +@dataclass +@cclass +class B_TestCase_test_compare_subclasses: + i: int + +@dataclass +@cclass +class C_TestCase_test_compare_subclasses(B_TestCase_test_compare_subclasses): + pass + @dataclass @cclass class C_TestCase_test_field_no_default: @@ -648,7 +676,7 @@ class TestCase(unittest.TestCase): for cls in [C0, C1]: with self.subTest(cls=cls): self.assertEqual(cls(), cls()) - for (idx, fn) in enumerate([lambda a, b: a < b, lambda a, b: a <= b, lambda a, b: a > b, lambda a, b: a >= b]): + for idx, fn in enumerate([lambda a, b: a < b, lambda a, b: a <= b, lambda a, b: a > b, lambda a, b: a >= b]): with self.subTest(idx=idx): with self.assertRaises(TypeError): fn(cls(), cls()) @@ -663,7 +691,7 @@ class TestCase(unittest.TestCase): with self.subTest(cls=cls): self.assertEqual(cls(1), cls(1)) self.assertNotEqual(cls(0), cls(1)) - for (idx, fn) in enumerate([lambda a, b: a < b, lambda a, b: a <= b, lambda a, b: a > b, lambda a, b: a >= b]): + for idx, fn in enumerate([lambda a, b: a < b, lambda a, b: a <= b, lambda a, b: a > b, lambda a, b: a >= b]): with self.subTest(idx=idx): with self.assertRaises(TypeError): fn(cls(0), cls(0)) @@ -675,6 +703,45 @@ class TestCase(unittest.TestCase): self.assertGreaterEqual(C(1), C(0)) self.assertGreaterEqual(C(1), C(1)) + def test_simple_compare(self): + C0 = C0_TestCase_test_simple_compare + C1 = C1_TestCase_test_simple_compare + for cls in [C0, C1]: + with self.subTest(cls=cls): + self.assertEqual(cls(0, 0), cls(0, 0)) + self.assertEqual(cls(1, 2), cls(1, 2)) + self.assertNotEqual(cls(1, 0), cls(0, 0)) + self.assertNotEqual(cls(1, 0), cls(1, 1)) + for idx, fn in enumerate([lambda a, b: a < b, lambda a, b: a <= b, lambda a, b: a > b, lambda a, b: a >= b]): + with self.subTest(idx=idx): + with self.assertRaises(TypeError): + fn(cls(0, 0), cls(0, 0)) + C = C_TestCase_test_simple_compare + for idx, fn in enumerate([lambda a, b: a == b, lambda a, b: a <= b, lambda a, b: a >= b]): + with self.subTest(idx=idx): + self.assertTrue(fn(C(0, 0), C(0, 0))) + for idx, fn in enumerate([lambda a, b: a < b, lambda a, b: a <= b, lambda a, b: a != b]): + with self.subTest(idx=idx): + self.assertTrue(fn(C(0, 0), C(0, 1))) + self.assertTrue(fn(C(0, 1), C(1, 0))) + self.assertTrue(fn(C(1, 0), C(1, 1))) + for idx, fn in enumerate([lambda a, b: a > b, lambda a, b: a >= b, lambda a, b: a != b]): + with self.subTest(idx=idx): + self.assertTrue(fn(C(0, 1), C(0, 0))) + self.assertTrue(fn(C(1, 0), C(0, 1))) + self.assertTrue(fn(C(1, 1), C(1, 0))) + + def test_compare_subclasses(self): + B = B_TestCase_test_compare_subclasses + C = C_TestCase_test_compare_subclasses + for idx, (fn, expected) in enumerate([(lambda a, b: a == b, False), (lambda a, b: a != b, True)]): + with self.subTest(idx=idx): + self.assertEqual(fn(B(0), C(0)), expected) + for idx, fn in enumerate([lambda a, b: a < b, lambda a, b: a <= b, lambda a, b: a > b, lambda a, b: a >= b]): + with self.subTest(idx=idx): + with self.assertRaises(TypeError): + fn(B(0), C(0)) + def test_field_no_default(self): C = C_TestCase_test_field_no_default self.assertEqual(C(5).x, 5) @@ -716,7 +783,7 @@ class TestCase(unittest.TestCase): self.assertNotEqual(Point3D(2017, 6, 3), Date(2017, 6, 3)) self.assertNotEqual(Point3D(1, 2, 3), (1, 2, 3)) with self.assertRaises(TypeError): - (x, y, z) = Point3D(4, 5, 6) + x, y, z = Point3D(4, 5, 6) Point3Dv1 = Point3Dv1_TestCase_test_not_other_dataclass self.assertNotEqual(Point3D(0, 0, 0), Point3Dv1()) From 8c505605086d3c293bb5746e911fddc5f90db9d6 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Fri, 24 Nov 2023 12:15:03 +0100 Subject: [PATCH 006/286] Use a type specific len() check function for sequence unpacking. --- Cython/Compiler/ExprNodes.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index afbcb4ace1c..4b509aa1c6b 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -8167,14 +8167,17 @@ def generate_special_parallel_unpacking_code(self, code, rhs, use_loop): none_check = "likely(%s != Py_None)" % rhs.py_result() if rhs.type is list_type: sequence_types = ['List'] + get_size_func = "__Pyx_PyList_GET_SIZE" if rhs.may_be_none(): sequence_type_test = none_check elif rhs.type is tuple_type: sequence_types = ['Tuple'] + get_size_func = "__Pyx_PyTuple_GET_SIZE" if rhs.may_be_none(): sequence_type_test = none_check else: sequence_types = ['Tuple', 'List'] + get_size_func = "__Pyx_PySequence_SIZE" tuple_check = 'likely(PyTuple_CheckExact(%s))' % rhs.py_result() list_check = 'PyList_CheckExact(%s)' % rhs.py_result() sequence_type_test = "(%s) || (%s)" % (tuple_check, list_check) @@ -8183,7 +8186,7 @@ def generate_special_parallel_unpacking_code(self, code, rhs, use_loop): code.putln("PyObject* sequence = %s;" % rhs.py_result()) # list/tuple => check size - code.putln("Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);") + code.putln("Py_ssize_t size = %s(sequence);" % get_size_func) code.putln("if (unlikely(size != %d)) {" % len(self.args)) code.globalstate.use_utility_code( UtilityCode.load_cached("RaiseTooManyValuesToUnpack", "ObjectHandling.c")) From 27095d5222cc77635f0eec3e81c6c461b095002d Mon Sep 17 00:00:00 2001 From: da-woods Date: Sat, 25 Nov 2023 18:12:02 +0000 Subject: [PATCH 007/286] Note on pure-Python mode and Numpy arrays (#5867) We've had a few questions asking about this so I think it makes sense to add a note to the documentation. --- docs/src/tutorial/numpy.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/src/tutorial/numpy.rst b/docs/src/tutorial/numpy.rst index a3a7d01f450..e1eddaf8170 100644 --- a/docs/src/tutorial/numpy.rst +++ b/docs/src/tutorial/numpy.rst @@ -9,6 +9,11 @@ Working with NumPy below, have less overhead, and can be passed around without requiring the GIL. They should be preferred to the syntax presented in this page. See :ref:`Cython for NumPy users `. + +.. NOTE:: There is currently no way to usefully specify Numpy arrays using + Python-style annotations and we do not currently plan to add one. + If you want to use annotation typing then we recommend using + typed memoryviews instead. You can use NumPy from Cython exactly the same as in regular Python, but by doing so you are losing potentially high speedups because Cython has support From 875293e00031ad04e4d4e8539b2a4a0aac60731f Mon Sep 17 00:00:00 2001 From: scoder Date: Sat, 25 Nov 2023 20:36:43 +0100 Subject: [PATCH 008/286] Speed up fstrings by avoiding a tuple for joining the unicode string parts. (GH-5866) --- Cython/Compiler/ExprNodes.py | 130 ++++++++++++++++------------------- Cython/Utility/StringTools.c | 27 ++++++-- 2 files changed, 82 insertions(+), 75 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 4b509aa1c6b..526fcc0e4c6 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -1746,19 +1746,26 @@ def coerce_to_boolean(self, env): bool_value = bool(self.value) return BoolNode(self.pos, value=bool_value, constant_result=bool_value) + def estimate_max_charval(self): + # Most strings will probably be ASCII. + if self.value.isascii(): + return 127 + max_charval = ord(max(self.value)) + if max_charval <= 255: + return 255 + elif max_charval <= 65535: + return 65535 + else: + return 1114111 + def contains_surrogates(self): return StringEncoding.string_contains_surrogates(self.value) def generate_evaluation_code(self, code): if self.type.is_pyobject: - # FIXME: this should go away entirely! - # Since string_contains_lone_surrogates() returns False for surrogate pairs in Py2/UCS2, - # Py2 can generate different code from Py3 here. Let's hope we get away with claiming that - # the processing of surrogate pairs in code was always ambiguous and lead to different results - # on P16/32bit Unicode platforms. if StringEncoding.string_contains_lone_surrogates(self.value): # lone (unpaired) surrogates are not really portable and cannot be - # decoded by the UTF-8 codec in Py3.3 + # decoded by the UTF-8 codec in Py3.3+ self.result_code = code.get_py_const(py_object_type, 'ustring') data_cname = code.get_string_const( StringEncoding.BytesLiteral(self.value.encode('unicode_escape'))) @@ -3581,85 +3588,68 @@ def may_be_none(self): def generate_evaluation_code(self, code): code.mark_pos(self.pos) num_items = len(self.values) - list_var = code.funcstate.allocate_temp(py_object_type, manage_ref=True) - ulength_var = code.funcstate.allocate_temp(PyrexTypes.c_py_ssize_t_type, manage_ref=False) - max_char_var = code.funcstate.allocate_temp(PyrexTypes.c_py_ucs4_type, manage_ref=False) + use_stack_memory = num_items < 32 - code.putln('%s = PyTuple_New(%s); %s' % ( - list_var, - num_items, - code.error_goto_if_null(list_var, self.pos))) - code.put_gotref(list_var, py_object_type) - code.putln("%s = 0;" % ulength_var) - code.putln("%s = 127;" % max_char_var) # at least ASCII character range + unknown_nodes = set() + max_char_value = 127 + for node in self.values: + if isinstance(node, UnicodeNode): + max_char_value = max(max_char_value, node.estimate_max_charval()) + elif isinstance(node, FormattedValueNode) and node.value.type.is_numeric: + # formatted C numbers are always ASCII + pass + else: + unknown_nodes.add(node) - for i, node in enumerate(self.values): + length_parts = [] + charval_parts = [str(max_char_value)] + for node in self.values: node.generate_evaluation_code(code) - node.make_owned_reference(code) - ulength = "__Pyx_PyUnicode_GET_LENGTH(%s)" % node.py_result() - max_char_value = "__Pyx_PyUnicode_MAX_CHAR_VALUE(%s)" % node.py_result() - is_ascii = False if isinstance(node, UnicodeNode): - try: - # most strings will be ASCII or at least Latin-1 - node.value.encode('iso8859-1') - max_char_value = '255' - node.value.encode('us-ascii') - is_ascii = True - except UnicodeEncodeError: - if max_char_value != '255': - # not ISO8859-1 => check BMP limit - max_char = max(map(ord, node.value)) - if max_char < 0xD800: - # BMP-only, no surrogate pairs used - max_char_value = '65535' - ulength = str(len(node.value)) - elif max_char >= 65536: - # clearly outside of BMP, and not on a 16-bit Unicode system - max_char_value = '1114111' - ulength = str(len(node.value)) - else: - # not really worth implementing a check for surrogate pairs here - # drawback: C code can differ when generating on Py2 with 2-byte Unicode - pass - else: - ulength = str(len(node.value)) - elif isinstance(node, FormattedValueNode) and node.value.type.is_numeric: - is_ascii = True # formatted C numbers are always ASCII + length_parts.append(str(len(node.value))) + else: + length_parts.append("__Pyx_PyUnicode_GET_LENGTH(%s)" % node.py_result()) + if node in unknown_nodes: + charval_parts.append("__Pyx_PyUnicode_MAX_CHAR_VALUE(%s)" % node.py_result()) - if not is_ascii: - code.putln("%s = (%s > %s) ? %s : %s;" % ( - max_char_var, max_char_value, max_char_var, max_char_value, max_char_var)) - code.putln("%s += %s;" % (ulength_var, ulength)) + if use_stack_memory: + values_array = code.funcstate.allocate_temp( + PyrexTypes.c_array_type(PyrexTypes.py_object_type, num_items), manage_ref=False) + else: + values_array = code.funcstate.allocate_temp( + PyrexTypes.CPtrType(PyrexTypes.py_object_type), manage_ref=False) + code.putln("%s = (PyObject **) PyMem_Calloc(%d, sizeof(PyObject*));" % (values_array, num_items)) + code.putln("if (unlikely(!%s)) {" % values_array) + code.putln("PyErr_NoMemory(); %s" % code.error_goto(self.pos)) + code.putln("}") - node.generate_giveref(code) - code.putln('#if CYTHON_ASSUME_SAFE_MACROS') - code.putln('PyTuple_SET_ITEM(%s, %s, %s);' % (list_var, i, node.py_result())) - code.putln('#else') - code.put_error_if_neg( - self.pos, - 'PyTuple_SetItem(%s, %s, %s)' % (list_var, i, node.py_result())) - code.putln('#endif') - node.generate_post_assignment_code(code) - node.free_temps(code) + for i, node in enumerate(self.values): + code.putln('%s[%d] = %s;' % (values_array, i, node.py_result())) code.mark_pos(self.pos) self.allocate_temp_result(code) code.globalstate.use_utility_code(UtilityCode.load_cached("JoinPyUnicode", "StringTools.c")) - code.putln('%s = __Pyx_PyUnicode_Join(%s, %d, %s, %s); %s' % ( + code.putln('%s = __Pyx_PyUnicode_Join(%s, %d, %s, %s);' % ( self.result(), - list_var, + values_array, num_items, - ulength_var, - max_char_var, - code.error_goto_if_null(self.py_result(), self.pos))) + ' + '.join(length_parts), + # or-ing isn't entirely correct here since it can produce values > 1114111, + # but we crop that in __Pyx_PyUnicode_Join(). + ' | '.join(charval_parts), + )) + + if not use_stack_memory: + code.putln("PyMem_Free(%s);" % values_array) + code.funcstate.release_temp(values_array) + + code.putln(code.error_goto_if_null(self.py_result(), self.pos)) self.generate_gotref(code) - code.put_decref_clear(list_var, py_object_type) - code.funcstate.release_temp(list_var) - code.funcstate.release_temp(ulength_var) - code.funcstate.release_temp(max_char_var) + for node in self.values: + node.generate_disposal_code(code) + node.free_temps(code) class FormattedValueNode(ExprNode): diff --git a/Cython/Utility/StringTools.c b/Cython/Utility/StringTools.c index dbe484ca96e..a8b12db24c1 100644 --- a/Cython/Utility/StringTools.c +++ b/Cython/Utility/StringTools.c @@ -812,14 +812,14 @@ static CYTHON_INLINE PyObject* __Pyx_PyBytes_Join(PyObject* sep, PyObject* value /////////////// JoinPyUnicode.proto /////////////// -static PyObject* __Pyx_PyUnicode_Join(PyObject* value_tuple, Py_ssize_t value_count, Py_ssize_t result_ulength, +static PyObject* __Pyx_PyUnicode_Join(PyObject** values, Py_ssize_t value_count, Py_ssize_t result_ulength, Py_UCS4 max_char); /////////////// JoinPyUnicode /////////////// //@requires: IncludeStringH //@substitute: naming -static PyObject* __Pyx_PyUnicode_Join(PyObject* value_tuple, Py_ssize_t value_count, Py_ssize_t result_ulength, +static PyObject* __Pyx_PyUnicode_Join(PyObject** values, Py_ssize_t value_count, Py_ssize_t result_ulength, Py_UCS4 max_char) { #if CYTHON_USE_UNICODE_INTERNALS && CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS PyObject *result_uval; @@ -827,6 +827,7 @@ static PyObject* __Pyx_PyUnicode_Join(PyObject* value_tuple, Py_ssize_t value_co Py_ssize_t i, char_pos; void *result_udata; + if (max_char > 1114111) max_char = 1114111; result_uval = PyUnicode_New(result_ulength, max_char); if (unlikely(!result_uval)) return NULL; result_ukind = (max_char <= 255) ? PyUnicode_1BYTE_KIND : (max_char <= 65535) ? PyUnicode_2BYTE_KIND : PyUnicode_4BYTE_KIND; @@ -834,12 +835,15 @@ static PyObject* __Pyx_PyUnicode_Join(PyObject* value_tuple, Py_ssize_t value_co result_udata = PyUnicode_DATA(result_uval); assert(kind_shift == 2 || kind_shift == 1 || kind_shift == 0); + if (unlikely((PY_SSIZE_T_MAX >> kind_shift) - result_ulength < 0)) + goto overflow; + char_pos = 0; for (i=0; i < value_count; i++) { int ukind; Py_ssize_t ulength; void *udata; - PyObject *uval = PyTuple_GET_ITEM(value_tuple, i); + PyObject *uval = values[i]; if (unlikely(__Pyx_PyUnicode_READY(uval))) goto bad; ulength = __Pyx_PyUnicode_GET_LENGTH(uval); @@ -874,10 +878,23 @@ static PyObject* __Pyx_PyUnicode_Join(PyObject* value_tuple, Py_ssize_t value_co return NULL; #else // non-CPython fallback + Py_ssize_t i; + PyObject *result = NULL; + PyObject *value_tuple = PyTuple_New(value_count); + if (unlikely(!value_tuple)) return NULL; CYTHON_UNUSED_VAR(max_char); CYTHON_UNUSED_VAR(result_ulength); - CYTHON_UNUSED_VAR(value_count); - return PyUnicode_Join($empty_unicode, value_tuple); + + for (i=0; i Date: Sat, 25 Nov 2023 20:51:24 +0100 Subject: [PATCH 009/286] Implement return type inference for methods of builtin types (GH-5865) * Also, infer 'str' in Py3 now, since it's a safe type. * Builtin types generally don't return None on slicing, and string types also never do it for indexing, so change "IndexNode.may_be_none()" accordingly. --- Cython/Compiler/Builtin.py | 172 ++++++++++++++++++++++++++- Cython/Compiler/ExprNodes.py | 52 ++++---- Cython/Compiler/Tests/TestBuiltin.py | 32 +++++ Cython/Compiler/TypeInference.py | 9 +- tests/run/bytesmethods.pyx | 4 +- tests/run/strmethods.pyx | 38 +++--- tests/run/strmethods_ll2.pyx | 9 ++ tests/run/type_inference.pyx | 32 ++++- 8 files changed, 296 insertions(+), 52 deletions(-) create mode 100644 Cython/Compiler/Tests/TestBuiltin.py create mode 100644 tests/run/strmethods_ll2.pyx diff --git a/Cython/Compiler/Builtin.py b/Cython/Compiler/Builtin.py index 743a6adba35..55d005f89b6 100644 --- a/Cython/Compiler/Builtin.py +++ b/Cython/Compiler/Builtin.py @@ -302,8 +302,7 @@ def declare_in_type(self, self_type): BuiltinMethod("__mul__", "Tz", "T", "__Pyx_PySequence_Multiply", utility_code=UtilityCode.load("PySequenceMultiply", "ObjectHandling.c")), ]), - ("str", "&PyString_Type", [BuiltinMethod("join", "TO", "O", "__Pyx_PyString_Join", - builtin_return_type='basestring', + ("str", "&PyString_Type", [BuiltinMethod("join", "TO", "T", "__Pyx_PyString_Join", utility_code=UtilityCode.load("StringJoin", "StringTools.c")), BuiltinMethod("__mul__", "Tz", "T", "__Pyx_PySequence_Multiply", utility_code=UtilityCode.load("PySequenceMultiply", "ObjectHandling.c")), @@ -411,6 +410,175 @@ def declare_in_type(self, self_type): }) +inferred_method_return_types = { + 'complex': dict( + conjugate='complex', + ), + 'int': dict( + bit_length='T', + bit_count='T', + to_bytes='bytes', + from_bytes='T', # classmethod + as_integer_ratio='tuple[int,int]', + is_integer='bint', + ), + 'float': dict( + as_integer_ratio='tuple[int,int]', + is_integer='bint', + hex='unicode', + fromhex='T', # classmethod + ), + 'list': dict( + index='Py_ssize_t', + count='Py_ssize_t', + ), + 'unicode': dict( + capitalize='T', + casefold='T', + center='T', + count='Py_ssize_t', + encode='bytes', + endswith='bint', + expandtabs='T', + find='Py_ssize_t', + format='T', + format_map='T', + index='Py_ssize_t', + isalnum='bint', + isalpha='bint', + isascii='bint', + isdecimal='bint', + isdigit='bint', + isidentifier='bint', + islower='bint', + isnumeric='bint', + isprintable='bint', + isspace='bint', + istitle='bint', + isupper='bint', + join='T', + ljust='T', + lower='T', + lstrip='T', + maketrans='dict[int,object]', # staticmethod + partition='tuple[T,T,T]', + removeprefix='T', + removesuffix='T', + replace='T', + rfind='Py_ssize_t', + rindex='Py_ssize_t', + rjust='T', + rpartition='tuple[T,T,T]', + rsplit='list[T]', + rstrip='T', + split='list[T]', + splitlines='list[T]', + startswith='bint', + strip='T', + swapcase='T', + title='T', + translate='T', + upper='T', + zfill='T', + ), + 'bytes': dict( + hex='unicode', + fromhex='T', # classmethod + count='Py_ssize_t', + removeprefix='T', + removesuffix='T', + decode='unicode', + endswith='bint', + find='Py_ssize_t', + index='Py_ssize_t', + join='T', + maketrans='bytes', # staticmethod + partition='tuple[T,T,T]', + replace='T', + rfind='Py_ssize_t', + rindex='Py_ssize_t', + rpartition='tuple[T,T,T]', + startswith='bint', + translate='T', + center='T', + ljust='T', + lstrip='T', + rjust='T', + rsplit='list[T]', + rstrip='T', + split='list[T]', + strip='T', + capitalize='T', + expandtabs='T', + isalnum='bint', + isalpha='bint', + isascii='bint', + isdigit='bint', + islower='bint', + isspace='bint', + istitle='bint', + isupper='bint', + lower='T', + splitlines='list[T]', + swapcase='T', + title='T', + upper='T', + zfill='T', + ), + 'bytearray': dict( + # Inherited from 'bytes' below. + ), + 'memoryview': dict( + tobytes='bytes', + hex='unicode', + tolist='list', + toreadonly='T', + cast='T', + ), + 'set': dict( + isdisjoint='bint', + isubset='bint', + issuperset='bint', + union='T', + intersection='T', + difference='T', + symmetric_difference='T', + copy='T', + ), + 'frozenset': dict( + # Inherited from 'set' below. + ), + 'dict': dict( + copy='T', + ), +} + +inferred_method_return_types['bytearray'].update(inferred_method_return_types['bytes']) +inferred_method_return_types['frozenset'].update(inferred_method_return_types['set']) +inferred_method_return_types['str'] = inferred_method_return_types['unicode'] + + +def find_return_type_of_builtin_method(builtin_type, method_name): + type_name = builtin_type.name + if type_name in inferred_method_return_types: + methods = inferred_method_return_types[type_name] + if method_name in methods: + return_type_name = methods[method_name] + if '[' in return_type_name: + # TODO: Keep the "[...]" part when we add support for generics. + return_type_name = return_type_name.partition('[')[0] + if return_type_name == 'T': + return builtin_type + if 'T' in return_type_name: + return_type_name = return_type_name.replace('T', builtin_type.name) + if return_type_name == 'bint': + return PyrexTypes.c_bint_type + elif return_type_name == 'Py_ssize_t': + return PyrexTypes.c_py_ssize_t_type + return builtin_scope.lookup(return_type_name).type + return PyrexTypes.py_object_type + + builtin_structs_table = [ ('Py_buffer', 'Py_buffer', [("buf", PyrexTypes.c_void_ptr_type), diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 526fcc0e4c6..6fde12a078e 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -3877,10 +3877,13 @@ def may_be_none(self): if base_type: if base_type.is_string: return False + if base_type in (unicode_type, bytes_type, str_type, bytearray_type, basestring_type): + return False if isinstance(self.index, SliceNode): # slicing! - if base_type in (bytes_type, bytearray_type, str_type, unicode_type, - basestring_type, list_type, tuple_type): + if base_type.is_builtin_type: + # It seems that none of the builtin types can return None for "__getitem__[slice]". + # Slices are not hashable, and thus cannot be used as key in dicts, for example. return False return ExprNode.may_be_none(self) @@ -5845,8 +5848,15 @@ class CallNode(ExprNode): may_return_none = None def infer_type(self, env): - # TODO(robertwb): Reduce redundancy with analyse_types. function = self.function + if function.is_attribute: + method_obj_type = function.obj.infer_type(env) + if method_obj_type.is_builtin_type: + result_type = Builtin.find_return_type_of_builtin_method(method_obj_type, function.attribute) + if result_type is not py_object_type: + return result_type + + # TODO(robertwb): Reduce redundancy with analyse_types. func_type = function.infer_type(env) if isinstance(function, NewExprNode): # note: needs call to infer_type() above @@ -5932,6 +5942,16 @@ def set_py_result_type(self, function, func_type=None): self.type = function.type_entry.type self.result_ctype = py_object_type self.may_return_none = False + elif function.is_attribute and function.obj.type.is_builtin_type: + method_obj_type = function.obj.type + result_type = Builtin.find_return_type_of_builtin_method(method_obj_type, function.attribute) + self.may_return_none = result_type is py_object_type + if result_type.is_pyobject: + self.type = result_type + elif result_type.equivalent_type: + self.type = result_type.equivalent_type + else: + self.type = py_object_type else: self.type = py_object_type @@ -11952,8 +11972,8 @@ def is_py_operation_types(self, type1, type2): def infer_builtin_types_operation(self, type1, type2): # b'abc' + 'abc' raises an exception in Py3, - # so we can safely infer the Py2 type for bytes here - string_types = (bytes_type, bytearray_type, str_type, basestring_type, unicode_type) + # so we can safely infer a mix here. + string_types = (bytes_type, bytearray_type, basestring_type, str_type, unicode_type) if type1 in string_types and type2 in string_types: return string_types[max(string_types.index(type1), string_types.index(type2))] @@ -12326,25 +12346,11 @@ def is_py_operation_types(self, type1, type2): or NumBinopNode.is_py_operation_types(self, type1, type2)) def infer_builtin_types_operation(self, type1, type2): - # b'%s' % xyz raises an exception in Py3<3.5, so it's safe to infer the type for Py2 and later Py3's. - if type1 is unicode_type: - # None + xyz may be implemented by RHS - if type2.is_builtin_type or not self.operand1.may_be_none(): - return type1 - elif type1 in (bytes_type, str_type, basestring_type): - if type2 is unicode_type: - return type2 - elif type2.is_numeric: + # b'%s' % xyz raises an exception in Py3<3.5, so it's safe to infer the type for later Py3's. + if type1 in (unicode_type, bytes_type, str_type, basestring_type): + # 'None % xyz' may be implemented by the RHS, but everything else will do string formatting. + if type2.is_builtin_type or not type2.is_pyobject or not self.operand1.may_be_none(): return type1 - elif self.operand1.is_string_literal: - if type1 is str_type or type1 is bytes_type: - if set(_find_formatting_types(self.operand1.value)) <= _safe_bytes_formats: - return type1 - return basestring_type - elif type1 is bytes_type and not type2.is_builtin_type: - return None # RHS might implement '% operator differently in Py3 - else: - return basestring_type # either str or unicode, can't tell return super().infer_builtin_types_operation(type1, type2) def zero_division_message(self): diff --git a/Cython/Compiler/Tests/TestBuiltin.py b/Cython/Compiler/Tests/TestBuiltin.py new file mode 100644 index 00000000000..ebc5278a328 --- /dev/null +++ b/Cython/Compiler/Tests/TestBuiltin.py @@ -0,0 +1,32 @@ +import builtins +import sys +import unittest + +from ..Builtin import ( + inferred_method_return_types, find_return_type_of_builtin_method, + builtin_scope, +) + + +class TestBuiltinReturnTypes(unittest.TestCase): + def test_find_return_type_of_builtin_method(self): + # It's enough to test the method existence in a recent Python that likely has them. + look_up_methods = sys.version_info >= (3,10) + + for type_name, methods in inferred_method_return_types.items(): + py_type = getattr(builtins, type_name if type_name != 'unicode' else 'str') + + for method_name, return_type_name in methods.items(): + builtin_type = builtin_scope.lookup(type_name).type + return_type = find_return_type_of_builtin_method(builtin_type, method_name) + + if return_type.is_builtin_type: + if '[' in return_type_name: + return_type_name = return_type_name.partition('[')[0] + if return_type_name == 'T': + return_type_name = type_name + self.assertEqual(return_type.name, return_type_name) + if look_up_methods: + self.assertTrue(hasattr(py_type, method_name), f"{type_name}.{method_name}") + else: + self.assertEqual(return_type.empty_declaration_code(pyrex=True), return_type_name) diff --git a/Cython/Compiler/TypeInference.py b/Cython/Compiler/TypeInference.py index d40d191534e..ffd48d45179 100644 --- a/Cython/Compiler/TypeInference.py +++ b/Cython/Compiler/TypeInference.py @@ -544,14 +544,7 @@ def aggressive_spanning_type(types, might_overflow, scope): def safe_spanning_type(types, might_overflow, scope): result_type = simply_type(reduce(find_spanning_type, types)) if result_type.is_pyobject: - # In theory, any specific Python type is always safe to - # infer. However, inferring str can cause some existing code - # to break, since we are also now much more strict about - # coercion from str to char *. See trac #553. - if result_type.name == 'str': - return py_object_type - else: - return result_type + return result_type elif (result_type is PyrexTypes.c_double_type or result_type is PyrexTypes.c_float_type): # Python's float type is just a C double, so it's safe to use diff --git a/tests/run/bytesmethods.pyx b/tests/run/bytesmethods.pyx index 5973a7334d4..ac4e9b719d0 100644 --- a/tests/run/bytesmethods.pyx +++ b/tests/run/bytesmethods.pyx @@ -259,7 +259,7 @@ def bytes_join(bytes s, *args): babab """ result = s.join(args) - assert cython.typeof(result) == 'Python object', cython.typeof(result) + assert cython.typeof(result) == 'bytes object', cython.typeof(result) return result @@ -275,7 +275,7 @@ def literal_join(*args): b|b|b|b """ result = b'|'.join(args) - assert cython.typeof(result) == 'Python object', cython.typeof(result) + assert cython.typeof(result) == 'bytes object', cython.typeof(result) return result def fromhex(bytes b): diff --git a/tests/run/strmethods.pyx b/tests/run/strmethods.pyx index 58d7a7801ef..77730929cf8 100644 --- a/tests/run/strmethods.pyx +++ b/tests/run/strmethods.pyx @@ -1,5 +1,15 @@ +# mode: run + +# cython: language_level=3 + cimport cython +# Also used by the language_level=2 tests in "strmethods_ll2.pyx" +assert cython.typeof(1 / 2) in ('long', 'double') +IS_LANGUAGE_LEVEL_3 = cython.typeof(1 / 2) == 'double' +str_type = "unicode object" if IS_LANGUAGE_LEVEL_3 else "str object" + + @cython.test_assert_path_exists( "//PythonCapiCallNode") def str_startswith(str s, sub, start=None, stop=None): @@ -75,33 +85,31 @@ def str_as_name(str): return str.endswith("x") -@cython.test_assert_path_exists( - "//SimpleCallNode", - "//SimpleCallNode//NoneCheckNode", - "//SimpleCallNode//AttributeNode[@is_py_attr = false]") +#@cython.test_fail_if_path_exists( +# "//SimpleCallNode", +# "//SimpleCallNode//NoneCheckNode", +# "//SimpleCallNode//AttributeNode[@is_py_attr = false]") def str_join(str s, args): """ >>> print(str_join('a', list('bbb'))) babab """ result = s.join(args) - assert cython.typeof(result) == 'basestring object', cython.typeof(result) + assert cython.typeof(result) == str_type, (cython.typeof(result), str_type) return result -@cython.test_fail_if_path_exists( - "//SimpleCallNode//NoneCheckNode", -) -@cython.test_assert_path_exists( - "//SimpleCallNode", - "//SimpleCallNode//AttributeNode[@is_py_attr = false]") +#@cython.test_fail_if_path_exists( +# "//SimpleCallNode", +# "//SimpleCallNode//NoneCheckNode", +# "//SimpleCallNode//AttributeNode[@is_py_attr = false]") def literal_join(args): """ >>> print(literal_join(list('abcdefg'))) a|b|c|d|e|f|g """ result = '|'.join(args) - assert cython.typeof(result) == 'basestring object', cython.typeof(result) + assert cython.typeof(result) == str_type, (cython.typeof(result), str_type) return result @@ -125,7 +133,7 @@ def mod_format(str s, values): >>> mod_format(None, RMod()) 123 """ - assert cython.typeof(s % values) == 'basestring object', cython.typeof(s % values) + assert cython.typeof(s % values) == "Python object", cython.typeof(s % values) return s % values @@ -138,7 +146,7 @@ def mod_format_literal(values): >>> mod_format_literal(['sa']) == "abc['sa']def" or mod_format(format1, ['sa']) True """ - assert cython.typeof('abc%sdef' % values) == 'basestring object', cython.typeof('abc%sdef' % values) + assert cython.typeof('abc%sdef' % values) == str_type, (cython.typeof('abc%sdef' % values), str_type) return 'abc%sdef' % values @@ -150,5 +158,5 @@ def mod_format_tuple(*values): Traceback (most recent call last): TypeError: not enough arguments for format string """ - assert cython.typeof('abc%sdef' % values) == 'basestring object', cython.typeof('abc%sdef' % values) + assert cython.typeof('abc%sdef' % values) == str_type, (cython.typeof('abc%sdef' % values), str_type) return 'abc%sdef' % values diff --git a/tests/run/strmethods_ll2.pyx b/tests/run/strmethods_ll2.pyx new file mode 100644 index 00000000000..63faae059a5 --- /dev/null +++ b/tests/run/strmethods_ll2.pyx @@ -0,0 +1,9 @@ +# mode: run + +# cython: language_level=2 + +""" +Same tests as 'strmethods.pyx', but using 'language_level=2'. +""" + +include "strmethods.pyx" diff --git a/tests/run/type_inference.pyx b/tests/run/type_inference.pyx index feb18817e6f..595ef1297be 100644 --- a/tests/run/type_inference.pyx +++ b/tests/run/type_inference.pyx @@ -270,6 +270,35 @@ def builtin_type_methods(): append(1) assert l == [1], str(l) + u = u'abc def' + split = u.split() + assert typeof(split) == 'list object', typeof(split) + + str_result1 = u.upper() + assert typeof(str_result1) == 'unicode object', typeof(str_result1) + str_result2 = u.upper().lower() + assert typeof(str_result2) == 'unicode object', typeof(str_result2) + str_result3 = u.upper().lower().strip() + assert typeof(str_result3) == 'unicode object', typeof(str_result3) + str_result4 = u.upper().lower().strip().lstrip() + assert typeof(str_result4) == 'unicode object', typeof(str_result4) + str_result5 = u.upper().lower().strip().lstrip().rstrip() + assert typeof(str_result5) == 'unicode object', typeof(str_result5) + str_result6 = u.upper().lower().strip().lstrip().rstrip().center(20) + assert typeof(str_result6) == 'unicode object', typeof(str_result6) + str_result7 = u.upper().lower().strip().lstrip().rstrip().center(20).format() + assert typeof(str_result7) == 'unicode object', typeof(str_result7) + str_result8 = u.upper().lower().strip().lstrip().rstrip().center(20).format().expandtabs(4) + assert typeof(str_result8) == 'unicode object', typeof(str_result8) + str_result9 = u.upper().lower().strip().lstrip().rstrip().center(20).format().expandtabs(4).swapcase() + assert typeof(str_result9) == 'unicode object', typeof(str_result9) + + predicate1 = u.isupper() + assert typeof(predicate1) == 'bint', typeof(predicate1) + predicate2 = u.istitle() + assert typeof(predicate2) == 'bint', typeof(predicate2) + + cdef int cfunc(int x): return x+1 @@ -544,9 +573,8 @@ def safe_only(): div_res = pyint_val / 7 assert typeof(div_res) == ("double" if IS_LANGUAGE_LEVEL_3 else "Python object"), typeof(div_res) - # we special-case inference to type str s = "abc" - assert typeof(s) == ("unicode object" if IS_LANGUAGE_LEVEL_3 else "Python object"), (typeof(s), str_type) + assert typeof(s) == str_type, (typeof(s), str_type) cdef str t = "def" assert typeof(t) == str_type, (typeof(t), str_type) From fd8fb85bb1f1e8a5b9f8a4b5a2014f2e0765a11a Mon Sep 17 00:00:00 2001 From: Matus Valo Date: Sat, 25 Nov 2023 21:06:41 +0100 Subject: [PATCH 010/286] [Docs] Provide tables listing basic cython types (#4980) --- docs/src/userguide/language_basics.rst | 64 ++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/docs/src/userguide/language_basics.rst b/docs/src/userguide/language_basics.rst index f45d6e12bae..c1db172e309 100644 --- a/docs/src/userguide/language_basics.rst +++ b/docs/src/userguide/language_basics.rst @@ -117,18 +117,20 @@ the declaration in most cases: f: cython.float = 2.5 g: cython.int[4] = [1, 2, 3, 4] h: cython.p_float = cython.address(f) + c: cython.doublecomplex = 2 + 3j .. group-tab:: Cython .. code-block:: cython - cdef int a_global_variable + cdef int a_global_variable = 42 def func(): cdef int i = 10, j, k cdef float f = 2.5 cdef int[4] g = [1, 2, 3, 4] cdef float *h = &f + cdef double complex c = 2 + 3j .. note:: @@ -289,7 +291,64 @@ Types The Cython language uses the normal C syntax for C types, including pointers. It provides all the standard C types, namely ``char``, ``short``, ``int``, ``long``, ``long long`` as well as their ``unsigned`` versions, -e.g. ``unsigned int`` (``cython.uint`` in Python code). +e.g. ``unsigned int`` (``cython.uint`` in Python code): + + +.. list-table:: Numeric Types + :widths: 25 25 + :header-rows: 1 + + * - Cython type + - Pure Python type + + * - ``bint`` + - ``cython.bint`` + * - ``char`` + - ``cython.char`` + * - ``signed char`` + - ``cython.schar`` + * - ``unsigned char`` + - ``cython.uchar`` + * - ``short`` + - ``cython.short`` + * - ``unsigned short`` + - ``cython.ushort`` + * - ``int`` + - ``cython.int`` + * - ``unsigned int`` + - ``cython.uint`` + * - ``long`` + - ``cython.long`` + * - ``unsigned long`` + - ``cython.ulong`` + * - ``long long`` + - ``cython.longlong`` + * - ``unsigned long long`` + - ``cython.ulonglong`` + * - ``float`` + - ``cython.float`` + * - ``double`` + - ``cython.double`` + * - ``long double`` + - ``cython.longdouble`` + * - ``float complex`` + - ``cython.floatcomplex`` + * - ``double complex`` + - ``cython.doublecomplex`` + * - ``long double complex`` + - ``cython.longdoublecomplex`` + * - ``size_t`` + - ``cython.size_t`` + * - ``Py_ssize_t`` + - ``cython.Py_ssize_t`` + * - ``Py_hash_t`` + - ``cython.Py_hash_t`` + * - ``Py_UCS4`` + - ``cython.Py_UCS4`` + +.. note:: + Additional types are declared in the `stdint pxd file `_. + The special ``bint`` type is used for C boolean values (``int`` with 0/non-0 values for False/True) and ``Py_ssize_t`` for (signed) sizes of Python containers. @@ -300,7 +359,6 @@ use a naming scheme with "p"s instead, separated from the type name with an unde a pointer to a C int. Further pointer types can be constructed with the ``cython.pointer()`` function, e.g. ``cython.pointer(cython.int)``. - Arrays use the normal C array syntax, e.g. ``int[10]``, and the size must be known at compile time for stack allocated arrays. Cython doesn't support variable length arrays from C99. Note that Cython uses array access for pointer dereferencing, as ``*x`` is not valid Python syntax, From b7f4e3de46b42dd984f0d99138299498075d3a13 Mon Sep 17 00:00:00 2001 From: da-woods Date: Sun, 26 Nov 2023 11:09:21 +0000 Subject: [PATCH 011/286] Document user-selectable C macros (#5864) I've shown the 4 that are most user-relevant clearly, and hidden most of the rest by default (but documented them if people really want to read them). --- docs/src/userguide/external_C_code.rst | 2 + .../source_files_and_compilation.rst | 119 ++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/docs/src/userguide/external_C_code.rst b/docs/src/userguide/external_C_code.rst index 6bd20dc9268..4abb53f4701 100644 --- a/docs/src/userguide/external_C_code.rst +++ b/docs/src/userguide/external_C_code.rst @@ -498,6 +498,8 @@ file consists of the full dotted name of the module, e.g. a module called the resulting ``.so`` file like a dynamic library. Beware that this is not portable, so it should be avoided. +.. _CYTHON_EXTERN_C: + C++ public declarations ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/src/userguide/source_files_and_compilation.rst b/docs/src/userguide/source_files_and_compilation.rst index 4ac8a8af6d5..239199b81f2 100644 --- a/docs/src/userguide/source_files_and_compilation.rst +++ b/docs/src/userguide/source_files_and_compilation.rst @@ -1123,3 +1123,122 @@ argument to ``cythonize``:: This will override the default directives as specified in the ``compiler_directives`` dictionary. Note that explicit per-file or local directives as explained above take precedence over the values passed to ``cythonize``. + +C macro defines +=============== + +Cython has a number of C macros that can be used to control compilation. Typically, these +would be set using ``extra_compile_args`` in `setup.py` (for example +``extra_compile_args=['-DCYTHON_USE_TYPE_SPECS=1']``), however they can also be set in +other ways like using the ``CFLAGS`` environmental variable. + +These macros are set automatically by Cython to sensible default values unless +you chose to explicitly override them, so they are a tool that most users +can happily ignore. Not all combinations of macros are compatible or tested, and +some change the default value of other macros. They are listed below in rough order from +most important to least important: + +``CYTHON_LIMITED_API`` + Turns on Cython's experimental Limited API support, meaning that one compiled module + can be used by many Python interpreter versions (at the cost of some performance). + At this stage many features do not work in the Limited API. If you use this macro + you should also set the macro ``Py_LIMITED_API`` to be the version hex for the + minimum Python version you want to support (>=3.7). ``0x03070000`` will support + Python 3.7 upwards. + +``CYTHON_PEP489_MULTI_PHASE_INIT`` + Uses multi-phase module initialization as described in PEP489. This improves + Python compatibility, especially when running the initial import of the code when it + makes attributes such as ``__file__`` available. It is therefore on by default + where supported. + +``CYTHON_USE_MODULE_STATE`` + Stores module data on a struct associated with the module object rather than as + C global variables. The advantage is that it should be possible to import the + same module more than once (e.g. in different sub-interpreters). At the moment + this is experimental and not all data has been moved. It also requires that + ``CYTHON_PEP489_MULTI_PHASE_INIT`` is off - we plan to remove this limitation + in the future. + +``CYTHON_USE_TYPE_SPECS`` + Defines ``cdef classes`` as `"heap types" `_ + rather than "static types". Practically this does not change a lot from a user + point of view, but it is needed to implement Limited API support. + +``CYTHON_EXTERN_C`` + Slightly different to the other macros, this controls how ``cdef public`` + functions appear to C++ code. See :ref:`CYTHON_EXTERN_C` for full details. + +There is a further list of macros which turn off various optimizations or language +features. Under normal circumstance Cython enables these automatically based on the +version of Python you are compiling for so there is no need to use them +to try to enable extra optimizations - all supported optimizations are enabled by +default. These are mostly relevant if you're tying to get Cython working in a +new and unsupported Python interpreter where you will typically want to set +them to 0 to *disable* optimizations. They are listed below for completeness but +hidden by default since most users will be uninterested in changing them. + +.. tabs:: + .. tab:: Hide + + .. tab:: Show + + ``CYTHON_USE_TYPE_SLOTS`` + If enabled, Cython will directly access members of the ``PyTypeObject`` + struct. + + ``CYTHON_USE_PYTYPE_LOOKUP`` + Use the internal `_PyType_Lookup()` function for more efficient access + to properties of C classes. + + ``CYTHON_USE_ASYNC_SLOTS`` + Support the ``tp_as_async`` attribute on type objects. + + ``CYTHON_USE_PYLONG_INTERNALS``/``CYTHON_USE_PYLIST_INTERNALS``/``CYTHON_USE_UNICODE_INTERNALS`` + Enable optimizations based on direct access into the internals of Python + ``int``/``list``/``unicode`` objects respectively. + + ``CYTHON_USE_UNICODE_WRITER`` + Use a faster (but internal) mechanism for building unicode strings, for + example in f-strings. + + ``CYTHON_AVOID_BORROWED_REFS`` + Avoid using "borrowed references" and ensure that Cython always holds + a reference to objects it manipulates. Most useful for + non-reference-counted implementations of Python, like PyPy + (where it is enabled by default). + + ``CYTHON_ASSUME_SAFE_MACROS`` + Use some C-API macros that increase performance by skipping error checking, + which may not be safe on all Python implementations (e.g. PyPy). + + ``CYTHON_FAST_GIL`` + On some Python versions this speeds up getting/releasing the GIL. + + ``CYTHON_UNPACK_METHODS`` + Try to speed up method calls at the cost of code-size. Linked to + the ``optimize.unpack_method_calls`` compiler directive - this macro + is used to selectively enable the compiler directive only on versions + of Python that support it. + + ``CYTHON_METH_FASTCALL``/``CYTHON_FAST_PYCALL`` + These are used internally to incrementally enable the vectorcall calling + mechanism on older Python versions (<3.8). + + ``CYTHON_PEP487_INIT_SUBCLASS`` + Enable `PEP-487 `_ ``__init_subclass__`` behaviour. + + ``CYTHON_USE_TP_FINALIZE`` + Use the ``tp_finalize`` type-slot instead of ``tp_dealloc``, + as described in `PEP-442 `_. + + ``CYTHON_USE_DICT_VERSIONS`` + Try to optimize attribute lookup by using versioned dictionaries + where supported. + + ``CYTHON_USE_EXC_INFO_STACK`` + Use an internal structure to track exception state, + used in CPython 3.7 and later. + + ``CYTHON_UPDATE_DESCRIPTOR_DOC`` + Attempt to provide docstrings also for special (double underscore) methods. From cb1d78b5e2a2d96a41e634c9ac670dc80ec75af4 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Sun, 26 Nov 2023 12:50:20 +0100 Subject: [PATCH 012/286] Update changelog. --- CHANGES.rst | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 2ee9886bb0d..e1fd6e14ad8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,9 +10,24 @@ Features added * Fused def function dispatch is a bit faster. +* Declarations for the ``wchar`` PyUnicode API were added. + (Github issue :issue:`5836`) + +* The Python "nogil" fork is now also detected with the new ``Py_GIL_DISABLED`` macro. + Patch by Hugo van Kemenade (Github issue :issue:`583652`) + Bugs fixed ---------- +* Comparing dataclasses could give different results than Python. + (Github issue :issue:`5857`) + +* ``float(std::string)`` generated invalid C code. + (Github issue :issue:`5818`) + +* Using ``cpdef`` functions with ``cimport_from_pyx`` failed. + (Github issue :issue:`5795`) + * A crash was fixed when string-formatting a Python value fails. (Github issue :issue:`5787`) @@ -23,12 +38,21 @@ Bugs fixed * A C compiler warning was resolved. (Github issue :issue:`5794`) +* Complex numbers failed to compile in MSVC with C11. + Patch by Lysandros Nikolaou. (Github issue :issue:`5809`) + * Some issues with the Limited API and with PyPy were resolved. (Github issues :issue:`5695`, :issue:`5696`) * A C++ issue in Python 3.13 was resolved. (Github issue :issue:`5790`) +* Several directives are now also available (as no-ops) in Python code. + (Github issue :issue:`5803`) + +* An error message was corrected. + Patch by Mads Ynddal. (Github issue :issue:`5805`) + 3.0.5 (2023-10-31) ================== From 248655889fe9ae554d12f5bb17cb61eb9878c99f Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Sun, 26 Nov 2023 12:51:01 +0100 Subject: [PATCH 013/286] Prepare release of 3.0.6. --- CHANGES.rst | 2 +- Cython/Shadow.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index e1fd6e14ad8..692771368a0 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,7 +2,7 @@ Cython Changelog ================ -3.0.6 (2023-??-??) +3.0.6 (2023-11-26) ================== Features added diff --git a/Cython/Shadow.py b/Cython/Shadow.py index f822bf175e7..e56ae00b41f 100644 --- a/Cython/Shadow.py +++ b/Cython/Shadow.py @@ -2,7 +2,7 @@ from __future__ import absolute_import # Possible version formats: "3.1.0", "3.1.0a1", "3.1.0a1.dev0" -__version__ = "3.0.5" +__version__ = "3.0.6" try: from __builtin__ import basestring From 8289efd793e2a43f88c548c071c6afb467615e45 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Sun, 26 Nov 2023 14:57:43 +0100 Subject: [PATCH 014/286] Update the list of authors in the package meta data to reflect the current project status. --- setup.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index a2fe7929194..da679ce7458 100755 --- a/setup.py +++ b/setup.py @@ -247,7 +247,7 @@ def run_build(): name='Cython', version=version, url='https://cython.org/', - author='Robert Bradshaw, Stefan Behnel, Dag Seljebotn, Greg Ewing, et al.', + author='Robert Bradshaw, Stefan Behnel, David Woods, Greg Ewing, et al.', author_email='cython-devel@python.org', description="The Cython compiler for writing C extensions in the Python language.", long_description=textwrap.dedent("""\ @@ -264,12 +264,12 @@ def run_build(): This makes Cython the ideal language for writing glue code for external C/C++ libraries, and for fast C modules that speed up the execution of Python code. - - The newest Cython release can always be downloaded from https://cython.org/. + + The newest Cython release can always be downloaded from https://cython.org/. Unpack the tarball or zip file, enter the directory, and then run:: - + pip install . - + Note that for one-time builds, e.g. for CI/testing, on platforms that are not covered by one of the wheel packages provided on PyPI *and* the pure Python wheel that we provide is not used, it is substantially faster than a full source build From 06f155984d80652f2b26997b09aa42a50da7c3a2 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Sun, 26 Nov 2023 15:02:41 +0100 Subject: [PATCH 015/286] build: Do not generate a universal py2+py3 wheel but only one for Py3. --- Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index ab714dfb2c3..ab17516c1ab 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ PACKAGENAME=Cython -PYTHON?=python +PYTHON?=python3 TESTOPTS?= REPO = git://github.com/cython/cython.git VERSION?=$(shell sed -ne 's|^__version__\s*=\s*"\([^"]*\)".*|\1|p' Cython/Shadow.py) @@ -33,11 +33,11 @@ sdist: dist/$(PACKAGENAME)-$(VERSION).tar.gz dist/$(PACKAGENAME)-$(VERSION).tar.gz: $(PYTHON) setup.py sdist -pywheel: dist/$(PACKAGENAME)-$(VERSION)-py2.py3-none-any.whl +pywheel: dist/$(PACKAGENAME)-$(VERSION)-py3-none-any.whl -dist/$(PACKAGENAME)-$(VERSION)-py2.py3-none-any.whl: - ${PYTHON} setup.py bdist_wheel --no-cython-compile --universal - [ -f "$@" ] # check that we generated the expected universal wheel +dist/$(PACKAGENAME)-$(VERSION)-py3-none-any.whl: + ${PYTHON} setup.py bdist_wheel --no-cython-compile + [ -f "$@" ] # check that we generated the expected Py3-only wheel TMPDIR = .repo_tmp .git: .gitrev From 125e0be329261c67b242cccc817e3161627fd803 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Mon, 27 Nov 2023 10:29:57 +0100 Subject: [PATCH 016/286] Set the encoding used in EndToEnd tests to UTF-8 on all platforms since we read it from a pipe anyway and don't want to care about platform specific encoding issues. --- runtests.py | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/runtests.py b/runtests.py index 2146c98c515..4d422a4c53b 100755 --- a/runtests.py +++ b/runtests.py @@ -1990,24 +1990,13 @@ def tearDown(self): break os.chdir(self.old_dir) - def _try_decode(self, content): - if not isinstance(content, bytes): - return content - try: - return content.decode() - except UnicodeDecodeError: - return content.decode('iso-8859-1') - def runTest(self): self.success = False old_path = os.environ.get('PYTHONPATH') - env = dict(os.environ) new_path = self.cython_syspath if old_path: new_path = new_path + os.pathsep + self.workdir + os.pathsep + old_path - env['PYTHONPATH'] = new_path - if not env.get("PYTHONIOENCODING"): - env["PYTHONIOENCODING"] = sys.stdout.encoding or sys.getdefaultencoding() + env = dict(os.environ, PYTHONPATH=new_path, PYTHONIOENCODING='utf8') cmd = [] out = [] err = [] @@ -2023,21 +2012,21 @@ def runTest(self): _out, _err = b'', b'' res = p cmd.append(command) - out.append(_out) - err.append(_err) + out.append(_out.decode('utf-8')) + err.append(_err.decode('utf-8')) if res == 0 and b'REFNANNY: ' in _out: res = -1 if res != 0: for c, o, e in zip(cmd, out, err): sys.stderr.write("[%d] %s\n%s\n%s\n\n" % ( - self.shard_num, c, self._try_decode(o), self._try_decode(e))) + self.shard_num, c, o, e)) sys.stderr.write("Final directory layout of '%s':\n%s\n\n" % ( self.name, '\n'.join(os.path.join(dirpath, filename) for dirpath, dirs, files in os.walk(".") for filename in files), )) self.assertEqual(0, res, "non-zero exit status, last output was:\n%r\n-- stdout:%s\n-- stderr:%s\n" % ( - ' '.join(command), self._try_decode(out[-1]), self._try_decode(err[-1]))) + ' '.join(command), out[-1], err[-1])) self.success = True From 16b87cfacaa4835020166d2f87fee80e943fd61e Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Mon, 27 Nov 2023 11:12:01 +0100 Subject: [PATCH 017/286] Make the time reporting in EndToEnd tests correctly map cython/cythonize script calls to the "etoe-build" stats bucket instead of the generic "etoe-run" category. --- runtests.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/runtests.py b/runtests.py index 4d422a4c53b..74d50d5fdea 100755 --- a/runtests.py +++ b/runtests.py @@ -2001,8 +2001,9 @@ def runTest(self): out = [] err = [] for command_no, command in enumerate(self.commands, 1): - with self.stats.time('%s(%d)' % (self.name, command_no), 'c', - 'etoe-build' if 'setup.py' in command else 'etoe-run'): + time_category = 'etoe-build' if ( + 'setup.py' in command or 'cythonize.py' in command or 'cython.py' in command) else 'etoe-run' + with self.stats.time('%s(%d)' % (self.name, command_no), 'c', time_category): if self.capture: p = subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE, env=env) _out, _err = p.communicate() From f0bc01d105cdb11440d33536c46feffe803083cc Mon Sep 17 00:00:00 2001 From: da-woods Date: Tue, 28 Nov 2023 08:40:00 +0000 Subject: [PATCH 018/286] Avoid writing non-latin1 module names in the file (GH-5874) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … and avoid potentially failing to do so. Closes https://github.com/cython/cython/issues/5873 --- Cython/Compiler/ModuleNode.py | 2 +- tests/run/unicode_imports.srctree | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 12bc9d5c34d..92af268b8c2 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -3584,7 +3584,7 @@ def generate_module_creation_code(self, env, code): code.putln("int add_module_result = PyState_AddModule(%s, &%s);" % ( module_temp, Naming.pymoduledef_cname)) code.putln("%s = 0; /* transfer ownership from %s to %s pseudovariable */" % ( - module_temp, module_temp, env.module_name + module_temp, module_temp, env.module_name.as_c_string_literal() )) # At this stage the module likely has a refcount of 2 - one owned by the list # inside PyState_AddModule and one owned by "__pyx_m" (and returned from this diff --git a/tests/run/unicode_imports.srctree b/tests/run/unicode_imports.srctree index 41306fe3ab7..f7048d6d4e3 100644 --- a/tests/run/unicode_imports.srctree +++ b/tests/run/unicode_imports.srctree @@ -22,7 +22,7 @@ from __future__ import unicode_literals from Cython.Build import cythonize -files = ["mymoð.pyx", "from_cy.pyx"] +files = ["mymoð.pyx", "from_cy.pyx", "测试.py" ] modules = cythonize(files) @@ -46,6 +46,11 @@ cdef struct S: int x cdef public api void cdef_func() # just to test generation of headers + +############ 测试.py ############# + +def g(): + return 1 ############ from_py.py ######### @@ -53,12 +58,15 @@ cdef public api void cdef_func() # just to test generation of headers import mymoð from mymoð import f +import 测试 __doc__ = """ >>> mymoð.f() True >>> f() True +>>> 测试.g() +1 """ ######### from_cy.pyx ########## @@ -68,6 +76,7 @@ True import mymoð from mymoð import f +import 测试 cimport pxd_moð from pxd_moð cimport S @@ -79,6 +88,14 @@ def test_imported(): True """ return mymoð.f() and f() # True and True + + +def test_imported2(): + """ + >>> test_imported2() + 1 + """ + return 测试.g() def test_cimported(): From a6fecd064ecedbd0f4525132abd752126471e396 Mon Sep 17 00:00:00 2001 From: da-woods Date: Tue, 28 Nov 2023 08:40:00 +0000 Subject: [PATCH 019/286] Avoid writing non-latin1 module names in the file (GH-5874) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … and avoid potentially failing to do so. Closes https://github.com/cython/cython/issues/5873 --- Cython/Compiler/ModuleNode.py | 2 +- tests/run/unicode_imports.srctree | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 826b14e4a03..b46b6cee6a8 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -3592,7 +3592,7 @@ def generate_module_creation_code(self, env, code): code.putln("int add_module_result = PyState_AddModule(%s, &%s);" % ( module_temp, Naming.pymoduledef_cname)) code.putln("%s = 0; /* transfer ownership from %s to %s pseudovariable */" % ( - module_temp, module_temp, env.module_name + module_temp, module_temp, env.module_name.as_c_string_literal() )) # At this stage the module likely has a refcount of 2 - one owned by the list # inside PyState_AddModule and one owned by "__pyx_m" (and returned from this diff --git a/tests/run/unicode_imports.srctree b/tests/run/unicode_imports.srctree index 262f4949aac..fc5e264725f 100644 --- a/tests/run/unicode_imports.srctree +++ b/tests/run/unicode_imports.srctree @@ -29,7 +29,7 @@ from __future__ import unicode_literals import sys from Cython.Build import cythonize -files = ["mymoð.pyx", "from_cy.pyx"] +files = ["mymoð.pyx", "from_cy.pyx", "测试.py" ] # For Python 2 and Python <= 3.4 just run pyx->c; @@ -57,6 +57,11 @@ cdef struct S: int x cdef public api void cdef_func() # just to test generation of headers + +############ 测试.py ############# + +def g(): + return 1 ############ from_py.py ######### @@ -64,12 +69,15 @@ cdef public api void cdef_func() # just to test generation of headers import mymoð from mymoð import f +import 测试 __doc__ = """ >>> mymoð.f() True >>> f() True +>>> 测试.g() +1 """ ######### from_cy.pyx ########## @@ -79,6 +87,7 @@ True import mymoð from mymoð import f +import 测试 cimport pxd_moð from pxd_moð cimport S @@ -90,6 +99,14 @@ def test_imported(): True """ return mymoð.f() and f() # True and True + + +def test_imported2(): + """ + >>> test_imported2() + 1 + """ + return 测试.g() def test_cimported(): From c1896e1680ef66707706a812ba418086f6d769f4 Mon Sep 17 00:00:00 2001 From: da-woods Date: Tue, 28 Nov 2023 08:55:16 +0000 Subject: [PATCH 020/286] Delete some Python 2 module init function code (GH-5875) --- Cython/Compiler/ModuleNode.py | 43 ++++------------------------------- 1 file changed, 4 insertions(+), 39 deletions(-) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 92af268b8c2..7826b478149 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -300,17 +300,9 @@ def h_entries(entries, api=0, pxd=0): h_code_main.putln("/* WARNING: the interface of the module init function changed in CPython 3.5. */") h_code_main.putln("/* It now returns a PyModuleDef instance instead of a PyModule instance. */") h_code_main.putln("") - h_code_main.putln("#if PY_MAJOR_VERSION < 3") - if env.module_name.isascii(): - py2_mod_name = env.module_name - else: - py2_mod_name = env.module_name.encode("ascii", errors="ignore").decode("utf-8") - h_code_main.putln('#error "Unicode module names are not supported in Python 2";') - h_code_main.putln("PyMODINIT_FUNC init%s(void);" % py2_mod_name) - h_code_main.putln("#else") py3_mod_func_name = self.mod_init_func_cname('PyInit', env) - warning_string = EncodedString('Use PyImport_AppendInittab("%s", %s) instead of calling %s directly.' % ( - py2_mod_name, py3_mod_func_name, py3_mod_func_name)) + warning_string = EncodedString('Use PyImport_AppendInittab(%s, %s) instead of calling %s directly.' % ( + env.module_name.as_c_string_literal(), py3_mod_func_name, py3_mod_func_name)) h_code_main.putln('/* WARNING: %s from Python 3.5 */' % warning_string.rstrip('.')) h_code_main.putln("PyMODINIT_FUNC %s(void);" % py3_mod_func_name) h_code_main.putln("") @@ -333,7 +325,6 @@ def h_entries(entries, api=0, pxd=0): h_code_main.putln('#define %s() __PYX_WARN_IF_%s_INIT_CALLED(%s())' % ( py3_mod_func_name, py3_mod_func_name, py3_mod_func_name)) h_code_main.putln('#endif') - h_code_main.putln('#endif') h_code_end.putln("") h_code_end.putln("#endif /* !%s */" % h_guard) @@ -1777,13 +1768,7 @@ def generate_dealloc_function(self, scope, code): if base_type.scope.needs_gc(): code.putln("PyObject_GC_Track(o);") else: - code.putln("#if PY_MAJOR_VERSION < 3") - # Py2 lacks guarantees that the type pointer is still valid if we dealloc the object - # at system exit time. Thus, we need an extra NULL check. - code.putln("if (!(%s) || PyType_IS_GC(%s)) PyObject_GC_Track(o);" % (base_cname, base_cname)) - code.putln("#else") code.putln("if (PyType_IS_GC(%s)) PyObject_GC_Track(o);" % base_cname) - code.putln("#endif") tp_dealloc = TypeSlots.get_base_slot_function(scope, tp_slot) if tp_dealloc is not None: @@ -2971,28 +2956,10 @@ def generate_module_init_func(self, imported_modules, env, code): code.enter_cfunc_scope(self.scope) code.putln("") code.putln(UtilityCode.load_as_string("PyModInitFuncType", "ModuleSetupCode.c")[0]) - if env.module_name.isascii(): - py2_mod_name = env.module_name - fail_compilation_in_py2 = False - else: - fail_compilation_in_py2 = True - # at this point py2_mod_name is largely a placeholder and the value doesn't matter - py2_mod_name = env.module_name.encode("ascii", errors="ignore").decode("utf8") - header2 = "__Pyx_PyMODINIT_FUNC init%s(void)" % py2_mod_name header3 = "__Pyx_PyMODINIT_FUNC %s(void)" % self.mod_init_func_cname('PyInit', env) header3 = EncodedString(header3) - code.putln("#if PY_MAJOR_VERSION < 3") # Optimise for small code size as the module init function is only executed once. - code.putln("%s CYTHON_SMALL_CODE; /*proto*/" % header2) - if fail_compilation_in_py2: - code.putln('#error "Unicode module names are not supported in Python 2";') - if self.scope.is_package: - code.putln("#if !defined(CYTHON_NO_PYINIT_EXPORT) && (defined(_WIN32) || defined(WIN32) || defined(MS_WINDOWS))") - code.putln("__Pyx_PyMODINIT_FUNC init__init__(void) { init%s(); }" % py2_mod_name) - code.putln("#endif") - code.putln(header2) - code.putln("#else") code.putln("%s CYTHON_SMALL_CODE; /*proto*/" % header3) if self.scope.is_package: code.putln("#if !defined(CYTHON_NO_PYINIT_EXPORT) && (defined(_WIN32) || defined(WIN32) || defined(MS_WINDOWS))") @@ -3025,8 +2992,6 @@ def generate_module_init_func(self, imported_modules, env, code): Naming.pymodinit_module_arg)) code.putln("#endif") # PEP489 - code.putln("#endif") # Py3 - # start of module init/exec function (pre/post PEP 489) code.putln("{") code.putln('int stringtab_initialized = 0;') @@ -3058,8 +3023,8 @@ def generate_module_init_func(self, imported_modules, env, code): env.module_name.as_c_string_literal()[1:-1]) code.putln("return -1;") code.putln("}") - code.putln("#elif PY_MAJOR_VERSION >= 3") - # Hack: enforce single initialisation also on reimports under different names on Python 3 (with PEP 3121/489). + code.putln("#else") + # Hack: enforce single initialisation also on reimports under different names (with PEP 3121/489). code.putln("if (%s) return __Pyx_NewRef(%s);" % ( Naming.module_cname, Naming.module_cname, From 1b0b49febab8755009a3ecd5f2b23bcf82e20af7 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Mon, 27 Nov 2023 10:29:57 +0100 Subject: [PATCH 021/286] Set the encoding used in EndToEnd tests to UTF-8 on all platforms since we read it from a pipe anyway and don't want to care about platform specific encoding issues. --- runtests.py | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/runtests.py b/runtests.py index 9494ab89795..5c780da1a43 100755 --- a/runtests.py +++ b/runtests.py @@ -2048,24 +2048,13 @@ def tearDown(self): break os.chdir(self.old_dir) - def _try_decode(self, content): - if not isinstance(content, bytes): - return content - try: - return content.decode() - except UnicodeDecodeError: - return content.decode('iso-8859-1') - def runTest(self): self.success = False old_path = os.environ.get('PYTHONPATH') - env = dict(os.environ) new_path = self.cython_syspath if old_path: new_path = new_path + os.pathsep + self.workdir + os.pathsep + old_path - env['PYTHONPATH'] = new_path - if not env.get("PYTHONIOENCODING"): - env["PYTHONIOENCODING"] = sys.stdout.encoding or sys.getdefaultencoding() + env = dict(os.environ, PYTHONPATH=new_path, PYTHONIOENCODING='utf8') cmd = [] out = [] err = [] @@ -2081,21 +2070,21 @@ def runTest(self): _out, _err = b'', b'' res = p cmd.append(command) - out.append(_out) - err.append(_err) + out.append(_out.decode('utf-8')) + err.append(_err.decode('utf-8')) if res == 0 and b'REFNANNY: ' in _out: res = -1 if res != 0: for c, o, e in zip(cmd, out, err): sys.stderr.write("[%d] %s\n%s\n%s\n\n" % ( - self.shard_num, c, self._try_decode(o), self._try_decode(e))) + self.shard_num, c, o, e)) sys.stderr.write("Final directory layout of '%s':\n%s\n\n" % ( self.name, '\n'.join(os.path.join(dirpath, filename) for dirpath, dirs, files in os.walk(".") for filename in files), )) self.assertEqual(0, res, "non-zero exit status, last output was:\n%r\n-- stdout:%s\n-- stderr:%s\n" % ( - ' '.join(command), self._try_decode(out[-1]), self._try_decode(err[-1]))) + ' '.join(command), out[-1], err[-1])) self.success = True From a766e98e87a5ffad78e10855884ae7e079479e74 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Tue, 28 Nov 2023 14:30:11 +0100 Subject: [PATCH 022/286] Revert "Avoid writing non-latin1 module names in the file (GH-5874)" This reverts commit a6fecd064ecedbd0f4525132abd752126471e396. Reverted due to missing changes that are currently only available in Cython 3.1. --- Cython/Compiler/ModuleNode.py | 2 +- tests/run/unicode_imports.srctree | 19 +------------------ 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index b46b6cee6a8..826b14e4a03 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -3592,7 +3592,7 @@ def generate_module_creation_code(self, env, code): code.putln("int add_module_result = PyState_AddModule(%s, &%s);" % ( module_temp, Naming.pymoduledef_cname)) code.putln("%s = 0; /* transfer ownership from %s to %s pseudovariable */" % ( - module_temp, module_temp, env.module_name.as_c_string_literal() + module_temp, module_temp, env.module_name )) # At this stage the module likely has a refcount of 2 - one owned by the list # inside PyState_AddModule and one owned by "__pyx_m" (and returned from this diff --git a/tests/run/unicode_imports.srctree b/tests/run/unicode_imports.srctree index fc5e264725f..262f4949aac 100644 --- a/tests/run/unicode_imports.srctree +++ b/tests/run/unicode_imports.srctree @@ -29,7 +29,7 @@ from __future__ import unicode_literals import sys from Cython.Build import cythonize -files = ["mymoð.pyx", "from_cy.pyx", "测试.py" ] +files = ["mymoð.pyx", "from_cy.pyx"] # For Python 2 and Python <= 3.4 just run pyx->c; @@ -57,11 +57,6 @@ cdef struct S: int x cdef public api void cdef_func() # just to test generation of headers - -############ 测试.py ############# - -def g(): - return 1 ############ from_py.py ######### @@ -69,15 +64,12 @@ def g(): import mymoð from mymoð import f -import 测试 __doc__ = """ >>> mymoð.f() True >>> f() True ->>> 测试.g() -1 """ ######### from_cy.pyx ########## @@ -87,7 +79,6 @@ True import mymoð from mymoð import f -import 测试 cimport pxd_moð from pxd_moð cimport S @@ -99,14 +90,6 @@ def test_imported(): True """ return mymoð.f() and f() # True and True - - -def test_imported2(): - """ - >>> test_imported2() - 1 - """ - return 测试.g() def test_cimported(): From 24bee75dd5a847d7e509975aca31092738215e9d Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Wed, 29 Nov 2023 08:58:46 +0100 Subject: [PATCH 023/286] Revert "Set the encoding used in EndToEnd tests to UTF-8 on all platforms since we read it from a pipe anyway and don't want to care about platform specific encoding issues." This reverts commit 1b0b49febab8755009a3ecd5f2b23bcf82e20af7. It produced test failures on old Python versions. --- runtests.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/runtests.py b/runtests.py index 5c780da1a43..9494ab89795 100755 --- a/runtests.py +++ b/runtests.py @@ -2048,13 +2048,24 @@ def tearDown(self): break os.chdir(self.old_dir) + def _try_decode(self, content): + if not isinstance(content, bytes): + return content + try: + return content.decode() + except UnicodeDecodeError: + return content.decode('iso-8859-1') + def runTest(self): self.success = False old_path = os.environ.get('PYTHONPATH') + env = dict(os.environ) new_path = self.cython_syspath if old_path: new_path = new_path + os.pathsep + self.workdir + os.pathsep + old_path - env = dict(os.environ, PYTHONPATH=new_path, PYTHONIOENCODING='utf8') + env['PYTHONPATH'] = new_path + if not env.get("PYTHONIOENCODING"): + env["PYTHONIOENCODING"] = sys.stdout.encoding or sys.getdefaultencoding() cmd = [] out = [] err = [] @@ -2070,21 +2081,21 @@ def runTest(self): _out, _err = b'', b'' res = p cmd.append(command) - out.append(_out.decode('utf-8')) - err.append(_err.decode('utf-8')) + out.append(_out) + err.append(_err) if res == 0 and b'REFNANNY: ' in _out: res = -1 if res != 0: for c, o, e in zip(cmd, out, err): sys.stderr.write("[%d] %s\n%s\n%s\n\n" % ( - self.shard_num, c, o, e)) + self.shard_num, c, self._try_decode(o), self._try_decode(e))) sys.stderr.write("Final directory layout of '%s':\n%s\n\n" % ( self.name, '\n'.join(os.path.join(dirpath, filename) for dirpath, dirs, files in os.walk(".") for filename in files), )) self.assertEqual(0, res, "non-zero exit status, last output was:\n%r\n-- stdout:%s\n-- stderr:%s\n" % ( - ' '.join(command), out[-1], err[-1])) + ' '.join(command), self._try_decode(out[-1]), self._try_decode(err[-1]))) self.success = True From 6ec1291b779e442cd526085a7daf69ff78dc7641 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Wed, 29 Nov 2023 10:39:48 +0100 Subject: [PATCH 024/286] Manually apply the safe fix from https://github.com/cython/cython/pull/5874 but without the test (which fails in the stable 3.0 branch for other reasons). Closes https://github.com/cython/cython/issues/5873 --- Cython/Compiler/ModuleNode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 826b14e4a03..b46b6cee6a8 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -3592,7 +3592,7 @@ def generate_module_creation_code(self, env, code): code.putln("int add_module_result = PyState_AddModule(%s, &%s);" % ( module_temp, Naming.pymoduledef_cname)) code.putln("%s = 0; /* transfer ownership from %s to %s pseudovariable */" % ( - module_temp, module_temp, env.module_name + module_temp, module_temp, env.module_name.as_c_string_literal() )) # At this stage the module likely has a refcount of 2 - one owned by the list # inside PyState_AddModule and one owned by "__pyx_m" (and returned from this From a8063c61120f15935744a806995af23a2890cc03 Mon Sep 17 00:00:00 2001 From: scoder Date: Wed, 29 Nov 2023 10:57:37 +0100 Subject: [PATCH 025/286] Add separate macro guard "CYTHON_ASSUME_SAFE_SIZE" (GH-5882) Add a separate macro guard "CYTHON_ASSUME_SAFE_SIZE" for calling Py*_GET_SIZE() without expecting failures. --- Cython/Compiler/ExprNodes.py | 18 +++---- Cython/Compiler/Nodes.py | 2 +- Cython/Utility/Builtins.c | 12 +++-- Cython/Utility/Coroutine.c | 11 ++++- Cython/Utility/CythonFunction.c | 30 ++++++++---- Cython/Utility/ExtensionTypes.c | 4 +- Cython/Utility/FunctionArguments.c | 4 +- Cython/Utility/ImportExport.c | 8 ++-- Cython/Utility/ModuleSetupCode.c | 30 +++++++++--- Cython/Utility/ObjectHandling.c | 30 ++++++++---- Cython/Utility/Optimize.c | 34 +++++++++---- Cython/Utility/StringTools.c | 48 +++++++++++++++---- Cython/Utility/TypeConversion.c | 4 +- .../source_files_and_compilation.rst | 4 ++ 14 files changed, 170 insertions(+), 69 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 6fde12a078e..809b1a18767 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -3017,7 +3017,7 @@ def generate_result_code(self, code): else: len_func = '__Pyx_PyTuple_GET_SIZE' code.putln("%s = %s(%s);" % (self.counter_cname, len_func, self.result())) - code.putln("#if !CYTHON_ASSUME_SAFE_MACROS") + code.putln("#if !CYTHON_ASSUME_SAFE_SIZE") code.putln(code.error_goto_if_neg(self.counter_cname, self.pos)) code.putln("#endif") code.putln("--%s;" % self.counter_cname) # len -> last item @@ -3066,7 +3066,7 @@ def generate_next_sequence_item(self, test_name, result_name, code): else: code.putln("{") code.putln("Py_ssize_t %s = %s;" % (Naming.quick_temp_cname, final_size)) - code.putln("#if !CYTHON_ASSUME_SAFE_MACROS") + code.putln("#if !CYTHON_ASSUME_SAFE_SIZE") code.putln(code.error_goto_if_neg(Naming.quick_temp_cname, self.pos)) code.putln("#endif") code.putln("if (%s >= %s) break;" % (self.counter_cname, Naming.quick_temp_cname)) @@ -8380,7 +8380,7 @@ def generate_starred_assignment_code(self, rhs, code): code.globalstate.use_utility_code( UtilityCode.load_cached("RaiseNeedMoreValuesToUnpack", "ObjectHandling.c")) length_temp = code.funcstate.allocate_temp(PyrexTypes.c_py_ssize_t_type, manage_ref=False) - code.putln('%s = PyList_GET_SIZE(%s);' % (length_temp, target_list)) + code.putln('%s = __Pyx_PyList_GET_SIZE(%s);' % (length_temp, target_list)) code.putln("if (unlikely(%s < %d)) {" % (length_temp, len(unpacked_fixed_items_right))) code.putln("__Pyx_RaiseNeedMoreValuesError(%d+%s); %s" % ( len(unpacked_fixed_items_left), length_temp, @@ -14177,12 +14177,12 @@ class CoerceToBooleanNode(CoercionNode): type = PyrexTypes.c_bint_type _special_builtins = { - Builtin.list_type: 'PyList_GET_SIZE', - Builtin.tuple_type: 'PyTuple_GET_SIZE', - Builtin.set_type: 'PySet_GET_SIZE', - Builtin.frozenset_type: 'PySet_GET_SIZE', - Builtin.bytes_type: 'PyBytes_GET_SIZE', - Builtin.bytearray_type: 'PyByteArray_GET_SIZE', + Builtin.list_type: '__Pyx_PyList_GET_SIZE', + Builtin.tuple_type: '__Pyx_PyTuple_GET_SIZE', + Builtin.set_type: '__Pyx_PySet_GET_SIZE', + Builtin.frozenset_type: '__Pyx_PySet_GET_SIZE', + Builtin.bytes_type: '__Pyx_PyBytes_GET_SIZE', + Builtin.bytearray_type: '__Pyx_PyByteArray_GET_SIZE', Builtin.unicode_type: '__Pyx_PyUnicode_IS_TRUE', } diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 6bb21a036fa..10afc0971ce 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -3868,7 +3868,7 @@ def generate_argument_parsing_code(self, env, code, decl_code): if self.signature_has_generic_args(): if self.signature.use_fastcall: code.putln("#if !CYTHON_METH_FASTCALL") - code.putln("#if CYTHON_ASSUME_SAFE_MACROS") + code.putln("#if CYTHON_ASSUME_SAFE_SIZE") code.putln("%s = PyTuple_GET_SIZE(%s);" % ( Naming.nargs_cname, Naming.args_cname)) code.putln("#else") diff --git a/Cython/Utility/Builtins.c b/Cython/Utility/Builtins.c index d6b67c8909c..e415ff926cb 100644 --- a/Cython/Utility/Builtins.c +++ b/Cython/Utility/Builtins.c @@ -331,13 +331,13 @@ static long __Pyx__PyObject_Ord(PyObject* c); /*proto*/ static long __Pyx__PyObject_Ord(PyObject* c) { Py_ssize_t size; if (PyBytes_Check(c)) { - size = PyBytes_GET_SIZE(c); + size = __Pyx_PyBytes_GET_SIZE(c); if (likely(size == 1)) { return (unsigned char) PyBytes_AS_STRING(c)[0]; } #if (!CYTHON_COMPILING_IN_PYPY) || (defined(PyByteArray_AS_STRING) && defined(PyByteArray_GET_SIZE)) } else if (PyByteArray_Check(c)) { - size = PyByteArray_GET_SIZE(c); + size = __Pyx_PyByteArray_GET_SIZE(c); if (likely(size == 1)) { return (unsigned char) PyByteArray_AS_STRING(c)[0]; } @@ -475,7 +475,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyFrozenSet_New(PyObject* it) { result = PyFrozenSet_New(it); if (unlikely(!result)) return NULL; - if ((PY_VERSION_HEX >= 0x031000A1) || likely(PySet_GET_SIZE(result))) + if ((PY_VERSION_HEX >= 0x030A00A1) || likely(__Pyx_PySet_GET_SIZE(result))) return result; // empty frozenset is a singleton (on Python <3.10) // seems wasteful, but CPython does the same @@ -500,7 +500,11 @@ static CYTHON_INLINE int __Pyx_PySet_Update(PyObject* set, PyObject* it) { PyObject *retval; #if CYTHON_USE_TYPE_SLOTS && !CYTHON_COMPILING_IN_PYPY if (PyAnySet_Check(it)) { - if (PySet_GET_SIZE(it) == 0) + Py_ssize_t size = __Pyx_PySet_GET_SIZE(it); + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(size == -1)) return -1; + #endif + if (size == 0) return 0; // fast and safe case: CPython will update our result set and return it retval = PySet_Type.tp_as_number->nb_inplace_or(set, it); diff --git a/Cython/Utility/Coroutine.c b/Cython/Utility/Coroutine.c index 24979b0fa0d..4c56fb7f904 100644 --- a/Cython/Utility/Coroutine.c +++ b/Cython/Utility/Coroutine.c @@ -511,7 +511,16 @@ static int __Pyx_PyGen__FetchStopIterationValue(PyThreadState *$local_tstate_cna // PyErr_SetObject() and friends put the value directly into ev else if (unlikely(PyTuple_Check(ev))) { // if it's a tuple, it is interpreted as separate constructor arguments (surprise!) - if (PyTuple_GET_SIZE(ev) >= 1) { + Py_ssize_t tuple_size = __Pyx_PyTuple_GET_SIZE(ev); + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(tuple_size == -1)) { + Py_XDECREF(tb); + Py_DECREF(ev); + Py_DECREF(et); + return -1; + } + #endif + if (tuple_size >= 1) { #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS value = PyTuple_GET_ITEM(ev, 0); Py_INCREF(value); diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 256d2ac29a4..e43b3a97975 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -764,7 +764,7 @@ static PyObject * __Pyx_CyFunction_CallMethod(PyObject *func, PyObject *self, Py return (*(PyCFunctionWithKeywords)(void*)meth)(self, arg, kw); case METH_NOARGS: if (likely(kw == NULL || PyDict_Size(kw) == 0)) { -#if CYTHON_ASSUME_SAFE_MACROS +#if CYTHON_ASSUME_SAFE_SIZE size = PyTuple_GET_SIZE(arg); #else size = PyTuple_Size(arg); @@ -789,7 +789,7 @@ static PyObject * __Pyx_CyFunction_CallMethod(PyObject *func, PyObject *self, Py break; case METH_O: if (likely(kw == NULL || PyDict_Size(kw) == 0)) { -#if CYTHON_ASSUME_SAFE_MACROS +#if CYTHON_ASSUME_SAFE_SIZE size = PyTuple_GET_SIZE(arg); #else size = PyTuple_Size(arg); @@ -863,7 +863,7 @@ static PyObject *__Pyx_CyFunction_CallAsMethod(PyObject *func, PyObject *args, P // CPython would normally use vectorcall directly instead of tp_call. __pyx_vectorcallfunc vc = __Pyx_CyFunction_func_vectorcall(cyfunc); if (vc) { -#if CYTHON_ASSUME_SAFE_MACROS +#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE return __Pyx_PyVectorcall_FastCallDict(func, vc, &PyTuple_GET_ITEM(args, 0), (size_t)PyTuple_GET_SIZE(args), kw); #else // avoid unused function warning @@ -878,11 +878,11 @@ static PyObject *__Pyx_CyFunction_CallAsMethod(PyObject *func, PyObject *args, P PyObject *new_args; PyObject *self; -#if CYTHON_ASSUME_SAFE_MACROS +#if CYTHON_ASSUME_SAFE_SIZE argc = PyTuple_GET_SIZE(args); #else argc = PyTuple_Size(args); - if (unlikely(!argc) < 0) return NULL; + if (unlikely(argc < 0)) return NULL; #endif new_args = PyTuple_GetSlice(args, 1, argc); @@ -924,7 +924,7 @@ static CYTHON_INLINE int __Pyx_CyFunction_Vectorcall_CheckArgs(__pyx_CyFunctionO } ret = 1; } - if (unlikely(kwnames) && unlikely(PyTuple_GET_SIZE(kwnames))) { + if (unlikely(kwnames) && unlikely(__Pyx_PyTuple_GET_SIZE(kwnames))) { PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", ((PyCFunctionObject*)cyfunc)->m_ml->ml_name); return -1; @@ -1230,7 +1230,10 @@ static int __Pyx_CyFunction_InitClassCell(PyObject *cyfunctions, PyObject *class //@requires: CythonFunctionShared static int __Pyx_CyFunction_InitClassCell(PyObject *cyfunctions, PyObject *classobj) { - Py_ssize_t i, count = PyList_GET_SIZE(cyfunctions); + Py_ssize_t i, count = __Pyx_PyList_GET_SIZE(cyfunctions); + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(count == -1)) return -1; + #endif for (i = 0; i < count; i++) { __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) @@ -1414,10 +1417,14 @@ __pyx_FusedFunction_getitem(__pyx_FusedFunctionObject *self, PyObject *idx) } if (PyTuple_Check(idx)) { - Py_ssize_t n = PyTuple_GET_SIZE(idx); - PyObject *list = PyList_New(n); + Py_ssize_t n = __Pyx_PyTuple_GET_SIZE(idx); + PyObject *list; int i; + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(n == -1)) return NULL; + #endif + list = PyList_New(n); if (unlikely(!list)) return NULL; @@ -1493,11 +1500,14 @@ static PyObject * __pyx_FusedFunction_call(PyObject *func, PyObject *args, PyObject *kw) { __pyx_FusedFunctionObject *binding_func = (__pyx_FusedFunctionObject *) func; - Py_ssize_t argc = PyTuple_GET_SIZE(args); + Py_ssize_t argc = __Pyx_PyTuple_GET_SIZE(args); PyObject *new_args = NULL; __pyx_FusedFunctionObject *new_func = NULL; PyObject *result = NULL; int is_staticmethod = binding_func->func.flags & __Pyx_CYFUNCTION_STATICMETHOD; + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(argc == -1)) return NULL; + #endif if (binding_func->self) { // Bound method call, put 'self' in the args tuple diff --git a/Cython/Utility/ExtensionTypes.c b/Cython/Utility/ExtensionTypes.c index 7016a33d3a6..2b3e0a10e14 100644 --- a/Cython/Utility/ExtensionTypes.c +++ b/Cython/Utility/ExtensionTypes.c @@ -113,11 +113,11 @@ static int __Pyx_validate_bases_tuple(const char *type_name, Py_ssize_t dictoffs // structure), we need to check that none of the base classes sets // it either. Py_ssize_t i, n; -#if CYTHON_ASSUME_SAFE_MACROS +#if CYTHON_ASSUME_SAFE_SIZE n = PyTuple_GET_SIZE(bases); #else n = PyTuple_Size(bases); - if (n < 0) return -1; + if (unlikely(n == -1)) return -1; #endif for (i = 1; i < n; i++) /* Skip first base */ { diff --git a/Cython/Utility/FunctionArguments.c b/Cython/Utility/FunctionArguments.c index 54e918f3fa7..71549ce5721 100644 --- a/Cython/Utility/FunctionArguments.c +++ b/Cython/Utility/FunctionArguments.c @@ -137,7 +137,7 @@ static int __Pyx_CheckKeywordStrings( #else if (CYTHON_METH_FASTCALL && likely(PyTuple_Check(kw))) { Py_ssize_t kwsize; -#if CYTHON_ASSUME_SAFE_MACROS +#if CYTHON_ASSUME_SAFE_SIZE kwsize = PyTuple_GET_SIZE(kw); #else kwsize = PyTuple_Size(kw); @@ -240,7 +240,7 @@ static int __Pyx_ParseOptionalKeywords( if (kwds_is_tuple) { Py_ssize_t size; -#if CYTHON_ASSUME_SAFE_MACROS +#if CYTHON_ASSUME_SAFE_SIZE size = PyTuple_GET_SIZE(kwds); #else size = PyTuple_Size(kwds); diff --git a/Cython/Utility/ImportExport.c b/Cython/Utility/ImportExport.c index 5fdb94adff8..72e8d673687 100644 --- a/Cython/Utility/ImportExport.c +++ b/Cython/Utility/ImportExport.c @@ -12,13 +12,11 @@ static PyObject *__Pyx__ImportDottedModule_Error(PyObject *name, PyObject *parts if (unlikely(PyErr_Occurred())) { PyErr_Clear(); } -#if CYTHON_ASSUME_SAFE_MACROS +#if CYTHON_ASSUME_SAFE_SIZE size = PyTuple_GET_SIZE(parts_tuple); #else size = PyTuple_Size(parts_tuple); - if (size < 0) { - goto bad; - } + if (size < 0) goto bad; #endif if (likely(size == count)) { partial_name = name; @@ -59,7 +57,7 @@ static PyObject *__Pyx__ImportDottedModule_Lookup(PyObject *name) { static PyObject *__Pyx_ImportDottedModule_WalkParts(PyObject *module, PyObject *name, PyObject *parts_tuple) { Py_ssize_t i, nparts; -#if CYTHON_ASSUME_SAFE_MACROS +#if CYTHON_ASSUME_SAFE_SIZE nparts = PyTuple_GET_SIZE(parts_tuple); #else nparts = PyTuple_Size(parts_tuple); diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index d480d64aca5..bf7894dc418 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -88,6 +88,8 @@ #define CYTHON_AVOID_BORROWED_REFS 1 #undef CYTHON_ASSUME_SAFE_MACROS #define CYTHON_ASSUME_SAFE_MACROS 0 + #undef CYTHON_ASSUME_SAFE_SIZE + #define CYTHON_ASSUME_SAFE_SIZE 0 #undef CYTHON_UNPACK_METHODS #define CYTHON_UNPACK_METHODS 0 #undef CYTHON_FAST_THREAD_STATE @@ -144,6 +146,9 @@ #define CYTHON_AVOID_BORROWED_REFS 1 #undef CYTHON_ASSUME_SAFE_MACROS #define CYTHON_ASSUME_SAFE_MACROS 0 + #ifndef CYTHON_ASSUME_SAFE_SIZE + #define CYTHON_ASSUME_SAFE_SIZE 1 + #endif #undef CYTHON_UNPACK_METHODS #define CYTHON_UNPACK_METHODS 0 #undef CYTHON_FAST_THREAD_STATE @@ -214,6 +219,8 @@ #endif #undef CYTHON_ASSUME_SAFE_MACROS #define CYTHON_ASSUME_SAFE_MACROS 0 + #undef CYTHON_ASSUME_SAFE_SIZE + #define CYTHON_ASSUME_SAFE_SIZE 0 #undef CYTHON_UNPACK_METHODS #define CYTHON_UNPACK_METHODS 0 #undef CYTHON_FAST_THREAD_STATE @@ -273,6 +280,9 @@ #ifndef CYTHON_ASSUME_SAFE_MACROS #define CYTHON_ASSUME_SAFE_MACROS 1 #endif + #ifndef CYTHON_ASSUME_SAFE_SIZE + #define CYTHON_ASSUME_SAFE_SIZE 1 + #endif #ifndef CYTHON_UNPACK_METHODS #define CYTHON_UNPACK_METHODS 1 #endif @@ -335,6 +345,10 @@ #ifndef CYTHON_ASSUME_SAFE_MACROS #define CYTHON_ASSUME_SAFE_MACROS 1 #endif + // CYTHON_ASSUME_SAFE_SIZE - Assume that Py*_GET_SIZE() calls do not fail and do not raise exceptions. + #ifndef CYTHON_ASSUME_SAFE_SIZE + #define CYTHON_ASSUME_SAFE_SIZE 1 + #endif #ifndef CYTHON_UNPACK_METHODS #define CYTHON_UNPACK_METHODS 1 #endif @@ -1150,18 +1164,22 @@ static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStrWithError(PyObject *dict, #define __Pyx_PySequence_SIZE(seq) Py_SIZE(seq) #define __Pyx_PyTuple_SET_ITEM(o, i, v) (PyTuple_SET_ITEM(o, i, v), (0)) #define __Pyx_PyList_SET_ITEM(o, i, v) (PyList_SET_ITEM(o, i, v), (0)) - #define __Pyx_PyTuple_GET_SIZE(o) PyTuple_GET_SIZE(o) - #define __Pyx_PyList_GET_SIZE(o) PyList_GET_SIZE(o) - #define __Pyx_PySet_GET_SIZE(o) PySet_GET_SIZE(o) - #define __Pyx_PyBytes_GET_SIZE(o) PyBytes_GET_SIZE(o) - #define __Pyx_PyByteArray_GET_SIZE(o) PyByteArray_GET_SIZE(o) #else #define __Pyx_PySequence_ITEM(o, i) PySequence_GetItem(o, i) // NOTE: might fail with exception => check for -1 #define __Pyx_PySequence_SIZE(seq) PySequence_Size(seq) - // Note that this doesn't leak a reference to whatever's at o[i] + // NOTE: this doesn't leak a reference to whatever is at o[i] #define __Pyx_PyTuple_SET_ITEM(o, i, v) PyTuple_SetItem(o, i, v) #define __Pyx_PyList_SET_ITEM(o, i, v) PyList_SetItem(o, i, v) +#endif + +#if CYTHON_ASSUME_SAFE_SIZE + #define __Pyx_PyTuple_GET_SIZE(o) PyTuple_GET_SIZE(o) + #define __Pyx_PyList_GET_SIZE(o) PyList_GET_SIZE(o) + #define __Pyx_PySet_GET_SIZE(o) PySet_GET_SIZE(o) + #define __Pyx_PyBytes_GET_SIZE(o) PyBytes_GET_SIZE(o) + #define __Pyx_PyByteArray_GET_SIZE(o) PyByteArray_GET_SIZE(o) +#else #define __Pyx_PyTuple_GET_SIZE(o) PyTuple_Size(o) #define __Pyx_PyList_GET_SIZE(o) PyList_Size(o) #define __Pyx_PySet_GET_SIZE(o) PySet_Size(o) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 87c8bda88f6..548ed5ab8d9 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -52,10 +52,13 @@ static void __Pyx_UnpackTupleError(PyObject *, Py_ssize_t index); /*proto*/ static void __Pyx_UnpackTupleError(PyObject *t, Py_ssize_t index) { if (t == Py_None) { __Pyx_RaiseNoneNotIterableError(); - } else if (PyTuple_GET_SIZE(t) < index) { - __Pyx_RaiseNeedMoreValuesError(PyTuple_GET_SIZE(t)); } else { - __Pyx_RaiseTooManyValuesError(index); + Py_ssize_t size = __Pyx_PyTuple_GET_SIZE(t); + if (size < index) { + __Pyx_RaiseNeedMoreValuesError(size); + } else { + __Pyx_RaiseTooManyValuesError(index); + } } } @@ -404,7 +407,7 @@ static PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j) { static CYTHON_INLINE PyObject *__Pyx_GetItemInt_{{type}}_Fast(PyObject *o, Py_ssize_t i, CYTHON_NCP_UNUSED int wraparound, CYTHON_NCP_UNUSED int boundscheck) { -#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS +#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE && !CYTHON_AVOID_BORROWED_REFS Py_ssize_t wrapped_i = i; if (wraparound & unlikely(i < 0)) { wrapped_i += Py{{type}}_GET_SIZE(o); @@ -424,7 +427,7 @@ static CYTHON_INLINE PyObject *__Pyx_GetItemInt_{{type}}_Fast(PyObject *o, Py_ss static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i, int is_list, CYTHON_NCP_UNUSED int wraparound, CYTHON_NCP_UNUSED int boundscheck) { -#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS && CYTHON_USE_TYPE_SLOTS +#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE && !CYTHON_AVOID_BORROWED_REFS && CYTHON_USE_TYPE_SLOTS if (is_list || PyList_CheckExact(o)) { Py_ssize_t n = ((!wraparound) | likely(i >= 0)) ? i : i + PyList_GET_SIZE(o); if ((!boundscheck) || (likely(__Pyx_is_valid_index(n, PyList_GET_SIZE(o))))) { @@ -500,7 +503,7 @@ static int __Pyx_SetItemInt_Generic(PyObject *o, PyObject *j, PyObject *v) { static CYTHON_INLINE int __Pyx_SetItemInt_Fast(PyObject *o, Py_ssize_t i, PyObject *v, int is_list, CYTHON_NCP_UNUSED int wraparound, CYTHON_NCP_UNUSED int boundscheck) { -#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS && CYTHON_USE_TYPE_SLOTS +#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE && !CYTHON_AVOID_BORROWED_REFS && CYTHON_USE_TYPE_SLOTS if (is_list || PyList_CheckExact(o)) { Py_ssize_t n = (!wraparound) ? i : ((likely(i >= 0)) ? i : i + PyList_GET_SIZE(o)); if ((!boundscheck) || likely(__Pyx_is_valid_index(n, PyList_GET_SIZE(o)))) { @@ -817,7 +820,7 @@ static PyObject *__Pyx_CalculateMetaclass(PyTypeObject *metaclass, PyObject *bas static PyObject *__Pyx_CalculateMetaclass(PyTypeObject *metaclass, PyObject *bases) { Py_ssize_t i, nbases; -#if CYTHON_ASSUME_SAFE_MACROS +#if CYTHON_ASSUME_SAFE_SIZE nbases = PyTuple_GET_SIZE(bases); #else nbases = PyTuple_Size(bases); @@ -869,7 +872,14 @@ static PyObject *__Pyx_FindInheritedMetaclass(PyObject *bases); /*proto*/ static PyObject *__Pyx_FindInheritedMetaclass(PyObject *bases) { PyObject *metaclass; - if (PyTuple_Check(bases) && PyTuple_GET_SIZE(bases) > 0) { + #if CYTHON_ASSUME_SAFE_SIZE + if (PyTuple_Check(bases) && PyTuple_GET_SIZE(bases) > 0) + #else + Py_ssize_t tuple_size = PyTuple_Check(bases) ? PyTuple_GetSize(bases) : 0; + if (unlikely(tuple_size == -1)) return NULL; + if (tuple_size > 0) + #endif + { PyTypeObject *metatype; #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS PyObject *base = PyTuple_GET_ITEM(bases, 0); @@ -969,7 +979,7 @@ __Pyx_PEP560_update_bases(PyObject *bases) PyObject *base = NULL, *meth, *new_base, *result, *new_bases = NULL; /*assert(PyTuple_Check(bases));*/ -#if CYTHON_ASSUME_SAFE_MACROS +#if CYTHON_ASSUME_SAFE_SIZE size_bases = PyTuple_GET_SIZE(bases); #else size_bases = PyTuple_Size(bases); @@ -1046,7 +1056,7 @@ __Pyx_PEP560_update_bases(PyObject *bases) #endif } } -#if CYTHON_ASSUME_SAFE_MACROS +#if CYTHON_ASSUME_SAFE_SIZE j = PyList_GET_SIZE(new_bases); #else j = PyList_Size(new_bases); diff --git a/Cython/Utility/Optimize.c b/Cython/Utility/Optimize.c index 84f6bea680e..78445d0262f 100644 --- a/Cython/Utility/Optimize.c +++ b/Cython/Utility/Optimize.c @@ -94,7 +94,7 @@ static CYTHON_INLINE int __Pyx_PyList_Extend(PyObject* L, PyObject* v) { static CYTHON_INLINE PyObject* __Pyx__PyObject_Pop(PyObject* L); /*proto*/ -#if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS +#if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE static CYTHON_INLINE PyObject* __Pyx_PyList_Pop(PyObject* L); /*proto*/ #define __Pyx_PyObject_Pop(L) (likely(PyList_CheckExact(L)) ? \ __Pyx_PyList_Pop(L) : __Pyx__PyObject_Pop(L)) @@ -114,7 +114,7 @@ static CYTHON_INLINE PyObject* __Pyx__PyObject_Pop(PyObject* L) { return __Pyx_PyObject_CallMethod0(L, PYIDENT("pop")); } -#if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS +#if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE static CYTHON_INLINE PyObject* __Pyx_PyList_Pop(PyObject* L) { /* Check that both the size is positive and no reallocation shrinking needs to be done. */ if (likely(PyList_GET_SIZE(L) > (((PyListObject*)L)->allocated >> 1))) { @@ -131,7 +131,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyList_Pop(PyObject* L) { static PyObject* __Pyx__PyObject_PopNewIndex(PyObject* L, PyObject* py_ix); /*proto*/ static PyObject* __Pyx__PyObject_PopIndex(PyObject* L, PyObject* py_ix); /*proto*/ -#if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS +#if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE static PyObject* __Pyx__PyList_PopIndex(PyObject* L, PyObject* py_ix, Py_ssize_t ix); /*proto*/ #define __Pyx_PyObject_PopIndex(L, py_ix, ix, is_signed, type, to_py_func) ( \ @@ -171,7 +171,7 @@ static PyObject* __Pyx__PyObject_PopIndex(PyObject* L, PyObject* py_ix) { return __Pyx_PyObject_CallMethod1(L, PYIDENT("pop"), py_ix); } -#if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS +#if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE static PyObject* __Pyx__PyList_PopIndex(PyObject* L, PyObject* py_ix, Py_ssize_t ix) { Py_ssize_t size = PyList_GET_SIZE(L); if (likely(size > (((PyListObject*)L)->allocated >> 1))) { @@ -377,15 +377,33 @@ static CYTHON_INLINE int __Pyx_dict_iter_next( return 1; } else if (PyTuple_CheckExact(iter_obj)) { Py_ssize_t pos = *ppos; - if (unlikely(pos >= PyTuple_GET_SIZE(iter_obj))) return 0; + Py_ssize_t tuple_size = __Pyx_PyTuple_GET_SIZE(iter_obj); + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(tuple_size == -1)) return -1; + #endif + if (unlikely(pos >= tuple_size)) return 0; *ppos = pos + 1; + #if CYTHON_ASSUME_SAFE_MACROS next_item = PyTuple_GET_ITEM(iter_obj, pos); + #else + next_item = PyTuple_GetItem(iter_obj, pos); + if (unlikely(!next_item)) return -1; + #endif Py_INCREF(next_item); } else if (PyList_CheckExact(iter_obj)) { Py_ssize_t pos = *ppos; - if (unlikely(pos >= PyList_GET_SIZE(iter_obj))) return 0; + Py_ssize_t list_size = __Pyx_PyList_GET_SIZE(iter_obj); + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(list_size == -1)) return -1; + #endif + if (unlikely(pos >= list_size)) return 0; *ppos = pos + 1; + #if CYTHON_ASSUME_SAFE_MACROS next_item = PyList_GET_ITEM(iter_obj, pos); + #else + next_item = PyList_GetItem(iter_obj, pos); + if (unlikely(!next_item)) return -1; + #endif Py_INCREF(next_item); } else #endif @@ -795,7 +813,7 @@ static double __Pyx__PyBytes_AsDouble(PyObject *obj, const char* start, Py_ssize static CYTHON_INLINE double __Pyx_PyBytes_AsDouble(PyObject *obj) { char* as_c_string; Py_ssize_t size; -#if CYTHON_ASSUME_SAFE_MACROS +#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE as_c_string = PyBytes_AS_STRING(obj); size = PyBytes_GET_SIZE(obj); #else @@ -808,7 +826,7 @@ static CYTHON_INLINE double __Pyx_PyBytes_AsDouble(PyObject *obj) { static CYTHON_INLINE double __Pyx_PyByteArray_AsDouble(PyObject *obj) { char* as_c_string; Py_ssize_t size; -#if CYTHON_ASSUME_SAFE_MACROS +#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE as_c_string = PyByteArray_AS_STRING(obj); size = PyByteArray_GET_SIZE(obj); #else diff --git a/Cython/Utility/StringTools.c b/Cython/Utility/StringTools.c index a8b12db24c1..161f4fdc8d1 100644 --- a/Cython/Utility/StringTools.c +++ b/Cython/Utility/StringTools.c @@ -256,7 +256,7 @@ static CYTHON_INLINE int __Pyx_PyBytes_Equals(PyObject* s1, PyObject* s2, int eq //@requires: IncludeStringH static CYTHON_INLINE int __Pyx_PyBytes_Equals(PyObject* s1, PyObject* s2, int equals) { -#if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API +#if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API || !(CYTHON_ASSUME_SAFE_SIZE && CYTHON_ASSUME_SAFE_MACROS) return PyObject_RichCompareBool(s1, s2, equals); #else if (s1 == s2) { @@ -319,7 +319,10 @@ static CYTHON_INLINE int __Pyx_GetItemInt_ByteArray_Fast(PyObject* string, Py_ss int wraparound, int boundscheck) { Py_ssize_t length; if (wraparound | boundscheck) { - length = PyByteArray_GET_SIZE(string); + length = __Pyx_PyByteArray_GET_SIZE(string); + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(length == -1)) return -1; + #endif if (wraparound & unlikely(i < 0)) i += length; if ((!boundscheck) || likely(__Pyx_is_valid_index(i, length))) { return (unsigned char) (PyByteArray_AS_STRING(string)[i]); @@ -349,7 +352,10 @@ static CYTHON_INLINE int __Pyx_SetItemInt_ByteArray_Fast(PyObject* string, Py_ss int wraparound, int boundscheck) { Py_ssize_t length; if (wraparound | boundscheck) { - length = PyByteArray_GET_SIZE(string); + length = __Pyx_PyByteArray_GET_SIZE(string); + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(length == -1)) return -1; + #endif if (wraparound & unlikely(i < 0)) i += length; if ((!boundscheck) || likely(__Pyx_is_valid_index(i, length))) { PyByteArray_AS_STRING(string)[i] = (char) v; @@ -383,6 +389,9 @@ static CYTHON_INLINE Py_UCS4 __Pyx_GetItemInt_Unicode_Fast(PyObject* ustring, Py if (unlikely(__Pyx_PyUnicode_READY(ustring) < 0)) return (Py_UCS4)-1; if (wraparound | boundscheck) { length = __Pyx_PyUnicode_GET_LENGTH(ustring); + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(length == -1)) return (Py_UCS4)-1; + #endif if (wraparound & unlikely(i < 0)) i += length; if ((!boundscheck) || likely(__Pyx_is_valid_index(i, length))) { return __Pyx_PyUnicode_READ_CHAR(ustring, i); @@ -514,7 +523,7 @@ static CYTHON_INLINE PyObject* __Pyx_decode_bytes( PyObject* (*decode_func)(const char *s, Py_ssize_t size, const char *errors)) { char* as_c_string; Py_ssize_t size; -#if CYTHON_ASSUME_SAFE_MACROS +#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE as_c_string = PyBytes_AS_STRING(string); size = PyBytes_GET_SIZE(string); #else @@ -536,7 +545,7 @@ static CYTHON_INLINE PyObject* __Pyx_decode_bytearray( PyObject* (*decode_func)(const char *s, Py_ssize_t size, const char *errors)) { char* as_c_string; Py_ssize_t size; -#if CYTHON_ASSUME_SAFE_MACROS +#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE as_c_string = PyByteArray_AS_STRING(string); size = PyByteArray_GET_SIZE(string); #else @@ -633,7 +642,10 @@ static int __Pyx_PyUnicode_Tailmatch( static int __Pyx_PyUnicode_TailmatchTuple(PyObject* s, PyObject* substrings, Py_ssize_t start, Py_ssize_t end, int direction) { - Py_ssize_t i, count = PyTuple_GET_SIZE(substrings); + Py_ssize_t i, count = __Pyx_PyTuple_GET_SIZE(substrings); + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(count == -1)) return -1; + #endif for (i = 0; i < count; i++) { Py_ssize_t result; #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS @@ -681,9 +693,16 @@ static int __Pyx_PyBytes_SingleTailmatch(PyObject* self, PyObject* arg, Py_buffer view; view.obj = NULL; + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(self_len == -1)) return -1; + #endif + if (PyBytes_Check(arg)) { sub_ptr = PyBytes_AS_STRING(arg); sub_len = PyBytes_GET_SIZE(arg); + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(sub_len == -1)) return -1; + #endif } else { if (unlikely(PyObject_GetBuffer(self, &view, PyBUF_SIMPLE) == -1)) return -1; @@ -722,6 +741,9 @@ static int __Pyx_PyBytes_SingleTailmatch(PyObject* self, PyObject* arg, static int __Pyx_PyBytes_TailmatchTuple(PyObject* self, PyObject* substrings, Py_ssize_t start, Py_ssize_t end, int direction) { Py_ssize_t i, count = PyTuple_GET_SIZE(substrings); + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(count == -1)) return -1; + #endif for (i = 0; i < count; i++) { int result; #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS @@ -777,10 +799,18 @@ static CYTHON_INLINE char __Pyx_PyBytes_GetItemInt(PyObject* bytes, Py_ssize_t i /////////////// bytes_index /////////////// static CYTHON_INLINE char __Pyx_PyBytes_GetItemInt(PyObject* bytes, Py_ssize_t index, int check_bounds) { - if (index < 0) - index += PyBytes_GET_SIZE(bytes); + if (index < 0) { + Py_ssize_t size = __Pyx_PyBytes_GET_SIZE(bytes); + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(size == -1)) return (char) -1; + #endif + index += size; + } if (check_bounds) { - Py_ssize_t size = PyBytes_GET_SIZE(bytes); + Py_ssize_t size = __Pyx_PyBytes_GET_SIZE(bytes); + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(size == -1)) return (char) -1; + #endif if (unlikely(!__Pyx_is_valid_index(index, size))) { PyErr_SetString(PyExc_IndexError, "string index out of range"); return (char) -1; diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index 219ef820f75..59743781def 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -507,7 +507,7 @@ static CYTHON_INLINE {{struct_type_decl}} {{funcname}}(PyObject *); /////////////// FromPyCTupleUtility /////////////// -#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS +#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE && !CYTHON_AVOID_BORROWED_REFS static void __Pyx_tuple_{{funcname}}(PyObject * o, {{struct_type_decl}} *result) { {{for ix, component in enumerate(components):}} {{py:attr = "result->f%s" % ix}} @@ -562,7 +562,7 @@ static void __Pyx_seq_{{funcname}}(PyObject * o, {{struct_type_decl}} *result) { static CYTHON_INLINE {{struct_type_decl}} {{funcname}}(PyObject * o) { {{struct_type_decl}} result; - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS + #if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE && !CYTHON_AVOID_BORROWED_REFS if (likely(PyTuple_Check(o) && PyTuple_GET_SIZE(o) == {{size}})) { __Pyx_tuple_{{funcname}}(o, &result); } else if (likely(PyList_Check(o) && PyList_GET_SIZE(o) == {{size}})) { diff --git a/docs/src/userguide/source_files_and_compilation.rst b/docs/src/userguide/source_files_and_compilation.rst index 239199b81f2..565118d334c 100644 --- a/docs/src/userguide/source_files_and_compilation.rst +++ b/docs/src/userguide/source_files_and_compilation.rst @@ -1212,6 +1212,10 @@ hidden by default since most users will be uninterested in changing them. Use some C-API macros that increase performance by skipping error checking, which may not be safe on all Python implementations (e.g. PyPy). + ``CYTHON_ASSUME_SAFE_SIZE`` + Prefer the ``Py*_GET_SIZE()`` C-API macros / inline-functions for builtin types + over their ``Py*_GetSize()`` counterparts if errors are not expected. + ``CYTHON_FAST_GIL`` On some Python versions this speeds up getting/releasing the GIL. From b9ae9a7f0801f602ae5e3c3633099e14b70ac1c6 Mon Sep 17 00:00:00 2001 From: Matus Valo Date: Wed, 29 Nov 2023 13:47:55 +0100 Subject: [PATCH 026/286] Use `performance_hint()` instead of `warning()` for `boundscheck()` (#5879) --- Cython/Compiler/ExprNodes.py | 2 +- tests/memoryview/memoryview.pyx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 809b1a18767..3fe92d19f7f 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -4764,7 +4764,7 @@ def buffer_lookup_code(self, code): if self.in_nogil_context: if self.is_buffer_access or self.is_memview_index: if code.globalstate.directives['boundscheck']: - warning(self.pos, "Use boundscheck(False) for faster access", level=1) + performance_hint(self.pos, "Use boundscheck(False) for faster access", code.globalstate) # Assign indices to temps of at least (s)size_t to allow further index calculations. self.index_temps = index_temps = [self.get_index_in_temp(code,ivar) for ivar in self.indices] diff --git a/tests/memoryview/memoryview.pyx b/tests/memoryview/memoryview.pyx index 536a0c77e42..41411da018e 100644 --- a/tests/memoryview/memoryview.pyx +++ b/tests/memoryview/memoryview.pyx @@ -1313,5 +1313,6 @@ def test_untyped_index(i): return mview_arr[i] # should generate a performance hint _PERFORMANCE_HINTS = """ +243:9: Use boundscheck(False) for faster access 1313:21: Index should be typed for more efficient access """ From 9385121155004b8ecec5583b6b8c5eda603db390 Mon Sep 17 00:00:00 2001 From: da-woods Date: Fri, 1 Dec 2023 07:48:05 +0000 Subject: [PATCH 027/286] Clarify performance hint wording (#5883) * Clarify performance hint wording I'm getting the impression that it this performance hint isn't sufficiently clear about what it wants you to modify. * Update Cython/Compiler/PyrexTypes.py Co-authored-by: Matus Valo * Simpify implementation * Fiddle with escaping of function_name --- Cython/Compiler/ExprNodes.py | 4 +++- Cython/Compiler/PyrexTypes.py | 23 ++++++++++++++++++----- tests/run/nogil.pyx | 16 ++++++++-------- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 3fe92d19f7f..ac02e70e7ce 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -6459,9 +6459,11 @@ def generate_result_code(self, code): if exc_check: if nogil: if not exc_checks: + perf_hint_entry = getattr(self.function, "entry", None) PyrexTypes.write_noexcept_performance_hint( self.pos, code.funcstate.scope, - function_name=None, void_return=self.type.is_void) + function_name=perf_hint_entry.name if perf_hint_entry else None, + void_return=self.type.is_void, is_call=True) code.globalstate.use_utility_code( UtilityCode.load_cached("ErrOccurredWithGIL", "Exceptions.c")) exc_checks.append("__Pyx_ErrOccurredWithGIL()") diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 989387704c4..a9802066479 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -5460,16 +5460,29 @@ def cap_length(s, max_prefix=63, max_len=1024): hash_prefix = hashlib.sha256(s.encode('ascii')).hexdigest()[:6] return '%s__%s__etc' % (hash_prefix, s[:max_len-17]) -def write_noexcept_performance_hint(pos, env, function_name=None, void_return=False): - on_what = "on '%s' " % function_name if function_name else "" +def write_noexcept_performance_hint(pos, env, function_name=None, void_return=False, is_call=False): + if function_name: + # we need it escaped everywhere we use it + function_name = "'%s'" % function_name + if is_call: + on_what = "after calling %s " % (function_name or 'function') + elif function_name: + on_what = "on %s " % function_name + else: + on_what ='' msg = ( "Exception check %swill always require the GIL to be acquired." ) % on_what - solutions = ["Declare the function as 'noexcept' if you control the definition and " - "you're sure you don't want the function to raise exceptions."] + the_function = function_name if function_name else "the function" + if is_call and not function_name: + the_function = the_function + " you are calling" + solutions = ["Declare %s as 'noexcept' if you control the definition and " + "you're sure you don't want the function to raise exceptions." + % the_function] if void_return: solutions.append( - "Use an 'int' return type on the function to allow an error code to be returned.") + "Use an 'int' return type on %s to allow an error code to be returned." % + the_function) if len(solutions) == 1: msg = "%s %s" % (msg, solutions[0]) else: diff --git a/tests/run/nogil.pyx b/tests/run/nogil.pyx index 0c1bfb0ceef..feed30ed324 100644 --- a/tests/run/nogil.pyx +++ b/tests/run/nogil.pyx @@ -200,19 +200,19 @@ def test_performance_hint_nogil(): # Note that we're only able to check the first line of the performance hint _PERFORMANCE_HINTS = """ -20:9: Exception check will always require the GIL to be acquired. +20:9: Exception check after calling 'f' will always require the GIL to be acquired. 24:5: Exception check on 'f' will always require the GIL to be acquired. 34:5: Exception check on 'release_gil_in_nogil' will always require the GIL to be acquired. 39:6: Exception check on 'release_gil_in_nogil2' will always require the GIL to be acquired. -49:28: Exception check will always require the GIL to be acquired. -51:29: Exception check will always require the GIL to be acquired. +49:28: Exception check after calling 'release_gil_in_nogil' will always require the GIL to be acquired. +51:29: Exception check after calling 'release_gil_in_nogil2' will always require the GIL to be acquired. 55:5: Exception check on 'get_gil_in_nogil' will always require the GIL to be acquired. 59:6: Exception check on 'get_gil_in_nogil2' will always require the GIL to be acquired. -68:24: Exception check will always require the GIL to be acquired. -70:25: Exception check will always require the GIL to be acquired. +68:24: Exception check after calling 'get_gil_in_nogil' will always require the GIL to be acquired. +70:25: Exception check after calling 'get_gil_in_nogil2' will always require the GIL to be acquired. 133:5: Exception check on 'copy_array_exception' will always require the GIL to be acquired. -184:28: Exception check will always require the GIL to be acquired. +184:28: Exception check after calling 'copy_array_exception' will always require the GIL to be acquired. 187:5: Exception check on 'voidexceptnogil_in_pxd' will always require the GIL to be acquired. -195:30: Exception check will always require the GIL to be acquired. -198:36: Exception check will always require the GIL to be acquired. +195:30: Exception check after calling 'voidexceptnogil_in_pxd' will always require the GIL to be acquired. +198:36: Exception check after calling 'voidexceptnogil_in_other_pxd' will always require the GIL to be acquired. """ From 1db67b93e126d6eb2ccbe09ac372965a07132c70 Mon Sep 17 00:00:00 2001 From: da-woods Date: Fri, 1 Dec 2023 07:48:05 +0000 Subject: [PATCH 028/286] Clarify performance hint wording (#5883) * Clarify performance hint wording I'm getting the impression that it this performance hint isn't sufficiently clear about what it wants you to modify. * Update Cython/Compiler/PyrexTypes.py Co-authored-by: Matus Valo * Simpify implementation * Fiddle with escaping of function_name --- Cython/Compiler/ExprNodes.py | 4 +++- Cython/Compiler/PyrexTypes.py | 23 ++++++++++++++++++----- tests/run/nogil.pyx | 16 ++++++++-------- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 6b5b2b75a3f..775d2f582fd 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -6482,9 +6482,11 @@ def generate_result_code(self, code): if exc_check: if nogil: if not exc_checks: + perf_hint_entry = getattr(self.function, "entry", None) PyrexTypes.write_noexcept_performance_hint( self.pos, code.funcstate.scope, - function_name=None, void_return=self.type.is_void) + function_name=perf_hint_entry.name if perf_hint_entry else None, + void_return=self.type.is_void, is_call=True) code.globalstate.use_utility_code( UtilityCode.load_cached("ErrOccurredWithGIL", "Exceptions.c")) exc_checks.append("__Pyx_ErrOccurredWithGIL()") diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 8d3ea552d7b..229985703d2 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -5438,16 +5438,29 @@ def cap_length(s, max_prefix=63, max_len=1024): hash_prefix = hashlib.sha256(s.encode('ascii')).hexdigest()[:6] return '%s__%s__etc' % (hash_prefix, s[:max_len-17]) -def write_noexcept_performance_hint(pos, env, function_name=None, void_return=False): - on_what = "on '%s' " % function_name if function_name else "" +def write_noexcept_performance_hint(pos, env, function_name=None, void_return=False, is_call=False): + if function_name: + # we need it escaped everywhere we use it + function_name = "'%s'" % function_name + if is_call: + on_what = "after calling %s " % (function_name or 'function') + elif function_name: + on_what = "on %s " % function_name + else: + on_what ='' msg = ( "Exception check %swill always require the GIL to be acquired." ) % on_what - solutions = ["Declare the function as 'noexcept' if you control the definition and " - "you're sure you don't want the function to raise exceptions."] + the_function = function_name if function_name else "the function" + if is_call and not function_name: + the_function = the_function + " you are calling" + solutions = ["Declare %s as 'noexcept' if you control the definition and " + "you're sure you don't want the function to raise exceptions." + % the_function] if void_return: solutions.append( - "Use an 'int' return type on the function to allow an error code to be returned.") + "Use an 'int' return type on %s to allow an error code to be returned." % + the_function) if len(solutions) == 1: msg = "%s %s" % (msg, solutions[0]) else: diff --git a/tests/run/nogil.pyx b/tests/run/nogil.pyx index 0c1bfb0ceef..feed30ed324 100644 --- a/tests/run/nogil.pyx +++ b/tests/run/nogil.pyx @@ -200,19 +200,19 @@ def test_performance_hint_nogil(): # Note that we're only able to check the first line of the performance hint _PERFORMANCE_HINTS = """ -20:9: Exception check will always require the GIL to be acquired. +20:9: Exception check after calling 'f' will always require the GIL to be acquired. 24:5: Exception check on 'f' will always require the GIL to be acquired. 34:5: Exception check on 'release_gil_in_nogil' will always require the GIL to be acquired. 39:6: Exception check on 'release_gil_in_nogil2' will always require the GIL to be acquired. -49:28: Exception check will always require the GIL to be acquired. -51:29: Exception check will always require the GIL to be acquired. +49:28: Exception check after calling 'release_gil_in_nogil' will always require the GIL to be acquired. +51:29: Exception check after calling 'release_gil_in_nogil2' will always require the GIL to be acquired. 55:5: Exception check on 'get_gil_in_nogil' will always require the GIL to be acquired. 59:6: Exception check on 'get_gil_in_nogil2' will always require the GIL to be acquired. -68:24: Exception check will always require the GIL to be acquired. -70:25: Exception check will always require the GIL to be acquired. +68:24: Exception check after calling 'get_gil_in_nogil' will always require the GIL to be acquired. +70:25: Exception check after calling 'get_gil_in_nogil2' will always require the GIL to be acquired. 133:5: Exception check on 'copy_array_exception' will always require the GIL to be acquired. -184:28: Exception check will always require the GIL to be acquired. +184:28: Exception check after calling 'copy_array_exception' will always require the GIL to be acquired. 187:5: Exception check on 'voidexceptnogil_in_pxd' will always require the GIL to be acquired. -195:30: Exception check will always require the GIL to be acquired. -198:36: Exception check will always require the GIL to be acquired. +195:30: Exception check after calling 'voidexceptnogil_in_pxd' will always require the GIL to be acquired. +198:36: Exception check after calling 'voidexceptnogil_in_other_pxd' will always require the GIL to be acquired. """ From 6fbcbb5b701aa58fc6a13de7b2f64b0f531420da Mon Sep 17 00:00:00 2001 From: Matus Valo Date: Fri, 1 Dec 2023 09:50:56 +0100 Subject: [PATCH 029/286] Modernise documentation examples (#5878) * from __future__ import print_function was removed * % operator replaced with f-string --- docs/examples/quickstart/build/hello.pyx | 2 +- docs/examples/tutorial/clibraries/test_queue.py | 2 -- docs/examples/userguide/extension_types/cheesy.py | 2 +- docs/examples/userguide/extension_types/cheesy.pyx | 2 +- .../examples/userguide/extension_types/my_module.pyx | 2 -- .../examples/userguide/extension_types/shrubbery.pyx | 1 - .../userguide/language_basics/cdef_block.pyx | 2 -- .../userguide/language_basics/compile_time.pyx | 2 -- .../language_basics/optional_subclassing.py | 2 -- .../language_basics/optional_subclassing.pyx | 4 +--- docs/examples/userguide/language_basics/override.py | 2 -- docs/examples/userguide/language_basics/override.pyx | 4 +--- .../userguide/language_basics/parameter_refcount.py | 2 -- .../userguide/language_basics/parameter_refcount.pyx | 2 -- docs/examples/userguide/memoryviews/quickstart.py | 12 ++++++------ docs/examples/userguide/memoryviews/quickstart.pyx | 12 ++++++------ .../userguide/numpy_tutorial/numpy_and_cython.ipynb | 1 - docs/src/userguide/extension_types.rst | 2 -- 18 files changed, 17 insertions(+), 41 deletions(-) diff --git a/docs/examples/quickstart/build/hello.pyx b/docs/examples/quickstart/build/hello.pyx index da1b827ac9a..a5cf55e7ec4 100644 --- a/docs/examples/quickstart/build/hello.pyx +++ b/docs/examples/quickstart/build/hello.pyx @@ -1,2 +1,2 @@ def say_hello_to(name): - print("Hello %s!" % name) + print(f"Hello {name}!") diff --git a/docs/examples/tutorial/clibraries/test_queue.py b/docs/examples/tutorial/clibraries/test_queue.py index 41b267395cf..d42c75cb99f 100644 --- a/docs/examples/tutorial/clibraries/test_queue.py +++ b/docs/examples/tutorial/clibraries/test_queue.py @@ -1,5 +1,3 @@ -from __future__ import print_function - import time import queue diff --git a/docs/examples/userguide/extension_types/cheesy.py b/docs/examples/userguide/extension_types/cheesy.py index 0995c399356..d0b59d8237c 100644 --- a/docs/examples/userguide/extension_types/cheesy.py +++ b/docs/examples/userguide/extension_types/cheesy.py @@ -10,7 +10,7 @@ def __cinit__(self): @property def cheese(self): - return "We don't have: %s" % self.cheeses + return f"We don't have: {self.cheeses}" @cheese.setter def cheese(self, value): diff --git a/docs/examples/userguide/extension_types/cheesy.pyx b/docs/examples/userguide/extension_types/cheesy.pyx index 2859d848fd8..f697e41ac4a 100644 --- a/docs/examples/userguide/extension_types/cheesy.pyx +++ b/docs/examples/userguide/extension_types/cheesy.pyx @@ -10,7 +10,7 @@ cdef class CheeseShop: @property def cheese(self): - return "We don't have: %s" % self.cheeses + return f"We don't have: {self.cheeses}" @cheese.setter def cheese(self, value): diff --git a/docs/examples/userguide/extension_types/my_module.pyx b/docs/examples/userguide/extension_types/my_module.pyx index fb0701c12f0..95bb2e15169 100644 --- a/docs/examples/userguide/extension_types/my_module.pyx +++ b/docs/examples/userguide/extension_types/my_module.pyx @@ -1,5 +1,3 @@ -from __future__ import print_function - cdef class Shrubbery: def __init__(self, w, h): diff --git a/docs/examples/userguide/extension_types/shrubbery.pyx b/docs/examples/userguide/extension_types/shrubbery.pyx index 8c4e587764b..1b2d883bbbe 100644 --- a/docs/examples/userguide/extension_types/shrubbery.pyx +++ b/docs/examples/userguide/extension_types/shrubbery.pyx @@ -1,4 +1,3 @@ -from __future__ import print_function cdef class Shrubbery: cdef int width cdef int height diff --git a/docs/examples/userguide/language_basics/cdef_block.pyx b/docs/examples/userguide/language_basics/cdef_block.pyx index c0c30302921..4753f8cd710 100644 --- a/docs/examples/userguide/language_basics/cdef_block.pyx +++ b/docs/examples/userguide/language_basics/cdef_block.pyx @@ -1,5 +1,3 @@ -from __future__ import print_function - cdef: struct Spam: int tons diff --git a/docs/examples/userguide/language_basics/compile_time.pyx b/docs/examples/userguide/language_basics/compile_time.pyx index f302dd241b5..d05c2a264c6 100644 --- a/docs/examples/userguide/language_basics/compile_time.pyx +++ b/docs/examples/userguide/language_basics/compile_time.pyx @@ -1,5 +1,3 @@ -from __future__ import print_function - DEF FavouriteFood = u"spam" DEF ArraySize = 42 DEF OtherArraySize = 2 * ArraySize + 17 diff --git a/docs/examples/userguide/language_basics/optional_subclassing.py b/docs/examples/userguide/language_basics/optional_subclassing.py index 480ae100b65..1f2d25d458a 100644 --- a/docs/examples/userguide/language_basics/optional_subclassing.py +++ b/docs/examples/userguide/language_basics/optional_subclassing.py @@ -1,5 +1,3 @@ -from __future__ import print_function - @cython.cclass class A: @cython.cfunc diff --git a/docs/examples/userguide/language_basics/optional_subclassing.pyx b/docs/examples/userguide/language_basics/optional_subclassing.pyx index b2a3d4decca..1eef3affd0e 100644 --- a/docs/examples/userguide/language_basics/optional_subclassing.pyx +++ b/docs/examples/userguide/language_basics/optional_subclassing.pyx @@ -1,6 +1,4 @@ -from __future__ import print_function - - + cdef class A: cdef foo(self): diff --git a/docs/examples/userguide/language_basics/override.py b/docs/examples/userguide/language_basics/override.py index f9e0be83fa7..540f79429de 100644 --- a/docs/examples/userguide/language_basics/override.py +++ b/docs/examples/userguide/language_basics/override.py @@ -1,5 +1,3 @@ -from __future__ import print_function - @cython.cclass class A: @cython.cfunc diff --git a/docs/examples/userguide/language_basics/override.pyx b/docs/examples/userguide/language_basics/override.pyx index 1a7ceefb70b..0e57c43ca7e 100644 --- a/docs/examples/userguide/language_basics/override.pyx +++ b/docs/examples/userguide/language_basics/override.pyx @@ -1,6 +1,4 @@ -from __future__ import print_function - - + cdef class A: cdef foo(self): diff --git a/docs/examples/userguide/language_basics/parameter_refcount.py b/docs/examples/userguide/language_basics/parameter_refcount.py index 2b25915ba90..bcca63906c2 100644 --- a/docs/examples/userguide/language_basics/parameter_refcount.py +++ b/docs/examples/userguide/language_basics/parameter_refcount.py @@ -1,5 +1,3 @@ -from __future__ import print_function - from cython.cimports.cpython.ref import PyObject import sys diff --git a/docs/examples/userguide/language_basics/parameter_refcount.pyx b/docs/examples/userguide/language_basics/parameter_refcount.pyx index 6fe3ffadd58..f42e9c391a0 100644 --- a/docs/examples/userguide/language_basics/parameter_refcount.pyx +++ b/docs/examples/userguide/language_basics/parameter_refcount.pyx @@ -1,5 +1,3 @@ -from __future__ import print_function - from cpython.ref cimport PyObject import sys diff --git a/docs/examples/userguide/memoryviews/quickstart.py b/docs/examples/userguide/memoryviews/quickstart.py index 4f31185b27f..cc575489026 100644 --- a/docs/examples/userguide/memoryviews/quickstart.py +++ b/docs/examples/userguide/memoryviews/quickstart.py @@ -14,7 +14,7 @@ cyarr_view = cython.declare(cython.int[:, :, :], cyarr) # Show the sum of all the arrays before altering it -print("NumPy sum of the NumPy array before assignments: %s" % narr.sum()) +print(f"NumPy sum of the NumPy array before assignments: {narr.sum()}") # We can copy the values from one memoryview into another using a single # statement, by either indexing with ... or (NumPy-style) with a colon. @@ -28,7 +28,7 @@ cyarr_view[0, 0, 0] = 1000 # Assigning into the memoryview on the NumPy array alters the latter -print("NumPy sum of NumPy array after assignments: %s" % narr.sum()) +print(f"NumPy sum of NumPy array after assignments: {narr.sum()}") # A function using a memoryview does not usually need the GIL @cython.nogil @@ -52,8 +52,8 @@ def sum3d(arr: cython.int[:, :, :]) -> cython.int: # A function accepting a memoryview knows how to use a NumPy array, # a C array, a Cython array... -print("Memoryview sum of NumPy array is %s" % sum3d(narr)) -print("Memoryview sum of C array is %s" % sum3d(carr)) -print("Memoryview sum of Cython array is %s" % sum3d(cyarr)) +print(f"Memoryview sum of NumPy array is {sum3d(narr)}") +print(f"Memoryview sum of C array is {sum3d(carr)}") +print(f"Memoryview sum of Cython array is {sum3d(cyarr)}") # ... and of course, a memoryview. -print("Memoryview sum of C memoryview is %s" % sum3d(carr_view)) +print(f"Memoryview sum of C memoryview is {sum3d(carr_view)}") diff --git a/docs/examples/userguide/memoryviews/quickstart.pyx b/docs/examples/userguide/memoryviews/quickstart.pyx index 0ba012b1a1b..73a3e4cdb71 100644 --- a/docs/examples/userguide/memoryviews/quickstart.pyx +++ b/docs/examples/userguide/memoryviews/quickstart.pyx @@ -14,7 +14,7 @@ cyarr = cvarray(shape=(3, 3, 3), itemsize=sizeof(int), format="i") cdef int [:, :, :] cyarr_view = cyarr # Show the sum of all the arrays before altering it -print("NumPy sum of the NumPy array before assignments: %s" % narr.sum()) +print(f"NumPy sum of the NumPy array before assignments: {narr.sum()}") # We can copy the values from one memoryview into another using a single # statement, by either indexing with ... or (NumPy-style) with a colon. @@ -28,7 +28,7 @@ carr_view[0, 0, 0] = 100 cyarr_view[0, 0, 0] = 1000 # Assigning into the memoryview on the NumPy array alters the latter -print("NumPy sum of NumPy array after assignments: %s" % narr.sum()) +print(f"NumPy sum of NumPy array after assignments: {narr.sum()}") # A function using a memoryview does not usually need the GIL cpdef int sum3d(int[:, :, :] arr) nogil: @@ -52,8 +52,8 @@ cpdef int sum3d(int[:, :, :] arr) nogil: # A function accepting a memoryview knows how to use a NumPy array, # a C array, a Cython array... -print("Memoryview sum of NumPy array is %s" % sum3d(narr)) -print("Memoryview sum of C array is %s" % sum3d(carr)) -print("Memoryview sum of Cython array is %s" % sum3d(cyarr)) +print(f"Memoryview sum of NumPy array is {sum3d(narr)}") +print(f"Memoryview sum of C array is {sum3d(carr)}") +print(f"Memoryview sum of Cython array is {sum3d(cyarr)}") # ... and of course, a memoryview. -print("Memoryview sum of C memoryview is %s" % sum3d(carr_view)) +print(f"Memoryview sum of C memoryview is {sum3d(carr_view)}") diff --git a/docs/examples/userguide/numpy_tutorial/numpy_and_cython.ipynb b/docs/examples/userguide/numpy_tutorial/numpy_and_cython.ipynb index e2fc5877343..d5b8e05699c 100644 --- a/docs/examples/userguide/numpy_tutorial/numpy_and_cython.ipynb +++ b/docs/examples/userguide/numpy_tutorial/numpy_and_cython.ipynb @@ -37,7 +37,6 @@ } ], "source": [ - "from __future__ import print_function\n", "%load_ext cython\n", "import Cython\n", "print(Cython.__version__)" diff --git a/docs/src/userguide/extension_types.rst b/docs/src/userguide/extension_types.rst index 09720c36ca6..4b3cf4eb50a 100644 --- a/docs/src/userguide/extension_types.rst +++ b/docs/src/userguide/extension_types.rst @@ -1034,8 +1034,6 @@ objects defined in the Python core or in a non-Cython extension module. Here is an example which will let you get at the C-level members of the built-in complex object:: - from __future__ import print_function - cdef extern from "complexobject.h": struct Py_complex: From 9b2d0a637fd35413d34a9d98e938bbdaa2bdd725 Mon Sep 17 00:00:00 2001 From: da-woods Date: Fri, 1 Dec 2023 11:16:45 +0000 Subject: [PATCH 030/286] Fix dataclasses __init__ from field with default and no-init (#5858) This should allow a field without a default argument afterwards (because the field doesn't appear as an argument in `__init__`). Also add a few notes on non-working tests from CPython. --- Cython/Compiler/Dataclass.py | 3 ++- Tools/make_dataclass_tests.py | 8 ++++++-- tests/run/pure_cdef_class_dataclass.py | 11 +++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Cython/Compiler/Dataclass.py b/Cython/Compiler/Dataclass.py index 39dbcb733c3..49ba0c562f1 100644 --- a/Cython/Compiler/Dataclass.py +++ b/Cython/Compiler/Dataclass.py @@ -419,7 +419,8 @@ def generate_init_code(code, init, node, fields, kw_only): annotation = "" assignment = '' if field.default is not MISSING or field.default_factory is not MISSING: - seen_default = True + if field.init.value: + seen_default = True if field.default_factory is not MISSING: ph_name = default_factory_placeholder else: diff --git a/Tools/make_dataclass_tests.py b/Tools/make_dataclass_tests.py index 7ad32427f9f..2dec4211928 100644 --- a/Tools/make_dataclass_tests.py +++ b/Tools/make_dataclass_tests.py @@ -49,12 +49,17 @@ ("TestKeywordArgs", "test_KW_ONLY_twice"), ("TestKeywordArgs", "test_defaults"), # uses local variable in class definition + # Also: difficulty lining up correct repr string when converting tests ("TestCase", "test_default_factory"), + # Also: Mock unassignable to list - legitimate for Cython to raise an error ("TestCase", "test_default_factory_with_no_init"), + # Also: attributes not available on class itself, only instances ("TestCase", "test_field_default"), ("TestCase", "test_function_annotations"), ("TestDescriptors", "test_lookup_on_instance"), + # Also: Mock unassignable to int - legitimate for Cython to raise an error ("TestCase", "test_default_factory_not_called_if_value_given"), + # Also: cdef classes never don't have the attribute ("TestCase", "test_class_attrs"), ("TestCase", "test_hash_field_rules"), ("TestStringAnnotations",), # almost all the texts here use local variables @@ -79,6 +84,7 @@ "test_class_var_frozen", ), # __annotations__ not present on cdef classes https://github.com/cython/cython/issues/4519 ("TestCase", "test_dont_include_other_annotations"), # __annotations__ + ("TestCase", "test_class_marker"), # __annotations__ ("TestDocString",), # don't think cython dataclasses currently set __doc__ # either cython.dataclasses.field or cython.dataclasses.dataclass called directly as functions # (will probably never be supported) @@ -162,8 +168,6 @@ ("TestReplace", "test_initvar_with_default_value"), # needs investigating # Maybe bugs? # ========== - # non-default argument 'z' follows default argument in dataclass __init__ - this message looks right to me! - ("TestCase", "test_class_marker"), # cython.dataclasses.field parameter 'metadata' must be a literal value - possibly not something we can support? ("TestCase", "test_field_metadata_custom_mapping"), ( diff --git a/tests/run/pure_cdef_class_dataclass.py b/tests/run/pure_cdef_class_dataclass.py index f8136958e1c..54f5ff957d7 100644 --- a/tests/run/pure_cdef_class_dataclass.py +++ b/tests/run/pure_cdef_class_dataclass.py @@ -76,3 +76,14 @@ def __post_init__(self): # and not initializing it will mess up repr assert not hasattr(self, "neither") self.neither = None + + +@cython.dataclasses.dataclass +class NonInitDefaultArgument: + """ + >>> NonInitDefaultArgument(1.0, "hello") + NonInitDefaultArgument(x=1.0, y=10, z='hello') + """ + x: float + y: int = cython.dataclasses.field(default=10, init=False) + z: str # This is allowed despite following a default argument, because the default argument isn't in init From 7d0d519b0cd5fd7ecf94a8ce2acde5026f345902 Mon Sep 17 00:00:00 2001 From: da-woods Date: Sat, 2 Dec 2023 10:29:00 +0000 Subject: [PATCH 031/286] Get all Plex/*.so modules to build in the Limited API (GH-5846) This PR (on top of https://github.com/cython/cython/pull/5845 and its dependencies) is sufficient to compile all the built modules in the Plex folder of Cython. --- Cython/Compiler/ModuleNode.py | 55 ++++++++++++++++++------ Cython/Compiler/Nodes.py | 17 ++++---- Cython/Compiler/TypeSlots.py | 8 +++- Cython/Utility/ExtensionTypes.c | 12 ++---- Cython/Utility/ImportExport.c | 72 ++++++++++++++++++++++++-------- Cython/Utility/ModuleSetupCode.c | 4 +- 6 files changed, 120 insertions(+), 48 deletions(-) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 7826b478149..eac4c858cc7 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -1780,9 +1780,15 @@ def generate_dealloc_function(self, scope, code): # cimported base type pointer directly interacts badly with # the module cleanup, which may already have cleared it. # In that case, fall back to traversing the type hierarchy. - code.putln("if (likely(%s)) __Pyx_PyType_GetSlot(%s, tp_dealloc, destructor)(o); " - "else __Pyx_call_next_tp_dealloc(o, %s);" % ( - base_cname, base_cname, slot_func_cname)) + # If we're using the module state then always go through the + # type hierarchy, because our access to the module state may + # have been lost (at least for the limited API version of + # using module state). + code.putln("#if !CYTHON_USE_MODULE_STATE") + code.putln("if (likely(%s)) __Pyx_PyType_GetSlot(%s, tp_dealloc, destructor)(o); else" % ( + base_cname, base_cname)) + code.putln("#endif") + code.putln("__Pyx_call_next_tp_dealloc(o, %s);" % slot_func_cname) code.globalstate.use_utility_code( UtilityCode.load_cached("CallNextTpDealloc", "ExtensionTypes.c")) else: @@ -1870,18 +1876,31 @@ def generate_traverse_function(self, scope, code, cclass_entry): code.putln("e = %s(o, v, a); if (e) return e;" % static_call) elif base_type.is_builtin_type: base_cname = base_type.typeptr_cname - code.putln("if (!%s->tp_traverse); else { e = %s->tp_traverse(o,v,a); if (e) return e; }" % ( - base_cname, base_cname)) + code.putln("{") + code.putln( + f"traverseproc traverse = __Pyx_PyType_GetSlot({base_cname}, tp_traverse, traverseproc);") + code.putln("if (!traverse); else { e = traverse(o,v,a); if (e) return e; }") + code.putln("}") else: # This is an externally defined type. Calling through the # cimported base type pointer directly interacts badly with # the module cleanup, which may already have cleared it. # In that case, fall back to traversing the type hierarchy. + # If we're using the module state then always go through the + # type hierarchy, because our access to the module state may + # have been lost (at least for the limited API version of + # using module state). base_cname = base_type.typeptr_cname + code.putln("#if !CYTHON_USE_MODULE_STATE") + code.putln("e = 0;") + code.putln("if (likely(%s)) {" % base_cname) code.putln( - "e = ((likely(%s)) ? ((%s->tp_traverse) ? %s->tp_traverse(o, v, a) : 0) : " - "__Pyx_call_next_tp_traverse(o, v, a, %s)); if (e) return e;" % ( - base_cname, base_cname, base_cname, slot_func)) + f"traverseproc traverse = __Pyx_PyType_GetSlot({base_cname}, tp_traverse, traverseproc);") + code.putln("if (traverse) { e = traverse(o, v, a); }") + code.putln("} else") + code.putln("#endif") + code.putln("{ e = __Pyx_call_next_tp_traverse(o, v, a, %s); }" % slot_func) + code.putln("if (e) return e;") code.globalstate.use_utility_code( UtilityCode.load_cached("CallNextTpTraverse", "ExtensionTypes.c")) @@ -1936,17 +1955,27 @@ def generate_clear_function(self, scope, code, cclass_entry): code.putln("%s(o);" % static_call) elif base_type.is_builtin_type: base_cname = base_type.typeptr_cname - code.putln("if (!%s->tp_clear); else %s->tp_clear(o);" % ( - base_cname, base_cname)) + code.putln("{") + code.putln(f"inquiry clear = __Pyx_PyType_GetSlot({base_cname}, tp_clear, inquiry);") + code.putln("if (clear) clear(o);") + code.putln("}") else: # This is an externally defined type. Calling through the # cimported base type pointer directly interacts badly with # the module cleanup, which may already have cleared it. # In that case, fall back to traversing the type hierarchy. + # If we're using the module state then always go through the + # type hierarchy, because our access to the module state may + # have been lost (at least for the limited API version of + # using module state). base_cname = base_type.typeptr_cname - code.putln( - "if (likely(%s)) { if (%s->tp_clear) %s->tp_clear(o); } else __Pyx_call_next_tp_clear(o, %s);" % ( - base_cname, base_cname, base_cname, slot_func)) + code.putln("#if !CYTHON_USE_MODULE_STATE") + code.putln("if (likely(%s)) {" % base_cname) + code.putln(f"inquiry clear = __Pyx_PyType_GetSlot({base_cname}, tp_clear, inquiry);") + code.putln("if (clear) clear(o);") + code.putln("} else") + code.putln("#endif") + code.putln("{ __Pyx_call_next_tp_clear(o, %s); }" % slot_func) code.globalstate.use_utility_code( UtilityCode.load_cached("CallNextTpClear", "ExtensionTypes.c")) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 10afc0971ce..fc4a31f180b 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -4926,9 +4926,15 @@ def generate_execution_code(self, code): if self.py_func.is_module_scope: code.putln("else {") else: - code.putln("else if (unlikely((Py_TYPE(%s)->tp_dictoffset != 0) || " - "__Pyx_PyType_HasFeature(Py_TYPE(%s), (Py_TPFLAGS_IS_ABSTRACT | Py_TPFLAGS_HEAPTYPE)))) {" % ( - self_arg, self_arg)) + code.putln("else if (") + code.putln("#if CYTHON_USE_TYPE_SLOTS || CYTHON_COMPILING_IN_PYPY") + code.putln(f"unlikely(Py_TYPE({self_arg})->tp_dictoffset != 0)") + code.putln("#else") + dict_str_const = code.get_py_string_const(EncodedString("__dict__")) + code.putln(f'PyObject_HasAttr({self_arg}, {dict_str_const})') + code.putln("#endif") + code.putln(" || unlikely(__Pyx_PyType_HasFeature(Py_TYPE(%s), (Py_TPFLAGS_IS_ABSTRACT | Py_TPFLAGS_HEAPTYPE)))) {" % ( + self_arg)) code.putln("#if CYTHON_USE_DICT_VERSIONS && CYTHON_USE_PYTYPE_LOOKUP && CYTHON_USE_TYPE_SLOTS") code.globalstate.use_utility_code( @@ -5784,12 +5790,9 @@ def generate_type_ready_code(entry, code, bases_tuple_cname=None, check_heap_typ typeptr_cname, type.vtabptr_cname, )) - # TODO: find a way to make this work with the Limited API! - code.putln("#if !CYTHON_COMPILING_IN_LIMITED_API") code.globalstate.use_utility_code( UtilityCode.load_cached('MergeVTables', 'ImportExport.c')) code.put_error_if_neg(entry.pos, "__Pyx_MergeVtables(%s)" % typeptr_cname) - code.putln("#endif") if not type.scope.is_internal and not type.scope.directives.get('internal'): # scope.is_internal is set for types defined by # Cython (such as closures), the 'internal' @@ -5822,9 +5825,7 @@ def generate_type_ready_code(entry, code, bases_tuple_cname=None, check_heap_typ # do so at runtime. code.globalstate.use_utility_code( UtilityCode.load_cached('SetupReduce', 'ExtensionTypes.c')) - code.putln("#if !CYTHON_COMPILING_IN_LIMITED_API") # FIXME code.put_error_if_neg(entry.pos, "__Pyx_setup_reduce((PyObject *) %s)" % typeptr_cname) - code.putln("#endif") def annotate(self, code): if self.type_init_args: diff --git a/Cython/Compiler/TypeSlots.py b/Cython/Compiler/TypeSlots.py index 3c890e56584..148070629d6 100644 --- a/Cython/Compiler/TypeSlots.py +++ b/Cython/Compiler/TypeSlots.py @@ -272,7 +272,13 @@ def generate_spec(self, scope, code): preprocessor_guard = "#if defined(Py_%s)" % self.slot_name if preprocessor_guard: code.putln(preprocessor_guard) + if self.used_ifdef: + # different from preprocessor guard - this defines if we *want* to define it, + # rather than if the slot exists + code.putln(f"#if {self.used_ifdef}") code.putln("{Py_%s, (void *)%s}," % (self.slot_name, value)) + if self.used_ifdef: + code.putln("#endif") if preprocessor_guard: code.putln("#endif") @@ -1096,7 +1102,7 @@ def __init__(self, old_binops): EmptySlot("tp_weaklist"), EmptySlot("tp_del"), EmptySlot("tp_version_tag"), - SyntheticSlot("tp_finalize", ["__del__"], "0", ifdef="PY_VERSION_HEX >= 0x030400a1", + SyntheticSlot("tp_finalize", ["__del__"], "0", used_ifdef="CYTHON_USE_TP_FINALIZE"), EmptySlot("tp_vectorcall", ifdef="PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800)"), EmptySlot("tp_print", ifdef="__PYX_NEED_TP_PRINT_SLOT == 1"), diff --git a/Cython/Utility/ExtensionTypes.c b/Cython/Utility/ExtensionTypes.c index 2b3e0a10e14..f23ff139782 100644 --- a/Cython/Utility/ExtensionTypes.c +++ b/Cython/Utility/ExtensionTypes.c @@ -416,16 +416,13 @@ static void __Pyx_call_next_tp_clear(PyObject* obj, inquiry current_tp_clear) { /////////////// SetupReduce.proto /////////////// -#if !CYTHON_COMPILING_IN_LIMITED_API static int __Pyx_setup_reduce(PyObject* type_obj); -#endif /////////////// SetupReduce /////////////// //@requires: ObjectHandling.c::PyObjectGetAttrStrNoError //@requires: ObjectHandling.c::PyObjectGetAttrStr //@substitute: naming -#if !CYTHON_COMPILING_IN_LIMITED_API static int __Pyx_setup_reduce_is_named(PyObject* meth, PyObject* name) { int ret; PyObject *name_attr; @@ -500,8 +497,8 @@ static int __Pyx_setup_reduce(PyObject* type_obj) { if (reduce == object_reduce || __Pyx_setup_reduce_is_named(reduce, PYIDENT("__reduce_cython__"))) { reduce_cython = __Pyx_PyObject_GetAttrStrNoError(type_obj, PYIDENT("__reduce_cython__")); if (likely(reduce_cython)) { - ret = PyDict_SetItem(((PyTypeObject*)type_obj)->tp_dict, PYIDENT("__reduce__"), reduce_cython); if (unlikely(ret < 0)) goto __PYX_BAD; - ret = PyDict_DelItem(((PyTypeObject*)type_obj)->tp_dict, PYIDENT("__reduce_cython__")); if (unlikely(ret < 0)) goto __PYX_BAD; + ret = __Pyx_SetItemOnTypeDict((PyTypeObject*)type_obj, PYIDENT("__reduce__"), reduce_cython); if (unlikely(ret < 0)) goto __PYX_BAD; + ret = __Pyx_DelItemOnTypeDict((PyTypeObject*)type_obj, PYIDENT("__reduce_cython__")); if (unlikely(ret < 0)) goto __PYX_BAD; } else if (reduce == object_reduce || PyErr_Occurred()) { // Ignore if we're done, i.e. if 'reduce' already has the right name and the original is gone. // Otherwise: error. @@ -513,8 +510,8 @@ static int __Pyx_setup_reduce(PyObject* type_obj) { if (!setstate || __Pyx_setup_reduce_is_named(setstate, PYIDENT("__setstate_cython__"))) { setstate_cython = __Pyx_PyObject_GetAttrStrNoError(type_obj, PYIDENT("__setstate_cython__")); if (likely(setstate_cython)) { - ret = PyDict_SetItem(((PyTypeObject*)type_obj)->tp_dict, PYIDENT("__setstate__"), setstate_cython); if (unlikely(ret < 0)) goto __PYX_BAD; - ret = PyDict_DelItem(((PyTypeObject*)type_obj)->tp_dict, PYIDENT("__setstate_cython__")); if (unlikely(ret < 0)) goto __PYX_BAD; + ret = __Pyx_SetItemOnTypeDict((PyTypeObject*)type_obj, PYIDENT("__setstate__"), setstate_cython); if (unlikely(ret < 0)) goto __PYX_BAD; + ret = __Pyx_DelItemOnTypeDict((PyTypeObject*)type_obj, PYIDENT("__setstate_cython__")); if (unlikely(ret < 0)) goto __PYX_BAD; } else if (!setstate || PyErr_Occurred()) { // Ignore if we're done, i.e. if 'setstate' already has the right name and the original is gone. // Otherwise: error. @@ -549,7 +546,6 @@ static int __Pyx_setup_reduce(PyObject* type_obj) { Py_XDECREF(setstate_cython); return ret; } -#endif /////////////// BinopSlot /////////////// diff --git a/Cython/Utility/ImportExport.c b/Cython/Utility/ImportExport.c index 72e8d673687..670197ae1b0 100644 --- a/Cython/Utility/ImportExport.c +++ b/Cython/Utility/ImportExport.c @@ -753,27 +753,24 @@ static void* __Pyx_GetVtable(PyTypeObject *type) { /////////////// MergeVTables.proto /////////////// //@requires: GetVTable -// TODO: find a way to make this work with the Limited API! -#if !CYTHON_COMPILING_IN_LIMITED_API static int __Pyx_MergeVtables(PyTypeObject *type); /*proto*/ -#endif /////////////// MergeVTables /////////////// -#if !CYTHON_COMPILING_IN_LIMITED_API static int __Pyx_MergeVtables(PyTypeObject *type) { - int i; + int i=0; + Py_ssize_t size; void** base_vtables; - __Pyx_TypeName tp_base_name; - __Pyx_TypeName base_name; + __Pyx_TypeName tp_base_name = NULL; + __Pyx_TypeName base_name = NULL; void* unknown = (void*)-1; - PyObject* bases = type->tp_bases; + PyObject* bases = __Pyx_PyType_GetSlot(type, tp_bases, PyObject*); int base_depth = 0; { - PyTypeObject* base = type->tp_base; + PyTypeObject* base = __Pyx_PyType_GetSlot(type, tp_base, PyTypeObject*); while (base) { base_depth += 1; - base = base->tp_base; + base = __Pyx_PyType_GetSlot(base, tp_base, PyTypeObject*); } } base_vtables = (void**) malloc(sizeof(void*) * (size_t)(base_depth + 1)); @@ -784,11 +781,31 @@ static int __Pyx_MergeVtables(PyTypeObject *type) { // resolution isn't possible and we must reject it just as when the // instance struct is so extended. (It would be good to also do this // check when a multiple-base class is created in pure Python as well.) - for (i = 1; i < PyTuple_GET_SIZE(bases); i++) { - void* base_vtable = __Pyx_GetVtable(((PyTypeObject*)PyTuple_GET_ITEM(bases, i))); +#if CYTHON_COMPILING_IN_LIMITED_API + size = PyTuple_Size(bases); + if (size < 0) goto other_failure; +#else + size = PyTuple_GET_SIZE(bases); +#endif + for (i = 1; i < size; i++) { + PyObject *basei; + void* base_vtable; +#if CYTHON_AVOID_BORROWED_REFS + basei = PySequence_GetItem(bases, i); + if (unlikely(!basei)) goto other_failure; +#elif !CYTHON_ASSUME_SAFE_MACROS + basei = PyTuple_GetItem(bases, i); + if (unlikely(!basei)) goto other_failure; +#else + basei = PyTuple_GET_ITEM(bases, i); +#endif + base_vtable = __Pyx_GetVtable((PyTypeObject*)basei); +#if CYTHON_AVOID_BORROWED_REFS + Py_DECREF(basei); +#endif if (base_vtable != NULL) { int j; - PyTypeObject* base = type->tp_base; + PyTypeObject* base = __Pyx_PyType_GetSlot(type, tp_base, PyTypeObject*); for (j = 0; j < base_depth; j++) { if (base_vtables[j] == unknown) { base_vtables[j] = __Pyx_GetVtable(base); @@ -800,7 +817,7 @@ static int __Pyx_MergeVtables(PyTypeObject *type) { // No more potential matching bases (with vtables). goto bad; } - base = base->tp_base; + base = __Pyx_PyType_GetSlot(base, tp_base, PyTypeObject*); } } } @@ -808,16 +825,37 @@ static int __Pyx_MergeVtables(PyTypeObject *type) { free(base_vtables); return 0; bad: - tp_base_name = __Pyx_PyType_GetName(type->tp_base); - base_name = __Pyx_PyType_GetName((PyTypeObject*)PyTuple_GET_ITEM(bases, i)); + { + PyTypeObject* basei = NULL; + PyTypeObject* tp_base = __Pyx_PyType_GetSlot(type, tp_base, PyTypeObject*); + tp_base_name = __Pyx_PyType_GetName(tp_base); +#if CYTHON_AVOID_BORROWED_REFS + basei = (PyTypeObject*)PySequence_GetItem(bases, i); + if (unlikely(!basei)) goto really_bad; +#elif !CYTHON_ASSUME_SAFE_MACROS + basei = (PyTypeObject*)PyTuple_GetItem(bases, i); + if (unlikely(!basei)) goto really_bad; +#else + basei = (PyTypeObject*)PyTuple_GET_ITEM(bases, i); +#endif + base_name = __Pyx_PyType_GetName(basei); +#if CYTHON_AVOID_BORROWED_REFS + Py_DECREF(basei); +#endif + } PyErr_Format(PyExc_TypeError, "multiple bases have vtable conflict: '" __Pyx_FMT_TYPENAME "' and '" __Pyx_FMT_TYPENAME "'", tp_base_name, base_name); +#if CYTHON_AVOID_BORROWED_REFS || !CYTHON_ASSUME_SAFE_MACROS +really_bad: // bad has failed! +#endif __Pyx_DECREF_TypeName(tp_base_name); __Pyx_DECREF_TypeName(base_name); +#if CYTHON_COMPILING_IN_LIMITED_API || CYTHON_AVOID_BORROWED_REFS || !CYTHON_ASSUME_SAFE_MACROS +other_failure: +#endif free(base_vtables); return -1; } -#endif /////////////// ImportNumPyArray.proto /////////////// diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index bf7894dc418..511d5b56d2e 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -1040,8 +1040,10 @@ static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStrWithError(PyObject *dict, // a little hacky, but it does work in the limited API . // (It doesn't work on PyPy but that probably isn't a bug.) #define __Pyx_SetItemOnTypeDict(tp, k, v) PyObject_GenericSetAttr((PyObject*)tp, k, v) + #define __Pyx_DelItemOnTypeDict(tp, k) PyObject_GenericSetAttr((PyObject*)tp, k, NULL) #else - #define __Pyx_SetItemOnTypeDict(tp, k, v) PyDict_SetItem(tp->tp_dict, k, v) + #define __Pyx_SetItemOnTypeDict(tp, k, v) PyDict_SetItem(((PyTypeObject*)(tp))->tp_dict, k, v) + #define __Pyx_DelItemOnTypeDict(tp, k) PyDict_DelItem(((PyTypeObject*)(tp))->tp_dict, k) #endif #if CYTHON_USE_TYPE_SPECS && PY_VERSION_HEX >= 0x03080000 From 4c778e6a0cc3987e337ae4a1922b0f959d4114a2 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Wed, 29 Nov 2023 11:17:06 +0100 Subject: [PATCH 032/286] Replace "Py*_Size() == -1" failure checks with "< 0" to indicate to the C compiler that a valid size is not negative. --- Cython/Utility/Builtins.c | 2 +- Cython/Utility/Coroutine.c | 2 +- Cython/Utility/CythonFunction.c | 6 +++--- Cython/Utility/Dataclasses.c | 2 +- Cython/Utility/ExtensionTypes.c | 2 +- Cython/Utility/ObjectHandling.c | 2 +- Cython/Utility/Optimize.c | 4 ++-- Cython/Utility/StringTools.c | 18 +++++++++--------- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Cython/Utility/Builtins.c b/Cython/Utility/Builtins.c index e415ff926cb..cde493d4c10 100644 --- a/Cython/Utility/Builtins.c +++ b/Cython/Utility/Builtins.c @@ -502,7 +502,7 @@ static CYTHON_INLINE int __Pyx_PySet_Update(PyObject* set, PyObject* it) { if (PyAnySet_Check(it)) { Py_ssize_t size = __Pyx_PySet_GET_SIZE(it); #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely(size == -1)) return -1; + if (unlikely(size < 0)) return -1; #endif if (size == 0) return 0; diff --git a/Cython/Utility/Coroutine.c b/Cython/Utility/Coroutine.c index 4c56fb7f904..74fbefeac09 100644 --- a/Cython/Utility/Coroutine.c +++ b/Cython/Utility/Coroutine.c @@ -513,7 +513,7 @@ static int __Pyx_PyGen__FetchStopIterationValue(PyThreadState *$local_tstate_cna // if it's a tuple, it is interpreted as separate constructor arguments (surprise!) Py_ssize_t tuple_size = __Pyx_PyTuple_GET_SIZE(ev); #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely(tuple_size == -1)) { + if (unlikely(tuple_size < 0)) { Py_XDECREF(tb); Py_DECREF(ev); Py_DECREF(et); diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index e43b3a97975..440fed2541c 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -1232,7 +1232,7 @@ static int __Pyx_CyFunction_InitClassCell(PyObject *cyfunctions, PyObject *class static int __Pyx_CyFunction_InitClassCell(PyObject *cyfunctions, PyObject *classobj) { Py_ssize_t i, count = __Pyx_PyList_GET_SIZE(cyfunctions); #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely(count == -1)) return -1; + if (unlikely(count < 0)) return -1; #endif for (i = 0; i < count; i++) { @@ -1421,7 +1421,7 @@ __pyx_FusedFunction_getitem(__pyx_FusedFunctionObject *self, PyObject *idx) PyObject *list; int i; #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely(n == -1)) return NULL; + if (unlikely(n < 0)) return NULL; #endif list = PyList_New(n); @@ -1506,7 +1506,7 @@ __pyx_FusedFunction_call(PyObject *func, PyObject *args, PyObject *kw) PyObject *result = NULL; int is_staticmethod = binding_func->func.flags & __Pyx_CYFUNCTION_STATICMETHOD; #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely(argc == -1)) return NULL; + if (unlikely(argc < 0)) return NULL; #endif if (binding_func->self) { diff --git a/Cython/Utility/Dataclasses.c b/Cython/Utility/Dataclasses.c index 6b1942bd04c..2facae1b4e7 100644 --- a/Cython/Utility/Dataclasses.c +++ b/Cython/Utility/Dataclasses.c @@ -98,7 +98,7 @@ static PyObject* __Pyx_DataclassesCallHelper(PyObject *callable, PyObject *kwds) static int __Pyx_DataclassesCallHelper_FilterToDict(PyObject *callable, PyObject *kwds, PyObject *new_kwds, PyObject *args_list, int is_kwonly) { Py_ssize_t size, i; size = PySequence_Size(args_list); - if (size == -1) return -1; + if (unlikely(size < 0)) return -1; for (i=0; i 0) #else Py_ssize_t tuple_size = PyTuple_Check(bases) ? PyTuple_GetSize(bases) : 0; - if (unlikely(tuple_size == -1)) return NULL; + if (unlikely(tuple_size < 0)) return NULL; if (tuple_size > 0) #endif { diff --git a/Cython/Utility/Optimize.c b/Cython/Utility/Optimize.c index 78445d0262f..9a7eb356fbe 100644 --- a/Cython/Utility/Optimize.c +++ b/Cython/Utility/Optimize.c @@ -379,7 +379,7 @@ static CYTHON_INLINE int __Pyx_dict_iter_next( Py_ssize_t pos = *ppos; Py_ssize_t tuple_size = __Pyx_PyTuple_GET_SIZE(iter_obj); #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely(tuple_size == -1)) return -1; + if (unlikely(tuple_size < 0)) return -1; #endif if (unlikely(pos >= tuple_size)) return 0; *ppos = pos + 1; @@ -394,7 +394,7 @@ static CYTHON_INLINE int __Pyx_dict_iter_next( Py_ssize_t pos = *ppos; Py_ssize_t list_size = __Pyx_PyList_GET_SIZE(iter_obj); #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely(list_size == -1)) return -1; + if (unlikely(list_size < 0)) return -1; #endif if (unlikely(pos >= list_size)) return 0; *ppos = pos + 1; diff --git a/Cython/Utility/StringTools.c b/Cython/Utility/StringTools.c index 161f4fdc8d1..6debcd86a14 100644 --- a/Cython/Utility/StringTools.c +++ b/Cython/Utility/StringTools.c @@ -321,7 +321,7 @@ static CYTHON_INLINE int __Pyx_GetItemInt_ByteArray_Fast(PyObject* string, Py_ss if (wraparound | boundscheck) { length = __Pyx_PyByteArray_GET_SIZE(string); #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely(length == -1)) return -1; + if (unlikely(length < 0)) return -1; #endif if (wraparound & unlikely(i < 0)) i += length; if ((!boundscheck) || likely(__Pyx_is_valid_index(i, length))) { @@ -354,7 +354,7 @@ static CYTHON_INLINE int __Pyx_SetItemInt_ByteArray_Fast(PyObject* string, Py_ss if (wraparound | boundscheck) { length = __Pyx_PyByteArray_GET_SIZE(string); #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely(length == -1)) return -1; + if (unlikely(length < 0)) return -1; #endif if (wraparound & unlikely(i < 0)) i += length; if ((!boundscheck) || likely(__Pyx_is_valid_index(i, length))) { @@ -390,7 +390,7 @@ static CYTHON_INLINE Py_UCS4 __Pyx_GetItemInt_Unicode_Fast(PyObject* ustring, Py if (wraparound | boundscheck) { length = __Pyx_PyUnicode_GET_LENGTH(ustring); #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely(length == -1)) return (Py_UCS4)-1; + if (unlikely(length < 0)) return (Py_UCS4)-1; #endif if (wraparound & unlikely(i < 0)) i += length; if ((!boundscheck) || likely(__Pyx_is_valid_index(i, length))) { @@ -644,7 +644,7 @@ static int __Pyx_PyUnicode_TailmatchTuple(PyObject* s, PyObject* substrings, Py_ssize_t start, Py_ssize_t end, int direction) { Py_ssize_t i, count = __Pyx_PyTuple_GET_SIZE(substrings); #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely(count == -1)) return -1; + if (unlikely(count < 0)) return -1; #endif for (i = 0; i < count; i++) { Py_ssize_t result; @@ -694,14 +694,14 @@ static int __Pyx_PyBytes_SingleTailmatch(PyObject* self, PyObject* arg, view.obj = NULL; #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely(self_len == -1)) return -1; + if (unlikely(self_len < 0)) return -1; #endif if (PyBytes_Check(arg)) { sub_ptr = PyBytes_AS_STRING(arg); sub_len = PyBytes_GET_SIZE(arg); #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely(sub_len == -1)) return -1; + if (unlikely(sub_len < 0)) return -1; #endif } else { if (unlikely(PyObject_GetBuffer(self, &view, PyBUF_SIMPLE) == -1)) @@ -742,7 +742,7 @@ static int __Pyx_PyBytes_TailmatchTuple(PyObject* self, PyObject* substrings, Py_ssize_t start, Py_ssize_t end, int direction) { Py_ssize_t i, count = PyTuple_GET_SIZE(substrings); #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely(count == -1)) return -1; + if (unlikely(count < 0)) return -1; #endif for (i = 0; i < count; i++) { int result; @@ -802,14 +802,14 @@ static CYTHON_INLINE char __Pyx_PyBytes_GetItemInt(PyObject* bytes, Py_ssize_t i if (index < 0) { Py_ssize_t size = __Pyx_PyBytes_GET_SIZE(bytes); #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely(size == -1)) return (char) -1; + if (unlikely(size < 0)) return (char) -1; #endif index += size; } if (check_bounds) { Py_ssize_t size = __Pyx_PyBytes_GET_SIZE(bytes); #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely(size == -1)) return (char) -1; + if (unlikely(size < 0)) return (char) -1; #endif if (unlikely(!__Pyx_is_valid_index(index, size))) { PyErr_SetString(PyExc_IndexError, "string index out of range"); From eb92b1582636f6d72dc172795a98469d25b5eda9 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Wed, 29 Nov 2023 11:23:57 +0100 Subject: [PATCH 033/286] Fix function name. --- Cython/Utility/ObjectHandling.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 22fb5f5f53e..80188388d21 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -875,7 +875,7 @@ static PyObject *__Pyx_FindInheritedMetaclass(PyObject *bases) { #if CYTHON_ASSUME_SAFE_SIZE if (PyTuple_Check(bases) && PyTuple_GET_SIZE(bases) > 0) #else - Py_ssize_t tuple_size = PyTuple_Check(bases) ? PyTuple_GetSize(bases) : 0; + Py_ssize_t tuple_size = PyTuple_Check(bases) ? PyTuple_Size(bases) : 0; if (unlikely(tuple_size < 0)) return NULL; if (tuple_size > 0) #endif From bc16d868bc57b815fed818e67b12042f938f0eda Mon Sep 17 00:00:00 2001 From: scoder Date: Sun, 3 Dec 2023 20:05:50 +0100 Subject: [PATCH 034/286] Guard PyUnicode_GET_LENGTH() usages by CYTHON_ASSUME_SAFE_SIZE (GH-5890) --- Cython/Compiler/ExprNodes.py | 1 + Cython/Utility/FunctionArguments.c | 9 ++++----- Cython/Utility/ImportExport.c | 19 ++++++++++++------- Cython/Utility/ModuleSetupCode.c | 5 +++-- Cython/Utility/StringTools.c | 23 +++++++++++++++++++++-- Cython/Utility/TypeConversion.c | 12 ++++++++---- 6 files changed, 49 insertions(+), 20 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index ac02e70e7ce..fbc3eb1ffae 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -3609,6 +3609,7 @@ def generate_evaluation_code(self, code): if isinstance(node, UnicodeNode): length_parts.append(str(len(node.value))) else: + # TODO: add exception handling for these macro calls if not ASSUME_SAFE_SIZE/MACROS length_parts.append("__Pyx_PyUnicode_GET_LENGTH(%s)" % node.py_result()) if node in unknown_nodes: charval_parts.append("__Pyx_PyUnicode_MAX_CHAR_VALUE(%s)" % node.py_result()) diff --git a/Cython/Utility/FunctionArguments.c b/Cython/Utility/FunctionArguments.c index 71549ce5721..a4fa2bb8d85 100644 --- a/Cython/Utility/FunctionArguments.c +++ b/Cython/Utility/FunctionArguments.c @@ -295,10 +295,9 @@ static int __Pyx_ParseOptionalKeywords( if (likely(PyUnicode_Check(key))) { while (*name) { int cmp = ( - #if !CYTHON_COMPILING_IN_PYPY - (__Pyx_PyUnicode_GET_LENGTH(**name) != __Pyx_PyUnicode_GET_LENGTH(key)) ? 1 : + #if CYTHON_ASSUME_SAFE_SIZE + (PyUnicode_GET_LENGTH(**name) != PyUnicode_GET_LENGTH(key)) ? 1 : #endif - // In Py2, we may need to convert the argument name from str to unicode for comparison. PyUnicode_Compare(**name, key) ); if (cmp < 0 && unlikely(PyErr_Occurred())) goto bad; @@ -317,8 +316,8 @@ static int __Pyx_ParseOptionalKeywords( PyObject*** argname = argnames; while (argname != first_kw_arg) { int cmp = (**argname == key) ? 0 : - #if !CYTHON_COMPILING_IN_PYPY - (__Pyx_PyUnicode_GET_LENGTH(**argname) != __Pyx_PyUnicode_GET_LENGTH(key)) ? 1 : + #if CYTHON_ASSUME_SAFE_SIZE + (PyUnicode_GET_LENGTH(**argname) != PyUnicode_GET_LENGTH(key)) ? 1 : #endif // need to convert argument name from bytes to unicode for comparison PyUnicode_Compare(**argname, key); diff --git a/Cython/Utility/ImportExport.c b/Cython/Utility/ImportExport.c index 670197ae1b0..df8b3b07f18 100644 --- a/Cython/Utility/ImportExport.c +++ b/Cython/Utility/ImportExport.c @@ -291,13 +291,18 @@ __Pyx_import_all_from(PyObject *locals, PyObject *v) PyErr_Clear(); break; } - if (skip_leading_underscores && - likely(PyUnicode_Check(name)) && - likely(__Pyx_PyUnicode_GET_LENGTH(name)) && - __Pyx_PyUnicode_READ_CHAR(name, 0) == '_') - { - Py_DECREF(name); - continue; + if (skip_leading_underscores && likely(PyUnicode_Check(name))) { + Py_ssize_t length = __Pyx_PyUnicode_GET_LENGTH(name); + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(length < 0)) { + Py_DECREF(name); + return -1; + } + #endif + if (likely(length) && __Pyx_PyUnicode_READ_CHAR(name, 0) == '_') { + Py_DECREF(name); + continue; + } } value = PyObject_GetAttr(v, name); if (value == NULL) diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 511d5b56d2e..17403230216 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -1061,7 +1061,6 @@ static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStrWithError(PyObject *dict, #if CYTHON_COMPILING_IN_LIMITED_API #define __Pyx_PyUnicode_READY(op) (0) - #define __Pyx_PyUnicode_GET_LENGTH(u) PyUnicode_GetLength(u) #define __Pyx_PyUnicode_READ_CHAR(u, i) PyUnicode_ReadChar(u, i) #define __Pyx_PyUnicode_MAX_CHAR_VALUE(u) ((void)u, 1114111U) #define __Pyx_PyUnicode_KIND(u) ((void)u, (0)) @@ -1079,7 +1078,6 @@ static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStrWithError(PyObject *dict, 0 : _PyUnicode_Ready((PyObject *)(op))) #endif - #define __Pyx_PyUnicode_GET_LENGTH(u) PyUnicode_GET_LENGTH(u) #define __Pyx_PyUnicode_READ_CHAR(u, i) PyUnicode_READ_CHAR(u, i) #define __Pyx_PyUnicode_MAX_CHAR_VALUE(u) PyUnicode_MAX_CHAR_VALUE(u) #define __Pyx_PyUnicode_KIND(u) ((int)PyUnicode_KIND(u)) @@ -1181,12 +1179,15 @@ static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStrWithError(PyObject *dict, #define __Pyx_PySet_GET_SIZE(o) PySet_GET_SIZE(o) #define __Pyx_PyBytes_GET_SIZE(o) PyBytes_GET_SIZE(o) #define __Pyx_PyByteArray_GET_SIZE(o) PyByteArray_GET_SIZE(o) + #define __Pyx_PyUnicode_GET_LENGTH(o) PyUnicode_GET_LENGTH(o) #else + // These all need exception checks for -1. #define __Pyx_PyTuple_GET_SIZE(o) PyTuple_Size(o) #define __Pyx_PyList_GET_SIZE(o) PyList_Size(o) #define __Pyx_PySet_GET_SIZE(o) PySet_Size(o) #define __Pyx_PyBytes_GET_SIZE(o) PyBytes_Size(o) #define __Pyx_PyByteArray_GET_SIZE(o) PyByteArray_Size(o) + #define __Pyx_PyUnicode_GET_LENGTH(o) PyUnicode_GetLength(o) #endif #if PY_VERSION_HEX >= 0x030d00A1 diff --git a/Cython/Utility/StringTools.c b/Cython/Utility/StringTools.c index 6debcd86a14..867f99b8f43 100644 --- a/Cython/Utility/StringTools.c +++ b/Cython/Utility/StringTools.c @@ -193,13 +193,22 @@ static CYTHON_INLINE int __Pyx_PyUnicode_Equals(PyObject* s1, PyObject* s2, int s1_is_unicode = PyUnicode_CheckExact(s1); s2_is_unicode = PyUnicode_CheckExact(s2); if (s1_is_unicode & s2_is_unicode) { - Py_ssize_t length; + Py_ssize_t length, length2; int kind; void *data1, *data2; + #if !CYTHON_COMPILING_IN_LIMITED_API if (unlikely(__Pyx_PyUnicode_READY(s1) < 0) || unlikely(__Pyx_PyUnicode_READY(s2) < 0)) return -1; + #endif length = __Pyx_PyUnicode_GET_LENGTH(s1); - if (length != __Pyx_PyUnicode_GET_LENGTH(s2)) { + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(length < 0)) return -1; + #endif + length2 = __Pyx_PyUnicode_GET_LENGTH(s2); + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(length2 < 0)) return -1; + #endif + if (length != length2) { goto return_ne; } #if CYTHON_USE_UNICODE_INTERNALS @@ -568,8 +577,13 @@ static CYTHON_INLINE PyObject* __Pyx_PyUnicode_Substring( static CYTHON_INLINE PyObject* __Pyx_PyUnicode_Substring( PyObject* text, Py_ssize_t start, Py_ssize_t stop) { Py_ssize_t length; + #if !CYTHON_COMPILING_IN_LIMITED_API if (unlikely(__Pyx_PyUnicode_READY(text) == -1)) return NULL; + #endif length = __Pyx_PyUnicode_GET_LENGTH(text); + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(length < 0)) return NULL; + #endif if (start < 0) { start += length; if (start < 0) @@ -874,9 +888,14 @@ static PyObject* __Pyx_PyUnicode_Join(PyObject** values, Py_ssize_t value_count, Py_ssize_t ulength; void *udata; PyObject *uval = values[i]; + #if !CYTHON_COMPILING_IN_LIMITED_API if (unlikely(__Pyx_PyUnicode_READY(uval))) goto bad; + #endif ulength = __Pyx_PyUnicode_GET_LENGTH(uval); + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(ulength < 0)) goto bad; + #endif if (unlikely(!ulength)) continue; if (unlikely((PY_SSIZE_T_MAX >> kind_shift) - ulength < char_pos)) diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index 59743781def..4ac109ccd2c 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -641,10 +641,14 @@ static CYTHON_INLINE Py_UNICODE __Pyx_PyObject_AsPy_UNICODE(PyObject* x) { const long maxval = 1114111; #endif if (PyUnicode_Check(x)) { - if (unlikely(__Pyx_PyUnicode_GET_LENGTH(x) != 1)) { - PyErr_Format(PyExc_ValueError, - "only single character unicode strings can be converted to Py_UNICODE, " - "got length %" CYTHON_FORMAT_SSIZE_T "d", __Pyx_PyUnicode_GET_LENGTH(x)); + Py_ssize_t length = __Pyx_PyUnicode_GET_LENGTH(x); + if (unlikely(length != 1)) { + // -1 indicates an error. + if (length >= 0) { + PyErr_Format(PyExc_ValueError, + "only single character unicode strings can be converted to Py_UNICODE, " + "got length %" CYTHON_FORMAT_SSIZE_T "d", length); + } return (Py_UNICODE)-1; } ival = PyUnicode_READ_CHAR(x, 0); From ca1a0eca47ef42830a2135f3caf5c3309a2c13d4 Mon Sep 17 00:00:00 2001 From: da-woods Date: Sun, 3 Dec 2023 19:17:46 +0000 Subject: [PATCH 035/286] Disable flakey line_trace test on Py3.12 windows 3.12 (GH-5891) I've been unable to reproduce it locally, so don't really have an idea how to fix it. Therefore, disable it. I've done so for one minor version only for now in the hope in magically improves with an update. --- tests/run/line_trace.pyx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/run/line_trace.pyx b/tests/run/line_trace.pyx index ea84fa15182..d1992e9ee4d 100644 --- a/tests/run/line_trace.pyx +++ b/tests/run/line_trace.pyx @@ -397,6 +397,15 @@ def fail_on_line_trace(fail_func, add_func, nogil_add_func): return trace +def skip_on_win_py3_12_0(func): + if sys.version_info == (3, 12, 0) and sys.platform == "win32": + # This test is mysteriously failing on the CI only. Disable + # it for now and hope that the next minor release fixes it. + return None + return func + + +@skip_on_win_py3_12_0 def disable_trace(func, *args, bint with_sys=False): """ >>> py_add = plain_python_functions["py_add"] From 59268a9696054c357e07015edbaf64341ecff973 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Sun, 3 Dec 2023 20:20:06 +0100 Subject: [PATCH 036/286] Fix sys.version_info check. --- tests/run/line_trace.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run/line_trace.pyx b/tests/run/line_trace.pyx index d1992e9ee4d..aee352335a7 100644 --- a/tests/run/line_trace.pyx +++ b/tests/run/line_trace.pyx @@ -398,7 +398,7 @@ def fail_on_line_trace(fail_func, add_func, nogil_add_func): def skip_on_win_py3_12_0(func): - if sys.version_info == (3, 12, 0) and sys.platform == "win32": + if sys.version_info[:3] == (3, 12, 0) and sys.platform == "win32": # This test is mysteriously failing on the CI only. Disable # it for now and hope that the next minor release fixes it. return None From bcc6799dad4bd496624bd377f2e6e0c4b3fc7a79 Mon Sep 17 00:00:00 2001 From: da-woods Date: Sun, 3 Dec 2023 19:33:31 +0000 Subject: [PATCH 037/286] Make __Pyx_PyObject_ToDouble() work in the Limited API (GH-5888) --- Cython/Compiler/PyrexTypes.py | 4 ++-- Cython/Utility/Optimize.c | 28 ++++++---------------------- Cython/Utility/TypeConversion.c | 8 +++++--- 3 files changed, 13 insertions(+), 27 deletions(-) diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index a9802066479..b6af4a5ab3f 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -2377,7 +2377,7 @@ class CFloatType(CNumericType): is_float = 1 to_py_function = "PyFloat_FromDouble" - from_py_function = "__pyx_PyFloat_AsDouble" + from_py_function = "__Pyx_PyFloat_AsDouble" exception_value = -1 @@ -2385,7 +2385,7 @@ def __init__(self, rank, math_h_modifier = ''): CNumericType.__init__(self, rank, 1) self.math_h_modifier = math_h_modifier if rank == RANK_FLOAT: - self.from_py_function = "__pyx_PyFloat_AsFloat" + self.from_py_function = "__Pyx_PyFloat_AsFloat" def assignable_from_resolved_type(self, src_type): return (src_type.is_numeric and not src_type.is_complex) or src_type is error_type diff --git a/Cython/Utility/Optimize.c b/Cython/Utility/Optimize.c index 9a7eb356fbe..619e054b285 100644 --- a/Cython/Utility/Optimize.c +++ b/Cython/Utility/Optimize.c @@ -598,7 +598,7 @@ static double __Pyx__PyObject_AsDouble(PyObject* obj); /* proto */ PyFloat_AsDouble(obj) : __Pyx__PyObject_AsDouble(obj)) #else #define __Pyx_PyObject_AsDouble(obj) \ -((likely(PyFloat_CheckExact(obj))) ? PyFloat_AS_DOUBLE(obj) : \ +((likely(PyFloat_CheckExact(obj))) ? __Pyx_PyFloat_AS_DOUBLE(obj) : \ likely(PyLong_CheckExact(obj)) ? \ PyLong_AsDouble(obj) : __Pyx__PyObject_AsDouble(obj)) #endif @@ -639,7 +639,7 @@ static double __Pyx__PyObject_AsDouble(PyObject* obj) { } #endif if (likely(float_value)) { - double value = PyFloat_AS_DOUBLE(float_value); + double value = __Pyx_PyFloat_AS_DOUBLE(float_value); Py_DECREF(float_value); return value; } @@ -845,11 +845,7 @@ static CYTHON_INLINE double __Pyx_PyByteArray_AsDouble(PyObject *obj) { static double __Pyx_SlowPyString_AsDouble(PyObject *obj) { PyObject *float_value = PyFloat_FromString(obj); if (likely(float_value)) { -#if CYTHON_ASSUME_SAFE_MACROS - double value = PyFloat_AS_DOUBLE(float_value); -#else - double value = PyFloat_AsDouble(float_value); -#endif + double value = __Pyx_PyFloat_AS_DOUBLE(float_value); Py_DECREF(float_value); return value; } @@ -1096,11 +1092,7 @@ static CYTHON_INLINE {{c_ret_type}} __Pyx_PyInt_{{'' if ret_type.is_pyobject els if (PyFloat_CheckExact({{pyval}})) { const long {{'a' if order == 'CObj' else 'b'}} = intval; -#if CYTHON_COMPILING_IN_LIMITED_API - double {{ival}} = __pyx_PyFloat_AsDouble({{pyval}}); -#else - double {{ival}} = PyFloat_AS_DOUBLE({{pyval}}); -#endif + double {{ival}} = __Pyx_PyFloat_AS_DOUBLE({{pyval}}); {{return_compare('(double)a', '(double)b', c_op)}} } @@ -1322,11 +1314,7 @@ static {{c_ret_type}} {{cfunc_name}}(PyObject *op1, PyObject *op2, long intval, {{if c_op in '+-*' or op in ('TrueDivide', 'Eq', 'Ne')}} if (PyFloat_CheckExact({{pyval}})) { const long {{'a' if order == 'CObj' else 'b'}} = intval; -#if CYTHON_COMPILING_IN_LIMITED_API - double {{ival}} = __pyx_PyFloat_AsDouble({{pyval}}); -#else - double {{ival}} = PyFloat_AS_DOUBLE({{pyval}}); -#endif + double {{ival}} = __Pyx_PyFloat_AS_DOUBLE({{pyval}}); {{if op in ('Eq', 'Ne')}} if ((double)a {{c_op}} (double)b) { {{return_true}}; @@ -1402,11 +1390,7 @@ static {{c_ret_type}} {{cfunc_name}}(PyObject *op1, PyObject *op2, double floatv {{endif}} if (likely(PyFloat_CheckExact({{pyval}}))) { -#if CYTHON_COMPILING_IN_LIMITED_API - {{fval}} = __pyx_PyFloat_AsDouble({{pyval}}); -#else - {{fval}} = PyFloat_AS_DOUBLE({{pyval}}); -#endif + {{fval}} = __Pyx_PyFloat_AS_DOUBLE({{pyval}}); {{zerodiv_check(fval)}} } else diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index 4ac109ccd2c..449cd49efff 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -123,11 +123,13 @@ static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t); static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject*); #if CYTHON_ASSUME_SAFE_MACROS -#define __pyx_PyFloat_AsDouble(x) (PyFloat_CheckExact(x) ? PyFloat_AS_DOUBLE(x) : PyFloat_AsDouble(x)) +#define __Pyx_PyFloat_AsDouble(x) (PyFloat_CheckExact(x) ? PyFloat_AS_DOUBLE(x) : PyFloat_AsDouble(x)) +#define __Pyx_PyFloat_AS_DOUBLE(x) PyFloat_AS_DOUBLE(x) #else -#define __pyx_PyFloat_AsDouble(x) PyFloat_AsDouble(x) +#define __Pyx_PyFloat_AsDouble(x) PyFloat_AsDouble(x) +#define __Pyx_PyFloat_AS_DOUBLE(x) PyFloat_AsDouble(x) #endif -#define __pyx_PyFloat_AsFloat(x) ((float) __pyx_PyFloat_AsDouble(x)) +#define __Pyx_PyFloat_AsFloat(x) ((float) __Pyx_PyFloat_AsDouble(x)) #define __Pyx_PyNumber_Int(x) (PyLong_CheckExact(x) ? __Pyx_NewRef(x) : PyNumber_Long(x)) // __Pyx_PyNumber_Float is now in its own section since it has dependencies (needed to make From 3cf4cf77e766e895b236e6c28c07106ef03abd67 Mon Sep 17 00:00:00 2001 From: da-woods Date: Sun, 3 Dec 2023 19:37:48 +0000 Subject: [PATCH 038/286] Fix exec() in the Limited API by calling the builtin function instead (GH-5886) --- Cython/Utility/Builtins.c | 22 +++++++++++++++++++++- Cython/Utility/ModuleSetupCode.c | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Cython/Utility/Builtins.c b/Cython/Utility/Builtins.c index cde493d4c10..f67b0387798 100644 --- a/Cython/Utility/Builtins.c +++ b/Cython/Utility/Builtins.c @@ -49,12 +49,17 @@ static CYTHON_INLINE PyObject* __Pyx_PyExec2(PyObject* o, PyObject* globals) { static PyObject* __Pyx_PyExec3(PyObject* o, PyObject* globals, PyObject* locals) { PyObject* result; +#if !CYTHON_COMPILING_IN_LIMITED_API PyObject* s = 0; char *code = 0; +#endif if (!globals || globals == Py_None) { globals = $moddict_cname; - } else if (unlikely(!PyDict_Check(globals))) { + } +#if !CYTHON_COMPILING_IN_LIMITED_API + // In Limited API we just use exec builtin which already has this + else if (unlikely(!PyDict_Check(globals))) { __Pyx_TypeName globals_type_name = __Pyx_PyType_GetName(Py_TYPE(globals)); PyErr_Format(PyExc_TypeError, @@ -63,10 +68,12 @@ static PyObject* __Pyx_PyExec3(PyObject* o, PyObject* globals, PyObject* locals) __Pyx_DECREF_TypeName(globals_type_name); goto bad; } +#endif if (!locals || locals == Py_None) { locals = globals; } +#if !CYTHON_COMPILING_IN_LIMITED_API if (__Pyx_PyDict_GetItemStr(globals, PYIDENT("__builtins__")) == NULL) { if (unlikely(PyDict_SetItem(globals, PYIDENT("__builtins__"), PyEval_GetBuiltins()) < 0)) goto bad; @@ -115,6 +122,19 @@ static PyObject* __Pyx_PyExec3(PyObject* o, PyObject* globals, PyObject* locals) bad: Py_XDECREF(s); return 0; +#else // CYTHON_COMPILING_IN_LIMITED_API + { + // For the limited API we just defer to the actual builtin + // (after setting up globals and locals) - there's too much we can't do otherwise + PyObject *builtins, *exec; + builtins = PyEval_GetBuiltins(); + if (!builtins) return NULL; + exec = PyDict_GetItemString(builtins, "exec"); + if (!exec) return NULL; + result = PyObject_CallFunctionObjArgs(exec, o, globals, locals, NULL); + return result; + } +#endif } //////////////////// GetAttr3.proto //////////////////// diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 17403230216..7aaa7cde09b 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -938,7 +938,7 @@ static CYTHON_INLINE int __Pyx__IsSameCFunction(PyObject *func, void *cfunc) { #endif #if CYTHON_COMPILING_IN_LIMITED_API - #define __Pyx_PyCode_HasFreeVars(co) (PyCode_GetNumFree(co) > 0) + // __Pyx_PyCode_HasFreeVars isn't easily emulated in the limited API (but isn't really necessary) #define __Pyx_PyFrame_SetLineNumber(frame, lineno) #else #define __Pyx_PyCode_HasFreeVars(co) (PyCode_GetNumFree(co) > 0) From 0f16f08079f9d448e9a15684cc636e06d2502c91 Mon Sep 17 00:00:00 2001 From: da-woods Date: Sun, 3 Dec 2023 19:42:02 +0000 Subject: [PATCH 039/286] Disable all freelist code outside CPython and add a new guard "CYTHON_USE_FREELISTS" for it (GH-5885) --- Cython/Compiler/ModuleNode.py | 8 ++++++-- Cython/Utility/ModuleSetupCode.c | 15 +++++++++++++++ docs/src/userguide/extension_types.rst | 2 ++ .../userguide/source_files_and_compilation.rst | 5 +++++ docs/src/userguide/special_methods.rst | 2 +- 5 files changed, 29 insertions(+), 3 deletions(-) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index eac4c858cc7..7ddcd583048 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -1532,10 +1532,12 @@ def generate_new_function(self, scope, code, cclass_entry): slot_func) code.putln("") if freelist_size: + code.putln("#if CYTHON_USE_FREELISTS") code.putln("static %s[%d];" % ( scope.parent_type.declaration_code(freelist_name), freelist_size)) code.putln("static int %s = 0;" % freecount_name) + code.putln("#endif") code.putln("") code.putln( "static PyObject *%s(PyTypeObject *t, %sPyObject *a, %sPyObject *k) {" % ( @@ -1565,7 +1567,7 @@ def generate_new_function(self, scope, code, cclass_entry): else: type_safety_check = ' & (int)(!__Pyx_PyType_HasFeature(t, (Py_TPFLAGS_IS_ABSTRACT | Py_TPFLAGS_HEAPTYPE)))' obj_struct = type.declaration_code("", deref=True) - code.putln("#if CYTHON_COMPILING_IN_CPYTHON") + code.putln("#if CYTHON_USE_FREELISTS") code.putln( "if (likely((int)(%s > 0) & (int)(t->tp_basicsize == sizeof(%s))%s)) {" % ( freecount_name, obj_struct, type_safety_check)) @@ -1804,7 +1806,7 @@ def generate_dealloc_function(self, scope, code): ' & (int)(!__Pyx_PyType_HasFeature(Py_TYPE(o), (Py_TPFLAGS_IS_ABSTRACT | Py_TPFLAGS_HEAPTYPE)))') type = scope.parent_type - code.putln("#if CYTHON_COMPILING_IN_CPYTHON") + code.putln("#if CYTHON_USE_FREELISTS") code.putln( "if (((int)(%s < %d) & (int)(Py_TYPE(o)->tp_basicsize == sizeof(%s))%s)) {" % ( freecount_name, @@ -3401,6 +3403,7 @@ def generate_module_cleanup_func(self, env, code): scope = cclass_type.scope freelist_name = scope.mangle_internal(Naming.freelist_name) freecount_name = scope.mangle_internal(Naming.freecount_name) + code.putln('#if CYTHON_USE_FREELISTS') code.putln("while (%s > 0) {" % freecount_name) code.putln("PyObject* o = (PyObject*)%s[--%s];" % ( freelist_name, freecount_name)) @@ -3412,6 +3415,7 @@ def generate_module_cleanup_func(self, env, code): code.putln("if (tp_free) tp_free(o);") code.putln("#endif") code.putln("}") + code.putln('#endif') # CYTHON_USE_FREELISTS # for entry in env.pynum_entries: # code.put_decref_clear(entry.cname, # PyrexTypes.py_object_type, diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 7aaa7cde09b..42030f3b701 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -116,6 +116,8 @@ #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC #define CYTHON_UPDATE_DESCRIPTOR_DOC 0 #endif + #undef CYTHON_USE_FREELISTS + #define CYTHON_USE_FREELISTS 0 #elif defined(PYPY_VERSION) #define CYTHON_COMPILING_IN_PYPY 1 @@ -180,6 +182,8 @@ #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC #define CYTHON_UPDATE_DESCRIPTOR_DOC 0 #endif + #undef CYTHON_USE_FREELISTS + #define CYTHON_USE_FREELISTS 0 #elif defined(CYTHON_LIMITED_API) // EXPERIMENTAL !! @@ -249,6 +253,8 @@ #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC #define CYTHON_UPDATE_DESCRIPTOR_DOC 0 #endif + #undef CYTHON_USE_FREELISTS + #define CYTHON_USE_FREELISTS 0 #elif defined(Py_GIL_DISABLED) || defined(Py_NOGIL) #define CYTHON_COMPILING_IN_PYPY 0 @@ -300,6 +306,12 @@ #define CYTHON_USE_DICT_VERSIONS 0 #undef CYTHON_USE_EXC_INFO_STACK #define CYTHON_USE_EXC_INFO_STACK 0 + #ifndef CYTHON_USE_FREELISTS + // TODO - we could probably enable CYTHON_USE_FREELISTS by default in future since + // this is just a variant of cpython now, but we'd need to be very careful to make + // them thread safe. Since it will probably work, let the user decide. + #define CYTHON_USE_FREELISTS 0 + #endif #else #define CYTHON_COMPILING_IN_PYPY 0 @@ -392,6 +404,9 @@ #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC #define CYTHON_UPDATE_DESCRIPTOR_DOC 1 #endif + #ifndef CYTHON_USE_FREELISTS + #define CYTHON_USE_FREELISTS 1 + #endif #endif #ifndef CYTHON_FAST_PYCCALL diff --git a/docs/src/userguide/extension_types.rst b/docs/src/userguide/extension_types.rst index 4b3cf4eb50a..41c99b581ad 100644 --- a/docs/src/userguide/extension_types.rst +++ b/docs/src/userguide/extension_types.rst @@ -658,6 +658,8 @@ definition, for example,:: # attributes and methods +.. _freelist: + Fast instantiation =================== diff --git a/docs/src/userguide/source_files_and_compilation.rst b/docs/src/userguide/source_files_and_compilation.rst index 565118d334c..d08d430ec0f 100644 --- a/docs/src/userguide/source_files_and_compilation.rst +++ b/docs/src/userguide/source_files_and_compilation.rst @@ -1246,3 +1246,8 @@ hidden by default since most users will be uninterested in changing them. ``CYTHON_UPDATE_DESCRIPTOR_DOC`` Attempt to provide docstrings also for special (double underscore) methods. + + ``CYTHON_USE_FREELISTS`` + Enable the use of freelists on extension types with + :ref:`the @cython.freelist decorator`. + diff --git a/docs/src/userguide/special_methods.rst b/docs/src/userguide/special_methods.rst index 38b80e7cd4e..027a46cfa04 100644 --- a/docs/src/userguide/special_methods.rst +++ b/docs/src/userguide/special_methods.rst @@ -223,7 +223,7 @@ Depending on the application, one way or the other may be better: These constants can be cimported from the ``cpython.object`` module. -* If you use the `functools.total_ordering`_ +* If you use the `functools.total_ordering `_ decorator on an extension type/``cdef`` class, Cython replaces it with a low-level reimplementation designed specifically for extension types. (On a normal Python classes, the ``functools`` decorator continues to work as before.) As a shortcut you can also use ``cython.total_ordering``, which From d500bdf65ec37a8e66c1264895e31d5474f6996e Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Sun, 3 Dec 2023 20:46:52 +0100 Subject: [PATCH 040/286] Fix a set usage bug in Errors.warn_once(). --- Cython/Compiler/Errors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Compiler/Errors.py b/Cython/Compiler/Errors.py index 0c46cb3a9e6..f3be0fd8b02 100644 --- a/Cython/Compiler/Errors.py +++ b/Cython/Compiler/Errors.py @@ -249,7 +249,7 @@ def warn_once(position, message, level=0): echo_file = threadlocal.cython_errors_echo_file if echo_file: _write_file_encode(echo_file, line) - warn_once_seen[message] = True + warn_once_seen.add(message) return warn From 9a22b49cb8573c5085994d8c9c5da2de6b2b577d Mon Sep 17 00:00:00 2001 From: da-woods Date: Sun, 3 Dec 2023 19:53:28 +0000 Subject: [PATCH 041/286] More Python 2 removals (GH-5869) Add a warning when an outdated special method is found and ignored. --- Cython/Compiler/TypeSlots.py | 86 ++++++++++---------------- tests/run/special_methods_T561_py3.pyx | 36 +++-------- 2 files changed, 40 insertions(+), 82 deletions(-) diff --git a/Cython/Compiler/TypeSlots.py b/Cython/Compiler/TypeSlots.py index 148070629d6..54f70bc9c18 100644 --- a/Cython/Compiler/TypeSlots.py +++ b/Cython/Compiler/TypeSlots.py @@ -6,7 +6,7 @@ from . import Naming from . import PyrexTypes -from .Errors import error +from .Errors import error, warn_once import copy @@ -222,22 +222,18 @@ class SlotDescriptor: # slot_name string Member name of the slot in the type object # is_initialised_dynamically Is initialised by code in the module init function # is_inherited Is inherited by subtypes (see PyType_Ready()) - # py3 Indicates presence of slot in Python 3 - # py2 Indicates presence of slot in Python 2 - # ifdef Full #ifdef string that slot is wrapped in. Using this causes py3, py2 and flags to be ignored.) + # ifdef Full #ifdef string that slot is wrapped in. Using this causes flags to be ignored. # used_ifdef Full #ifdef string that the slot value is wrapped in (otherwise it is assigned NULL) # Unlike "ifdef" the slot is defined and this just controls if it receives a value def __init__(self, slot_name, dynamic=False, inherited=False, - py3=True, py2=True, ifdef=None, is_binop=False, + ifdef=None, is_binop=False, used_ifdef=None): self.slot_name = slot_name self.is_initialised_dynamically = dynamic self.is_inherited = inherited self.ifdef = ifdef self.used_ifdef = used_ifdef - self.py3 = py3 - self.py2 = py2 self.is_binop = is_binop def slot_code(self, scope): @@ -248,15 +244,9 @@ def spec_value(self, scope): def preprocessor_guard_code(self): ifdef = self.ifdef - py2 = self.py2 - py3 = self.py3 guard = None if ifdef: guard = "#if %s" % ifdef - elif not py3 or py3 == '': - guard = "#if PY_MAJOR_VERSION < 3" - elif not py2: - guard = "#if PY_MAJOR_VERSION >= 3" return guard def generate_spec(self, scope, code): @@ -267,7 +257,7 @@ def generate_spec(self, scope, code): return preprocessor_guard = self.preprocessor_guard_code() if not preprocessor_guard: - if self.py3 and self.slot_name.startswith('bf_'): + if self.slot_name.startswith('bf_'): # The buffer protocol requires Limited API 3.11, so check if the spec slots are available. preprocessor_guard = "#if defined(Py_%s)" % self.slot_name if preprocessor_guard: @@ -324,9 +314,6 @@ def generate(self, scope, code): if end_pypy_guard: code.putln("#endif") - if self.py3 == '': - code.putln("#else") - code.putln("0, /*reserved*/") if preprocessor_guard: code.putln("#endif") @@ -358,8 +345,8 @@ class FixedSlot(SlotDescriptor): # # value string - def __init__(self, slot_name, value, py3=True, py2=True, ifdef=None): - SlotDescriptor.__init__(self, slot_name, py3=py3, py2=py2, ifdef=ifdef) + def __init__(self, slot_name, value, ifdef=None): + SlotDescriptor.__init__(self, slot_name, ifdef=ifdef) self.value = value def slot_code(self, scope): @@ -369,8 +356,8 @@ def slot_code(self, scope): class EmptySlot(FixedSlot): # Descriptor for a type slot whose value is always 0. - def __init__(self, slot_name, py3=True, py2=True, ifdef=None): - FixedSlot.__init__(self, slot_name, "0", py3=py3, py2=py2, ifdef=ifdef) + def __init__(self, slot_name, ifdef=None): + FixedSlot.__init__(self, slot_name, "0", ifdef=ifdef) class MethodSlot(SlotDescriptor): @@ -381,8 +368,8 @@ class MethodSlot(SlotDescriptor): # alternatives [string] Alternative list of __xxx__ names for the method def __init__(self, signature, slot_name, method_name, method_name_to_slot, - fallback=None, py3=True, py2=True, ifdef=None, inherited=True): - SlotDescriptor.__init__(self, slot_name, py3=py3, py2=py2, + fallback=None, ifdef=None, inherited=True): + SlotDescriptor.__init__(self, slot_name, ifdef=ifdef, inherited=inherited) self.signature = signature self.slot_name = slot_name @@ -392,19 +379,23 @@ def __init__(self, signature, slot_name, method_name, method_name_to_slot, # if fallback: self.alternatives.append(fallback) - for alt in (self.py2, self.py3): - if isinstance(alt, (tuple, list)): - slot_name, method_name = alt - self.alternatives.append(method_name) - method_name_to_slot[method_name] = self def slot_code(self, scope): entry = scope.lookup_here(self.method_name) if entry and entry.is_special and entry.func_cname: + for method_name in self.alternatives: + alt_entry = scope.lookup_here(method_name) + if alt_entry: + warn_once(alt_entry.pos, + f"{method_name} was removed in Python 3; ignoring it and using {self.method_name} instead", + 2) return entry.func_cname for method_name in self.alternatives: entry = scope.lookup_here(method_name) if entry and entry.is_special and entry.func_cname: + warn_once(entry.pos, + f"{method_name} was removed in Python 3; use {self.method_name} instead", + 2) return entry.func_cname return "0" @@ -907,9 +898,6 @@ def is_reverse_number_slot(name): '__del__': Signature("T", 'r') } - -PyNumberMethods_Py2only_GUARD = "PY_MAJOR_VERSION < 3 || (CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX < 0x03050000)" - #------------------------------------------------------------------------------------------ # # The main slot table. This table contains descriptors for all the @@ -938,8 +926,6 @@ def __init__(self, old_binops): BinopSlot(bf, "nb_add", "__add__", method_name_to_slot), BinopSlot(bf, "nb_subtract", "__sub__", method_name_to_slot), BinopSlot(bf, "nb_multiply", "__mul__", method_name_to_slot), - BinopSlot(bf, "nb_divide", "__div__", method_name_to_slot, - ifdef = PyNumberMethods_Py2only_GUARD), BinopSlot(bf, "nb_remainder", "__mod__", method_name_to_slot), BinopSlot(bf, "nb_divmod", "__divmod__", method_name_to_slot), BinopSlot(ptf, "nb_power", "__pow__", method_name_to_slot), @@ -947,29 +933,21 @@ def __init__(self, old_binops): MethodSlot(unaryfunc, "nb_positive", "__pos__", method_name_to_slot), MethodSlot(unaryfunc, "nb_absolute", "__abs__", method_name_to_slot), MethodSlot(inquiry, "nb_bool", "__bool__", method_name_to_slot, - py2 = ("nb_nonzero", "__nonzero__")), + fallback="__nonzero__"), MethodSlot(unaryfunc, "nb_invert", "__invert__", method_name_to_slot), BinopSlot(bf, "nb_lshift", "__lshift__", method_name_to_slot), BinopSlot(bf, "nb_rshift", "__rshift__", method_name_to_slot), BinopSlot(bf, "nb_and", "__and__", method_name_to_slot), BinopSlot(bf, "nb_xor", "__xor__", method_name_to_slot), BinopSlot(bf, "nb_or", "__or__", method_name_to_slot), - EmptySlot("nb_coerce", ifdef = PyNumberMethods_Py2only_GUARD), MethodSlot(unaryfunc, "nb_int", "__int__", method_name_to_slot, fallback="__long__"), - MethodSlot(unaryfunc, "nb_long", "__long__", method_name_to_slot, - fallback="__int__", py3 = ""), + EmptySlot("nb_long (reserved)"), MethodSlot(unaryfunc, "nb_float", "__float__", method_name_to_slot), - MethodSlot(unaryfunc, "nb_oct", "__oct__", method_name_to_slot, - ifdef = PyNumberMethods_Py2only_GUARD), - MethodSlot(unaryfunc, "nb_hex", "__hex__", method_name_to_slot, - ifdef = PyNumberMethods_Py2only_GUARD), # Added in release 2.0 MethodSlot(ibinaryfunc, "nb_inplace_add", "__iadd__", method_name_to_slot), MethodSlot(ibinaryfunc, "nb_inplace_subtract", "__isub__", method_name_to_slot), MethodSlot(ibinaryfunc, "nb_inplace_multiply", "__imul__", method_name_to_slot), - MethodSlot(ibinaryfunc, "nb_inplace_divide", "__idiv__", method_name_to_slot, - ifdef = PyNumberMethods_Py2only_GUARD), MethodSlot(ibinaryfunc, "nb_inplace_remainder", "__imod__", method_name_to_slot), MethodSlot(ptf, "nb_inplace_power", "__ipow__", method_name_to_slot), MethodSlot(ibinaryfunc, "nb_inplace_lshift", "__ilshift__", method_name_to_slot), @@ -1015,15 +993,6 @@ def __init__(self, old_binops): ) self.PyBufferProcs = ( - MethodSlot(readbufferproc, "bf_getreadbuffer", "__getreadbuffer__", method_name_to_slot, - py3 = False), - MethodSlot(writebufferproc, "bf_getwritebuffer", "__getwritebuffer__", method_name_to_slot, - py3 = False), - MethodSlot(segcountproc, "bf_getsegcount", "__getsegcount__", method_name_to_slot, - py3 = False), - MethodSlot(charbufferproc, "bf_getcharbuffer", "__getcharbuffer__", method_name_to_slot, - py3 = False), - MethodSlot(getbufferproc, "bf_getbuffer", "__getbuffer__", method_name_to_slot), MethodSlot(releasebufferproc, "bf_releasebuffer", "__releasebuffer__", method_name_to_slot) ) @@ -1042,10 +1011,8 @@ def __init__(self, old_binops): EmptySlot("tp_getattr"), EmptySlot("tp_setattr"), - # tp_compare (Py2) / tp_reserved (Py3<3.5) / tp_as_async (Py3.5+) is always used as tp_as_async in Py3 - MethodSlot(cmpfunc, "tp_compare", "__cmp__", method_name_to_slot, ifdef="PY_MAJOR_VERSION < 3"), SuiteSlot(self. PyAsyncMethods, "__Pyx_PyAsyncMethodsStruct", "tp_as_async", - self.substructures, ifdef="PY_MAJOR_VERSION >= 3"), + self.substructures), MethodSlot(reprfunc, "tp_repr", "__repr__", method_name_to_slot), @@ -1134,6 +1101,15 @@ def __init__(self, old_binops): MethodSlot(descrsetfunc, "", "__set__", method_name_to_slot) MethodSlot(descrdelfunc, "", "__delete__", method_name_to_slot) + #------------------------------------------------------------------------- + # + # Legacy "fallback" Py2 slots. Don't appear in the generated slot table, + # but match the "fallback" argument of a slot that does + # + #------------------------------------------------------------------------- + MethodSlot(inquiry, "", "__nonzero__", method_name_to_slot) + MethodSlot(unaryfunc, "", "__long__", method_name_to_slot) + def get_special_method_signature(self, name): # Given a method name, if it is a special method, # return its signature, else return None. diff --git a/tests/run/special_methods_T561_py3.pyx b/tests/run/special_methods_T561_py3.pyx index 56ea83ceca3..95edd6260b2 100644 --- a/tests/run/special_methods_T561_py3.pyx +++ b/tests/run/special_methods_T561_py3.pyx @@ -1,5 +1,5 @@ # ticket: t561 -# tag: py3 +# tag: py3, warnings # This file tests the behavior of special methods under Python 3 # after #561. (Only methods whose behavior differs between Python 2 and 3 # are tested here; see special_methods_T561.pyx for the rest of the tests.) @@ -8,32 +8,9 @@ __doc__ = u""" >>> vs0 = VerySpecial(0) VS __init__ 0 - >>> # Python 3 does not use __cmp__, so any provided __cmp__ method is - >>> # discarded under Python 3. - >>> vs0_cmp = vs0.__cmp__ # doctest: +ELLIPSIS - Traceback (most recent call last): - AttributeError: 'special_methods_T561_py3.VerySpecial' object has no attribute '__cmp__'... - - >>> # Python 3 does not use __div__ or __idiv__, so these methods are - >>> # discarded under Python 3. - >>> vs0_div = vs0.__div__ # doctest: +ELLIPSIS - Traceback (most recent call last): - AttributeError: 'special_methods_T561_py3.VerySpecial' object has no attribute '__div__'... - >>> vs0_rdiv = vs0.__rdiv__ # doctest: +ELLIPSIS - Traceback (most recent call last): - AttributeError: 'special_methods_T561_py3.VerySpecial' object has no attribute '__rdiv__'... - >>> vs0_idiv = vs0.__idiv__ # doctest: +ELLIPSIS - Traceback (most recent call last): - AttributeError: 'special_methods_T561_py3.VerySpecial' object has no attribute '__idiv__'... - - >>> # Python 3 does not use __oct__ or __hex__, so these methods are - >>> # discarded under Python 3. - >>> vs0_oct = vs0.__oct__ # doctest: +ELLIPSIS - Traceback (most recent call last): - AttributeError: 'special_methods_T561_py3.VerySpecial' object has no attribute '__oct__'... - >>> vs0_hex = vs0.__hex__ # doctest: +ELLIPSIS - Traceback (most recent call last): - AttributeError: 'special_methods_T561_py3.VerySpecial' object has no attribute '__hex__'... + >>> # Python 3 does not use __cmp__, __div__, __idiv__, __oct__ or __hex__; + >>> # These methods have no special behaviour and aren't tested beyond that + >>> # they don't break compilation. >>> # Python 3 does not use __long__; if you define __long__ but not >>> # __int__, the __long__ definition will be used for __int__. @@ -79,3 +56,8 @@ cdef class VerySpecial: cdef class Long: def __long__(self): print "Long __long__" + +_WARNINGS = """ +38:4: __nonzero__ was removed in Python 3; use __bool__ instead +57:4: __long__ was removed in Python 3; use __int__ instead +""" From f82dc93680020a9578d78f55ccc2b4311dea11c4 Mon Sep 17 00:00:00 2001 From: da-woods Date: Sun, 3 Dec 2023 19:57:52 +0000 Subject: [PATCH 042/286] Remove old Python 2 cpython pxd files (GH-5870) --- Cython/Includes/cpython/__init__.pxd | 5 -- Cython/Includes/cpython/cobject.pxd | 36 ----------- Cython/Includes/cpython/int.pxd | 89 --------------------------- Cython/Includes/cpython/oldbuffer.pxd | 63 ------------------- tests/run/cython_includes.pyx | 3 - 5 files changed, 196 deletions(-) delete mode 100644 Cython/Includes/cpython/cobject.pxd delete mode 100644 Cython/Includes/cpython/int.pxd delete mode 100644 Cython/Includes/cpython/oldbuffer.pxd diff --git a/Cython/Includes/cpython/__init__.pxd b/Cython/Includes/cpython/__init__.pxd index 7ad2684aa79..1254a1229ab 100644 --- a/Cython/Includes/cpython/__init__.pxd +++ b/Cython/Includes/cpython/__init__.pxd @@ -149,7 +149,6 @@ from cpython.mapping cimport * from cpython.iterator cimport * from cpython.type cimport * from cpython.number cimport * -from cpython.int cimport * from cpython.bool cimport * from cpython.long cimport * from cpython.float cimport * @@ -165,10 +164,6 @@ from cpython.getargs cimport * from cpython.pythread cimport * from cpython.pystate cimport * -# Python <= 2.x -from cpython.cobject cimport * -from cpython.oldbuffer cimport * - # Python >= 2.4 from cpython.set cimport * diff --git a/Cython/Includes/cpython/cobject.pxd b/Cython/Includes/cpython/cobject.pxd deleted file mode 100644 index 497d8a92e80..00000000000 --- a/Cython/Includes/cpython/cobject.pxd +++ /dev/null @@ -1,36 +0,0 @@ - -cdef extern from "Python.h": - - ########################################################################### - # Warning: - # - # The CObject API is deprecated as of Python 3.1. Please switch to - # the new Capsules API. - ########################################################################### - - int PyCObject_Check(object p) - # Return true if its argument is a PyCObject. - - object PyCObject_FromVoidPtr(void* cobj, void (*destr)(void *)) - # Return value: New reference. - # - # Create a PyCObject from the void * cobj. The destr function will - # be called when the object is reclaimed, unless it is NULL. - - object PyCObject_FromVoidPtrAndDesc(void* cobj, void* desc, void (*destr)(void *, void *)) - # Return value: New reference. - # - # Create a PyCObject from the void * cobj. The destr function will - # be called when the object is reclaimed. The desc argument can be - # used to pass extra callback data for the destructor function. - - void* PyCObject_AsVoidPtr(object self) except? NULL - # Return the object void * that the PyCObject self was created with. - - void* PyCObject_GetDesc(object self) except? NULL - # Return the description void * that the PyCObject self was created with. - - int PyCObject_SetVoidPtr(object self, void* cobj) except 0 - # Set the void pointer inside self to cobj. The PyCObject must not - # have an associated destructor. Return true on success, false on - # failure. diff --git a/Cython/Includes/cpython/int.pxd b/Cython/Includes/cpython/int.pxd deleted file mode 100644 index 50babff6151..00000000000 --- a/Cython/Includes/cpython/int.pxd +++ /dev/null @@ -1,89 +0,0 @@ -cdef extern from "Python.h": - ctypedef unsigned long long PY_LONG_LONG - - ############################################################################ - # Integer Objects - ############################################################################ - # PyTypeObject PyInt_Type - # This instance of PyTypeObject represents the Python plain - # integer type. This is the same object as int and types.IntType. - - bint PyInt_Check(object o) - # Return true if o is of type PyInt_Type or a subtype of - # PyInt_Type. - - bint PyInt_CheckExact(object o) - # Return true if o is of type PyInt_Type, but not a subtype of - # PyInt_Type. - - object PyInt_FromString(char *str, char **pend, int base) - # Return value: New reference. - # Return a new PyIntObject or PyLongObject based on the string - # value in str, which is interpreted according to the radix in - # base. If pend is non-NULL, *pend will point to the first - # character in str which follows the representation of the - # number. If base is 0, the radix will be determined based on the - # leading characters of str: if str starts with '0x' or '0X', - # radix 16 will be used; if str starts with '0', radix 8 will be - # used; otherwise radix 10 will be used. If base is not 0, it must - # be between 2 and 36, inclusive. Leading spaces are ignored. If - # there are no digits, ValueError will be raised. If the string - # represents a number too large to be contained within the - # machine's long int type and overflow warnings are being - # suppressed, a PyLongObject will be returned. If overflow - # warnings are not being suppressed, NULL will be returned in this - # case. - - object PyInt_FromLong(long ival) - # Return value: New reference. - # Create a new integer object with a value of ival. - # The current implementation keeps an array of integer objects for - # all integers between -5 and 256, when you create an int in that - # range you actually just get back a reference to the existing - # object. So it should be possible to change the value of 1. I - # suspect the behaviour of Python in this case is undefined. :-) - - object PyInt_FromSsize_t(Py_ssize_t ival) - # Return value: New reference. - # Create a new integer object with a value of ival. If the value - # is larger than LONG_MAX or smaller than LONG_MIN, a long integer - # object is returned. - - object PyInt_FromSize_t(size_t ival) - # Return value: New reference. - # Create a new integer object with a value of ival. If the value - # exceeds LONG_MAX, a long integer object is returned. - - long PyInt_AsLong(object io) except? -1 - # Will first attempt to cast the object to a PyIntObject, if it is - # not already one, and then return its value. If there is an - # error, -1 is returned, and the caller should check - # PyErr_Occurred() to find out whether there was an error, or - # whether the value just happened to be -1. - - long PyInt_AS_LONG(object io) - # Return the value of the object io. No error checking is performed. - - unsigned long PyInt_AsUnsignedLongMask(object io) except? -1 - # Will first attempt to cast the object to a PyIntObject or - # PyLongObject, if it is not already one, and then return its - # value as unsigned long. This function does not check for - # overflow. - - PY_LONG_LONG PyInt_AsUnsignedLongLongMask(object io) except? -1 - # Will first attempt to cast the object to a PyIntObject or - # PyLongObject, if it is not already one, and then return its - # value as unsigned long long, without checking for overflow. - - Py_ssize_t PyInt_AsSsize_t(object io) except? -1 - # Will first attempt to cast the object to a PyIntObject or - # PyLongObject, if it is not already one, and then return its - # value as Py_ssize_t. - - long PyInt_GetMax() - # Return the system's idea of the largest integer it can handle - # (LONG_MAX, as defined in the system header files). - - int PyInt_ClearFreeList() - # Clear the integer free list. Return the number of items that could not be freed. - # New in version 2.6. diff --git a/Cython/Includes/cpython/oldbuffer.pxd b/Cython/Includes/cpython/oldbuffer.pxd deleted file mode 100644 index 0222428ed48..00000000000 --- a/Cython/Includes/cpython/oldbuffer.pxd +++ /dev/null @@ -1,63 +0,0 @@ -# Legacy Python 2 buffer interface. -# -# These functions are no longer available in Python 3, use the new -# buffer interface instead. - -cdef extern from "Python.h": - cdef enum _: - Py_END_OF_BUFFER - # This constant may be passed as the size parameter to - # PyBuffer_FromObject() or PyBuffer_FromReadWriteObject(). It - # indicates that the new PyBufferObject should refer to base object - # from the specified offset to the end of its exported - # buffer. Using this enables the caller to avoid querying the base - # object for its length. - - bint PyBuffer_Check(object p) - # Return true if the argument has type PyBuffer_Type. - - object PyBuffer_FromObject(object base, Py_ssize_t offset, Py_ssize_t size) - # Return value: New reference. - # - # Return a new read-only buffer object. This raises TypeError if - # base doesn't support the read-only buffer protocol or doesn't - # provide exactly one buffer segment, or it raises ValueError if - # offset is less than zero. The buffer will hold a reference to the - # base object, and the buffer's contents will refer to the base - # object's buffer interface, starting as position offset and - # extending for size bytes. If size is Py_END_OF_BUFFER, then the - # new buffer's contents extend to the length of the base object's - # exported buffer data. - - object PyBuffer_FromReadWriteObject(object base, Py_ssize_t offset, Py_ssize_t size) - # Return value: New reference. - # - # Return a new writable buffer object. Parameters and exceptions - # are similar to those for PyBuffer_FromObject(). If the base - # object does not export the writeable buffer protocol, then - # TypeError is raised. - - object PyBuffer_FromMemory(void *ptr, Py_ssize_t size) - # Return value: New reference. - # - # Return a new read-only buffer object that reads from a specified - # location in memory, with a specified size. The caller is - # responsible for ensuring that the memory buffer, passed in as - # ptr, is not deallocated while the returned buffer object - # exists. Raises ValueError if size is less than zero. Note that - # Py_END_OF_BUFFER may not be passed for the size parameter; - # ValueError will be raised in that case. - - object PyBuffer_FromReadWriteMemory(void *ptr, Py_ssize_t size) - # Return value: New reference. - # - # Similar to PyBuffer_FromMemory(), but the returned buffer is - # writable. - - object PyBuffer_New(Py_ssize_t size) - # Return value: New reference. - # - # Return a new writable buffer object that maintains its own memory - # buffer of size bytes. ValueError is returned if size is not zero - # or positive. Note that the memory buffer (as returned by - # PyObject_AsWriteBuffer()) is not specifically aligned. diff --git a/tests/run/cython_includes.pyx b/tests/run/cython_includes.pyx index af91f6f9e80..efdca6e9bb9 100644 --- a/tests/run/cython_includes.pyx +++ b/tests/run/cython_includes.pyx @@ -12,7 +12,6 @@ cimport cpython.bytearray cimport cpython.bytes cimport cpython.cellobject cimport cpython.ceval -cimport cpython.cobject cimport cpython.codecs cimport cpython.complex cimport cpython.contextvars @@ -26,7 +25,6 @@ cimport cpython.function cimport cpython.genobject cimport cpython.getargs cimport cpython.instance -cimport cpython.int cimport cpython.iterator cimport cpython.iterobject cimport cpython.list @@ -40,7 +38,6 @@ cimport cpython.method cimport cpython.module cimport cpython.number cimport cpython.object -cimport cpython.oldbuffer cimport cpython.pycapsule cimport cpython.pylifecycle cimport cpython.pystate From a4a0f1e1a2c848181e5474e093d96173cc566988 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Mon, 4 Dec 2023 12:28:11 +0100 Subject: [PATCH 043/286] Add missing Cython macro guard definitions to the NOGIL-CPython platform section. The default values should be reasonable based on what was there and what CPython uses, but were not validated. --- Cython/Utility/ModuleSetupCode.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 42030f3b701..a55b4815b87 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -266,11 +266,17 @@ #ifndef CYTHON_USE_TYPE_SLOTS #define CYTHON_USE_TYPE_SLOTS 1 #endif + #ifndef CYTHON_USE_TYPE_SPECS + #define CYTHON_USE_TYPE_SPECS 0 + #endif #undef CYTHON_USE_PYTYPE_LOOKUP #define CYTHON_USE_PYTYPE_LOOKUP 0 #ifndef CYTHON_USE_ASYNC_SLOTS #define CYTHON_USE_ASYNC_SLOTS 1 #endif + #ifndef CYTHON_USE_PYLONG_INTERNALS + #define CYTHON_USE_PYLONG_INTERNALS 0 + #endif #undef CYTHON_USE_PYLIST_INTERNALS #define CYTHON_USE_PYLIST_INTERNALS 0 #ifndef CYTHON_USE_UNICODE_INTERNALS @@ -278,8 +284,6 @@ #endif #undef CYTHON_USE_UNICODE_WRITER #define CYTHON_USE_UNICODE_WRITER 0 - #undef CYTHON_USE_PYLONG_INTERNALS - #define CYTHON_USE_PYLONG_INTERNALS 0 #ifndef CYTHON_AVOID_BORROWED_REFS #define CYTHON_AVOID_BORROWED_REFS 0 #endif @@ -294,11 +298,22 @@ #endif #undef CYTHON_FAST_THREAD_STATE #define CYTHON_FAST_THREAD_STATE 0 + #undef CYTHON_FAST_GIL + #define CYTHON_FAST_GIL 0 + #ifndef CYTHON_METH_FASTCALL + #define CYTHON_METH_FASTCALL 1 + #endif #undef CYTHON_FAST_PYCALL #define CYTHON_FAST_PYCALL 0 + #ifndef CYTHON_PEP487_INIT_SUBCLASS + #define CYTHON_PEP487_INIT_SUBCLASS 1 + #endif #ifndef CYTHON_PEP489_MULTI_PHASE_INIT #define CYTHON_PEP489_MULTI_PHASE_INIT 1 #endif + #ifndef CYTHON_USE_MODULE_STATE + #define CYTHON_USE_MODULE_STATE 0 + #endif #ifndef CYTHON_USE_TP_FINALIZE #define CYTHON_USE_TP_FINALIZE 1 #endif @@ -306,11 +321,14 @@ #define CYTHON_USE_DICT_VERSIONS 0 #undef CYTHON_USE_EXC_INFO_STACK #define CYTHON_USE_EXC_INFO_STACK 0 + #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC + #define CYTHON_UPDATE_DESCRIPTOR_DOC 1 + #endif #ifndef CYTHON_USE_FREELISTS - // TODO - we could probably enable CYTHON_USE_FREELISTS by default in future since - // this is just a variant of cpython now, but we'd need to be very careful to make - // them thread safe. Since it will probably work, let the user decide. - #define CYTHON_USE_FREELISTS 0 + // TODO - we could probably enable CYTHON_USE_FREELISTS by default in future since + // this is just a variant of cpython now, but we'd need to be very careful to make + // them thread safe. Since it will probably work, let the user decide. + #define CYTHON_USE_FREELISTS 0 #endif #else From 914cad5112c2042a26fc14489b4a768603f828bc Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Mon, 4 Dec 2023 20:57:13 +0100 Subject: [PATCH 044/286] Guard all usages of "__pyx_vectorcallfunc" with "CYTHON_VECTORCALL || CYTHON_BACKPORT_VECTORCALL". --- Cython/Utility/CythonFunction.c | 4 ++-- Cython/Utility/ObjectHandling.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 440fed2541c..765763b889f 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -858,7 +858,7 @@ static PyObject *__Pyx_CyFunction_CallAsMethod(PyObject *func, PyObject *args, P PyObject *result; __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *) func; -#if CYTHON_METH_FASTCALL +#if CYTHON_METH_FASTCALL && (CYTHON_VECTORCALL || CYTHON_BACKPORT_VECTORCALL) // Prefer vectorcall if available. This is not the typical case, as // CPython would normally use vectorcall directly instead of tp_call. __pyx_vectorcallfunc vc = __Pyx_CyFunction_func_vectorcall(cyfunc); @@ -906,7 +906,7 @@ static PyObject *__Pyx_CyFunction_CallAsMethod(PyObject *func, PyObject *args, P return result; } -#if CYTHON_METH_FASTCALL +#if CYTHON_METH_FASTCALL && (CYTHON_VECTORCALL || CYTHON_BACKPORT_VECTORCALL) // Check that kwnames is empty (if you want to allow keyword arguments, // simply pass kwnames=NULL) and figure out what to do with "self". // Return value: diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 80188388d21..f04016ecf4e 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -2393,13 +2393,13 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_CallNoArg(PyObject *func) { /////////////// PyVectorcallFastCallDict.proto /////////////// -#if CYTHON_METH_FASTCALL +#if CYTHON_METH_FASTCALL && (CYTHON_VECTORCALL || CYTHON_BACKPORT_VECTORCALL) static CYTHON_INLINE PyObject *__Pyx_PyVectorcall_FastCallDict(PyObject *func, __pyx_vectorcallfunc vc, PyObject *const *args, size_t nargs, PyObject *kw); #endif /////////////// PyVectorcallFastCallDict /////////////// -#if CYTHON_METH_FASTCALL +#if CYTHON_METH_FASTCALL && (CYTHON_VECTORCALL || CYTHON_BACKPORT_VECTORCALL) // Slow path when kw is non-empty static PyObject *__Pyx_PyVectorcall_FastCallDict_kw(PyObject *func, __pyx_vectorcallfunc vc, PyObject *const *args, size_t nargs, PyObject *kw) { From e25e647954e06847a50393768b856863bb9d303d Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Mon, 4 Dec 2023 20:58:53 +0100 Subject: [PATCH 045/286] Disable "CYTHON_METH_FASTCALL" in nogil-CPython because it also requires "CYTHON_FAST_PYCALL" to be enabled in order to make use of the vectorcall protocol. I don't know why "CYTHON_FAST_PYCALL" is disabled here, though. Both could probably be enabled in recent CPython versions. --- Cython/Utility/ModuleSetupCode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index a55b4815b87..49c2e3191ec 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -301,7 +301,7 @@ #undef CYTHON_FAST_GIL #define CYTHON_FAST_GIL 0 #ifndef CYTHON_METH_FASTCALL - #define CYTHON_METH_FASTCALL 1 + #define CYTHON_METH_FASTCALL 0 #endif #undef CYTHON_FAST_PYCALL #define CYTHON_FAST_PYCALL 0 From 238cb6b719624f19e8a4a35ec1453ebd241139f8 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Mon, 4 Dec 2023 20:59:45 +0100 Subject: [PATCH 046/286] Fix an "#if" guard indentation to align with its "#else" and "#endif". --- Cython/Utility/FunctionArguments.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Utility/FunctionArguments.c b/Cython/Utility/FunctionArguments.c index a4fa2bb8d85..2f3bab8744f 100644 --- a/Cython/Utility/FunctionArguments.c +++ b/Cython/Utility/FunctionArguments.c @@ -445,7 +445,7 @@ static int __Pyx_MergeKeywords(PyObject *kwdict, PyObject *source_mapping) { #define __Pyx_NumKwargs_FASTCALL(kwds) PyTuple_GET_SIZE(kwds) #define __Pyx_KwValues_FASTCALL(args, nargs) ((args) + (nargs)) static CYTHON_INLINE PyObject * __Pyx_GetKwValue_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues, PyObject *s); -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000 + #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000 CYTHON_UNUSED static PyObject *__Pyx_KwargsAsDict_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues);/*proto*/ #else #define __Pyx_KwargsAsDict_FASTCALL(kw, kwvalues) _PyStack_AsDict(kwvalues, kw) From 02ddde36567db52ecfb5004039221db8e2bec60c Mon Sep 17 00:00:00 2001 From: da-woods Date: Mon, 4 Dec 2023 20:16:09 +0000 Subject: [PATCH 047/286] Make several C-API macro usages robust for the Limited API (GH-5845) --- Cython/Compiler/ExprNodes.py | 3 ++ Cython/Utility/Builtins.c | 35 ++++++++++++++- Cython/Utility/ObjectHandling.c | 32 ++++++++++--- Cython/Utility/Optimize.c | 14 ++++++ Cython/Utility/StringTools.c | 79 +++++---------------------------- Cython/Utility/TypeConversion.c | 19 ++++---- 6 files changed, 97 insertions(+), 85 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index fbc3eb1ffae..9edac7d2fea 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -14179,6 +14179,7 @@ class CoerceToBooleanNode(CoercionNode): type = PyrexTypes.c_bint_type + # Note that all of these need a check if CYTHON_ASSUME_SAFE_MACROS is false _special_builtins = { Builtin.list_type: '__Pyx_PyList_GET_SIZE', Builtin.tuple_type: '__Pyx_PyTuple_GET_SIZE', @@ -14217,6 +14218,8 @@ def generate_result_code(self, code): checks = ["(%s != Py_None)" % self.arg.py_result()] if self.arg.may_be_none() else [] checks.append("(%s(%s) != 0)" % (test_func, self.arg.py_result())) code.putln("%s = %s;" % (self.result(), '&&'.join(checks))) + code.putln(code.error_goto_if( + "((!CYTHON_ASSUME_SAFE_MACROS) && %s < 0)" % self.result(), self.pos)) else: code.putln( "%s = __Pyx_PyObject_IsTrue(%s); %s" % ( diff --git a/Cython/Utility/Builtins.c b/Cython/Utility/Builtins.c index f67b0387798..414d9efa363 100644 --- a/Cython/Utility/Builtins.c +++ b/Cython/Utility/Builtins.c @@ -353,14 +353,30 @@ static long __Pyx__PyObject_Ord(PyObject* c) { if (PyBytes_Check(c)) { size = __Pyx_PyBytes_GET_SIZE(c); if (likely(size == 1)) { +#if CYTHON_ASSUME_SAFE_MACROS return (unsigned char) PyBytes_AS_STRING(c)[0]; +#else + char *data = PyBytes_AsString(c); + if (unlikely(!data)) return -1; + return (unsigned char) data[0]; +#endif } -#if (!CYTHON_COMPILING_IN_PYPY) || (defined(PyByteArray_AS_STRING) && defined(PyByteArray_GET_SIZE)) +#if !CYTHON_ASSUME_SAFE_SIZE + else if (unlikely(size < 0)) return -1; +#endif } else if (PyByteArray_Check(c)) { size = __Pyx_PyByteArray_GET_SIZE(c); if (likely(size == 1)) { +#if CYTHON_ASSUME_SAFE_MACROS return (unsigned char) PyByteArray_AS_STRING(c)[0]; +#else + char *data = PyByteArray_AsString(c); + if (unlikely(!data)) return -1; + return (unsigned char) data[0]; +#endif } +#if !CYTHON_ASSUME_SAFE_SIZE + else if (unlikely(size < 0)) return -1; #endif } else { // FIXME: support character buffers - but CPython doesn't support them either @@ -495,8 +511,23 @@ static CYTHON_INLINE PyObject* __Pyx_PyFrozenSet_New(PyObject* it) { result = PyFrozenSet_New(it); if (unlikely(!result)) return NULL; - if ((PY_VERSION_HEX >= 0x030A00A1) || likely(__Pyx_PySet_GET_SIZE(result))) + if ((__PYX_LIMITED_VERSION_HEX >= 0x030A00A1) +#if CYTHON_COMPILING_IN_LIMITED_API + || __Pyx_get_runtime_version() >= 0x030A00A1 +#endif + ) return result; + { + Py_ssize_t size = __Pyx_PySet_GET_SIZE(result); + if (likely(size > 0)) + return result; +#if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(size < 0)) { + Py_DECREF(result); + return NULL; + } +#endif + } // empty frozenset is a singleton (on Python <3.10) // seems wasteful, but CPython does the same Py_DECREF(result); diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index f04016ecf4e..cce7e482bd4 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -54,6 +54,9 @@ static void __Pyx_UnpackTupleError(PyObject *t, Py_ssize_t index) { __Pyx_RaiseNoneNotIterableError(); } else { Py_ssize_t size = __Pyx_PyTuple_GET_SIZE(t); + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(size < 0)) return; + #endif if (size < index) { __Pyx_RaiseNeedMoreValuesError(size); } else { @@ -82,12 +85,8 @@ static int __Pyx_IternextUnpackEndCheck(PyObject *retval, Py_ssize_t expected) { /////////////// UnpackTuple2.proto /////////////// -#define __Pyx_unpack_tuple2(tuple, value1, value2, is_tuple, has_known_size, decref_tuple) \ - (likely(is_tuple || PyTuple_Check(tuple)) ? \ - (likely(has_known_size || PyTuple_GET_SIZE(tuple) == 2) ? \ - __Pyx_unpack_tuple2_exact(tuple, value1, value2, decref_tuple) : \ - (__Pyx_UnpackTupleError(tuple, 2), -1)) : \ - __Pyx_unpack_tuple2_generic(tuple, value1, value2, has_known_size, decref_tuple)) +static CYTHON_INLINE int __Pyx_unpack_tuple2( + PyObject* tuple, PyObject** value1, PyObject** value2, int is_tuple, int has_known_size, int decref_tuple); static CYTHON_INLINE int __Pyx_unpack_tuple2_exact( PyObject* tuple, PyObject** value1, PyObject** value2, int decref_tuple); @@ -99,6 +98,27 @@ static int __Pyx_unpack_tuple2_generic( //@requires: UnpackTupleError //@requires: RaiseNeedMoreValuesToUnpack +static CYTHON_INLINE int __Pyx_unpack_tuple2( + PyObject* tuple, PyObject** value1, PyObject** value2, int is_tuple, int has_known_size, int decref_tuple) { + if (likely(is_tuple || PyTuple_Check(tuple))) { + Py_ssize_t size; + if (has_known_size) { + return __Pyx_unpack_tuple2_exact(tuple, value1, value2, decref_tuple); + } + size = __Pyx_PyTuple_GET_SIZE(tuple); + if (likely(size == 2)) { + return __Pyx_unpack_tuple2_exact(tuple, value1, value2, decref_tuple); + } + if (size >= 0) { + // "size == -1" indicates an error already. + __Pyx_UnpackTupleError(tuple, 2); + } + return -1; + } else { + return __Pyx_unpack_tuple2_generic(tuple, value1, value2, has_known_size, decref_tuple); + } +} + static CYTHON_INLINE int __Pyx_unpack_tuple2_exact( PyObject* tuple, PyObject** pvalue1, PyObject** pvalue2, int decref_tuple) { PyObject *value1 = NULL, *value2 = NULL; diff --git a/Cython/Utility/Optimize.c b/Cython/Utility/Optimize.c index 619e054b285..519cee8ea72 100644 --- a/Cython/Utility/Optimize.c +++ b/Cython/Utility/Optimize.c @@ -361,8 +361,22 @@ static CYTHON_INLINE int __Pyx_dict_iter_next( } Py_INCREF(key); Py_INCREF(value); + #if CYTHON_ASSUME_SAFE_MACROS PyTuple_SET_ITEM(tuple, 0, key); PyTuple_SET_ITEM(tuple, 1, value); + #else + if (unlikely(PyTuple_SetItem(tuple, 0, key) < 0)) { + // decref value; PyTuple_SetItem decrefs key on failure + Py_DECREF(value); + Py_DECREF(tuple); + return -1; + } + if (unlikely(PyTuple_SetItem(tuple, 1, value) < 0)) { + // PyTuple_SetItem decrefs value on failure + Py_DECREF(tuple); + return -1; + } + #endif *pitem = tuple; } else { if (pkey) { diff --git a/Cython/Utility/StringTools.c b/Cython/Utility/StringTools.c index 867f99b8f43..8187af29b87 100644 --- a/Cython/Utility/StringTools.c +++ b/Cython/Utility/StringTools.c @@ -75,75 +75,13 @@ static CYTHON_INLINE int __Pyx_UnicodeContainsUCS4(PyObject* unicode, Py_UCS4 ch //////////////////// PyUCS4InUnicode //////////////////// -#if PY_VERSION_HEX < 0x03090000 || (defined(PyUnicode_WCHAR_KIND) && defined(PyUnicode_AS_UNICODE)) - -#if PY_VERSION_HEX < 0x03090000 -#define __Pyx_PyUnicode_AS_UNICODE(op) PyUnicode_AS_UNICODE(op) -#define __Pyx_PyUnicode_GET_SIZE(op) PyUnicode_GET_SIZE(op) -#else -// Avoid calling deprecated C-API functions in Py3.9+ that PEP-623 schedules for removal in Py3.12. -// https://www.python.org/dev/peps/pep-0623/ -#define __Pyx_PyUnicode_AS_UNICODE(op) (((PyASCIIObject *)(op))->wstr) -#define __Pyx_PyUnicode_GET_SIZE(op) ((PyCompactUnicodeObject *)(op))->wstr_length -#endif - -#if !defined(Py_UNICODE_SIZE) || Py_UNICODE_SIZE == 2 -static int __Pyx_PyUnicodeBufferContainsUCS4_SP(Py_UNICODE* buffer, Py_ssize_t length, Py_UCS4 character) { - /* handle surrogate pairs for Py_UNICODE buffers in 16bit Unicode builds */ - Py_UNICODE high_val, low_val; - Py_UNICODE* pos; - high_val = (Py_UNICODE) (0xD800 | (((character - 0x10000) >> 10) & ((1<<10)-1))); - low_val = (Py_UNICODE) (0xDC00 | ( (character - 0x10000) & ((1<<10)-1))); - for (pos=buffer; pos < buffer+length-1; pos++) { - if (unlikely((high_val == pos[0]) & (low_val == pos[1]))) return 1; - } - return 0; -} -#endif - -static int __Pyx_PyUnicodeBufferContainsUCS4_BMP(Py_UNICODE* buffer, Py_ssize_t length, Py_UCS4 character) { - Py_UNICODE uchar; - Py_UNICODE* pos; - uchar = (Py_UNICODE) character; - for (pos=buffer; pos < buffer+length; pos++) { - if (unlikely(uchar == pos[0])) return 1; - } - return 0; -} -#endif - static CYTHON_INLINE int __Pyx_UnicodeContainsUCS4(PyObject* unicode, Py_UCS4 character) { - const int kind = PyUnicode_KIND(unicode); - #ifdef PyUnicode_WCHAR_KIND - if (likely(kind != PyUnicode_WCHAR_KIND)) - #endif - { - Py_ssize_t i; - const void* udata = PyUnicode_DATA(unicode); - const Py_ssize_t length = PyUnicode_GET_LENGTH(unicode); - for (i=0; i < length; i++) { - if (unlikely(character == PyUnicode_READ(kind, udata, i))) return 1; - } - return 0; - } - -#if PY_VERSION_HEX < 0x03090000 || (defined(PyUnicode_WCHAR_KIND) && defined(PyUnicode_AS_UNICODE)) -#if !defined(Py_UNICODE_SIZE) || Py_UNICODE_SIZE == 2 - if ((sizeof(Py_UNICODE) == 2) && unlikely(character > 65535)) { - return __Pyx_PyUnicodeBufferContainsUCS4_SP( - __Pyx_PyUnicode_AS_UNICODE(unicode), - __Pyx_PyUnicode_GET_SIZE(unicode), - character); - } else -#endif - { - return __Pyx_PyUnicodeBufferContainsUCS4_BMP( - __Pyx_PyUnicode_AS_UNICODE(unicode), - __Pyx_PyUnicode_GET_SIZE(unicode), - character); - - } -#endif + // Note that from Python 3.7, the indices of FindChar are adjusted to match the bounds + // so need to check the length + Py_ssize_t idx = PyUnicode_FindChar(unicode, character, 0, PY_SSIZE_T_MAX, 1); + if (unlikely(idx == -2)) return -1; + // >= 0: found the index, == -1: not found + return idx >= 0; } @@ -597,8 +535,13 @@ static CYTHON_INLINE PyObject* __Pyx_PyUnicode_Substring( return __Pyx_NewRef($empty_unicode); if (start == 0 && stop == length) return __Pyx_NewRef(text); +#if CYTHON_COMPILING_IN_LIMITED_API + // PyUnicode_Substring() does not support negative indexing but is otherwise fine to use. + return PyUnicode_Substring(text, start, stop); +#else return PyUnicode_FromKindAndData(PyUnicode_KIND(text), PyUnicode_1BYTE_DATA(text) + start*PyUnicode_KIND(text), stop-start); +#endif } diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index 449cd49efff..32244c3179d 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -586,15 +586,16 @@ static CYTHON_INLINE Py_UCS4 __Pyx_PyUnicode_AsPy_UCS4(PyObject*); /////////////// UnicodeAsUCS4 /////////////// static CYTHON_INLINE Py_UCS4 __Pyx_PyUnicode_AsPy_UCS4(PyObject* x) { - Py_ssize_t length; - length = PyUnicode_GET_LENGTH(x); - if (likely(length == 1)) { - return PyUnicode_READ_CHAR(x, 0); - } - PyErr_Format(PyExc_ValueError, - "only single character unicode strings can be converted to Py_UCS4, " - "got length %" CYTHON_FORMAT_SSIZE_T "d", length); - return (Py_UCS4)-1; + Py_ssize_t length = __Pyx_PyUnicode_GET_LENGTH(x); + if (likely(length == 1)) { + return __Pyx_PyUnicode_READ_CHAR(x, 0); + } else if (likely(length >= 0)) { + // "length == -1" indicates an error already. + PyErr_Format(PyExc_ValueError, + "only single character unicode strings can be converted to Py_UCS4, " + "got length %" CYTHON_FORMAT_SSIZE_T "d", length); + } + return (Py_UCS4)-1; } From f64f0678a16ee69acbc00a5c5953f8864c90eae6 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Fri, 8 Dec 2023 15:45:23 +0100 Subject: [PATCH 048/286] Avoid unused "enumerate()" in loop. --- Cython/Compiler/FusedNode.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cython/Compiler/FusedNode.py b/Cython/Compiler/FusedNode.py index 5b7d25d10ee..94e856babd9 100644 --- a/Cython/Compiler/FusedNode.py +++ b/Cython/Compiler/FusedNode.py @@ -152,13 +152,13 @@ def copy_cdef(self, env): type.specialize_entry(entry, cname) # Reuse existing Entries (e.g. from .pxd files). - for i, orig_entry in enumerate(env.cfunc_entries): + for orig_entry in env.cfunc_entries: if entry.cname == orig_entry.cname and type.same_as_resolved_type(orig_entry.type): - copied_node.entry = env.cfunc_entries[i] + copied_node.entry = orig_entry if not copied_node.entry.func_cname: copied_node.entry.func_cname = entry.func_cname - entry = copied_node.entry - type = entry.type + entry = orig_entry + type = orig_entry.type break else: new_cfunc_entries.append(entry) From 28b64adade66442bfb02c6ff2bc7c76cff2501bc Mon Sep 17 00:00:00 2001 From: da-woods Date: Fri, 8 Dec 2023 19:46:50 +0000 Subject: [PATCH 049/286] Fix an issue trying list.index indexing in FusedNode (#5896) * Fix an issue trying list.index indexing in FusedNode In some Python versions, generating the error message when .index fails to find an index leads to a compiler crash. Fix this by not relying on type being fully set up while generating __str__. Fixes #5894 and #5588 * Check "in" before indexing --- Cython/Compiler/FusedNode.py | 7 +++---- Cython/Compiler/PyrexTypes.py | 7 ++++++- tests/run/fused_cpdef.pxd | 4 ++++ tests/run/fused_cpdef.pyx | 13 +++++++++++++ 4 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 tests/run/fused_cpdef.pxd diff --git a/Cython/Compiler/FusedNode.py b/Cython/Compiler/FusedNode.py index 94e856babd9..2202aed8f1a 100644 --- a/Cython/Compiler/FusedNode.py +++ b/Cython/Compiler/FusedNode.py @@ -196,12 +196,11 @@ def copy_cdef(self, env): break # replace old entry with new entries - try: + if self.node.entry in env.cfunc_entries: cindex = env.cfunc_entries.index(self.node.entry) - except ValueError: - env.cfunc_entries.extend(new_cfunc_entries) - else: env.cfunc_entries[cindex:cindex+1] = new_cfunc_entries + else: + env.cfunc_entries.extend(new_cfunc_entries) if orig_py_func: self.py_func = self.make_fused_cpdef(orig_py_func, env, diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index b6af4a5ab3f..1fd46754e06 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -3294,7 +3294,12 @@ def declaration_code(self, entity_code, if self.is_overridable: arg_decl_list.append("int %s" % Naming.skip_dispatch_cname) if self.optional_arg_count: - arg_decl_list.append(self.op_arg_struct.declaration_code(Naming.optional_args_cname)) + if self.op_arg_struct: + arg_decl_list.append(self.op_arg_struct.declaration_code(Naming.optional_args_cname)) + else: + # op_arg_struct may not be initialized at this point if this class is being used + # to prepare a Python error message or similar. In this case, just omit the args. + assert for_display if self.has_varargs: arg_decl_list.append("...") arg_decl_code = ", ".join(arg_decl_list) diff --git a/tests/run/fused_cpdef.pxd b/tests/run/fused_cpdef.pxd new file mode 100644 index 00000000000..e4f4a600dd6 --- /dev/null +++ b/tests/run/fused_cpdef.pxd @@ -0,0 +1,4 @@ +cimport cython + +cdef class C: + cpdef object has_default_struct(self, cython.floating x, a=?) diff --git a/tests/run/fused_cpdef.pyx b/tests/run/fused_cpdef.pyx index 3979570b75c..4f569c24c3e 100644 --- a/tests/run/fused_cpdef.pyx +++ b/tests/run/fused_cpdef.pyx @@ -205,3 +205,16 @@ def test_defaults(): >>> mutable_default(3,[]) [3] """ + +cdef class C: + cpdef object has_default_struct(self, cython.floating x, a=None): + return x, a + +# https://github.com/cython/cython/issues/5588 +# On some Python versions this was causing a compiler crash +def test_call_has_default_struct(C c, double x): + """ + >>> test_call_has_default_struct(C(), 5.) + (5.0, None) + """ + return c.has_default_struct(x) From 66a6be18edc6074b3d274cbac71750c34237abef Mon Sep 17 00:00:00 2001 From: da-woods Date: Fri, 8 Dec 2023 19:46:50 +0000 Subject: [PATCH 050/286] Fix an issue trying list.index indexing in FusedNode (#5896) * Fix an issue trying list.index indexing in FusedNode In some Python versions, generating the error message when .index fails to find an index leads to a compiler crash. Fix this by not relying on type being fully set up while generating __str__. Fixes #5894 and #5588 * Check "in" before indexing --- Cython/Compiler/FusedNode.py | 7 +++---- Cython/Compiler/PyrexTypes.py | 7 ++++++- tests/run/fused_cpdef.pxd | 4 ++++ tests/run/fused_cpdef.pyx | 13 +++++++++++++ 4 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 tests/run/fused_cpdef.pxd diff --git a/Cython/Compiler/FusedNode.py b/Cython/Compiler/FusedNode.py index 31793a404a8..d12c9e9c86f 100644 --- a/Cython/Compiler/FusedNode.py +++ b/Cython/Compiler/FusedNode.py @@ -198,12 +198,11 @@ def copy_cdef(self, env): break # replace old entry with new entries - try: + if self.node.entry in env.cfunc_entries: cindex = env.cfunc_entries.index(self.node.entry) - except ValueError: - env.cfunc_entries.extend(new_cfunc_entries) - else: env.cfunc_entries[cindex:cindex+1] = new_cfunc_entries + else: + env.cfunc_entries.extend(new_cfunc_entries) if orig_py_func: self.py_func = self.make_fused_cpdef(orig_py_func, env, diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 229985703d2..40f6fc60381 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -3301,7 +3301,12 @@ def declaration_code(self, entity_code, if self.is_overridable: arg_decl_list.append("int %s" % Naming.skip_dispatch_cname) if self.optional_arg_count: - arg_decl_list.append(self.op_arg_struct.declaration_code(Naming.optional_args_cname)) + if self.op_arg_struct: + arg_decl_list.append(self.op_arg_struct.declaration_code(Naming.optional_args_cname)) + else: + # op_arg_struct may not be initialized at this point if this class is being used + # to prepare a Python error message or similar. In this case, just omit the args. + assert for_display if self.has_varargs: arg_decl_list.append("...") arg_decl_code = ", ".join(arg_decl_list) diff --git a/tests/run/fused_cpdef.pxd b/tests/run/fused_cpdef.pxd new file mode 100644 index 00000000000..e4f4a600dd6 --- /dev/null +++ b/tests/run/fused_cpdef.pxd @@ -0,0 +1,4 @@ +cimport cython + +cdef class C: + cpdef object has_default_struct(self, cython.floating x, a=?) diff --git a/tests/run/fused_cpdef.pyx b/tests/run/fused_cpdef.pyx index 3378a5083a9..e397a46258c 100644 --- a/tests/run/fused_cpdef.pyx +++ b/tests/run/fused_cpdef.pyx @@ -209,3 +209,16 @@ def test_defaults(): >>> mutable_default(3,[]) [3] """ + +cdef class C: + cpdef object has_default_struct(self, cython.floating x, a=None): + return x, a + +# https://github.com/cython/cython/issues/5588 +# On some Python versions this was causing a compiler crash +def test_call_has_default_struct(C c, double x): + """ + >>> test_call_has_default_struct(C(), 5.) + (5.0, None) + """ + return c.has_default_struct(x) From 7f01021383afe77672519f97c5aff19ff45ccfd2 Mon Sep 17 00:00:00 2001 From: da-woods Date: Sat, 9 Dec 2023 14:40:33 +0000 Subject: [PATCH 051/286] Fix __repr__ for BuiltinObjectType (#5897) --- Cython/Compiler/PyrexTypes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 1fd46754e06..7deb99c2e3e 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -1438,7 +1438,7 @@ def __str__(self): return "%s object" % self.name def __repr__(self): - return "<%s>"% self.cname + return "<%s>"% self.typeptr_cname def default_coerced_ctype(self): if self.name in ('bytes', 'bytearray'): From c841e13679ef8e8f5575c1e57ef5c89bef4057a2 Mon Sep 17 00:00:00 2001 From: da-woods Date: Sun, 10 Dec 2023 16:38:27 +0000 Subject: [PATCH 052/286] Fix some exceptions in the unicode cimports (#5902) And add a missing function. Possibly worth backporting to 3.0.x (at least partly) --- Cython/Includes/cpython/unicode.pxd | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Cython/Includes/cpython/unicode.pxd b/Cython/Includes/cpython/unicode.pxd index a7d24e42823..c798ae2b644 100644 --- a/Cython/Includes/cpython/unicode.pxd +++ b/Cython/Includes/cpython/unicode.pxd @@ -194,7 +194,7 @@ cdef extern from *: # string is null-terminated in case this is required by the application. # Also, note that the wchar_t* string might contain null characters, # which would cause the string to be truncated when used with most C functions. - Py_ssize_t PyUnicode_AsWideChar(object o, wchar_t *w, Py_ssize_t size) + Py_ssize_t PyUnicode_AsWideChar(object o, wchar_t *w, Py_ssize_t size) except -1 # Convert the Unicode object to a wide character string. The output # string always ends with a null character. If size is not NULL, @@ -207,7 +207,7 @@ cdef extern from *: # Returns a buffer allocated by PyMem_New (use PyMem_Free() to free it) # on success. On error, returns NULL and *size is undefined. Raises a # MemoryError if memory allocation is failed. - wchar_t *PyUnicode_AsWideCharString(object o, Py_ssize_t *size) + wchar_t *PyUnicode_AsWideCharString(object o, Py_ssize_t *size) except NULL # Unicode Methods @@ -394,7 +394,11 @@ cdef extern from *: # This caches the UTF-8 representation of the string in the Unicode # object, and subsequent calls will return a pointer to the same buffer. # The caller is not responsible for deallocating the buffer - const char* PyUnicode_AsUTF8AndSize(object unicode, Py_ssize_t *size) + const char* PyUnicode_AsUTF8AndSize(object unicode, Py_ssize_t *size) except NULL + + + # As PyUnicode_AsUTF8AndSize(), but does not store the size. + const char *PyUnicode_AsUTF8(object unicode) except NULL # These are the UTF-16 codec APIs: From 44fb246a6a80bc71b3cd0a96d6acd7c2c10a0b25 Mon Sep 17 00:00:00 2001 From: da-woods Date: Sun, 10 Dec 2023 16:38:27 +0000 Subject: [PATCH 053/286] Fix some exceptions in the unicode cimports (#5902) And add a missing function. Possibly worth backporting to 3.0.x (at least partly) --- Cython/Includes/cpython/unicode.pxd | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Cython/Includes/cpython/unicode.pxd b/Cython/Includes/cpython/unicode.pxd index a7d24e42823..c798ae2b644 100644 --- a/Cython/Includes/cpython/unicode.pxd +++ b/Cython/Includes/cpython/unicode.pxd @@ -194,7 +194,7 @@ cdef extern from *: # string is null-terminated in case this is required by the application. # Also, note that the wchar_t* string might contain null characters, # which would cause the string to be truncated when used with most C functions. - Py_ssize_t PyUnicode_AsWideChar(object o, wchar_t *w, Py_ssize_t size) + Py_ssize_t PyUnicode_AsWideChar(object o, wchar_t *w, Py_ssize_t size) except -1 # Convert the Unicode object to a wide character string. The output # string always ends with a null character. If size is not NULL, @@ -207,7 +207,7 @@ cdef extern from *: # Returns a buffer allocated by PyMem_New (use PyMem_Free() to free it) # on success. On error, returns NULL and *size is undefined. Raises a # MemoryError if memory allocation is failed. - wchar_t *PyUnicode_AsWideCharString(object o, Py_ssize_t *size) + wchar_t *PyUnicode_AsWideCharString(object o, Py_ssize_t *size) except NULL # Unicode Methods @@ -394,7 +394,11 @@ cdef extern from *: # This caches the UTF-8 representation of the string in the Unicode # object, and subsequent calls will return a pointer to the same buffer. # The caller is not responsible for deallocating the buffer - const char* PyUnicode_AsUTF8AndSize(object unicode, Py_ssize_t *size) + const char* PyUnicode_AsUTF8AndSize(object unicode, Py_ssize_t *size) except NULL + + + # As PyUnicode_AsUTF8AndSize(), but does not store the size. + const char *PyUnicode_AsUTF8(object unicode) except NULL # These are the UTF-16 codec APIs: From 820a929c9ae3f02757b5249d12c5e5b8bc689211 Mon Sep 17 00:00:00 2001 From: da-woods Date: Sun, 10 Dec 2023 16:43:48 +0000 Subject: [PATCH 054/286] Document CYTHON_ATOMICS (#5900) --- docs/src/userguide/source_files_and_compilation.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/src/userguide/source_files_and_compilation.rst b/docs/src/userguide/source_files_and_compilation.rst index d08d430ec0f..7112ddc0521 100644 --- a/docs/src/userguide/source_files_and_compilation.rst +++ b/docs/src/userguide/source_files_and_compilation.rst @@ -1250,4 +1250,9 @@ hidden by default since most users will be uninterested in changing them. ``CYTHON_USE_FREELISTS`` Enable the use of freelists on extension types with :ref:`the @cython.freelist decorator`. + + ``CYTHON_ATOMICS`` + Enable the use of atomic reference counting (as opposed to locking then + reference counting) in Cython typed memoryviews. + From 7820b09e904192a5d06010f94b2d420c74615c4e Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Mon, 11 Dec 2023 09:18:09 +0100 Subject: [PATCH 055/286] Remove an unused import. --- Cython/Compiler/ParseTreeTransforms.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Cython/Compiler/ParseTreeTransforms.py b/Cython/Compiler/ParseTreeTransforms.py index d7ebbd66380..a633f9a9694 100644 --- a/Cython/Compiler/ParseTreeTransforms.py +++ b/Cython/Compiler/ParseTreeTransforms.py @@ -23,7 +23,6 @@ from .TreeFragment import TreeFragment from .StringEncoding import EncodedString from .Errors import error, warning, CompileError, InternalError -from .Code import UtilityCode class SkipDeclarations: From 14c154a90c5bf2ba89fe1414f42d9826eabe4f56 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Mon, 11 Dec 2023 09:45:04 +0100 Subject: [PATCH 056/286] Refactor the pipeline debug code to avoid a useless use of exec(). --- Cython/Compiler/Pipeline.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/Cython/Compiler/Pipeline.py b/Cython/Compiler/Pipeline.py index b3e531f4c72..384fc6a8020 100644 --- a/Cython/Compiler/Pipeline.py +++ b/Cython/Compiler/Pipeline.py @@ -343,8 +343,6 @@ def insert_into_pipeline(pipeline, transform, before=None, after=None): # Running a pipeline # -_pipeline_entry_points = {} - try: from threading import local as _threadlocal except ImportError: @@ -360,10 +358,25 @@ def get_timings(): return {} +_pipeline_entry_points = {} + +def _make_debug_phase_runner(phase_name): + # Create a new wrapper for each step to show the name in profiles. + try: + return _pipeline_entry_points[phase_name] + except KeyError: + pass + + def run(phase, data): + return phase(data) + + run.__name__ = run.__qualname__ = phase_name + _pipeline_entry_points[phase_name] = run + return run + + def run_pipeline(pipeline, source, printtree=True): from .Visitor import PrintTree - exec_ns = globals().copy() if DebugFlags.debug_verbose_pipeline else None - try: timings = threadlocal.cython_pipeline_timings except AttributeError: @@ -385,12 +398,7 @@ def run(phase, data): phase_name = getattr(phase, '__name__', type(phase).__name__) if DebugFlags.debug_verbose_pipeline: print("Entering pipeline phase %r" % phase) - # create a new wrapper for each step to show the name in profiles - try: - run = _pipeline_entry_points[phase_name] - except KeyError: - exec("def %s(phase, data): return phase(data)" % phase_name, exec_ns) - run = _pipeline_entry_points[phase_name] = exec_ns[phase_name] + run = _make_debug_phase_runner(phase_name) t = time() data = run(phase, data) From 1ff5f3bd69f66bb5dc56b5e59416d60f62ca61d6 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Mon, 11 Dec 2023 09:53:06 +0100 Subject: [PATCH 057/286] Refactor "FindInvalidUseOfFusedTypes" from a transform into a simple TreeVisitor since it doesn't actually transform anything. Also make it report errors inside of functions even if a return type error was already reported, as well as for nested functions inside of fused functions. --- Cython/Compiler/ParseTreeTransforms.py | 28 +++++++++++++++++--------- Cython/Compiler/Pipeline.py | 2 +- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/Cython/Compiler/ParseTreeTransforms.py b/Cython/Compiler/ParseTreeTransforms.py index a633f9a9694..dad62dbf125 100644 --- a/Cython/Compiler/ParseTreeTransforms.py +++ b/Cython/Compiler/ParseTreeTransforms.py @@ -2824,27 +2824,35 @@ def visit_IndexNode(self, node): return node -class FindInvalidUseOfFusedTypes(CythonTransform): +class FindInvalidUseOfFusedTypes(TreeVisitor): + + def __call__(self, tree, phase=None): + self._in_fused_function = False + self.visit(tree) + return tree + + visit_Node = TreeVisitor.visitchildren def visit_FuncDefNode(self, node): - # Errors related to use in functions with fused args will already - # have been detected - if not node.has_fused_arguments: + outer_status = self._in_fused_function + self._in_fused_function = node.has_fused_arguments + + if not self._in_fused_function: + # Errors related to use in functions with fused args will already + # have been detected. if not node.is_generator_body and node.return_type.is_fused: error(node.pos, "Return type is not specified as argument type") - else: - self.visitchildren(node) - return node + self.visitchildren(node) + self._in_fused_function = outer_status def visit_ExprNode(self, node): - if node.type and node.type.is_fused: + if not self._in_fused_function and node.type and node.type.is_fused: error(node.pos, "Invalid use of fused types, type cannot be specialized") + # Errors in subtrees are likely related, so do not recurse. else: self.visitchildren(node) - return node - class ExpandInplaceOperators(EnvTransform): diff --git a/Cython/Compiler/Pipeline.py b/Cython/Compiler/Pipeline.py index 384fc6a8020..05d3735ffc8 100644 --- a/Cython/Compiler/Pipeline.py +++ b/Cython/Compiler/Pipeline.py @@ -212,7 +212,7 @@ def create_pipeline(context, mode, exclude_classes=()): _check_c_declarations, InlineDefNodeCalls(context), AnalyseExpressionsTransform(context), - FindInvalidUseOfFusedTypes(context), + FindInvalidUseOfFusedTypes(), ExpandInplaceOperators(context), IterationTransform(context), SwitchTransform(context), From b8d1b4df82b7124032f5072adb2f47882893e37b Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Mon, 11 Dec 2023 12:01:48 +0100 Subject: [PATCH 058/286] Fix "compile all" mode. --- Cython/Compiler/ParseTreeTransforms.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Cython/Compiler/ParseTreeTransforms.py b/Cython/Compiler/ParseTreeTransforms.py index dad62dbf125..65f7257e6b8 100644 --- a/Cython/Compiler/ParseTreeTransforms.py +++ b/Cython/Compiler/ParseTreeTransforms.py @@ -2826,12 +2826,13 @@ def visit_IndexNode(self, node): class FindInvalidUseOfFusedTypes(TreeVisitor): - def __call__(self, tree, phase=None): + def __call__(self, tree): self._in_fused_function = False self.visit(tree) return tree - visit_Node = TreeVisitor.visitchildren + def visit_Node(self, node): + self.visitchildren(node) def visit_FuncDefNode(self, node): outer_status = self._in_fused_function From bbe62406ec5b3ab8570963dd31b080a3f2b4f1a3 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Mon, 11 Dec 2023 12:23:30 +0100 Subject: [PATCH 059/286] Update changelog. --- CHANGES.rst | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 692771368a0..625a81d83e5 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,22 @@ Cython Changelog ================ +3.0.7 (202?-??-??) +================== + +Bugs fixed +---------- + +* Some declarations in ``cpython.unicode`` were fixed and extended. + (Github issue :issue:`5902`) + +* Compiling fused types used in pxd files could crash Cython in Python 3.11+. + (Github issues :issue:`5894`, :issue:`5588`) + +* Source files with non-ASCII file names could crash Cython. + (Github issue :issue:`5873`) + + 3.0.6 (2023-11-26) ================== @@ -14,7 +30,7 @@ Features added (Github issue :issue:`5836`) * The Python "nogil" fork is now also detected with the new ``Py_GIL_DISABLED`` macro. - Patch by Hugo van Kemenade (Github issue :issue:`583652`) + Patch by Hugo van Kemenade. (Github issue :issue:`5852`) Bugs fixed ---------- From f76f912a21d6d02a1243b0f5caa7064713ed0206 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Mon, 11 Dec 2023 17:19:57 +0100 Subject: [PATCH 060/286] Update changelog. --- CHANGES.rst | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 3a55f661134..523e9266707 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,10 +5,63 @@ Cython Changelog 3.1.0 (202?-??-??) ================== +Features added +-------------- + +* Integer operations on known ``int`` types are faster. + (Github issues :issue:`5785`) + +* Many issues with the Limited C-API were resolved. + (Github issues :issue:`5697`, :issue:`5798`, :issue:`5845`, :issue:`5846`, + :issue:`5885`, :issue:`5886`, :issue:`5888`) + +* Dataclasses support the ``match_args`` option. + (Github issue :issue:`5381`) + +* f-strings are slightly faster. + (Github issue :issue:`5866`) + +* Most builtin methods now provide their return type for type inference. + (Github issue :issue:`5865`) + +* ``.isprintable()`` is optimised for Unicode characters. + (Github issue :issue:`3277`) + +* The parser was updated for Unicode 15.1 (as provided by CPython 3.13a1). + +Bugs fixed +---------- + +* Dataclasses did not handle default fields without init value correctly. + (Github issue :issue:`5858`) + +* The ``-a`` option in the IPython magic no longer copies the complete HTML document + into the notebook but only a more reasonable content snippet. + Patch by Min RK. (Github issue :issue:`5760`) + +* Uselessly referring to C enums (not enum values) as Python objects is now rejected. + Patch by Vyas Ramasubramani. (Github issue :issue:`5638`) + +* Several C++ warnings about ``char*`` casts were resolved. + (Github issues :issue:`5515`, :issue:`5847`) + Other changes ------------- -* Support for Python 2.7 - 3.6 was removed. +* Support for Python 2.7 - 3.6 was removed, along with large chunks of legacy code. + (Github issue :issue:`2800`) + +* ``language_level=3`` is now the default. + ``language_level=3str`` has become a legacy alias. + (Github issue :issue:`5827`) + +* The Python ``int`` type now maps directly to ``PyLong`` and is inferred accordingly. + (Github issue :issue:`4237`) + +* Usages of the outdated ``WITH_THREAD`` macro guard were removed. + (Github issue :issue:`5812`) + +* Includes all fixes as of Cython 3.0.7. 3.0.7 (202?-??-??) From a2d73066fe70ff1ec3d4ac4fd1a1a75bf66b48dc Mon Sep 17 00:00:00 2001 From: da-woods Date: Mon, 11 Dec 2023 18:57:12 +0000 Subject: [PATCH 061/286] Fix conflicting enum to_py function with multiple modules (#5887) Fix conflicting names of cpdef enum to_py functions when the an enum with the name name exists in multiple modules. Instead use the cname to name the to_py function since we have already ensured that it is unique and mangled with the module name. Possibly fixes #5860 (it definitely fixes a real bug, but that project has far too many dependencies for me to test, so who knows if it fixes *that* bug). --- Cython/Compiler/PyrexTypes.py | 2 +- tests/run/cpdef_enums_import.srctree | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 7deb99c2e3e..91d7a6b6f37 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -4302,7 +4302,7 @@ class EnumMixin: def create_enum_to_py_utility_code(self, env): from .UtilityCode import CythonUtilityCode - self.to_py_function = "__Pyx_Enum_%s_to_py" % self.name + self.to_py_function = "__Pyx_Enum_%s_to_py" % type_identifier(self) if self.entry.scope != env.global_scope(): module_name = self.entry.scope.qualified_name else: diff --git a/tests/run/cpdef_enums_import.srctree b/tests/run/cpdef_enums_import.srctree index 928a2d0b156..7dd6e2fbe82 100644 --- a/tests/run/cpdef_enums_import.srctree +++ b/tests/run/cpdef_enums_import.srctree @@ -8,7 +8,7 @@ from Cython.Build.Dependencies import cythonize from distutils.core import setup setup( - ext_modules = cythonize(["enums.pyx", "no_enums.pyx"]), + ext_modules = cythonize(["enums.pyx", "enums_same_name.pyx", "no_enums.pyx"]), ) ######## enums.pyx ######## @@ -28,6 +28,14 @@ cpdef enum NamedEnumType: cpdef foo() +######## enums_same_name.pyx ############ + +######## enums_same_name.pxd ############ + +# Note - same name as enums.pxd but shouldn't conflict +cpdef enum NamedEnumType: + Value = 1 + ######## enums_without_pyx.pxd ##### cpdef enum EnumTypeNotInPyx: @@ -37,10 +45,16 @@ cpdef enum EnumTypeNotInPyx: from enums cimport * from enums_without_pyx cimport * +cimport enums_same_name def get_named_enum_value(): return NamedEnumType.NamedEnumValue +def get_from_enums_same_name(): + # This should not generate conflicting "to py" functions with the other + # identically named enum from a different pxd file. + return enums_same_name.NamedEnumType.Value + def get_named_without_pyx(): # This'll generate a warning but return a c int return EnumTypeNotInPyx.AnotherEnumValue @@ -49,6 +63,7 @@ def get_named_without_pyx(): # We can import enums with a star import. from enums import * +import enums_same_name print(dir()) assert 'BAR' in dir() and 'FOO' in dir() @@ -64,3 +79,5 @@ assert no_enums.get_named_enum_value() == NamedEnumType.NamedEnumValue # In this case the enum isn't accessible from Python (by design) # but the conversion to Python goes through a reasonable fallback assert no_enums.get_named_without_pyx() == 500 + +assert no_enums.get_from_enums_same_name() == enums_same_name.NamedEnumType.Value From e6490a642ae4663f1972a3868f4346d95b604171 Mon Sep 17 00:00:00 2001 From: da-woods Date: Mon, 11 Dec 2023 18:57:12 +0000 Subject: [PATCH 062/286] Fix conflicting enum to_py function with multiple modules (#5887) Fix conflicting names of cpdef enum to_py functions when the an enum with the name name exists in multiple modules. Instead use the cname to name the to_py function since we have already ensured that it is unique and mangled with the module name. Possibly fixes #5860 (it definitely fixes a real bug, but that project has far too many dependencies for me to test, so who knows if it fixes *that* bug). --- Cython/Compiler/PyrexTypes.py | 2 +- tests/run/cpdef_enums_import.srctree | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 40f6fc60381..91ded10e50b 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -4310,7 +4310,7 @@ class EnumMixin(object): def create_enum_to_py_utility_code(self, env): from .UtilityCode import CythonUtilityCode - self.to_py_function = "__Pyx_Enum_%s_to_py" % self.name + self.to_py_function = "__Pyx_Enum_%s_to_py" % type_identifier(self) if self.entry.scope != env.global_scope(): module_name = self.entry.scope.qualified_name else: diff --git a/tests/run/cpdef_enums_import.srctree b/tests/run/cpdef_enums_import.srctree index 928a2d0b156..7dd6e2fbe82 100644 --- a/tests/run/cpdef_enums_import.srctree +++ b/tests/run/cpdef_enums_import.srctree @@ -8,7 +8,7 @@ from Cython.Build.Dependencies import cythonize from distutils.core import setup setup( - ext_modules = cythonize(["enums.pyx", "no_enums.pyx"]), + ext_modules = cythonize(["enums.pyx", "enums_same_name.pyx", "no_enums.pyx"]), ) ######## enums.pyx ######## @@ -28,6 +28,14 @@ cpdef enum NamedEnumType: cpdef foo() +######## enums_same_name.pyx ############ + +######## enums_same_name.pxd ############ + +# Note - same name as enums.pxd but shouldn't conflict +cpdef enum NamedEnumType: + Value = 1 + ######## enums_without_pyx.pxd ##### cpdef enum EnumTypeNotInPyx: @@ -37,10 +45,16 @@ cpdef enum EnumTypeNotInPyx: from enums cimport * from enums_without_pyx cimport * +cimport enums_same_name def get_named_enum_value(): return NamedEnumType.NamedEnumValue +def get_from_enums_same_name(): + # This should not generate conflicting "to py" functions with the other + # identically named enum from a different pxd file. + return enums_same_name.NamedEnumType.Value + def get_named_without_pyx(): # This'll generate a warning but return a c int return EnumTypeNotInPyx.AnotherEnumValue @@ -49,6 +63,7 @@ def get_named_without_pyx(): # We can import enums with a star import. from enums import * +import enums_same_name print(dir()) assert 'BAR' in dir() and 'FOO' in dir() @@ -64,3 +79,5 @@ assert no_enums.get_named_enum_value() == NamedEnumType.NamedEnumValue # In this case the enum isn't accessible from Python (by design) # but the conversion to Python goes through a reasonable fallback assert no_enums.get_named_without_pyx() == 500 + +assert no_enums.get_from_enums_same_name() == enums_same_name.NamedEnumType.Value From 6e47a82ca639b3eaa1fef840b629ef7d57b07fb3 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Tue, 12 Dec 2023 09:25:56 +0100 Subject: [PATCH 063/286] Add tests for Unicode identifiers in typedefs and fused types. --- tests/run/unicode_identifiers.pyx | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/run/unicode_identifiers.pyx b/tests/run/unicode_identifiers.pyx index c33f9cb6ecf..dd5d23e39f0 100644 --- a/tests/run/unicode_identifiers.pyx +++ b/tests/run/unicode_identifiers.pyx @@ -233,3 +233,31 @@ cdef class NormalizeAttrCdef: self.fi = 5 def get(self): return self.fi + + +ctypedef long äntägär + +def use_typedef(x: äntägär): + """ + >>> use_typedef(5) + 10 + """ + cdef äntägär i = x + return i + x + + +ctypedef fused nümbärs: + float + äntägär + + +def use_fused_typedef(x: nümbärs): + """ + >>> use_fused_typedef(4) + 8 + >>> use_fused_typedef(4.5) + 9.0 + """ + cdef nümbärs i = x + assert cython.typeof(i) in ('float', 'äntägär'), cython.typeof(i) + return i + x From 2f93a5f92a80352e72f5fa9679fd282cb7a497ad Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Tue, 12 Dec 2023 09:45:58 +0100 Subject: [PATCH 064/286] Use str.isascii() instead of a slower trial encoding (which was needed in Py<3.7). --- Cython/Build/Dependencies.py | 6 +----- Cython/Compiler/ModuleNode.py | 12 +++++------- Cython/Compiler/Scanning.py | 4 +--- Cython/Compiler/StringEncoding.py | 10 ---------- Cython/Compiler/Symtab.py | 30 ++++++++++++++---------------- Cython/Compiler/Visitor.py | 11 ++--------- 6 files changed, 23 insertions(+), 50 deletions(-) diff --git a/Cython/Build/Dependencies.py b/Cython/Build/Dependencies.py index 660ec393ccf..48aeeb7d938 100644 --- a/Cython/Build/Dependencies.py +++ b/Cython/Build/Dependencies.py @@ -1180,12 +1180,8 @@ def __contains__(self, val): return filtered_list for m in module_list: - # TODO: use m.name.isascii() in Py3.7+ - try: - m.name.encode("ascii") + if m.name.isascii(): continue - except UnicodeEncodeError: - pass m.export_symbols = make_filtered_list( "PyInit_" + m.name.rsplit(".", 1)[-1], m.export_symbols, diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 7ddcd583048..3014d729421 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -3452,19 +3452,17 @@ def generate_main_method(self, env, code): def punycode_module_name(self, prefix, name): # adapted from PEP483 - try: - name = '_' + name.encode('ascii').decode('ascii') - except UnicodeEncodeError: + if name.isascii(): + name = '_' + name + else: name = 'U_' + name.encode('punycode').replace(b'-', b'_').decode('ascii') return "%s%s" % (prefix, name) def wrong_punycode_module_name(self, name): # to work around a distutils bug by also generating an incorrect symbol... - try: - name.encode("ascii") + if name.isascii(): return None # workaround is not needed - except UnicodeEncodeError: - return "PyInitU" + ("_"+name).encode('punycode').replace(b'-', b'_').decode('ascii') + return "PyInitU" + ("_"+name).encode('punycode').replace(b'-', b'_').decode('ascii') def mod_init_func_cname(self, prefix, env): # from PEP483 diff --git a/Cython/Compiler/Scanning.py b/Cython/Compiler/Scanning.py index 337ef23b824..245792926cb 100644 --- a/Cython/Compiler/Scanning.py +++ b/Cython/Compiler/Scanning.py @@ -346,9 +346,7 @@ def __init__(self, file, filename, parent_scanner=None, self.next() def normalize_ident(self, text): - try: - text.encode('ascii') # really just name.isascii but supports Python 2 and 3 - except UnicodeEncodeError: + if not text.isascii(): text = normalize('NFKC', text) self.produce(IDENT, text) diff --git a/Cython/Compiler/StringEncoding.py b/Cython/Compiler/StringEncoding.py index e31cdd48fec..a81a5a8ec86 100644 --- a/Cython/Compiler/StringEncoding.py +++ b/Cython/Compiler/StringEncoding.py @@ -132,16 +132,6 @@ def as_c_string_literal(self): s = bytes_literal(self.byteencode(), self.encoding) return s.as_c_string_literal() - if not hasattr(str, "isascii"): - def isascii(self): - # not defined for Python3.7+ since the class already has it - try: - self.encode("ascii") - except UnicodeEncodeError: - return False - else: - return True - def string_contains_surrogates(ustring): """ diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index 67a83b43e1a..f3a0f9980e3 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -43,26 +43,24 @@ def c_safe_identifier(cname): def punycodify_name(cname, mangle_with=None): # if passed the mangle_with should be a byte string # modified from PEP489 - try: - cname.encode('ascii') - except UnicodeEncodeError: - cname = cname.encode('punycode').replace(b'-', b'_').decode('ascii') - if mangle_with: - # sometimes it necessary to mangle unicode names alone where - # they'll be inserted directly into C, because the punycode - # transformation can turn them into invalid identifiers - cname = "%s_%s" % (mangle_with, cname) - elif cname.startswith(Naming.pyrex_prefix): - # a punycode name could also be a valid ascii variable name so - # change the prefix to distinguish - cname = cname.replace(Naming.pyrex_prefix, - Naming.pyunicode_identifier_prefix, 1) + if cname.isascii(): + return cname + + cname = cname.encode('punycode').replace(b'-', b'_').decode('ascii') + if mangle_with: + # sometimes it necessary to mangle unicode names alone where + # they'll be inserted directly into C, because the punycode + # transformation can turn them into invalid identifiers + cname = "%s_%s" % (mangle_with, cname) + elif cname.startswith(Naming.pyrex_prefix): + # a punycode name could also be a valid ascii variable name so + # change the prefix to distinguish + cname = cname.replace(Naming.pyrex_prefix, + Naming.pyunicode_identifier_prefix, 1) return cname - - class BufferAux: writable_needed = False diff --git a/Cython/Compiler/Visitor.py b/Cython/Compiler/Visitor.py index d32f7b5cf11..282e7950266 100644 --- a/Cython/Compiler/Visitor.py +++ b/Cython/Compiler/Visitor.py @@ -575,15 +575,8 @@ def visit_UnopNode(self, node): ### dispatch to specific handlers def _find_handler(self, match_name, has_kwargs): - try: - match_name.encode('ascii') - except UnicodeEncodeError: - # specifically when running the Cython compiler under Python 2 - # getattr can't take a unicode string. - # Classes with unicode names won't have specific handlers and thus it - # should be OK to return None. - # Doing the test here ensures that the same code gets run on - # Python 2 and 3 + if not match_name.isascii(): + # Classes with unicode names won't have specific handlers. return None call_type = 'general' if has_kwargs else 'simple' From a67d1609b7841a6d4034b9d8fd2c5dcd92b8a1e1 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Thu, 14 Dec 2023 12:17:35 +0100 Subject: [PATCH 065/286] test runner: simplify unittest module importing. --- runtests.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/runtests.py b/runtests.py index 74d50d5fdea..b3d3776697b 100755 --- a/runtests.py +++ b/runtests.py @@ -1,7 +1,5 @@ #!/usr/bin/env python -from __future__ import print_function - import atexit import base64 import doctest @@ -1869,6 +1867,7 @@ def package_matches(dirname): return dirname == "Tests" loader = unittest.TestLoader() + from importlib import import_module if include_debugger: skipped_dirs = [] @@ -1895,9 +1894,7 @@ def package_matches(dirname): continue if any(1 for match in exclude_selectors if match(modulename)): continue - module = __import__(modulename) - for x in modulename.split('.')[1:]: - module = getattr(module, x) + module = import_module(modulename) suite.addTests([loader.loadTestsFromModule(module)]) From bef0c37e029fb9a1d951fc67fa32b55765e722ba Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Thu, 14 Dec 2023 14:47:01 +0100 Subject: [PATCH 066/286] test runner: simplify doctest module importing. --- runtests.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/runtests.py b/runtests.py index b3d3776697b..484252e6b40 100755 --- a/runtests.py +++ b/runtests.py @@ -1903,6 +1903,7 @@ def package_matches(dirname): if dirname == 'Debugger' and not include_debugger: return False return dirname not in ("Mac", "Distutils", "Plex", "Tempita") + def file_matches(filename): filename, ext = os.path.splitext(filename) excludelist = ['libcython', 'libpython', 'test_libcython_in_gdb', @@ -1912,7 +1913,10 @@ def file_matches(filename): '#' in filename and not filename.startswith('.') and not filename in excludelist) + import doctest + from importlib import import_module + for dirpath, dirnames, filenames in os.walk(path): for dir in list(dirnames): if not package_matches(dir): @@ -1931,9 +1935,7 @@ def file_matches(filename): if 'in_gdb' in modulename: # These should only be imported from gdb. continue - module = __import__(modulename) - for x in modulename.split('.')[1:]: - module = getattr(module, x) + module = import_module(modulename) if hasattr(module, "__doc__") or hasattr(module, "__test__"): try: suite.addTest(doctest.DocTestSuite(module)) From 0b3c45b59626858a9d6717583429496f50b9f441 Mon Sep 17 00:00:00 2001 From: Matus Valo Date: Thu, 14 Dec 2023 19:20:15 +0100 Subject: [PATCH 067/286] [Docs] Reorganize and improve documenation of `include_dirs` and `include_path` (#5899) Co-authored-by: da-woods --- docs/src/userguide/language_basics.rst | 2 + docs/src/userguide/numpy_tutorial.rst | 61 ++++++++++++++++- .../source_files_and_compilation.rst | 68 ++++++++----------- 3 files changed, 89 insertions(+), 42 deletions(-) diff --git a/docs/src/userguide/language_basics.rst b/docs/src/userguide/language_basics.rst index c1db172e309..a4b990146e9 100644 --- a/docs/src/userguide/language_basics.rst +++ b/docs/src/userguide/language_basics.rst @@ -1510,6 +1510,7 @@ if the corresponding definition file also defines that type. and classes from each other without the Python overhead. To read more about what how to do that, you can see :ref:`pxd_files`. +.. _definition_file: The definition file ------------------- @@ -1538,6 +1539,7 @@ wants to access :keyword:`cdef` attributes and methods, or to inherit from presence in a definition file does that. You only need a public declaration if you want to make something available to external C code. +.. _include_statement: The include statement and include files --------------------------------------- diff --git a/docs/src/userguide/numpy_tutorial.rst b/docs/src/userguide/numpy_tutorial.rst index d2828ebaba4..86ff682783d 100644 --- a/docs/src/userguide/numpy_tutorial.rst +++ b/docs/src/userguide/numpy_tutorial.rst @@ -108,8 +108,13 @@ then execute : This will install the newest Cython into SAGE. +.. _numpy_compilation: + +Compilation +=========== + Manual compilation -==================== +------------------ As it is always important to know what is going on, I'll describe the manual method here. First Cython is run: @@ -143,6 +148,60 @@ in your Cython code. This creates :file:`yourmod.so` in the same directory, which is importable by Python by using a normal ``import yourmod`` statement. + +Compilation using setuptools +---------------------------- + +Setuptools allows us to create setup.py file to automate compilation of both Cython files and generated C files.:: + + from setuptools import Extension, setup + from Cython.Build import cythonize + import numpy + + extensions = [ + Extension("*", ["*.pyx"], + include_dirs=[numpy.get_include()]), + ] + setup( + name="My hello app", + ext_modules=cythonize(extensions), + ) + +The path to the NumPy headers is passed to the C compiler via the ``include_dirs=[numpy.get_include()]`` parameter. + +.. note:: + + Using memoryviews or importing NumPy with ``import numpy`` does not mean that + you have to add the path to NumPy include files. You need to add this path only + if you use ``cimport numpy``. + +Despite this, you may still get warnings like the following from the compiler, +because Cython is not disabling the usage of the old deprecated Numpy API:: + + .../include/numpy/npy_1_7_deprecated_api.h:15:2: warning: #warning "Using deprecated NumPy API, disable it by " "#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-Wcpp] + +In Cython 3.0, you can get rid of this warning by defining the C macro +``NPY_NO_DEPRECATED_API`` as ``NPY_1_7_API_VERSION`` +in your build, e.g.:: + + # distutils: define_macros=NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION + +or (see below):: + + Extension( + ..., + define_macros=[("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION")], + ) + +With older Cython releases, setting this macro will fail the C compilation, +because Cython generates code that uses this deprecated C-API. However, the +warning has no negative effects even in recent NumPy versions. +You can ignore it until you (or your library's users) switch to a newer NumPy +version that removes this long deprecated API, in which case you also need to +use Cython 3.0 or later. Thus, the earlier you switch to Cython 3.0, the +better for your users. + + The first Cython program ========================== diff --git a/docs/src/userguide/source_files_and_compilation.rst b/docs/src/userguide/source_files_and_compilation.rst index 7112ddc0521..18a473122a1 100644 --- a/docs/src/userguide/source_files_and_compilation.rst +++ b/docs/src/userguide/source_files_and_compilation.rst @@ -150,7 +150,11 @@ documentation`_. To compile the extension for use in the current directory use: Configuring the C-Build ------------------------ -If you have include files in non-standard places you can pass an +.. note:: + + More details on building Cython modules that use cimport numpy can be found in the :ref:`Numpy section ` of the user guide. + +If you have :ref:`Cython include files ` or :ref:`Cython definition files ` in non-standard places you can pass an ``include_path`` parameter to ``cythonize``:: from setuptools import setup @@ -161,43 +165,6 @@ If you have include files in non-standard places you can pass an ext_modules=cythonize("src/*.pyx", include_path=[...]), ) -Often, Python packages that offer a C-level API provide a way to find -the necessary include files, e.g. for NumPy:: - - include_path = [numpy.get_include()] - -.. note:: - - Using memoryviews or importing NumPy with ``import numpy`` does not mean that - you have to add the path to NumPy include files. You need to add this path only - if you use ``cimport numpy``. - -Despite this, you may still get warnings like the following from the compiler, -because Cython is not disabling the usage of the old deprecated Numpy API:: - - .../include/numpy/npy_1_7_deprecated_api.h:15:2: warning: #warning "Using deprecated NumPy API, disable it by " "#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-Wcpp] - -In Cython 3.0, you can get rid of this warning by defining the C macro -``NPY_NO_DEPRECATED_API`` as ``NPY_1_7_API_VERSION`` -in your build, e.g.:: - - # distutils: define_macros=NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION - -or (see below):: - - Extension( - ..., - define_macros=[("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION")], - ) - -With older Cython releases, setting this macro will fail the C compilation, -because Cython generates code that uses this deprecated C-API. However, the -warning has no negative effects even in recent NumPy versions including 1.18.x. -You can ignore it until you (or your library's users) switch to a newer NumPy -version that removes this long deprecated API, in which case you also need to -use Cython 3.0 or later. Thus, the earlier you switch to Cython 3.0, the -better for your users. - If you need to specify compiler options, libraries to link with or other linker options you will need to create ``Extension`` instances manually (note that glob syntax can still be used to specify multiple extensions @@ -222,9 +189,30 @@ in one line):: ext_modules=cythonize(extensions), ) +Some useful options to know about are + +* ``include_dirs``- list of directories to search for C/C++ header files (in Unix form for portability), +* ``libraries`` - list of library names (not filenames or paths) to link against, +* ``library_dirs`` - list of directories to search for C/C++ libraries at link time. + Note that when using setuptools, you should import it before Cython, otherwise, both might disagree about the class to use here. +Often, Python packages that offer a C-level API provide a way to find +the necessary C header files:: + + from setuptools import Extension, setup + from Cython.Build import cythonize + + extensions = [ + Extension("*", ["*.pyx"], + include_dirs=["/usr/local/include"]), + ] + setup( + name="My hello app", + ext_modules=cythonize(extensions), + ) + If your options are static (for example you do not need to call a tool like ``pkg-config`` to determine them) you can also provide them directly in your .pyx or .pxd source file using a special comment block at the start of the file:: @@ -258,9 +246,7 @@ as follows:: ) The :class:`Extension` class takes many options, and a fuller explanation can -be found in the `setuptools documentation`_. Some useful options to know about -are ``include_dirs``, ``libraries``, and ``library_dirs`` which specify where -to find the ``.h`` and library files when linking to external libraries. +be found in the `setuptools documentation`_. .. _setuptools documentation: https://setuptools.readthedocs.io/ From 08235481da0f5334ccda14828268ad8ca28d5cc0 Mon Sep 17 00:00:00 2001 From: da-woods Date: Thu, 14 Dec 2023 21:34:03 +0000 Subject: [PATCH 068/286] Remove unused Entry.pep563_annotation (#5909) I don't think it was ever used --- Cython/Compiler/Symtab.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index f3a0f9980e3..62c8a68a2ec 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -167,7 +167,6 @@ class Entry: borrowed = 0 init = "" annotation = None - pep563_annotation = None visibility = 'private' is_builtin = 0 is_cglobal = 0 From a034c74b25f28badf51422a9c0e5df51887b4d73 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Fri, 15 Dec 2023 12:13:21 +0100 Subject: [PATCH 069/286] test: work around docstring changes in Py3.13. --- tests/run/r_docstrings.pyx | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/run/r_docstrings.pyx b/tests/run/r_docstrings.pyx index 4ee3f87351e..514946b53a4 100644 --- a/tests/run/r_docstrings.pyx +++ b/tests/run/r_docstrings.pyx @@ -42,8 +42,8 @@ Compare with standard Python: ... ''' ... This is a function docstring. ... ''' - >>> Pyf.__doc__ - '\\n This is a function docstring.\\n ' + >>> Pyf.__doc__.strip() # .strip() is needed because Py3.13 removes the indentation. + 'This is a function docstring.' >>> class PyC(object): ... ''' @@ -58,21 +58,21 @@ Compare with standard Python: >>> class PyCSS(PyCS): ... docstring_copy_CSS = __doc__ - >>> PyC.__doc__ - '\\n This is a class docstring.\\n ' - >>> PyC.docstring_copy_C - '\\n This is a class docstring.\\n ' - >>> PyCS.docstring_copy_C - '\\n This is a class docstring.\\n ' - >>> PyCSS.docstring_copy_C - '\\n This is a class docstring.\\n ' - - >>> PyCS.__doc__ - '\\n This is a subclass docstring.\\n ' - >>> PyCS.docstring_copy_CS - '\\n This is a subclass docstring.\\n ' - >>> PyCSS.docstring_copy_CS - '\\n This is a subclass docstring.\\n ' + >>> PyC.__doc__.strip() + 'This is a class docstring.' + >>> PyC.docstring_copy_C.strip() + 'This is a class docstring.' + >>> PyCS.docstring_copy_C.strip() + 'This is a class docstring.' + >>> PyCSS.docstring_copy_C.strip() + 'This is a class docstring.' + + >>> PyCS.__doc__.strip() + 'This is a subclass docstring.' + >>> PyCS.docstring_copy_CS.strip() + 'This is a subclass docstring.' + >>> PyCSS.docstring_copy_CS.strip() + 'This is a subclass docstring.' >>> PyCSS.__doc__ >>> PyCSS.docstring_copy_CSS From 599710889695c3f613a665cb15aa32637f1fdbe1 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Fri, 15 Dec 2023 12:19:59 +0100 Subject: [PATCH 070/286] test: work around builtin changes in Py3.13. --- tests/run/hasattr.pyx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/run/hasattr.pyx b/tests/run/hasattr.pyx index 00d005d6cf4..700bfb74bcc 100644 --- a/tests/run/hasattr.pyx +++ b/tests/run/hasattr.pyx @@ -31,10 +31,15 @@ def wrap_hasattr(obj, name): False >>> Foo().baz #doctest: +ELLIPSIS Traceback (most recent call last): - ... ZeroDivisionError: ... - >>> wrap_hasattr(Foo(), "baz") + >>> import sys + >>> if sys.version_info < (3,13): wrap_hasattr(Foo(), "baz") # doctest: +ELLIPSIS + ... else: print(False) False + >>> if sys.version_info >= (3,13): wrap_hasattr(Foo(), "baz") # doctest: +ELLIPSIS + ... else: raise ZeroDivisionError + Traceback (most recent call last): + ZeroDivisionError... >>> hasattr(Foo(), None) #doctest: +ELLIPSIS Traceback (most recent call last): ... From 12b43eb1f9abdbf4ce74ac1723acd033d7474ae5 Mon Sep 17 00:00:00 2001 From: scoder Date: Sat, 16 Dec 2023 14:39:07 +0100 Subject: [PATCH 071/286] Improve optimisation of "dict.pop()" (GH-5911) Optimise "dict.pop()" in Py3.13 and special-case the common "discard and ignore" use case. --- Cython/Compiler/Optimize.py | 25 ++++++++++++++-- Cython/Utility/ObjectHandling.c | 4 +++ Cython/Utility/Optimize.c | 52 +++++++++++++++++++++++++++++---- tests/run/dict_pop.pyx | 35 ++++++++++++++++++++++ 4 files changed, 107 insertions(+), 9 deletions(-) diff --git a/Cython/Compiler/Optimize.py b/Cython/Compiler/Optimize.py index ce5d58e253c..6068e9bef24 100644 --- a/Cython/Compiler/Optimize.py +++ b/Cython/Compiler/Optimize.py @@ -3367,21 +3367,40 @@ def _handle_simple_method_dict_setdefault(self, node, function, args, is_unbound PyrexTypes.CFuncTypeArg("default", PyrexTypes.py_object_type, None), ]) + PyDict_Pop_ignore_func_type = PyrexTypes.CFuncType( + PyrexTypes.c_int_type, [ + PyrexTypes.CFuncTypeArg("dict", PyrexTypes.py_object_type, None), + PyrexTypes.CFuncTypeArg("key", PyrexTypes.py_object_type, None), + PyrexTypes.CFuncTypeArg("default", PyrexTypes.py_object_type, None), + ], + exception_value=PyrexTypes.c_int_type.exception_value, + ) + def _handle_simple_method_dict_pop(self, node, function, args, is_unbound_method): """Replace dict.pop() by a call to _PyDict_Pop(). """ + capi_func = "__Pyx_PyDict_Pop" + utility_code_name = 'py_dict_pop' + func_type = self.PyDict_Pop_func_type + if len(args) == 2: args.append(ExprNodes.NullNode(node.pos)) - elif len(args) != 3: + elif len(args) == 3: + if not node.result_is_used: + # special case: we can ignore the default value + capi_func = "__Pyx_PyDict_Pop_ignore" + utility_code_name = 'py_dict_pop_ignore' + func_type = self.PyDict_Pop_ignore_func_type + else: self._error_wrong_arg_count('dict.pop', node, args, "2 or 3") return node return self._substitute_method_call( node, function, - "__Pyx_PyDict_Pop", self.PyDict_Pop_func_type, + capi_func, func_type, 'pop', is_unbound_method, args, may_return_none=True, - utility_code=load_c_utility('py_dict_pop')) + utility_code=load_c_utility(utility_code_name)) Pyx_BinopInt_func_types = { (ctype, ret_type): PyrexTypes.CFuncType( diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index cce7e482bd4..ea10719fac1 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1780,7 +1780,9 @@ static int __Pyx_TryUnpackUnboundCMethod(__Pyx_CachedCFunction* target) { /////////////// CallUnboundCMethod0.proto /////////////// //@substitute: naming +CYTHON_UNUSED static PyObject* __Pyx__CallUnboundCMethod0(__Pyx_CachedCFunction* cfunc, PyObject* self); /*proto*/ + #if CYTHON_COMPILING_IN_CPYTHON // FASTCALL methods receive "&empty_tuple" as simple "PyObject[0]*" #define __Pyx_CallUnboundCMethod0(cfunc, self) \ @@ -1823,6 +1825,7 @@ static PyObject* __Pyx__CallUnboundCMethod0(__Pyx_CachedCFunction* cfunc, PyObje /////////////// CallUnboundCMethod1.proto /////////////// +CYTHON_UNUSED static PyObject* __Pyx__CallUnboundCMethod1(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg);/*proto*/ #if CYTHON_COMPILING_IN_CPYTHON @@ -1886,6 +1889,7 @@ static PyObject* __Pyx__CallUnboundCMethod1(__Pyx_CachedCFunction* cfunc, PyObje /////////////// CallUnboundCMethod2.proto /////////////// +CYTHON_UNUSED static PyObject* __Pyx__CallUnboundCMethod2(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg1, PyObject* arg2); /*proto*/ #if CYTHON_COMPILING_IN_CPYTHON diff --git a/Cython/Utility/Optimize.c b/Cython/Utility/Optimize.c index 519cee8ea72..444ccce91fd 100644 --- a/Cython/Utility/Optimize.c +++ b/Cython/Utility/Optimize.c @@ -263,17 +263,57 @@ static CYTHON_INLINE PyObject *__Pyx_PyDict_Pop(PyObject *d, PyObject *key, PyOb /////////////// py_dict_pop /////////////// static CYTHON_INLINE PyObject *__Pyx_PyDict_Pop(PyObject *d, PyObject *key, PyObject *default_value) { -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030d0000 - if ((1)) { - return _PyDict_Pop(d, key, default_value); - } else - // avoid "function unused" warnings -#endif +#if PY_VERSION_HEX >= 0x030d00A2 + PyObject *value; + if (PyDict_Pop(d, key, &value) == 0) { + if (default_value) { + Py_INCREF(default_value); + } else { + PyErr_SetObject(PyExc_KeyError, key); + } + value = default_value; + } + // On error, PyDict_Pop() returns -1 and sets value to NULL (our own exception return value). + return value; +#elif CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030d0000 + return _PyDict_Pop(d, key, default_value); +#else if (default_value) { return CALL_UNBOUND_METHOD(PyDict_Type, "pop", d, key, default_value); } else { return CALL_UNBOUND_METHOD(PyDict_Type, "pop", d, key); } +#endif +} + + +/////////////// py_dict_pop_ignore.proto /////////////// + +static CYTHON_INLINE int __Pyx_PyDict_Pop_ignore(PyObject *d, PyObject *key, PyObject *default_value); /*proto*/ + +/////////////// py_dict_pop_ignore /////////////// + +static CYTHON_INLINE int __Pyx_PyDict_Pop_ignore(PyObject *d, PyObject *key, PyObject *default_value) { + // We take the "default_value" as argument to avoid "unused" warnings, but we ignore it here. +#if PY_VERSION_HEX >= 0x030d00A2 + int result = PyDict_Pop(d, key, NULL); + CYTHON_UNUSED_VAR(default_value); + return (unlikely(result == -1)) ? -1 : 0; +#else + PyObject *value; + CYTHON_UNUSED_VAR(default_value); + + #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030d0000 + value = _PyDict_Pop(d, key, Py_None); + #else + value = CALL_UNBOUND_METHOD(PyDict_Type, "pop", d, key, Py_None); + #endif + + if (unlikely(value == NULL)) + return -1; + Py_DECREF(value); + return 0; +#endif } diff --git a/tests/run/dict_pop.pyx b/tests/run/dict_pop.pyx index 1efd5b82221..a8a09ed9888 100644 --- a/tests/run/dict_pop.pyx +++ b/tests/run/dict_pop.pyx @@ -3,6 +3,11 @@ cimport cython +class FailHash: + def __hash__(self): + raise TypeError() + + @cython.test_assert_path_exists("//PythonCapiCallNode") @cython.test_fail_if_path_exists("//AttributeNode") def dict_pop(dict d, key): @@ -10,6 +15,11 @@ def dict_pop(dict d, key): >>> d = { 1: 10, 2: 20 } >>> dict_pop(d, 1) (10, {2: 20}) + >>> dict_pop(d, FailHash()) + Traceback (most recent call last): + TypeError + >>> d + {2: 20} >>> dict_pop(d, 3) Traceback (most recent call last): KeyError: 3 @@ -26,6 +36,11 @@ def dict_pop_default(dict d, key, default): >>> d = { 1: 10, 2: 20 } >>> dict_pop_default(d, 1, "default") (10, {2: 20}) + >>> dict_pop_default(d, FailHash(), 30) + Traceback (most recent call last): + TypeError + >>> d + {2: 20} >>> dict_pop_default(d, 3, None) (None, {2: 20}) >>> dict_pop_default(d, 3, "default") @@ -36,6 +51,26 @@ def dict_pop_default(dict d, key, default): return d.pop(key, default), d +@cython.test_assert_path_exists("//PythonCapiCallNode") +@cython.test_fail_if_path_exists("//AttributeNode") +def dict_pop_ignored(dict d, key): + """ + >>> d = {1: 2, 'a': 'b'} + >>> dict_pop_ignored(d, 'a') + >>> d + {1: 2} + >>> dict_pop_ignored(d, FailHash()) + Traceback (most recent call last): + TypeError + >>> d + {1: 2} + >>> dict_pop_ignored(d, 123) + >>> d + {1: 2} + """ + d.pop(key, None) + + cdef class MyType: cdef public int i def __init__(self, i): From 0b35f35c833829877a389fb78971847dabedf3aa Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Sat, 16 Dec 2023 15:11:37 +0100 Subject: [PATCH 072/286] Minor code cleanups. --- Cython/Compiler/Parsing.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Cython/Compiler/Parsing.py b/Cython/Compiler/Parsing.py index 0b6650a9911..34d23891590 100644 --- a/Cython/Compiler/Parsing.py +++ b/Cython/Compiler/Parsing.py @@ -787,7 +787,6 @@ def p_name(s, name): def wrap_compile_time_constant(pos, value): - rep = repr(value) if value is None: return ExprNodes.NoneNode(pos) elif value is Ellipsis: @@ -795,9 +794,9 @@ def wrap_compile_time_constant(pos, value): elif isinstance(value, bool): return ExprNodes.BoolNode(pos, value=value) elif isinstance(value, int): - return ExprNodes.IntNode(pos, value=rep, constant_result=value) + return ExprNodes.IntNode(pos, value=repr(value), constant_result=value) elif isinstance(value, float): - return ExprNodes.FloatNode(pos, value=rep, constant_result=value) + return ExprNodes.FloatNode(pos, value=repr(value), constant_result=value) elif isinstance(value, complex): node = ExprNodes.ImagNode(pos, value=repr(value.imag), constant_result=complex(0.0, value.imag)) if value.real: @@ -813,13 +812,12 @@ def wrap_compile_time_constant(pos, value): bvalue = bytes_literal(value, 'ascii') # actually: unknown encoding, but BytesLiteral requires one return ExprNodes.BytesNode(pos, value=bvalue, constant_result=value) elif isinstance(value, tuple): - args = [wrap_compile_time_constant(pos, arg) - for arg in value] - if None not in args: - return ExprNodes.TupleNode(pos, args=args) - else: + args = [wrap_compile_time_constant(pos, arg) for arg in value] + if None in args: # error already reported return None + return ExprNodes.TupleNode(pos, args=args) + error(pos, "Invalid type for compile-time constant: %r (type %s)" % (value, value.__class__.__name__)) return None From e0308468b3227db20f3802a70382061826636000 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Mon, 18 Dec 2023 12:40:51 +0100 Subject: [PATCH 073/286] Update changelog. --- CHANGES.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 625a81d83e5..b45c6d37991 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,6 +8,10 @@ Cython Changelog Bugs fixed ---------- +* ``cpdef`` enums with the same name cimported from different modules could lead to + invalid C code. + (Github issue :issue:`5887`) + * Some declarations in ``cpython.unicode`` were fixed and extended. (Github issue :issue:`5902`) @@ -3335,6 +3339,27 @@ Other changes .. _`PEP-479`: https://www.python.org/dev/peps/pep-0479 +.. _0.29.37: + +0.29.37 (2023-12-18) +==================== + +Bugs fixed +---------- + +* Fix a potential crash while cleaning up subtypes of externally imported extension + types when terminating Python. This was introduced in Cython 0.29.35. + +* Fix a ``complex`` related compile error on Windows. + (Github issue :issue:`5512`) + +* Compiling fused types used in pxd files could crash Cython in Python 3.11+. + (Github issues :issue:`5894`, :issue:`5588`) + +* ``cythonize`` failed to consider the ``CYTHON_FORCE_REGEN`` env variable. + Patch by Harmen Stoppels. (Github issue :issue:`5712`) + + .. _0.29.36: 0.29.36 (2023-07-04) From 9f5219643ab3f610426bbc55fbf033bfd13118bf Mon Sep 17 00:00:00 2001 From: da-woods Date: Tue, 19 Dec 2023 09:41:29 +0000 Subject: [PATCH 074/286] Fix await/yield/yield from in genexpression iteration (GH-5898) Ignore the generator expression iterator when searching for yield/await nodes - the iterator is evaluated in the outer scope instead. Closes https://github.com/cython/cython/issues/5851 --- Cython/Compiler/ParseTreeTransforms.pxd | 2 + Cython/Compiler/ParseTreeTransforms.py | 21 +++++++--- tests/run/async_def.pyx | 52 +++++++++++++++++++++++-- 3 files changed, 66 insertions(+), 9 deletions(-) diff --git a/Cython/Compiler/ParseTreeTransforms.pxd b/Cython/Compiler/ParseTreeTransforms.pxd index 2e0fc55f338..4e8d91f6921 100644 --- a/Cython/Compiler/ParseTreeTransforms.pxd +++ b/Cython/Compiler/ParseTreeTransforms.pxd @@ -52,10 +52,12 @@ cdef class YieldNodeCollector(TreeVisitor): cdef public bint has_return_value cdef public bint has_yield cdef public bint has_await + cdef list excludes @cython.final cdef class MarkClosureVisitor(CythonTransform): cdef bint needs_closure + cdef list excludes @cython.final cdef class CreateClosureClasses(CythonTransform): diff --git a/Cython/Compiler/ParseTreeTransforms.py b/Cython/Compiler/ParseTreeTransforms.py index 65f7257e6b8..f98d88ad635 100644 --- a/Cython/Compiler/ParseTreeTransforms.py +++ b/Cython/Compiler/ParseTreeTransforms.py @@ -210,7 +210,7 @@ def visit_LambdaNode(self, node): def visit_GeneratorExpressionNode(self, node): # unpack a generator expression into the corresponding DefNode collector = YieldNodeCollector() - collector.visitchildren(node.loop) + collector.visitchildren(node.loop, attrs=None, exclude=["iterator"]) node.def_node = Nodes.DefNode( node.pos, name=node.name, doc=None, args=[], star_arg=None, starstar_arg=None, @@ -3153,7 +3153,7 @@ def visit_TryFinallyStatNode(self, node): class YieldNodeCollector(TreeVisitor): - def __init__(self): + def __init__(self, excludes=[]): super().__init__() self.yields = [] self.returns = [] @@ -3162,9 +3162,11 @@ def __init__(self): self.has_return_value = False self.has_yield = False self.has_await = False + self.excludes = excludes def visit_Node(self, node): - self.visitchildren(node) + if node not in self.excludes: + self.visitchildren(node) def visit_YieldExprNode(self, node): self.yields.append(node) @@ -3200,7 +3202,11 @@ def visit_LambdaNode(self, node): pass def visit_GeneratorExpressionNode(self, node): - pass + # node.loop iterator is evaluated outside the generator expression + if isinstance(node.loop, Nodes._ForInStatNode): + # Possibly should handle ForFromStatNode + # but for now do nothing + self.visit(node.loop.iterator) def visit_CArgDeclNode(self, node): # do not look into annotations @@ -3214,6 +3220,7 @@ class MarkClosureVisitor(CythonTransform): def visit_ModuleNode(self, node): self.needs_closure = False + self.excludes = [] self.visitchildren(node) return node @@ -3223,7 +3230,7 @@ def visit_FuncDefNode(self, node): node.needs_closure = self.needs_closure self.needs_closure = True - collector = YieldNodeCollector() + collector = YieldNodeCollector(self.excludes) collector.visitchildren(node) if node.is_async_def: @@ -3282,7 +3289,11 @@ def visit_ClassDefNode(self, node): return node def visit_GeneratorExpressionNode(self, node): + excludes = self.excludes + if isinstance(node.loop, Nodes._ForInStatNode): + self.excludes = [node.loop.iterator] node = self.visit_LambdaNode(node) + self.excludes = excludes if not isinstance(node.loop, Nodes._ForInStatNode): # Possibly should handle ForFromStatNode # but for now do nothing diff --git a/tests/run/async_def.pyx b/tests/run/async_def.pyx index bf5f653245c..262560ee912 100644 --- a/tests/run/async_def.pyx +++ b/tests/run/async_def.pyx @@ -8,15 +8,16 @@ Cython specific tests in addition to "test_coroutines_pep492.pyx" """ -def run_async(coro): - #assert coro.__class__ is types.GeneratorType - assert coro.__class__.__name__ in ('coroutine', '_GeneratorWrapper'), coro.__class__.__name__ +def run_async(coro, assert_type=True, send_value=None): + if assert_type: + #assert coro.__class__ is types.GeneratorType + assert coro.__class__.__name__ in ('coroutine', '_GeneratorWrapper'), coro.__class__.__name__ buffer = [] result = None while True: try: - buffer.append(coro.send(None)) + buffer.append(coro.send(send_value)) except StopIteration as ex: result = ex.value break @@ -58,3 +59,46 @@ async def outer_with_nested(called): called.append('return inner') return inner + +# used in "await_in_genexpr_iterator" +async def h(arg): + return [arg, arg+1] + +async def await_in_genexpr_iterator(): + """ + >>> _, x = run_async(await_in_genexpr_iterator()) + >>> x + [4, 6] + """ + lst = list # obfuscate from any optimizations cython might try + return lst(x*2 for x in await h(2)) + +def yield_in_genexpr_iterator(): + """ + Same test as await_in_genexpr_iterator but with yield. + (Possibly in the wrong place, but grouped with related tests) + + >>> g = yield_in_genexpr_iterator() + >>> g.send(None) + >>> _, x = run_async(g, assert_type=False, send_value=[2, 3]) + >>> x + [4, 6] + """ + lst = list # obfuscate from any optimizations cython might try + return lst(x*2 for x in (yield)) + +def h_yield_from(arg): + yield + return [arg, arg+1] + +def yield_from_in_genexpr_iterator(): + """ + Same test as await_in_genexpr_iterator but with "yield from". + (Possibly in the wrong place, but grouped with related tests) + + >>> _, x = run_async(yield_from_in_genexpr_iterator(), assert_type=False) + >>> x + [4, 6] + """ + lst = list # obfuscate from any optimizations cython might try + return lst(x*2 for x in (yield from h_yield_from(2))) From 3c4acd2c35ffcfd381ddc2521992ea324831d3cb Mon Sep 17 00:00:00 2001 From: da-woods Date: Tue, 19 Dec 2023 09:41:29 +0000 Subject: [PATCH 075/286] Fix await/yield/yield from in genexpression iteration (GH-5898) Ignore the generator expression iterator when searching for yield/await nodes - the iterator is evaluated in the outer scope instead. Closes https://github.com/cython/cython/issues/5851 --- Cython/Compiler/ParseTreeTransforms.pxd | 2 + Cython/Compiler/ParseTreeTransforms.py | 21 +++++++--- tests/run/async_def.pyx | 52 +++++++++++++++++++++++-- 3 files changed, 66 insertions(+), 9 deletions(-) diff --git a/Cython/Compiler/ParseTreeTransforms.pxd b/Cython/Compiler/ParseTreeTransforms.pxd index b44c290a1a8..ee34c9420c2 100644 --- a/Cython/Compiler/ParseTreeTransforms.pxd +++ b/Cython/Compiler/ParseTreeTransforms.pxd @@ -54,10 +54,12 @@ cdef class YieldNodeCollector(TreeVisitor): cdef public bint has_return_value cdef public bint has_yield cdef public bint has_await + cdef list excludes @cython.final cdef class MarkClosureVisitor(CythonTransform): cdef bint needs_closure + cdef list excludes @cython.final cdef class CreateClosureClasses(CythonTransform): diff --git a/Cython/Compiler/ParseTreeTransforms.py b/Cython/Compiler/ParseTreeTransforms.py index e47861f6007..f0d34ca7608 100644 --- a/Cython/Compiler/ParseTreeTransforms.py +++ b/Cython/Compiler/ParseTreeTransforms.py @@ -215,7 +215,7 @@ def visit_LambdaNode(self, node): def visit_GeneratorExpressionNode(self, node): # unpack a generator expression into the corresponding DefNode collector = YieldNodeCollector() - collector.visitchildren(node.loop) + collector.visitchildren(node.loop, attrs=None, exclude=["iterator"]) node.def_node = Nodes.DefNode( node.pos, name=node.name, doc=None, args=[], star_arg=None, starstar_arg=None, @@ -3155,7 +3155,7 @@ def visit_TryFinallyStatNode(self, node): class YieldNodeCollector(TreeVisitor): - def __init__(self): + def __init__(self, excludes=[]): super(YieldNodeCollector, self).__init__() self.yields = [] self.returns = [] @@ -3164,9 +3164,11 @@ def __init__(self): self.has_return_value = False self.has_yield = False self.has_await = False + self.excludes = excludes def visit_Node(self, node): - self.visitchildren(node) + if node not in self.excludes: + self.visitchildren(node) def visit_YieldExprNode(self, node): self.yields.append(node) @@ -3202,7 +3204,11 @@ def visit_LambdaNode(self, node): pass def visit_GeneratorExpressionNode(self, node): - pass + # node.loop iterator is evaluated outside the generator expression + if isinstance(node.loop, Nodes._ForInStatNode): + # Possibly should handle ForFromStatNode + # but for now do nothing + self.visit(node.loop.iterator) def visit_CArgDeclNode(self, node): # do not look into annotations @@ -3216,6 +3222,7 @@ class MarkClosureVisitor(CythonTransform): def visit_ModuleNode(self, node): self.needs_closure = False + self.excludes = [] self.visitchildren(node) return node @@ -3225,7 +3232,7 @@ def visit_FuncDefNode(self, node): node.needs_closure = self.needs_closure self.needs_closure = True - collector = YieldNodeCollector() + collector = YieldNodeCollector(self.excludes) collector.visitchildren(node) if node.is_async_def: @@ -3284,7 +3291,11 @@ def visit_ClassDefNode(self, node): return node def visit_GeneratorExpressionNode(self, node): + excludes = self.excludes + if isinstance(node.loop, Nodes._ForInStatNode): + self.excludes = [node.loop.iterator] node = self.visit_LambdaNode(node) + self.excludes = excludes if not isinstance(node.loop, Nodes._ForInStatNode): # Possibly should handle ForFromStatNode # but for now do nothing diff --git a/tests/run/async_def.pyx b/tests/run/async_def.pyx index 64b5af48647..8cefc95da72 100644 --- a/tests/run/async_def.pyx +++ b/tests/run/async_def.pyx @@ -10,15 +10,16 @@ Cython specific tests in addition to "test_coroutines_pep492.pyx" import sys -def run_async(coro): - #assert coro.__class__ is types.GeneratorType - assert coro.__class__.__name__ in ('coroutine', '_GeneratorWrapper'), coro.__class__.__name__ +def run_async(coro, assert_type=True, send_value=None): + if assert_type: + #assert coro.__class__ is types.GeneratorType + assert coro.__class__.__name__ in ('coroutine', '_GeneratorWrapper'), coro.__class__.__name__ buffer = [] result = None while True: try: - buffer.append(coro.send(None)) + buffer.append(coro.send(send_value)) except StopIteration as ex: result = ex.value if sys.version_info >= (3, 5) else ex.args[0] if ex.args else None break @@ -60,3 +61,46 @@ async def outer_with_nested(called): called.append('return inner') return inner + +# used in "await_in_genexpr_iterator" +async def h(arg): + return [arg, arg+1] + +async def await_in_genexpr_iterator(): + """ + >>> _, x = run_async(await_in_genexpr_iterator()) + >>> x + [4, 6] + """ + lst = list # obfuscate from any optimizations cython might try + return lst(x*2 for x in await h(2)) + +def yield_in_genexpr_iterator(): + """ + Same test as await_in_genexpr_iterator but with yield. + (Possibly in the wrong place, but grouped with related tests) + + >>> g = yield_in_genexpr_iterator() + >>> g.send(None) + >>> _, x = run_async(g, assert_type=False, send_value=[2, 3]) + >>> x + [4, 6] + """ + lst = list # obfuscate from any optimizations cython might try + return lst(x*2 for x in (yield)) + +def h_yield_from(arg): + yield + return [arg, arg+1] + +def yield_from_in_genexpr_iterator(): + """ + Same test as await_in_genexpr_iterator but with "yield from". + (Possibly in the wrong place, but grouped with related tests) + + >>> _, x = run_async(yield_from_in_genexpr_iterator(), assert_type=False) + >>> x + [4, 6] + """ + lst = list # obfuscate from any optimizations cython might try + return lst(x*2 for x in (yield from h_yield_from(2))) From 6d89e6d7f5b744390fc9c7b295677d8841f92328 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Tue, 19 Dec 2023 10:47:23 +0100 Subject: [PATCH 076/286] Update changelog. --- CHANGES.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index b45c6d37991..1e5666df280 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,12 +2,15 @@ Cython Changelog ================ -3.0.7 (202?-??-??) +3.0.7 (2023-12-19) ================== Bugs fixed ---------- +* In the iterator of generator expressions, ``await`` and ``yield`` were not correctly analysed. + (Github issue :issue:`5851`) + * ``cpdef`` enums with the same name cimported from different modules could lead to invalid C code. (Github issue :issue:`5887`) From 38769b6da4962bd9655edb6baf1a944aa7abbc4e Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Tue, 19 Dec 2023 10:47:52 +0100 Subject: [PATCH 077/286] Prepare release of 3.0.7. --- Cython/Shadow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Shadow.py b/Cython/Shadow.py index e56ae00b41f..b37949a5762 100644 --- a/Cython/Shadow.py +++ b/Cython/Shadow.py @@ -2,7 +2,7 @@ from __future__ import absolute_import # Possible version formats: "3.1.0", "3.1.0a1", "3.1.0a1.dev0" -__version__ = "3.0.6" +__version__ = "3.0.7" try: from __builtin__ import basestring From f17009c232d0cb5f68cb279bc16d6764e9576426 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Tue, 19 Dec 2023 11:02:15 +0100 Subject: [PATCH 078/286] Update changelog. --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 1e5666df280..85b5449b7e1 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -24,6 +24,9 @@ Bugs fixed * Source files with non-ASCII file names could crash Cython. (Github issue :issue:`5873`) +* Includes all bug-fixes and features from the 0.29 maintenance branch + up to the :ref:`0.29.37` release. + 3.0.6 (2023-11-26) ================== From f84c8465031d80c511191b9cf15e3178040d0330 Mon Sep 17 00:00:00 2001 From: da-woods Date: Tue, 19 Dec 2023 10:31:32 +0000 Subject: [PATCH 079/286] Fix utilitycode generation for const fused typedef (GH-5233) Closes https://github.com/cython/cython/issues/5230 --- Cython/Compiler/FusedNode.py | 8 +++++--- Cython/Compiler/Naming.py | 1 + tests/run/fused_types.pyx | 27 +++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/Cython/Compiler/FusedNode.py b/Cython/Compiler/FusedNode.py index 2202aed8f1a..6865a2cb42d 100644 --- a/Cython/Compiler/FusedNode.py +++ b/Cython/Compiler/FusedNode.py @@ -1,7 +1,8 @@ import copy from . import (ExprNodes, PyrexTypes, MemoryView, - ParseTreeTransforms, StringEncoding, Errors) + ParseTreeTransforms, StringEncoding, Errors, + Naming) from .ExprNodes import CloneNode, ProxyNode, TupleNode from .Nodes import FuncDefNode, CFuncDefNode, StatListNode, DefNode from ..Utils import OrderedSet @@ -302,9 +303,10 @@ def _fused_instance_checks(self, normal_types, pyx_code, env): """) def _dtype_name(self, dtype): + name = str(dtype).replace('_', '__').replace(' ', '_') if dtype.is_typedef: - return '___pyx_%s' % dtype - return str(dtype).replace(' ', '_') + name = Naming.fused_dtype_prefix + name + return name def _dtype_type(self, dtype): if dtype.is_typedef: diff --git a/Cython/Compiler/Naming.py b/Cython/Compiler/Naming.py index d8af93a4239..ed88a7f18b0 100644 --- a/Cython/Compiler/Naming.py +++ b/Cython/Compiler/Naming.py @@ -132,6 +132,7 @@ error_without_exception_cname = pyrex_prefix + "error_without_exception" binding_cfunc = pyrex_prefix + "binding_PyCFunctionType" fused_func_prefix = pyrex_prefix + 'fuse_' +fused_dtype_prefix = pyrex_prefix + 'fused_dtype_' quick_temp_cname = pyrex_prefix + "temp" # temp variable for quick'n'dirty temping tp_dict_version_temp = pyrex_prefix + "tp_dict_version" obj_dict_version_temp = pyrex_prefix + "obj_dict_version" diff --git a/tests/run/fused_types.pyx b/tests/run/fused_types.pyx index 72305d3a6da..31d5cf1d594 100644 --- a/tests/run/fused_types.pyx +++ b/tests/run/fused_types.pyx @@ -24,6 +24,13 @@ fused_type3 = cython.fused_type(int, double) fused_composite = cython.fused_type(fused_type2, fused_type3) just_float = cython.fused_type(float) +ctypedef int inttypedef +ctypedef double doubletypedef +fused_with_typedef = cython.fused_type(inttypedef, doubletypedef) + +ctypedef float const_inttypedef # misleading name +fused_misleading_name = cython.fused_type(const_inttypedef, char) + def test_pure(): """ @@ -554,6 +561,26 @@ def convert_to_ptr(cython.floating x): elif cython.floating is double: return handle_double(&x) +def constfused_with_typedef(const fused_with_typedef[:] x): + """ + >>> constfused_with_typedef(get_array(8, 'd')) + 5.0 + >>> constfused_with_typedef(get_intc_array()) + 5 + """ + return x[5] + +def constfused_typedef_name_clashes(const fused_with_typedef[:] x, fused_misleading_name[:] y): + """ + This'll deliberately end up with two typedefs that generate the same name in dispatch code + (and thus one needs to end up numbered to make it work). + It's mainly a compile test and the runtime part is fairly token. + + >>> constfused_typedef_name_clashes(get_intc_array(), get_array(4, 'f')) + (5, 5.0) + """ + return x[5], y[5] + cdef double get_double(): return 1.0 cdef float get_float(): From ce4fb1a67a9c65bbf6758fa7fc9e7cdf66565033 Mon Sep 17 00:00:00 2001 From: da-woods Date: Tue, 19 Dec 2023 10:31:32 +0000 Subject: [PATCH 080/286] Fix utilitycode generation for const fused typedef (GH-5233) Closes https://github.com/cython/cython/issues/5230 --- Cython/Compiler/FusedNode.py | 8 +++++--- Cython/Compiler/Naming.py | 1 + tests/run/fused_types.pyx | 28 ++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/Cython/Compiler/FusedNode.py b/Cython/Compiler/FusedNode.py index d12c9e9c86f..2ce4142f1cb 100644 --- a/Cython/Compiler/FusedNode.py +++ b/Cython/Compiler/FusedNode.py @@ -3,7 +3,8 @@ import copy from . import (ExprNodes, PyrexTypes, MemoryView, - ParseTreeTransforms, StringEncoding, Errors) + ParseTreeTransforms, StringEncoding, Errors, + Naming) from .ExprNodes import CloneNode, ProxyNode, TupleNode from .Nodes import FuncDefNode, CFuncDefNode, StatListNode, DefNode from ..Utils import OrderedSet @@ -307,9 +308,10 @@ def _fused_instance_checks(self, normal_types, pyx_code, env): """) def _dtype_name(self, dtype): + name = str(dtype).replace('_', '__').replace(' ', '_') if dtype.is_typedef: - return '___pyx_%s' % dtype - return str(dtype).replace(' ', '_') + name = Naming.fused_dtype_prefix + name + return name def _dtype_type(self, dtype): if dtype.is_typedef: diff --git a/Cython/Compiler/Naming.py b/Cython/Compiler/Naming.py index 140fe043598..65b6e1a7fb3 100644 --- a/Cython/Compiler/Naming.py +++ b/Cython/Compiler/Naming.py @@ -132,6 +132,7 @@ error_without_exception_cname = pyrex_prefix + "error_without_exception" binding_cfunc = pyrex_prefix + "binding_PyCFunctionType" fused_func_prefix = pyrex_prefix + 'fuse_' +fused_dtype_prefix = pyrex_prefix + 'fused_dtype_' quick_temp_cname = pyrex_prefix + "temp" # temp variable for quick'n'dirty temping tp_dict_version_temp = pyrex_prefix + "tp_dict_version" obj_dict_version_temp = pyrex_prefix + "obj_dict_version" diff --git a/tests/run/fused_types.pyx b/tests/run/fused_types.pyx index 9cebb189d84..e2cb319ba9e 100644 --- a/tests/run/fused_types.pyx +++ b/tests/run/fused_types.pyx @@ -24,6 +24,14 @@ fused_type3 = cython.fused_type(int, double) fused_composite = cython.fused_type(fused_type2, fused_type3) just_float = cython.fused_type(float) +ctypedef int inttypedef +ctypedef double doubletypedef +fused_with_typedef = cython.fused_type(inttypedef, doubletypedef) + +ctypedef float const_inttypedef # misleading name +fused_misleading_name = cython.fused_type(const_inttypedef, char) + + def test_pure(): """ >>> test_pure() @@ -553,6 +561,26 @@ def convert_to_ptr(cython.floating x): elif cython.floating is double: return handle_double(&x) +def constfused_with_typedef(const fused_with_typedef[:] x): + """ + >>> constfused_with_typedef(get_array(8, 'd')) + 5.0 + >>> constfused_with_typedef(get_intc_array()) + 5 + """ + return x[5] + +def constfused_typedef_name_clashes(const fused_with_typedef[:] x, fused_misleading_name[:] y): + """ + This'll deliberately end up with two typedefs that generate the same name in dispatch code + (and thus one needs to end up numbered to make it work). + It's mainly a compile test and the runtime part is fairly token. + + >>> constfused_typedef_name_clashes(get_intc_array(), get_array(4, 'f')) + (5, 5.0) + """ + return x[5], y[5] + cdef double get_double(): return 1.0 cdef float get_float(): From e57193ccc5337f08b6efd4225d21594906df0cb8 Mon Sep 17 00:00:00 2001 From: Matus Valo Date: Wed, 20 Dec 2023 10:32:31 +0100 Subject: [PATCH 081/286] [Docs] Migrate Cython for NumPy users section to pure python (#5913) Co-authored-by: da-woods --- .../numpy_tutorial/compute_fused_types.py | 45 +++++ .../numpy_tutorial/compute_fused_types.pyx | 1 + .../numpy_tutorial/compute_infer_types.py | 36 ++++ .../numpy_tutorial/compute_infer_types.pyx | 2 + .../numpy_tutorial/compute_memview.py | 37 ++++ .../numpy_tutorial/compute_memview.pyx | 3 + .../numpy_tutorial/compute_prange.py | 52 ++++++ .../numpy_tutorial/compute_prange.pyx | 5 +- .../userguide/numpy_tutorial/compute_typed.py | 53 ++++++ .../numpy_tutorial/compute_typed.pyx | 3 + .../numpy_tutorial/numpy_and_cython.ipynb | 2 +- docs/src/userguide/compute_typed_html.jpg | Bin 116673 -> 0 bytes docs/src/userguide/compute_typed_py_html.png | Bin 0 -> 223732 bytes docs/src/userguide/compute_typed_pyx_html.png | Bin 0 -> 216430 bytes docs/src/userguide/numpy_tutorial.rst | 162 +++++++++++++++--- 15 files changed, 373 insertions(+), 28 deletions(-) create mode 100644 docs/examples/userguide/numpy_tutorial/compute_fused_types.py create mode 100644 docs/examples/userguide/numpy_tutorial/compute_infer_types.py create mode 100644 docs/examples/userguide/numpy_tutorial/compute_memview.py create mode 100644 docs/examples/userguide/numpy_tutorial/compute_prange.py create mode 100644 docs/examples/userguide/numpy_tutorial/compute_typed.py delete mode 100644 docs/src/userguide/compute_typed_html.jpg create mode 100644 docs/src/userguide/compute_typed_py_html.png create mode 100644 docs/src/userguide/compute_typed_pyx_html.png diff --git a/docs/examples/userguide/numpy_tutorial/compute_fused_types.py b/docs/examples/userguide/numpy_tutorial/compute_fused_types.py new file mode 100644 index 00000000000..4fcc6382561 --- /dev/null +++ b/docs/examples/userguide/numpy_tutorial/compute_fused_types.py @@ -0,0 +1,45 @@ +# cython: infer_types=True +import numpy as np +import cython + +my_type = cython.fused_type(cython.int, cython.double, cython.longlong) + + + +@cython.exceptval(check=False) +@cython.cfunc +def clip(a: my_type, min_value: my_type, max_value: my_type) -> my_type: + return min(max(a, min_value), max_value) + + +@cython.boundscheck(False) +@cython.wraparound(False) +def compute(array_1: my_type[:, ::1], array_2: my_type[:, ::1], a: my_type, b: my_type, c: my_type): + + x_max = array_1.shape[0] + y_max = array_1.shape[1] + + assert tuple(array_1.shape) == tuple(array_2.shape) + + if my_type is cython.int: + dtype = np.intc + elif my_type is cython.double: + dtype = np.double + elif my_type is cython.longlong: + dtype = np.longlong + + result = np.zeros((x_max, y_max), dtype=dtype) + result_view: my_type[:, ::1] = result + + tmp: my_type + x: cython.Py_ssize_t + y: cython.Py_ssize_t + + for x in range(x_max): + for y in range(y_max): + + tmp = clip(array_1[x, y], 2, 10) + tmp = tmp * a + array_2[x, y] * b + result_view[x, y] = tmp + c + + return result diff --git a/docs/examples/userguide/numpy_tutorial/compute_fused_types.pyx b/docs/examples/userguide/numpy_tutorial/compute_fused_types.pyx index af5ef9071af..cdecdef630f 100644 --- a/docs/examples/userguide/numpy_tutorial/compute_fused_types.pyx +++ b/docs/examples/userguide/numpy_tutorial/compute_fused_types.pyx @@ -34,6 +34,7 @@ def compute(my_type[:, ::1] array_1, my_type[:, ::1] array_2, my_type a, my_type cdef my_type tmp cdef Py_ssize_t x, y + for x in range(x_max): for y in range(y_max): diff --git a/docs/examples/userguide/numpy_tutorial/compute_infer_types.py b/docs/examples/userguide/numpy_tutorial/compute_infer_types.py new file mode 100644 index 00000000000..416e0b9d580 --- /dev/null +++ b/docs/examples/userguide/numpy_tutorial/compute_infer_types.py @@ -0,0 +1,36 @@ +# cython: infer_types=True +import numpy as np +import cython + +DTYPE = np.intc + +@cython.cfunc +def clip(a: cython.int, min_value: cython.int, max_value: cython.int) -> cython.int: + return min(max(a, min_value), max_value) + + +@cython.boundscheck(False) +@cython.wraparound(False) +def compute(array_1: cython.int[:, ::1], array_2: cython.int[:, ::1], + a: cython.int, b: cython.int, c: cython.int): + + x_max = array_1.shape[0] + y_max = array_1.shape[1] + + assert tuple(array_1.shape) == tuple(array_2.shape) + + result = np.zeros((x_max, y_max), dtype=DTYPE) + result_view: cython.int[:, ::1] = result + + tmp: cython.int + x: cython.Py_ssize_t + y: cython.Py_ssize_t + + for x in range(x_max): + for y in range(y_max): + + tmp = clip(array_1[x, y], 2, 10) + tmp = tmp * a + array_2[x, y] * b + result_view[x, y] = tmp + c + + return result diff --git a/docs/examples/userguide/numpy_tutorial/compute_infer_types.pyx b/docs/examples/userguide/numpy_tutorial/compute_infer_types.pyx index 3882c289dd5..b17eb139edf 100644 --- a/docs/examples/userguide/numpy_tutorial/compute_infer_types.pyx +++ b/docs/examples/userguide/numpy_tutorial/compute_infer_types.pyx @@ -13,6 +13,7 @@ cdef int clip(int a, int min_value, int max_value): @cython.wraparound(False) def compute(int[:, ::1] array_1, int[:, ::1] array_2, int a, int b, int c): + x_max = array_1.shape[0] y_max = array_1.shape[1] @@ -24,6 +25,7 @@ def compute(int[:, ::1] array_1, int[:, ::1] array_2, int a, int b, int c): cdef int tmp cdef Py_ssize_t x, y + for x in range(x_max): for y in range(y_max): diff --git a/docs/examples/userguide/numpy_tutorial/compute_memview.py b/docs/examples/userguide/numpy_tutorial/compute_memview.py new file mode 100644 index 00000000000..1706b661100 --- /dev/null +++ b/docs/examples/userguide/numpy_tutorial/compute_memview.py @@ -0,0 +1,37 @@ +import numpy as np +import cython + +DTYPE = np.intc + +@cython.cfunc +def clip(a: cython.int, min_value: cython.int, max_value: cython.int) -> cython.int: + return min(max(a, min_value), max_value) + + +def compute(array_1: cython.int[:, :], array_2: cython.int[:, :], + a: cython.int, b: cython.int, c: cython.int): + + x_max: cython.Py_ssize_t = array_1.shape[0] + y_max: cython.Py_ssize_t = array_1.shape[1] + + # array_1.shape is now a C array, no it's not possible + # to compare it simply by using == without a for-loop. + # To be able to compare it to array_2.shape easily, + # we convert them both to Python tuples. + assert tuple(array_1.shape) == tuple(array_2.shape) + + result = np.zeros((x_max, y_max), dtype=DTYPE) + result_view: cython.int[:, :] = result + + tmp: cython.int + x: cython.Py_ssize_t + y: cython.Py_ssize_t + + for x in range(x_max): + for y in range(y_max): + + tmp = clip(array_1[x, y], 2, 10) + tmp = tmp * a + array_2[x, y] * b + result_view[x, y] = tmp + c + + return result diff --git a/docs/examples/userguide/numpy_tutorial/compute_memview.pyx b/docs/examples/userguide/numpy_tutorial/compute_memview.pyx index 166cd6df3f8..084bea37336 100644 --- a/docs/examples/userguide/numpy_tutorial/compute_memview.pyx +++ b/docs/examples/userguide/numpy_tutorial/compute_memview.pyx @@ -1,5 +1,6 @@ import numpy as np + DTYPE = np.intc @@ -9,6 +10,7 @@ cdef int clip(int a, int min_value, int max_value): def compute(int[:, :] array_1, int[:, :] array_2, int a, int b, int c): + cdef Py_ssize_t x_max = array_1.shape[0] cdef Py_ssize_t y_max = array_1.shape[1] @@ -24,6 +26,7 @@ def compute(int[:, :] array_1, int[:, :] array_2, int a, int b, int c): cdef int tmp cdef Py_ssize_t x, y + for x in range(x_max): for y in range(y_max): diff --git a/docs/examples/userguide/numpy_tutorial/compute_prange.py b/docs/examples/userguide/numpy_tutorial/compute_prange.py new file mode 100644 index 00000000000..68fc8a11f5b --- /dev/null +++ b/docs/examples/userguide/numpy_tutorial/compute_prange.py @@ -0,0 +1,52 @@ +# tag: openmp + +# distutils: extra_compile_args=-fopenmp +# distutils: extra_link_args=-fopenmp + +import numpy as np +import cython +from cython.parallel import prange + +my_type = cython.fused_type(cython.int, cython.double, cython.longlong) + + +# We declare our plain c function nogil +@cython.exceptval(check=False) +@cython.nogil +@cython.cfunc +def clip(a: my_type, min_value: my_type, max_value: my_type) -> my_type: + return min(max(a, min_value), max_value) + + +@cython.boundscheck(False) +@cython.wraparound(False) +def compute(array_1: my_type[:, ::1], array_2: my_type[:, ::1], a: my_type, b: my_type, c: my_type): + + x_max: cython.Py_ssize_t = array_1.shape[0] + y_max: cython.Py_ssize_t = array_1.shape[1] + + assert tuple(array_1.shape) == tuple(array_2.shape) + + if my_type is cython.int: + dtype = np.intc + elif my_type is cython.double: + dtype = np.double + elif my_type is cython.longlong: + dtype = np.longlong + + result = np.zeros((x_max, y_max), dtype=dtype) + result_view: my_type[:, ::1] = result + + tmp: my_type + x: cython.Py_ssize_t + y: cython.Py_ssize_t + + # We use prange here. + for x in prange(x_max, nogil=True): + for y in range(y_max): + + tmp = clip(array_1[x, y], 2, 10) + tmp = tmp * a + array_2[x, y] * b + result_view[x, y] = tmp + c + + return result diff --git a/docs/examples/userguide/numpy_tutorial/compute_prange.pyx b/docs/examples/userguide/numpy_tutorial/compute_prange.pyx index 562c7307093..45f6525930a 100644 --- a/docs/examples/userguide/numpy_tutorial/compute_prange.pyx +++ b/docs/examples/userguide/numpy_tutorial/compute_prange.pyx @@ -1,6 +1,4 @@ # tag: openmp -# You can ignore the previous line. -# It's for internal testing of the cython documentation. # distutils: extra_compile_args=-fopenmp # distutils: extra_link_args=-fopenmp @@ -16,7 +14,7 @@ ctypedef fused my_type: # We declare our plain c function nogil -cdef my_type clip(my_type a, my_type min_value, my_type max_value) nogil: +cdef my_type clip(my_type a, my_type min_value, my_type max_value) noexcept nogil: return min(max(a, min_value), max_value) @@ -42,6 +40,7 @@ def compute(my_type[:, ::1] array_1, my_type[:, ::1] array_2, my_type a, my_type cdef my_type tmp cdef Py_ssize_t x, y + # We use prange here. for x in prange(x_max, nogil=True): for y in range(y_max): diff --git a/docs/examples/userguide/numpy_tutorial/compute_typed.py b/docs/examples/userguide/numpy_tutorial/compute_typed.py new file mode 100644 index 00000000000..b6d90c564a9 --- /dev/null +++ b/docs/examples/userguide/numpy_tutorial/compute_typed.py @@ -0,0 +1,53 @@ +import numpy as np +import cython +# We now need to fix a datatype for our arrays. I've used the variable +# DTYPE for this, which is assigned to the usual NumPy runtime +# type info object. +DTYPE = np.intc + +# @cython.cfunc means here that this function is a plain C function (so faster). +# To get all the benefits, we type the arguments and the return value. +@cython.exceptval(check=False) +@cython.cfunc +def clip(a: cython.int, min_value: cython.int, max_value: cython.int) -> cython.int: + return min(max(a, min_value), max_value) + + +def compute(array_1, array_2, a: cython.int, b: cython.int, c: cython.int): + + # Annotation is also used within functions to type variables. It + # can only be used at the top indentation level (there are non-trivial + # problems with allowing them in other places, though we'd love to see + # good and thought out proposals for it). + x_max: cython.Py_ssize_t = array_1.shape[0] + y_max: cython.Py_ssize_t = array_1.shape[1] + + assert array_1.shape == array_2.shape + assert array_1.dtype == DTYPE + assert array_2.dtype == DTYPE + + result = np.zeros((x_max, y_max), dtype=DTYPE) + + # It is very important to type ALL your variables. You do not get any + # warnings if not, only much slower code (they are implicitly typed as + # Python objects). + # For the "tmp" variable, we want to use the same data type as is + # stored in the array, so we use int because it correspond to np.intc. + # NB! An important side-effect of this is that if "tmp" overflows its + # datatype size, it will simply wrap around like in C, rather than raise + # an error like in Python. + + tmp: cython.int + + # cython.Py_ssize_t is the proper C type for Python array indices. + x: cython.Py_ssize_t + y: cython.Py_ssize_t + + for x in range(x_max): + for y in range(y_max): + + tmp = clip(array_1[x, y], 2, 10) + tmp = tmp * a + array_2[x, y] * b + result[x, y] = tmp + c + + return result diff --git a/docs/examples/userguide/numpy_tutorial/compute_typed.pyx b/docs/examples/userguide/numpy_tutorial/compute_typed.pyx index cccc1aa3b2d..a78589d4ebe 100644 --- a/docs/examples/userguide/numpy_tutorial/compute_typed.pyx +++ b/docs/examples/userguide/numpy_tutorial/compute_typed.pyx @@ -11,6 +11,8 @@ cdef int clip(int a, int min_value, int max_value): return min(max(a, min_value), max_value) + + def compute(array_1, array_2, int a, int b, int c): # The "cdef" keyword is also used within functions to type variables. It @@ -40,6 +42,7 @@ def compute(array_1, array_2, int a, int b, int c): # Py_ssize_t is the proper C type for Python array indices. cdef Py_ssize_t x, y + for x in range(x_max): for y in range(y_max): diff --git a/docs/examples/userguide/numpy_tutorial/numpy_and_cython.ipynb b/docs/examples/userguide/numpy_tutorial/numpy_and_cython.ipynb index d5b8e05699c..5d79001a1ae 100644 --- a/docs/examples/userguide/numpy_tutorial/numpy_and_cython.ipynb +++ b/docs/examples/userguide/numpy_tutorial/numpy_and_cython.ipynb @@ -761,7 +761,7 @@ "\n", "\n", "# We declare our plain c function nogil\n", - "cdef my_type clip(my_type a, my_type min_value, my_type max_value) nogil:\n", + "cdef my_type clip(my_type a, my_type min_value, my_type max_value) noexcept nogil:\n", " return min(max(a, min_value), max_value)\n", "\n", "\n", diff --git a/docs/src/userguide/compute_typed_html.jpg b/docs/src/userguide/compute_typed_html.jpg deleted file mode 100644 index a1e0065734100b194bec5ffe9d12e5620fc11d48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 116673 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 PTWuX9 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 diff --git a/docs/src/userguide/compute_typed_py_html.png b/docs/src/userguide/compute_typed_py_html.png new file mode 100644 index 0000000000000000000000000000000000000000..a75173111d533837fa5bacdcc820957a4440caa3 GIT binary patch literal 223732 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
W(V={T#QTY-^%aEWV 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 z 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 m9QvW{AQF zr2s^r{Y|(ovG-HJc~jP-6ATDO)WR_jW5e5cH}I+wfn=hj8yU*7XTJD#zDE%yfkKuq zCe67DfXA%_kX6l5Q(L0KvbQ}r+=GoWz$sOO>HKv{iFExhsJlZM$lIaOryW=E^W1WxgTOJTn_hWJ^o?-yn!0KG>Hzv7M z-R+$KI!y#B-_0(fr>u^&cOkNgp%;7ec2(z=SVt0OF~j`$nt0ZK9bh&6jZ#Gq`Opth{nqmuhSwTbWKmD1JwZBl>$1vsTI zf``M@Tl)m7)FsODq>-sIjH6kP>Atrdxbf7+*{@3wY0Uj=U zN@{|d@b}G&LdW;Rj#}tAxTQus-%0i$FciC-9MO6CT>muvSaABb!nWt0ZPqhKxGE5& zf~?hOVdwwAkBk{KB}EOg(&9O+DgNjY`B6~2p&g)jorENE;;yVK`wC1KqMcA9UWrE( zPiR3?`Za`2vLFw2aTK8Cdi#FXy=Iq=o6XkH$ZdOp zruRpo%?jD6fSvCq9$di`S6R^*HtXwoLKPe^R2SoSnDmd?e(J>|qwl9do~Ayzx@%9; z>n`!o9I>$9XL3mIE3|LX%0Xw73RQGIP0rym%)OO%3L~}zNWXfH2{rq1)Ztkjf>_K- z91Jd6<2nf-ux6IlnYW0sj5((2%D8#JH{U)@^AEwpvP;JsClJ)7@VJ6L_mkxQSmnJmx@o__!}}7r@500;9gzIF|z_IA|5u4)eXvVK_sgk1VbO zT|?$8v~47+dt{8rkY2vfpUTZHAHV&pPVRvK_XeyZS~rC}SQ2r;=4KT}lR-+X9b3kF z@HEpHdRpa4w}_P}b~%~Qr!^)Cbi{knr?396W_@d#&SHdLBLPNOrE&c-Zd4vzj8wRG zG$=8dASy&JjW0aAHT{tc^jLg(>c^q>*(zgw?C9p_ko`tqt zf49j~$#pSOY$e9kYPt;sL&$g$$-&O5XMhR|jrrRne2A}H?6_57&cZ$0=M9Rz>}@Or zpL+z(aQU!68jX9v_Gc-JSk2Sb_9qd#vNbRvpdKfwbsTp)=oL0a-+*sh0bx$fT6hG8 zV!~TuNUIu3b?TfYG7%xHM`&HI>pnsV8UMBGD{Y+as~;o~O|9KP%eT%eay3>i!5ikW zh&S367G|_lUFhE8e~9FR``R;3W*CC64%vP|%16`DQH&IT<_KTNEo!;__myQULIC7} zBczyblkxF`Tl?8lBJ*QaE6Qp^%|C1iBMZD7Pc!kOZFx&`2fM@ZnYG} zl*ZM=o7L`vhmbaW%dUb>F%^&PMQYJ0a^HYu>ouGHt;)Fm0#&y8zXJoi1K;7uj&BDz zJEPJKw{G3-`E`CbuGmu62UwMh-o$;kcMCa}1LC}A2Et~vUg0ZSi88Dji*xr>boZbK zQCE!P_l*H2CBv`%IHK3^9a_5M`Ml%WXY)|GTDqzOsG~>{2dSNCKB7tSy3o*Sd@q%d zH@eCb9S2Rv+fXF>Kw(Y;Kq1({0_uAL@_p-=xkFBhTEc$R zbbaQ05|c`h4w~fqnM-Aj{Nt$ae`+7!xW*6KRp)+^I1#xS3Uj0*Rw^T*{CB*J(wbY< z>I4^W_{Ed?D>`}i%BACYA{DYHxqB7yOb1&w+bM zf17+zPCpWcyhZM_*Gn)O8DS7RMVsX)X~Fmc54I-B{cIuks-L1dY`yH;w9%pB-N!I5 z&eja^^ha}B_^QUw+`h%b`H<4haj~dM&fIonnxDHz4H$|_dLfOUMsDQeoXGYWMbDrk zI<&u5W5;~MDwbB6A_WD!XI6rt2L9M3QKC4qL%Cginun=KN85444p0#du&?pHKKwJ4*dP=a*uaTs`I(grIikRQ!SCkho?No=$K5vj z_f{V8B?W>^o)h3vAH=J%UZdAH2tQr_eq{wLiIL|X4c6gpZq&jG8U;)cIT!M7F!PDK z&%&QF>xc}l$vv5E@~5+?6#VZMX8?lUzN)jCIy@`{nXdnNoBwsg?1y|2XQ6+u+ayYg z2(Jxh@U`;)d!xY%(BZvlO+uJe{_lYP*UR9?5)>64i2Q0~Edr8oT?i>Mrq8bmNamiB z+FUfndysgt3^o83z66YZh`AXt;gdfHn$=#sU6_lUIM|8Z-vX)R*ct%Mofc9A6(PIr z27hU$aVB$ud&S>+$5QO7M9r^-PK<{ZOqKc296`m?rS1-&sj zoAshf*IT))+V?hj^{{^S%K?ZKUh+)JPOBYYN*g75qC2I7_w-eyUf<{i4S(6ZC;Z_U zCyrRSZj^xYEBB~?2X{xxbxYP~6LuW85r{SC9&9(wXt=fk{$VCi1VMkye`d_IdY2e0 zd6O8sBaou@;Lvs~;PHI7*A_7J!MPGnI2jn)Eq!G5V?gqU=Tfa`Do_*_vp$|0;eML0 zfBl)kN9QVq*}jj#ooh}FaVyRFG@7gR*U;F>=I-Q|kIoBfS+O^)w6)#)4X(df(lt-# z!O3bo%_;qoN9aZpUrX3BMYrQJyfN~8an0)E|k(%EU(`0#(R_11AwZe9Dh0>aQk zBS^P2NF&|dARQtg($d{s(kKm*!hm!)l0)~ z`&x(b7g-I){$}Jxw6;3GcNE{XNcsh(`jr#xV{A|V`q2FQ5hfp`=wfW)^vfU67I*|- z^;7`*c(Avw>(e9zHSGTy{WzUIQ*I#2IbF5DcF3}C+nv#PV~2xg z<$3(~*u|%EKBs%7T?J@ zZFYV)=v#w2eC9=4wJYB9g!+$^^(`#se(V=*_3|mW6GG0HzY|~N{sgU)T*2yODgpVq zrV8I<>HzDnz0nWFUVHdVoD(OEkpxY$YrTjEjWCzPfRPOK{?Rwe1TzU_35Sx6IQRXb zqWORRd)%N;e_)&&YT1>RYiXh`cR0+M z(PE!-lz?-4bw!+Q_;B2HsLb7v6m4qah!JmrZ2MJy)~w)@bL<|}q*M3R;_gg>#js=P zrgvw0zmFlKIgT=d@!rcz7GNK2Cb+*pg+$||bC=l;J)h{#_;M2HGpD;Wb$D976VhNI zzs#@J`#zKAb7}L0jwDi@-4PEq3Hds>lL7jOGso8au;kgaQtvMkADThoO}s@?SA(p^(_4eMV7rf}$S>7hY*hQGutc|M z;A>cx|CD$Wvp&y(>A1`rfuz6tC}0+1J)P|@&}7!9q3OnoBI(AzNads~?Ot0Ccjnay zTrAkH7ZF6Eg?`&z=H$+`VKJNVb2+%bBjR#Z5w^E5Tcx;KTwl6A7N2?FNAhs}c;MYPar(By|U)q3@icpzBZM~if5)W;5VbAFtppOH3qCpG zD2^fK89^r%oYj<_U}|AR>wz;V(?ZVEPz99^nwathj^IdzAwQ?heWD2MPDe-+WF_!F zNrlI-Usn>E8z$HyNGT0*-n-`T{bI&MZFYCzREI{iQG)Wptj8Qx>{kN-lDCleKC!P= z@!nsrN3!MlhN(C=f+{e>+C9>B(2cP*!xqY=(6d_Slmu?>qVaP=?-k;}3f<}eOdWe; zYTP30Vkg{er4DtYs%9g6VmZ_SQq4ZSmU6FSQP%qSOk|AS%{W@cMVrEmty(axOEp`) z1M-$1hcSJTuMyUKQ35vHn2MEff-|k+8?7&c-5#>(!$G?tC+e$A%Z*kfRS(?BvUp4; zjXZOfd6fOSC{+D5!v)OJsMD{`<_ditD0ev?Ob&XtWqZVrZ*fe+Zw)dpSSX`9{D8Ux zc}UE>a_>9NW;+@K>Fx}cP;4v$Io_J>r2b(wIkc9P=2-w?fJ|J~)Y}qaq(K?sm_PydBI5m8 zA3v*<6XRd(ef7GgCZU~6J@qzcFU+q|i0R(A>%eFSJfj25AyISM$eb6AY}K4gZe13V zzrNBvY3i&|+qQqDy}KaDoy}n##`7mMagGBi(sNJNBwV-_Mvh0ex=;8$euSK8@W>-!BBiGa zr$5nDM}USUu?`;aXWj4?12Hpr$ZruR+UZoe)AdDC=Ek#40-_`=xf@?S_KI5%k7OE= zzF3>(ZDHJU?qcz)rk<1sz}p0@t&nQ*{OMj4{W17+t}UG2%-!wG{Uj}VOkbt)A2EAJ z?@BZ`KF>B0h6n@)4Kqiv|GBECZOE3j>@@w(=MlzxQ{LOrA|rVaO99EGBEO#qIaGG9 zJS`xQ#Nm>EQv~9pl8Um|>>(k~Aa&oss?$&16Y5kcoEXg!hsmWuh{Afya#bGYYg)dS zRkGzNN{-eW2Br9PuAB%*jOJDmw%++8eP3J{)$jb&PA+zT2)URxj0~OjVob9%N(d)4 zrP8E*9mKS#QY6pc;g@G3X%<4>CDC$UnB~Pmh;ZslmD;7?^olyDdb!&?ZXX!mU0}r$ zQ1LOoCKF_=AO})nlgdJdH?kOHhT%;VdOpvNw#=g_|5=Md>)~06C^_cXz)DQnIotWI zz}mSEJ96qO7b}E={d5uj`6|(_6Ub5F(YbpH9o@IpIkH3m2m6rCawL|ZmrAC(*rVoS ztI~PW=fyy)iC6}lNQ%$>Fjvn~-DPf@W=WI-7Vq3I&CtYtr;)7^*%tEDX@#I;`Q^OK z1~3j=_fP+R5wpz4Pqj&{{>NQEpP?4?j3139=N(w&_L++6hHXD3o%`Od%Q$eUnS3!K zT*=mYs|U-w{W(oFVfSjWvU?n=0?OCBrsw=^5VcUN;JmIs%iD1QX=E>7r0N{=?GA{o zLg=jeRWG(IW*YOod;pcw8`L@7&BGO;t6hRy|oU5w+7iKUH=vr+RZg4s>LLi8&k zeH;BjzsRE{pKXyQ++{A<9U*;OO#TcvQHAAGpQ$u?@Uh=Wot8EgpYp1miQJxnBEJ%! zcH)*Qh~+eC_6=3?>2wrmkqJny9p zx@3}B3`Ismx94BY1phaGTi&zm% z(p5xto^w&~Z2i&W zS+v6b@0~uyjT_2mlwM|hL0F+XZ94Si`us_wwQIIS`F~{z9USAa>no2YYH>TvLoWQ1 zEs^OwB{sX8r@|w=Ihz(G#4KOqP%>V1edDwhX=e3NkbB)Mq#%TI(3IaZ5DiA~PDDe= zl>=9A()%ZBh{zCpyYTqFT=XcHsqm1|C~l1Kz+81B&Kkt&LKXB)(C2=cZj(5Y(KfR| z<@;mQCVGkxf;Y`A*I}ds=o=?;ki%)OY?o)qGrzkE6zO!#9QSK@OgD3EX!!n0sTlii z=N1NDi7LLx5W2Cbu-FY&u%`~p%b&d!$Zt)Y^K{?@R!&Y*jUM5u|wS}Py5n+aOnxoQX#mz({Pyr+P+b3w!9W!n(y}|6HM-gg- z?&G=FH=|T}gm;7ti&plEqLy+-dchwuxL8CxNs#v8GYvjQ)EeSU@Hy5zf87~r3bvuzMH!OhJ2z%#-d|HC#>l1y4A`w)yzxpec` z&mP=Lge&$gR??#^2&}*j_XT^`!AhL|`$3c**BA4Ikjfwj4gw15vNp93wU0^$mIUh{ z>uh`FUjC3TA&>CU`1{C1_Ckrp!i-cSjkGe%cJw}LU@E<+?ybZ9h_2IRO+bX8>`A3EBW0apxMEm&R}*C&ev~2SWIAF?PRaLi5%lF z(dJLv?TO4^j){LLbSv!pnu&>V8&=3Er-{xVbUP&>8&x!&*CChS+kDu96fF^T0^xoN zKJ7yyX90uqw(+9I`e1&j{8&yddzWC=2>3&Zt9Kexb_#GWpv@xHFet_#fyiO1+LXp` zjEvczEPI7II0*p}0i7grLQ`Fi^oL(2(iZVx7^3l7Jk8V z6zu_DK3U^~zbUpdQwDar({erR_FPqodPyb{MHG zf7aIqEirk8RGv8}ym0`14;B*3hQojCde#Yg6+fvK`20%RJ}4}+&zB7q`=TR-RPp_- z2kOAeoGSN_)1tS6@fJ8@XV5a`CRoGxneSewgyQo1RsJX0QZ$zNkB9rr_~Tl%?glvi zG;v;R^pm@~l`*DxD6Q72&QQSkX5aJ2bY=)H!O=nmGK$nEn+OX5f429;yDm}NsM>4T z3E~LxhwzPUo>k$%;Ow9j|zN&cY_Jz>>G9N=XRygAhZLk!WnL`V5sjV-nMph$7HJfAb!3G;2BllQTAui9f zQ59R!+QsqY?Ufac&{Q=}XNlB%>H-E+_l+(hOiBy(my8Kc_r6c1{fqhj@GWt8g|=+e zC28iA5%LNp08m{ebIP4!xc5WFRkq%aCWx3@_N~++5`~z5DS3_+$HUs*iwhhYcyEkP z7&y^6hylE-A;itk3fwQ|1l>AbNS0Z??e^f5OD#Dx>vj9SSxh1RCUPe6w0W%l=NOeO zhd9lw2yTw*2O~OK^RPz+OVgTG3j3;~FW6Z7*(PftsPOW7Dje;v7~oq}f`pmgvE>P3 zRzFGD2JH+YAR&h^YC-qoUnJbY_$tMd;-s@*?BoU9j}p=s|{79KJbF>cQwk zEB!eT!{AU&@6(tDULU9XLN8&W`9;e{mP z7@>58HFdWalt`Nr_qF6|`R-pmz(*k)d_4BT}`f-hK^L-&M0DpT>~Ny zlP_{)#BKtOoSjdiM{1X7-nKkSe+|hFAl8*d9-iCl?K~yU@^WPMSKa^6mWFjajlGL` zZXW$DkqsnE8oLt)WaWxjHSWE`mvf}Gy6A> z9CRiJ!_}>v%T-*y?mZ;dyk~|v>{BkG4_ko(UW=IHDLD|=AI)o|A&~~02m^5b&CD_y zMEkMGQazqt#L}SbM(%9h0?r&nx~Y0|V^}+72#wsrG~w zT)1=CU)wG2r|pHHcEQ%TBWT$z6(jYwZogqr2;qFcG0jE&`mPBMK2npGAYu>Nkf^y> zJ-5{3webC--{M<#6vQD6J@^JUWrx57P0wMFS))}y_&^NCqr^u~#KAzUgBntE$^VW{ z18BJ?&lOd^ReI+B86RU%k7VCj|SeoR?7N5&PZ^P(OH&HLzc*_=_Ag}5hwNvRyV;0dcy{%Ty*rD{r zc9j%sR&A0q?BLNn^qH+!{WqeYdM?oR!%sk@-RFjT;ZUhpM^QEX@^1+KUwD4|ZGa!W z`Ctn4;ufyCX5v3u0Bi^YWN-}?_pH7)5Tbv57HAiO5l|?}SEZi{f=>0Rt+jnn=N4nU zzl6D2ya!28v_A^*2;Y@Gfi+o(v8=-FCne@k#x0h;x4WW{ph?~ACVj|s`ZAQc_+wGm z$F_Lsv&xdLVXU9$v@-wRYrq2_>KFM?n%p^VY^S#J-^D+_G7-UaI%BFZ)#47Dj)dy& z4x5@t%%RagsIjN!EV2wJql}I-3)}^jFY}Bmz+rD0XK|6^$BnR)n8@Z8ax)a;70KGZL~pk~T3^v$Sy#$Ejq`O+OQI9V_MZG505Tc3NY!q|qi4bv9yu|irfH~~{m`VDc^iE(l z@k=xovI+6xd$ty_yMyMRUtLD{tR;Cpja-~CZ6{MRD^#8;-kwOzm>jjBYU+Pf*WgBP zR-Tt6T)r~j{#<6&FxtY}x}z1&mZ(JLa~3T;WxQZB!8+&r&n}N>HiyI2nZx>=Sf>b? z&-E$;ThqW+4zS$TJ0<;Z*;(d!>P0obi(;IMB_r`|)v;)oJdfVaQxE;NuOx+VKA2Iu{YXaVQ2qj|#7xLIS_c#VSmJQ{{*_rPRq?$M z1>uctmAI$9`pnNlC1<3F&DfS`(9y6D{mkXzflHa@pdFm2|0jI;u+2YBlQW_FQLaSr zGLP)FU8hM~YliOZj4oqXjO?U!uR&puHWxFV6D?C+1HdPX*5#Dl6Fps?L7T+J&`-LZm$H zZ-~h^shUrJ3&#)VEXZf^b9SAb@cd-`yD7XThjVY5wfaiteXh#$2d^zwr<+kpHOKRD z!AJMCGpT27+@a+0xp$jMi9QH=*H?m@?;0F#1Tprn<9Gp0*)~hnz|$HL*rRW=0N<&f z6HrLK9D&?EQ48doJ%71Sk<}!5xm5i6D|?Lh{fP!X6A}niuZ_;PMOgR5(o|2p!LQ(e zTls`quA7B|BcK2gDm0U39U=$x7d`}!!iyVuo(;Dce_rcAY8o!lzLLd9KGOaD&}ZQM3hlDvuk>GzSg;Tfm%V-phy}gDizyO-8=>gDU#2BqwWT;9 z0XhgBlwz@SR94p~eV!di-vZ(2Ue2ZB`eXZ}b!;T{M5@C90mhNNWjHG`_xU~8l!EDF zhs8C(_P(yNBEw=}#Zv%}>xtyl^axfHONSdsob*WfXQ?D>F?r@~{H$dYTj3D0;BagkIrIrIfGJW8eB{s^-2R(x|N z7n%PFxFp5Hw#qih`C^%u_=6C66yp#RTzm4@Piv9y%|w~7Kmwy`mG>gs;VPc_f>iYM zv&_l&qsNCZm~c-n!k^~Q(icCr&u$an83q*vDyjA9r)h)Ane$1&(=|VOUYvcz!YMJ0M)_I*PJEOj;K~__f&(;8)TxYkJr4nc zIQ#`GK{{*aqg9pCYnj*I7)FHUHKg?qz;D*TsjDBuk3%9CTquhW&eqms+=T}`gcdg- znIu%@(V}9Og~|mu?!QVq*=9OQoYhpkB!ip~sD!|x9B$>WWyXylZfhe*CRPD!pd_Ij z@4u%ZD$l{!dOwWXBMupc@Uad)49pWO?id2fYeu&nYmF}`2y1X%83W!Ryr3>qMsP5W zRvRYN^)k|V>0{C(3JzEe5e;S!sVrlN;%2c_4O+mZ$SutBEML&j$8hlMjPU1z4+xiD zK|zrKF?j}q&^kOR+rLp;tBt2^8xy6PEnaFoLpAKWh!8>Aj@UQm_ohomr`O?GY)ucp zP#(-Z1Sf~CfwjV2-`k{cY^M!mKi-9r11Kr6wn!PTcW<;4PS2Om!S(5)I&R>S@5qM^ znd%;Mw@(ZC1t5PK84#A|KsrU4LL7?1#}1V5io%cLK_3wEe^O^7UqB^vN6gN)ZjQIs zyWgrG73^Xy-i4#;^B03lpA$j8J78$@fo8h1MNnemGV^RfGIHnMw<2r-s&;2cxgmUqmBqARmWy=83j`@@)BV6Y87H z9dQ)e&>x=35i|zuLSCDwQeGo=er^f!6foQx|wKy#)P+%5$&33>z>xUys z4|%hi8zvIeUf8Jzu?30iqhfcCy~TbNK#uVTaQ{D^jD`flX)U=oYjVU=)$ARCNlA8Z z)nR~l+&o>)xI{%0P*g86*DbE;SV$lU1m&oYQ~GKum8&vzdlZd(fbCGJeBvZ<1F`xF zqHA6{ggcW%WsM_Zux9~WAzl|WNm-MolR^$dqVsNvZcAM)O^|Tuq8vTD-B;K4L_#1; zH72yeYgY^Zg7liLNI>(Lf9P=+yWu!yJL^i6O6S-&iD?g$V-Um*V(6h^Qq`%@+3%Ku z)R&D58b`;XM0~k4QB#z32#(R;Rq)OQM5-z z?5?>7oST<9$sJVGcajMK_&|9`qT*;|7rZKBCH;#TXRB6)wvRR_jg#hbp(=wv53-`h z2^8RM5mf5Mnd*4no<+IF-z%%V6Ez56`L>8TBbG@vX;`LJ&qbQC*cY|qLqH2J=#?HN z_wu)IKFxqZ95_9D#XDf;VD@jNivMX$JEHY;8P=xjWs&7n(94)yVM+NG!QzMVge^zh zp+Q608uL}&{miIGsb`9B6+DFw4NY{acyOi(nDlqcbxdy7^t;kK8}UA>`P>c&`Qzo) zFeq4O87%UPCV=8Fs_ZC@9Dnu;cF7FSrynhUw+{rYq9}R2)E5k?voQewD*F?dJfDvx zBgoQ1+z=;bV2f|o;Qyy(8N;Nr$yr?Xg%qa6{+_u3#IJU+4jK5DKm_de*_WEJzwOKa zaJr^35niWx!fsEpe;GbNfuluea&|P5JOA}DH2;fd{sB615HW8C&oZVjEq*=; zY~5*LXORS~F4$Uv6XpUI|6qjuW%nX!RODEJJikh@vj#uJFMRqLU8+}CzF<=aTkQQr zl)h-GhDyp~`%Sp5RAOab<}%}@fJ>QSmC?VR2&g>K;oSA&xDyn`3Ki1UW)UtI?Ao03 zq+?wO4Bc`(0d|r%m3F!Qu9CpP8{fx1GT+bZeX*{ESw4+lg?)V*o_D-9{rBpgfi_y6 zSw%9}+J}<))GxR!*cbrxCjFUmgSS9$EJvN;FH8FLb}7f@JCkC_v8p)5CP` zH=8LB`~@#LKhy*B4V#R7AEu^?RJ&jv`#;hmAWr{nE0E!c%M4riv0S$GUr>wr*tI=e zmK!v=90I%guGDhz=J5vVd)tlc{D_G=4jz|Mvgz;gXQgqE*!O z(>X0-sQ3-efa|)X(q*E$wV&R-)`8yyMhs4H*)Gmj2zNfj?0esyGdK>41|r^Y$^5i{ zx&UYV+Hv^5?kN#SW8{i<+4resAJ$Zd*(~x7B=$RS5}16dG4^L@GxY7%w51vQL!HVHpS;|1W11KpuA`Rr#Vg81KJWw+Np| z_Lm{KDo&5a|vh~ST?4R#G z0p{^4OW}M<+o(bK@tSIaMnJm*Tz@tq0Q+j!`fjVn=N`D{2_WUVTNePv?~nX*1=pB8UV9v1m?ZrHyDmRp^@-UzC9b_ z#A|tJJ@u4g@%hW$`GoRJqszsVb`#(MOn#gdc$)%}iid!{@yJar$Q2$5V~8)y z{O@ZA9Ah0;5w%7f{v+T$$}OOowiAjx%xa`?I@==|u;)mxRv_dVfG$lYdj{Y%;y>fv zK;Z$k*CpSlhls_XC}*P<`H+mmv9)eV$#_)?$Ww$rCDt0C$Y9(n`YolIw5v& z{Kw-4i>b6Pveg$15GJBOqzO_)hz(SN}M@Wv{d7r{yX@a(C{-*M>;e*4e3Yg9Yck z$$sxEm}{#6iGa&F9F@C_6L17gc#K-OR%d1JetPje++Hv`3XPBr@E=r9G+)G622Yxw ztn~<4X9;;PLQJ|Ma5>-q0vBFc%~uyID+ibX(*tMxZxMGBl!Y8@=BrPfks^=Uo`js! zF3DUM9Uh4eq}ZgwLbL##ytD%6Fl2Fky!zPoj~w0@RJIMeKk|K&4=W(w4>fq7C!_xZ z+muf(%s6fb*5sNC_@H7YU;#)?qqMVem%vFXzX1jZjsXs(8=#^l$#p&7mgVuh{7tz% zc$UT5cF#cpXO!#naEpxPchCN0o}sz+6Do9ekTY!h?yQfJ>9)FV$)nT`|HksCjup1D z;E6=8Evzwokfo_iCL#Fd$_hj9xSeu4@9`HuB&eSM_U;5t=yoTsd!s)Ar=sOD9Xjx~ zp{{=H=1KVBJmE>%n_`~A+Vj34l+@$bQ=y_j9`N$1{>~r|#}sP@%g|Yg=FKE}tYv6*gn}vn%3T0hF!SkqZidm)~KJA;tR zwSMSY7xd{qLx$gja0p&4I$<0-3A`J1s-$Zd|Uml_;u^{@?ihFa@HY?Y6?z957G)`=o~eQ zK`|P0I#!v!kM6Fg{dAs;vw+p@?lT8`VO=jsF?6IPY-Frx9)H^2Z$2Aw3Vr1m_7od@ z(nDbMx#^%*Z_i`)xpy>_7SQo#e>npQTNRVLn?#SbkkmV$GykaF3wMzJ569f@;V-hp z;sUU-jzu~oplO_a;hH^7y2BX)GffIM4V%f29(A^`#Rh=<1v~E6{S%4aAs7cSA<$Rm z0=qQ!1WwG=t@&6E#O+oF%@Pdn!g}S(2m5KGK+)cR<*d%K*q6DS5F%{LU;R z9YAP1Ojy-Q3FlsNGvhICf5uwo=zV{EVhaS~Vvi342|g@3S2neCv^84DSNxzEu7Z}U zr3KXfj%;#}uiS>RE9<0n&kc|jc(>R~7zWWGxKsT{l0=RI0OQoQeg*zv#$KR?s2YT= zJE-u}XFw4puOhIXyCIUL@fR7+0Sip6TlcFpseAq}e*CO@I4{j|eLk1#)AMXXGJw!n z61=vz*?mwJrWw?ZzY^d~Lm;-|*BM}9e6Y&jiPhHlD07tOJdB^7;jEY@6TD0#pPbLa zs$2aF^|cODQq|@*|9;YUrHnmcAT6l*z%W};%d97J8vy%-!toC?Em8q(Fq5xxTmTSq zfl$3=KN0m4nKiPlxlSdGk?Bu>6W{-c=_5}C#6k5zr*WuUA18k`Z5udGX<}S)2Wggh2r?FAxGv4D z!i5IQO$Yy=sh$SX71U4lt5`Z>flQWd#n9J5 zBL(nNx{l|I_%9yVWHpg%%g6m%Ci?e#Xg z_!)Dj9m=fBei4_+Lzqs*%K#y+eAv0XLEib5<{kGxj!VY~YZkaPXA&KH!h}E(R>^`m zQz4f0F3cmz{3QY?F3j48Q!79R62pkYnPo?U?Bfp+y8+3Giq(@Z@@NrF%j2bk@foyX zhbRWEN&i_U;9~m^ux-%TNal7fT7;6g(7%Vxq*XyqKLJ@H%`_dq5zXPknCSt zcgczIuK5r#v$)v_{BwywiNFZwdsmY*Gyh}#qH|Lb)+yIitmCreeP!Un?L(NU*zB_kJsjoQ2ff)3?O zfN!JA!Cv|stF^7+uLOW(=ijaTe{DXI2rkJ!d@u!waO<>+=^MRKFB2+@41nnn>o+U1 zmm3`e)gPxyR;>PYT)?8TC&5McGe*9f&OeQOZh9_u#<~C>{3$w4;DL>!3uV7`xOP*qqZtXJ*}Md!X$e zmDGp*FY5sMo-zpBIzK-|6UJe`8!Q9j?6h?n%Q+yRg*8p=FTD%ox-N`bW%gYm=uW5v zET1O|m0gMLnk!>%3$1httKauy1L?}AnG#LLw>JN^k_2{GeqHHL{bAM@qX>(zU;d$2 z$3iKgS$^-Txz@N*fbtXmUpB>tfD+V>*!J}3NjWskqK&HNzh~a_Pe1k7PbzzWA>_@f zRW!gx|4WGhN2LOZHbvEEWgPi0zg3g|tHf{~ItcmK0jVfqv7xH*d9!IT|7&r;Wikbk z*WN=1G5_sj^!0uc63q&#C&{cvhxp988(I~H21+?PS0xvNPmkN*TkfmY>zH<)-uwRe z^pL}}m#~{j#-z8EnT#IINSP!F6TaOdzS<$S(~zhiP|VQoC%nG7T5b}4x-DItELOpL zce7vd=xw^VTK`6-FIC}8sr9PvF_SFdcct4C(1K_6y{4!L_@&IZtATH3>_PIo4M-l! zz5+!{sb9i5aVM`${c6bJs5qu#PL11%=^qq8yUE3-Zo#&x!S{Zh><}n!lnsmsFt)tU zLSY>ca!-(?&_ky zT+>A-*%OxTH`L()@KwEsyCWL6nZ-yS(?_qXPS2ywOm^t2<%j#9c@<^90>hd58;!1J z%?@EuA-&QhP6DPP2;8P_G{Yv2eCzGNMHX8J3Ab1MzB|nMFf5+R$@CTKn1x(bf5m{* zGRfv!X+NL41Z92mG*r2Cd>OJS_K;$A}r~1|?-dxwhg?0FQV};ut27qgm<|F~Gno zM)w)|&Z1#iaxSzAezmnfM&P!RRmsGpUe`A^(D?F=o^5kwlls=(!TUvM=n{ea6Di


x$VyL^ZStpG-njE47H4$PrPMfi-l2k8#q>6`%=r=>Z*PIkt|qUaHNg9;h! z!)4_%#kkK1j%X+tD@|_r)BxP|HQf(Vx8Q2&DsG2*^$zF4nl9xsc64)yAn8)z&6&$LWbkOr0!A!-h+~FKFJ8*p!}5FOOC?(06nU`-7h=dy9n;e;X zC(Yt)*RR$jv<}g|Pso})YJf;hW&AY%zAjZkwM3OuSwsbUlkRRg)GaaDf2GW4m9|MV z#RYa>xY$?Y*(*I!-kY_EM}3cN+M8w}pV_s>k@^&^%b09E2!+eTN&$rC zj}kGNc2&Q?H#KJbhd75`W(6b1u9pU;=5lA3x=jV7lW}?!egL>-I$`gwT(ohmkr9kg zZ-15Pr+&y>@G+(0Q_G43`%5^q?(p6aHPqK97Ja@0vnG5lx7g?L;ES3@+D4C1w6W>| z?ETSd3KybbeF${xn?upO5X2IxCXl)PyV8e_!)=N|cU?nIRK`@qd>NU}tQrXq1QOgh z9kvF572Rw7#u5Cfrg&VfTfAy~`>|XeHYpF??|JubDZi0+Q#1J4`Ykek&TGv7jUcN= zs3plPecYb}-`ALUz<{H+{6NlQgXaC7e!4U!J!w*Th^`33P+hNAyl5H+Fm#eiN{Sd> zCVz<4XJxI4w1Be8-~35fqi)5GkquvctN%a{CEY!vry@tiMn5wk%h@GHKwMc0>Y+*` z2!58*mu79S_NQ9s#SfNQ$~1F{BIqbGm6We5MgCM?XFn|W!bsvUr3u5n=T0`* zgS0bDN+*)NY&&vZ?~Ucsrv%rMh$tW$hmv0P;hf8`LjH&vlgNlFXfa8^N^7w>Ax$^_ z_-zNaRlflFCw@cFv7WJfQsf7BfLs^GpJvEZ`SXi+XvT@=Kq5l>Es4jWsIvis`;weM zEAiGt+x?j34(tBo6rx69`#f>8@kG_vpHOuR^tE%m@B_sMn$B4t84gda6%?nGJ&!H2!xq<=i<{JA_x@L=(_6@in zefFq!OvDn5-vB>@uy_d$YQCTR7?$lDShd_u0o(66TH4?TD={)x*SwSaNm=UI_y=H@ z`t-rdM1$Q;U$>t8>SrtRtuFMu0{Unw)GKF_G0EG<%*Q0_+RbR0epa>L z;=uaEzBrD*9rF=Juhbrjh)7CR)MRPfW=NGhI?dk~i;_Ilgw(RcP*2mN)AwaJ{0KK? z)qcdoy+$=n9|@|nX`)X!h$WKgxJDznDIUGm99R9)7#kq!i<6)W({HmQYf5&6iH788 zDW1;ct^d9%RiAu(1`uu?ZVdI1IaQhpE7L*@L-aO}zmYx;KI9z34Q(BbR)`@QS%?$bQY%n1hsJhvZ32 zi8`bolTj6{As)MP_I%_~Iwcli6y0gvh>PwwCQ0#K$UA$*>=n^>W^u`Y9ion<5yjf> zIBOjydOKtMDD=VB4C;pvDX0UB8%djNrlAo83w=@&GmRZ8`1Vep>aC_43*qJ0SE0Qr zkDv9%iCLcYMnUvjxjxvVVsY$A>($15FYq!Sz{};Pd|BVd9)rh1uk`ZBU?W4*s+@?1 zD3Bss-r*;Mq1cn5c}EBdj9~Or3%g3hvRM zpPAv1Zk<2NIK_B0?4#m0Xl(CJ>Q&xr$hYnR!CBeu)MGDqD^9bi{DKA_z*AFvn{-5N{E|sJ`HB3Yvo~zTWRtm!vKNHPbM@_{VV@a zA9hwb(}^e3is)VliNCO-DcnCf#!;*SJ5cBq5>DN{!K5PWq($?nW{|=w->TmY@bHE7 z_M8my($i$R-iyRr#|eMl9`s`kE!4JzJ%T-=(s3A{*M9qi`GO3)7Y@E&gAn6KL}AWRw9#S{bq!&^=U&|m8=$8xI49*EPz4_tB)l3ib`q_ zNw#4-GXv5dA|V!xj0JPI=6UlPI3{|6cSv)Iq8QdVmA_@h+COFN(xAPG*;p(gVB@=_ zy|wcE9V?cxYEcx^sHLm<0$=2pj_ViJ@Q8xO{2RjhrWIo2o(+FX4ts1ZmwxI@8ASud zx}vo#v2?MM&Zy!FbU{m6&?@MzL&T?2D<^s!*kEOAgq)P$5FLG)^+2c)D)h%d{}EL%^B!p4D8khI5`qAr#m*HYqr=#~Xz z@X}wj;28PyxTw=N$dT(;R1ULAn~_c@0)f~#{2d`CF{*n-9T;!yoZ|V zL#g_(ELsJBy!pTwJ4!4#51ZT8QTpP&-Zympebb`9>uQT@X1mm(9~F>96Ps3%cP!G!LIxDIeD*IO%4QDrVEI$j;=o1s z?aolUx{E`_ZQX!Q1xmpxxya+5$|8gM^Ry3b$(0RTsKY`kI@)WTzOJ5D||KX`Kc%_{=OLk#RjtVYPK+3gmkhQ3jcYW{YTbp;w-xz z`?WVaYm{uhK}z4dY-8SYZ)7suJc&1;(m>ryXB&sQ^`Hx!5zt|?N)OZf}CE=o`DO*p~<;@}uddSJ&sUmOl0aPHGwS$v8F`8EZ{; zCbQ+`k{~nmFvgDcFIq1+_(B=P!$7S(tqis~`?5Ti!&7kvk8@G~ibX?#<^~xi#jP`-* zg11Or7^oZw5m9rUjnJQfpbB)$4NIj1IUdUCE!Q6npLiak6HalfA3Y{B-b??MasC6E zX98@var9g68|Rk#yE(Z^|+eV_^U|HQ1B!Ku%S4o<71Hd%?G)1 zQTeT^{2=XVu+Hha=J{@yAcV;D8%i=!(G}`ecN4Xhr6uZQOkyx)q|kS&7`8#Imw@YU zDENbYg7Ond$|8l5Z(}yT5R2!WGvlgN1RxwLNOQp(Lb8;X5z@sE_Pj7SB{h{>i0NPy z;I?JM%LkQZ_j_BOhTsFwb%L8>FqGu9lnSreP_+VvA{9~21&P18$cMf=U{E%3jipSz zV@Whr^T8nCrXxeO^laYY6eyEj_Ny4-{b20pVRos*q-#R~X{@3+j(3lOvJGUxbVP#{ z9^0}Fj$jEC?(tQsQ*uEi8@EaNB>d97j?n>P9l}PBVbXgOf)^;hC|u{T+mXbW^TtpI z_w|H8*{^2IiI5s1Uz?5{ClHREc2A?8Gu{>n)vJM*>=o>&1t_5F4@oVHHo?viAxEKu z$1mw0Pf@gn2kNz&gKq=ek>e0fpm+Ib?N@l@>KrJOBc#MUkdp~M=}~`al39Igk1YH# zk5{(}#SEiHIa0Ii$a5biUoZKx9I#{(bsg&c#oqS50%xK3ahG3Gji>h#=QNoX0qSX= z8#+)-FDVr2MdTC`o{+NXWrjc&95Wr8ucJ;nJ7d%?6$F{QoSq1h$&SN55HisIEXIBiS9fGATvZb_Lf{?&vhgt;cs;zZJc$t;(x_dePS>t5uemIOu8@*> zk>J!_5s`8uHE?_J`X_DkMYR|!g2r-F{#G$YV?TbP9Lhb~%dc<9bmNH&TB>5g3SK;7 z__?tv!`Q*bG!g#sm{{ZybG&K}#3Y|T-Ofu{9M%J-GIbGW-{7CDNAZp=4o6|()(d9l zh*l7+VW`8wKf)G@PA_ISa62oxtSPBw4;vvAtACU6Zb4Y~xV=FigNl1#R}{L23C%3v zGh%g|rtksX1q!o6+IxZ?@4;Um>C`_A&xm{nbHXQ!B>Ta;3)dR{yiwy`2yQsxB;cy2 zeAdo4fmvW7G+WN9hws!KuGdsoLPGr$efzx0P;14gbT<_5?2*>u?6el3H&>a|%FqmD z2g|(py_=gc+jFI&rK&kG;!x_6K7;(D&=sOZGh*>VC@eR{JwqB;`$&aLji#Y+-AjC! z>v1C&?*%5Sqi zaGA0<@%E~MH~KxO2V;p1l%%QY(+E#2R6U~F$BTL@5;uCo+|Zd>(kRaw-ZfM&=lXfI zr?R+2q=J}w`XpW?O6?7wDSDe?=WHrH!Pe&4@M@{-B#C3-$eodIyCf%Rt(HotBbRkc z5e@AWGg8Y83$f>fgvz@4y5be94Z~p-?T&I>D_p5_=HF_`E4$35VG6K+`^&(3idIxJ zxW=A21N&@f9AIfxBAd|p6c*B|>h=wb7p^##;x=LMgVj&$1Qy-XOa+s19SwA1!5_Gp z2HE7|s~8}Y7tTQq!f**{k}KFrsJCvm-S%adhsH#y?7bOSKLroAaJ=aTHO7=8X)xC) z^{~i4=BQY5Tke0~JP^N%OWS z#@Gp!SIw0k=e9N1*9~+~T4+^3dQ9lK6!9>I*PrUvp?asE)4u4O{8(w58`ERhl`qnF zt#wwuRO`44*?R;SggirHMRVvfL*Cf^-hUW@zn~Ra+&Qmw|47-W8J7L63 z$Em+rtvUjL`@cL#fDPcO1yEaOmO*;i#HK;N&HrCc9dOqQ_|ttij?Z+b^8X$DN`?}M zPR{u5e_$vuP)fb>e~~ASDL3bpAlWJ#4HC>tJ}A!UH%%wcAX3{f(zl=khefI@92Hxqacq-G<6T?xvKdNn{3kd$aJ(36M(`D`S#Jd1j7sT$gP>NBPfxI%%-3^52R8#al0N zUHe8;SGQSJewr5ys0lDt)H2 z7y^rM7*)A=4GT9OdV}UFthN3imtTfqA#WQr*8XN>Z+R-GoPP>^2w`PgJ?5w@Kqlnn z#P>Px`8;$4m&}vn%^=@(ABj6K={otY(M~CquBjW!5mg>*Yo(*6`zQrW`+84~9WpLm z&L1ZLIV|tN(yknRtVurz(6;-ypjInplytDIv}C&-vt88=ukxwdyiI2<#;3_{vFuULee#x|H&x(?kLg6f~yvwQk^S%JZ!fs~Hq$>6; zgZigK(i5v0sx%IaDvr6%Md51P zpLe$ZSRdV3kCvb4eu1Z-JWe1+yw~+u1FD6O0fVv^x-r;)E0lCSIqeb}+lrG{0mhMM zi`i*sxF%F>;1AZn{pzO`WsDT!cmi;{hTUJ^7|xa|U4R0b3)OJ^kAR^prdrGZ*?H5% zb9sxB{ThXJ`Xss&g*nwV*XyOyP_rSlGzs3eq&2GN+(@L?>i6~`vlakPPK#~o(KT@I zta*O^Q#`!dQ2`uio3*Q8%tjd}CD&Z&Z8ge{AknzLi ziqticq8%bQbCC&DHx&MgOlK-l0|n2mcvkmyKJ~i~MHgn8KS6%5e3a!s&&pF)lV)0t zZMW|tweCBU+g~n*9Gfdm^bWI>AL}4?k;9)MPpf1s9W_`#c||>$3I+=@HI9i=-L0$4 z#)bbA8dW9g9r}&+{jvx6OYE^Ytm9k2gfp-^@0&)O)Zo~uq7JpA|Vt=f=0gRr~gD8jMkUduZ zV8>rC@_V!2X;4=uOH`R^os(FV?OPui~PU~iI1eywn~v)szp?-JGEyk8T4t`5;JtMKa}k36%C# zi7G1BOc&DO)7isB{uT4Pb4fn}GhV#Yqqbdt?p~yO?f&V~LF;U7vztK|ir@p-kT8FS z?|fROj~ln132^S)>Wzs$GHgeNI^YI4n?5sAU5~20nHSa~=*+9s+dU0b3Fv|AAuPnMby(BS1o0G`b^vv}e`SsuAbR7#a1m zWz(>+${P4kr(ybzV(G-DNBx9+y{O7p;xuP2cb+?P2};mT-l1#Sl&(#|m$h4bi6M{m zy%rT<$Fg?EXOxgJ-CLmlgeavJ{$`EmJg0iWKZ$?IAGC%Y$TH|HqWL+Xl%V-?LWzW) zq421j(D9|c*YpvIz-sOOk9!m2_e=i6ALRvgf*CCixxNKP=9cQ0GD{UqxftPlpCQ4O z^!C{O=xB{U+b;(n1CJtDT=Goz6B<~AvcF;&Q8Bgb1bl!p7A6kGafEpnqOoV9y%fuhX#A! zCALjZwT>fT@f~nwmx@M$Ngt;uFcyBC(^w#}FRN0Vq^cifsFSR~qb!@Kjq^$lISK>R zBWKs?!`vo~)ycBaEN4H)MGxC}6;!j=46|K(&)*_PpA5?!#@V4MvE_G!6anSFS?L0b z;EwU`vq9sT#}G;8W!Z!yZ}mP``Pi&Ep2W)xSX2!!E#rLP0FE3`-?7)#CjFXjS0 zt@YGUo(1y}P_W{@_=w@e?1=veK7S7fl3A2fTP~u7jf7<&1e} zRH1w{Uzya#+U%HLcxObdC5$eb=VCmi|0T883>O(-7vPj&mcmUaOryD#aTkq?cZ7)* zBNa(=lqoYM&cEjU68?2A9?xjvSse22VF%AHT-rgxGd45o$^oBZxc9)Kp9`4;KPq03 zzgW0#OG-?i>K9;E@}5_W>WRujN{D1P<6JQX4I?%s#IZOr#htsGp{~Gj8d@vF|3F0v3LLt>ch{2n|?yn2Z zA42+#T*HEE{K=@(_g6j5Ef(87Rf9bUKb_>s=Bw@{RIt=Wf-_<+YU3*kTV12{`V?mM z1m|BA-oJI|N$+9Q6E0d}NlM{>G?qs5#k*oyED;2^rhk|1Pg3cuJ3RDA;n_*nb%W(8 z)k_M?#+;8v{mN_??|KLWM?%P#6XxD`@vG^;2R)jQ7W_LP-p)Hksr^&36x~s~aWGq* z!{;pzVZd6$vVs!C)|phm`>BEUF(hYXg~qaK>f^oqesy_F#MxY}0A)hE?onG`N zGZtXd@g{`B^ERyn@se4dtt2)&X+AN~uVbOUQM+ov;?}msGrzsn|I>tVfofrsEoMtp z?3vGcLQ-xF%sw;*Q$V92IS^=~vHb zmChJ6R{xO&C`F?_t;1p?h{Bv;1}eP|_{&)Ui~@CGOjcJ|B-Z^UbOJ8OGL5twFz9Wt1SA{~x_ zM}O>5w(ewF(z(nDQLd6TX>`27--Xznb?Ibmdad!CQ0Gq$B!+lj+z-4S6s9}C2IS2& zov2b(Yo4u4d+Cb*6&-Ym(I{t-WRn&XJ~cmWz95EY?s@)q+|;5f7Ks&>KF!`Rr#Inc z(7p57=nimv)I?1?t)t3h3V2JjgZ)`pMrOo#E3_zD?KkeG>yc$ectZKkh4L~Lty!fJ zZ+{14J4$8#ac=SKJJXwvd~x(1?Sy27xi^#}Z&TIDTNpk4oF0#ae<>-C5YSum!Z8SU zlDsJtrI{IIb)0Mpu}(brZaecc*WvkEsi#2-E0kIXRw_|^n3=G^DHwT_Rry#mipSX} zWwgz;n_VbK^Wq##Oay)eHS@;JQ}2Pkj8juVg8dA>u-D2X7b7yN;+{sU2!eNGScJlp zVkqmpFEObd8uAKThFG3 zD1XMp1SsR_d+!s>(RU=EV7$zQzbFW+O_HF?C=izf9fk}VzSc1JD26asp!Mok{k@_2 z0UKJQuZ&v8=1YS<4(*shH-8e_%1Ks({(~Wy`5wn>-PbV?kJZhz!&$~X3ta5!X-m|V zEXMld{ME0~5y$u3M=36$f<4wr1c$b+BRUKVjQfw8aEuMm?MCT-2=S(4pk+|;VV$r5 z+G;k*sFC?T$M)_Bg=C@z4*7+nc6pz+d?$$Q0%{Y))txFL^>F0eJ%bDZg!qzr5yKOD z+FN8&OAY8n_K^Is;+f-;r0-;(cSn(`${bK(pusqi!ziEUsorLNqv!`*z4A=Zj{N4* znWtJUnWusqf5I~TBnlMNcpKU7T9L{xYW;&(-Ge>6e1c44L(Y*@x3@^#sxGi-H`;|W zj`yc!p5nHtG@P0#G$VQ>%YB6?V8}J`d9N6S-Av7#jKU)kMib!C#Wu(`hfH8ScgW;E zeKnXHB8}e7y^!&abjWWo-IU+YN#*EG_fSZNKkri>vvJ-?$Dmu33duA^QD)i|=`5I2 zUM@<)_=IcN&9&)bV3!}}AxkWl{PsrEQ&Q_lYF}7RW$f$Ajz^Bk!*0_BF|!}Elk^?o zGQ!;uy!bCn5L0K6`QAs#_4)TSX=WEMPNspEskFf$mYZ;9cc~-BNGj7zZ)N#O!1hoK z9B%m8bP%G!V6R3=@4pwqEXHe}QS(Ude>wXFEU-ID_b_s#uJj8>*msk+*A11dRz(cD zfTKo`N|WKQw{OmpAlI+Q*|1sibC2D*A6W;vF{Dx@>xzC?)1{D4|4^P17vovNw#>&6 zmxm!zMM$k^z<3@20%r=Tkpw^Ti$)o z3Fl#K(+Kyk<+oJ+6Ym)D)sqoU!H(xcFH^RsLx(LG$jNEm1Fz=#XhETq&5j2K zjKSb8OLde&e%T}3jym7|C_<0!@-+faN%;377s+Tdzv_d*@&%^59nRYG{>L<|fU|x zUft4ZRqVkah;`D(g@;rdROQH**%n<y!k{1ed6S^#adnWgGHY(nA215?Mqpt2SHymLR4=##(SveU8c`@xOa`_cVDdw4y4kR;5~zYU*YDd9Fa}NSHamY6=}ZX={(iJZTo93(T^t_ zc3C>C4tdY-We*o;PR+6~0!){#PON#<%3g=Qd2N$ty5mp6ZTV7ywy)J;sRDw+1ckM< zGCjYWBE=%RX;LfQ#K#Z8T{>1+;BEyB)c~Wg!K%ey#f@v6u^Mcz1gA>FKPTcXZ2pR6 z?MR{$*5|#^X!5a$PG#!2t$Kr1eKgej^<6}qpdp4==K*ukoBb6NqZXtJBYZrIk1n1& zp*(!Apt@w%NV`%;@l)Q5r$(#~&bpvCh8M6Hg9wHc#K+C>@~G*bnvZyqHEJxv#qgNO zGo7bFr`$H_?Pe7tdgah?QW7%*o&^)ryc{@IOBYYux0HHhGC(u%d$sQv_O9=j`(Xu7wE1Dt{a6x=3DA-RHz5Gy=!HI&_ zilr{CtGR#K8K2=SVC}h)l=fKyZ`K&b$d%-3^2!zRfx>9Iq0Si;|p9{YW z`>GWWH_`Dkk?PHVKLc(C_~7srHhp3#_^l2!U&lXp_<#IHo&k!aNq1o++OP~1^qGeF z`=#=~k8Vj3_kpljK8td4TzKRtTq^(h&!_@Kf&>$QdBXgJr^>o_Ck~bctG98mahhYI zYowX00r6KI2ax_~%ALm)U>3c^S*{RYW}U1iyVxKdKAAqO0>Gp>o=_B3)!H#hq4%*{56`7w9 zZWhgR>F36Ak_7MVcHNf@j`|J73sQIDwbNMLnjDnVrV!Li~7gPV4u)_M-l}H zjIkjA1Ed>oT{>Z#rZ{O=I`s>5Do`krM+PEtyb3Zxg~a|mTHx6W^1_f0lA+T_n4hj%!#}Rm$MMq`k*bAZ zpks_)O^2=!xDAzyw{Z`OLU?AzG|^=Fb*}RJy##dlb@>lSxBc+;)!L>EA-Kh@|C{7_R0=EbiF<^9$wuiIE-h0cH{mkAs4cft%PEF(K>L$S7Uqo zGD2BMZ!T0_J*6i>cQD3=c9Y|0COj#S^4VXFZb(8}#(dfLc9k+t;00tLr>yjMh`gad zL55WSn^pLsTgTq8@W&c=802N6(7IKO7&F^s%f<+zEg zoo*95OCPOqo|L~gIiQ?>z_{#DB6R3O+KID2b4E?jqJ%213rA7zI-vX9I0H* zV+nnc1X!14a9+MK_kjhRf5B5=70Ks#W)yJJm5>ASvPwG5I#K?KOb4ElpwG4ce#Zic zq0v@FON4Grm#+XR&@fpX?^bg*F>(|ZeIhW7TS{cIVwYhGa2=o=O{K3NA~5aMd9~X+ z6a-Y)^lAMsP1XzYxHtmUi)Msz8X-?!B8d3va5|EL7*cYW6L(VFq`F z>hjEEt*U-(J(0ig2?En>w3nO0bBbczfsU@HQ>4k7Fc8&8u-O&Ij!4Ly%@d{c_4!uU zrxwRleT)y?mcKKJ1|R}*44aoh`6jV1HDGZb4|3O>{`Utnm_7Uu50IHMQD zQ;zcr0pgvy)D?IJkRZ3qZ-dp@_DNhjR}fci?v-8!zi(?6Fi8rs;ykIzKo>_Ou=b67 z2V%zRNq^Awa7e3G)Tg%}h>rcuuTPd2*8I7dNJmoC^(>EqI0y)I;xb^XQ6cs- zfSC~9O3V6ep~bG%<<|N9q>!2L29JlO=?E5j(+eO3Zn#c3+RynAYc84(=-e6|UW26U zI_Y~LGk45L*kN+6ZA4@IF++URLwvJ6 zK#1x$YBR#QbFcI3{FYr7=YLi$(icEw#So015|rg(x5`JnDw&co8wZ~TO7P4znC_gm z0n15_QHf9DKC3I2{tG-dL&GQ)Nr14zQ*imGU$qAC^SJ0K5)RDTEvCgjjZd`qzYpVz zEK^FHX8OPRAWoQ1da9M^348$;B_|x=>X@f#3e=gn7^~r^b2w}W_Iyr$e*-Xav>o2J zH_62AQ5-MpF*i$*SkzWWsT|E+9Iy05jQ1Kp(kdDBbsj@1zflFs*8sW=;T(ZiPjB0^ z2JfL0q{p`DJ6V>!C`Vop+m}+s9`;g4At&W7KVN5s^#(+{9Ioo0I0JQ$&$sKN09;&b zIaA{Fe7zvUmQ7^}POe3~1s1CDG>Ys`J0aINBZ%(rH&vPlc--zF<_`RDoGj)O_{);~ zfT%Hg`8Ojl*_2}hQ(eap@4WM^Q>`LPOOb55R{`(rF^OiaX9!cy)JRT;x5;(W90Yu< zZesB*AMz_BZp3+=kFx9OF-k7++e54Ot=*#@z+`FfCdx1{PdW)?Fc}6qoPj1KIjd4= zzSP0Jmxh`o1Jf`>Q*lOrh#`-`{amMJu-_T_;SIy|EMhRE(PnXa&72#o7!Uez+9?Xo zW$~OTaG(byVD7(w!Sa;3{x2)mR&2sm6Z1LR>~&#$tSr&{nP1qE&ZyGXS}HYA)^r$%X_G1a)%I z`;gK9XpUc&Z;_in(V$fKcW8{(XVYW3F_m2Awms4BQ^$n^tLCQqRzR5{M?$*LhJw{g ze(20d|8TLfb>{hD%c{xYTcz685<`wfOG6P?Nj^Z6QvawCD^`P%Vfcx~sYr4tHbY{* z6jcVvXb>qx!G6hlz7+t+{8+C=W&5mOnE`?A_gN-NM4+gAbXFcnDsr;gef|JVhuC5e z&c<06kW8y2KgfV@jhBTVL>gU0UaV1EYIooIMj})nunqZUEAj|9W;Ar1gWim1;|ZvD zNEg$>`m_BAf`z-?xP1Q&RtdebZ7)cL@QPwv3yei;ym&O8w_KTh}F_@(i#@OZz038E-4v zvX^NMkWYj)sX`b;6;2KVMnS68db+0_w|Yk(yIK1$V>hS;TUTHRaHxWGhzQJ|l)999 zKMJVV?Y!(q$>VTy{|~Vh>5_4^GEEz^!LqIU{gx>aB?hhqAjgh1l2zon1~i!2zm;0- zJ|hjdl2X>WJS%(Sz$K#72@P}5cGh;u(U`Np&s4pz#{d~S-fm6OzS=xX{3tPN_VJkM zH*S&b)9SQQrXjRjIgc)*L^4{!QgbLDrM>V{&2b2{#JItj2}hlQndBvlq44r4W95nn zl?=it)1awmj@!aRb~M(tupo3_ajY}!d%p~dl~|kFQ929vQsW{$Q7Vo@qeHnsGsmRT zYX>2Q3oBv-dvBzYWy9~nI3nugELiOH!ZSAD3p|~RaJ@cV+71Cq2};kA?Dt%16(3ny zc*HpOfs{7qgl`+1h}|XH1lY+@&@Eh`o$5+UMUO#UfQeV}x;dsk zP3N|^;Mp=2TVxUv?YAkE_O0E!R;J}675-+L?iJb!>_96eXr2OFDm7H%bHfq*>!{1M zQ?>l~NkR$Q%y8o*!+2Udf=ZiXdet5@1t%;O~7to0@ zKNnt$4wHcWHYn;RmGKjPObr&j)-RX)Yod9${-eQ^BxhshNYyTMKDgZfKflp zBVC~Z>KZ{rDvza-3)E0B*T5vT^~CkqXTLc92MZn|B&=%1z+!w3`e3Jghkg|_@~6v; zqzBv|J}cVfxK$d+wY^N@7#y;6!L^`dH})VD$EiMBRQP$Gv-WLTwOoGRBIy(ww__^T zmb=HHY-5@q-a zya!PREGTWCmp}5#OcD~&dR?IaCN^KU4n?X-jrs2+SvePIb=%x9-xIP{5n~y)e+Yck zsbBaua+14d_d(iztxC4d^r{}N*87HGUnbb@D8RF$O@3?2OBwufqW0tNd=|vT*<)ZSnN9Ab_``ca21CNqs z=03TxyEn#Ctf6eif;E=}o`axeI>nsxNW<98^xB)sqi> zWm}20y?75D&K{?DP(z-tbAFUsSPLBnW^~{|9RRJfbz3U(LJ)+gH~KvW;3NA3R0{qst(QhW_WjCO*>W6?Bi(R%|(*il~osOoX_acPn{vJpH; z9rRli7zClS(UU{$p-f%2e^BAgGjNZ=2={2PpAxxlT`%b0(msfz6TDxr-M`syo~hWm zT1HEctD^n8H15t9A)Z3)jM+>4D1w1A5W9W%moYuD4HFo3G+IIs$DhS?7Bf zpD}o-6Xw2vV)ERC-)%EuiRw)H#p?=jR6hHp`Xo;;y(DaeHN`EN7R=rE&m2TEk7!1( z_ze=zd4YnSoQ|9!mW%9l8+Nau`7+pd)>|mUAjgcPt}U}Pn=8I;>TMN|_5*!Sv3`Uh zX6cIb3+N2*F_6VfU(WpyRx;7N9OgMJLDS&Tpe#F0Ne|g4pEmn6-Mp}zo(;Cl-vc+& z`K)pnS_saBqPDr&!+)Sv!amsVeZ)jJ}%{ZGd>sMCiGvjnd9mrsu zorfCpIa!=z8y$xOol`zi96Qj7CcC2j5dSUQJSre*@shgsmiSd&P9-QVT{#K_q{>jZoshW6&yMn@$ANf!4OucZU0DK zUzULrmGnitaoo6Si7ehMTh>h&8uA$40{C#Qe0kUs`kIO=P=+9AMTA;V)Q@cCbH@@i zk75LJfaD5UHZb&zhU>Px`^x3cAjTjX$x_Gon~TdV3M^9b^TiMmGH-)o^?s_g9b=Be zPVlCiagot@oOfuN5LF1uBs2!wps430AlTi8neOTM@_!&F_a(#wg*BNHJtP|#3rwvYw7>s)qh~OuV~?df|x}xUtQeS z(23c2r%sWAQ8W{3acDl|{IwmF?jJd9LUS)6*s@P#&6o5~;(&n!6|pJogB>gLQ<+c_ zs=TQx@n!w0Imu$L2w(OuseX`$yc(`8S2_q1j4mIq|JHmmhCPT^{uZ#8dHAF=v8^JO z@=WCaL;`S=jsAd<9M`&r>W9JjaK&+BK+3Th9wboaBye|DtC#=p_2{4+mEwPgoUL`V zWq$~A=ng=$^f!1AvGnaZB*PC43lZUHbPO zK6O{<8C_<6`Rz}GDR`q<+Iz!>14vOVPhC%tC;$VYoNlyUNRJyodDG^0lKArcI11F6 zxyu!hV~>^bKv&M4MOZQuz}RYN^k=>Z;1(y3s=ViO+qj@dE0ao>03d{%`x*Eq5;V6# z$DxP2|3aq$lqfSeMdmr4;)I?%s9jOQJz5PG39^}NQn>6kznkMyBp+Ab$ZGt`6MCD$ z=iRidO66CL>zx8@vx!$(^C~|Oe|&=NKrcG5ma|=KF0xHH5@^>0^M6rG%#}@DVw_Et z>QbnC9DHV|=6fCJo^K zRDjxOjk80a@?2h{VVOoKK3wQI#81DLST!j9^ z-$}?&k^(Ufm#h}52&~dp2`vR8(rZv?RY4O{uXez(bP8XZewx;v|?#olUHxL zGF~rJ#&=PVIq1p8_q@%Mc#6fYu^@*E)!^?0{3&8bSR;>Jo%YDo89K_J^!Strt#0Bg zezglv-#%y~`LCb1eVJ}$J0vx(#-Sx*c=%a&o)hQow-rI@#j&_MaD*-MOm-wQxyN^M z&nb}&aV=UO^K4Q1ucdchj6B^aHn}sUQnJkvr>d7dYSE$j!o#+@0XMFsM zE4rRn0H68~n`20Z0@zC;rs#EeEp`E>Ps*K-r#$Nqe=NP4k9P4H+s^}MD>PZ{Jon7S zIXK!i{f=(F$+p-<(sdY@RjhWZmC%hIzdQ4H^FLTBEzfdvDQVoQ^t6|{v>G?<{eoU( zo88oujz9TejopRiTx*?DPVu^O8|Slj;e22H=&#&i&K})d((gTfXWl zdAt$R0#%%vsvQXTaF;)5Iz6EdjsBp|0P7nG7yN;B98MJj>wZ-#akLcOP_XBY_{-kN zNRuJmju|-hIAX*2OFnv?R2GCn1v^ae(?3c|CtzeWTBTMaQ02f#1E|k>G7o>7h&-NO zCf}{Gx!KM9GW1@T!0VHW)odwAv^Q)r@R~JiAWWQuqeu+$&ifOHvZ>qG0OlegFYxRF zSUh&xk{fefw^p$*z)_GS>i}*^)3wGD)?&R67?mmC0kT86T#eonaM`k%=Ury2aCQ2RI)nFeMPMf#HW?41#cV9 z(}Yv5_S)D#<4flupMCBe0?d`H-&+0W1;Ez(EF5qT?A10-fr_dCKB@)&W6!>iXMQiV<3W zo2S0E%p=nZUa=^Q#(Q$WC1BUxC`1-su6R1-*o{tU3Jwltp_iza%k3oMSa}CxuWai7 z7LCICwChFsea!XlXj7EDYyKB^=`aU^nV#NN%S}LDRwifB<>Wa4-Qyl7^j@f){_oGO-`?5>V^` z1=o)g%@XqtpL@u~1pnlvGh8H((%kHGmG%seT-t21Y8(8TT?V3t5z5L^d!i1x%2baj zQZad!atI5mG>!}!qoZjIdlxjUza1SVNJk6}a-lcmo4anVi=nhxlp2j$%lZbb?Hh#SeBaSwY0<$4;1t>u&Kivs2FHkg zu`^))OdVWh{g$I|%Wf>>w1eo}8IAFjLE!nzzv-?66-qdDq%IY4xq5X|6*mIbcaC)A zh6NTBOM!c|5d?Pw@N{W4483^0AJObcv z7P{PQe>RFGhI-4f0@p&Rz;~T&pX(m*I9SUDaU^gKfOE)r-1}yKd`GhO2KuW*;9STA zav%v%AzpA&eOhBHS_L^4ep!Q=MUjOB0#TZEVFVr9Ef-&_4-LLM??Vp4Wd6-gCrQPU zrt6k*;!yCyHA3%>;QJ80k4W z(){Z}NFxF!^nAtru}~Ma=&xJm!Bc3wqoC^@L0C9#00-C!oXXe^rV&tItU~@)uWxwH z((`~}5kM;V7SUxe$1_9_esiaD<}}HP0t~xvbMEvO?`ObORve7G?Ygb?kWZsPXNM}? z7bX7AJ!2A$kY*bY2o>QCMfZ^r*hf|*6rpxrd8HEvPvYa-A(~B^P!OS48``D-W1ON? zZK)Fb3ko-)#@?dy)_B;i2E8KhH!Ak%7_Q@*=gq}hJCrYRU4YE;uEOTD{S=!P{T6|P zZ#tHMXa3Y^WxhB~&#IEH{cZTuUNB36Vm^7EhVpeSl^gd?4ngGOt(EwDU8blVb#nq-c@L>IU7)v>$KDvq>zF2nMP!Qt;Vwk z((kNo*J3Obn5!Oelgh}k(sBR0?mu!rC|fYlGWi&Ozq1f-x<~IF($h7wUC0IPL}Yp{ zg^?dwg~(K}wcBp*JH%Vwl?tz$;{$f0pZXQ}(T=bbKr+$xbmspG-`FN-^Sv;%Rk6ut zF)zkvH;1E{8tt~7t9h3~Py%|#QUr}ScYhmn zSadM_S1kUwk|zr?K{>)+63`j1mi&9lzq#SRYjP?ZXtc}nZqdp=m74uT@)LJ;j>P|S zcAy1>J^-W(3E^NpT0^45y~|NJP>MwwLX z8WFIl+CZ(!Ly3?i!jcxQKE^BaC#TSCbt$XRYLq4f{O4K#lU>un=#OhA|HFAWH=vzP zjE0JWK@)QS)RXBafrks#jL5v_Ux5s6MjpX)-S>8pEr9hWYZ!cL$zr{^`KSoTf9S&B zom_u^fCLGm!6B*Ttw^3MH5T&w9L|=p+t!#8r;&d@ zXW}jx^4*09PGmbbjx~=$Ae+{X6?pOJQn@{xlVP`AGNG6A<8k?~kLbTw^M)c`0U9DF z1mwpV2A^92E@@{t|NBaL|BJ4#4v1>)y1ybwhk$fS3sTapNOyO4cbBBpP}1Fq zbazXafOHJqFv*Zcj;FmvXd+536+v)0;c!!rebjgF4iEzs=nZeY@F zR-CKQq0?!!tr;CnVQIR?h9lYe3{V5kKh#7z4AFIO1YE& zl$dBW_R=!s_lLPI0t7V_H;uwQuVA65u#hC&`GEKDrTo7?+O<4|69Bhd`zs6n1;YII zHR)UmY)K~NLdmyGDalg5#cUh|-dXq$25?k4o$5bOC=ujs^e1X@XJP+M%)c{2gpxWJ zMVYeQO560Gd2|*gV%&`6{*#YpPqV=~DeI}#U<~Qc5q0R_<>=oamk9FLBoSR)YTDov z04XXgE|vyPK=&v^oslmXmm-XT_AW*s&JqF8<+f<*mlFAJZ0*l`6M0J!m%1FGRfC~c zrY^TTo?$^3C8wYOYhz9#?VWUx+MZ-_1mQe8;hAc7&yS+YH{r^WCrZ6 z_xnw4;|CUcW_|`dk)PM4@i^t*-5gb&0prS~0m;qw9dr`LZ~kz1U28gwz1V1%@qd2A z9ny4nzb~Lf^}zeKj*crY6`iBS(>? zkN6mb`^sVU@BQZQRQP~RFA0zRhMgTn*{h!EU&2eALTT`hg!R(f_@6P1LJ;`%F7;{Y z2faGHf7wkE0cmTH@zh_0M0C+VA)00goMp%aGvFVl(l3 zUc7rxQG=DF7WH?{_uqUPQi1zIvBRK0uKSOV4D5XmkRQFSP4(sf{Jd?3z^{(akI&A> z(p!FukK7N>B}af^^^#s|P}ce9P?pl)LjeUl@T;fnSgUbA%EABIjw&&C!X>?A;g23h zl3yRCh->_wHVAd*y3+y9O!Ozf5FOL@k+kUHPfwGAO zeiOU!NfBK_CLn`K0Q4s>3V-?N>zrLf_eR^jpr*ipg~iK*XTRmFh(8u<4Ysr0dlVgc zCRr-+=xX(1VA0p)7AO}BOwI?Oz+u3Lp5j;XWaOOwOL6cbP$1U$V-O%Xq0{kak^@?S zQhsg?j+W&3zbeUej)b%V($aOEIfUP?+#N^}7Xr71-jg*SDk&vZQ?gKJ5eqIYmd~yT6Vdkg?IukNND3}|TzLnGW|WZCEhib4j> zQ1uFJnqOyY2LnL2Z3Z;(O#@gcm&TTEFWDa|_7x}`+ zY_Yb(Yi?h+!L922(L4n(MI^O7TOAQ(H@1HeVk{6pNzU;f@kxc;x9W7>%>)ENf*c>2 zTTC;e2|sm>q4v7j{6*+eK#-`zq73m*07FbSNT-Uj3bzz=SEr7&(R^IrYSy|8nD#|m zs^bxb%hEY4`znK5dRoN*l0avEobV!}KqKy9tyV*xq*d(n+V`X2Ws#QEVr5>n{r>C` zB>=sbli&x;l?$F-?;SWp$HiPSCEoHo9?kig_il2OYP;1@Q`;tz@}Ctmx+9!`BaSsD?UAx}4d#>squ$h(EE|UV%bBhuw2dFQ;>b0X%>v?+P z8;Sm-vtbNEn2z_@pwPv4WmdC;3%_=UOwK58iOu9=RR}> zgAdmGnwN^+2$x~-InmN78*4SM!eg?Zw6HhR5?t@X9>t2y}m0R3NyHAzILS~Sxzes zZDyxkURC?spB;B|YcvuQ)?P>U`(8~5-fn*?I;eH`@jZ%X9BP=y8& zhqM9KIyJNNpLk(lw9@lq^R0vwFS$3#koY;OreP`raq!c2Cljrft(b=aqMB|^DlW-7 z^PJ($e$JCsa7Wu`fjdWqHWp5|(0aSuWWrIE*;ByAs?H9WhUWNfQ+MiNS?jnzGMdch4~2?R1tX?ZyEXdVZd)b{ zCekV|4j(6JOc$z@$kJ4}pS-?YLPTG3Xh3TcCV{2sOMgOTUZB+vb?6OowOa8kJ6<7d zaTxLh!s2I&!Gi2=$5!CkLqYb}!l`(^ixy9*89 z$|#x4TBcQfolo89chHN7BkMbBca~sB zv#ae-uD2NLdfh&S3gnL#U9Jc4R;zrA?UO-_l7QqVgz?kqpL&qM;0zJnF*xAhsS!=m zSS0f%1apD0>NvTu5Y(s??W2-gWc6@|W)r&9IL;ZNi?I>laO?T;uG0uGd6ADxEB5ee zusEJ_pP`xScih*tpqrVJ8BoicRopbsGk@S(wSaLnHbwcG`Js<^|~>-ae|z7=ccLtzjpN zpG*fxIap7P`cL>CUu!CA%vHX2m?!tz)*|FSt{p8;VQiW@&3Us&J@c8s zAu%Tnkg{8(a!~C=GKv%I6y^i^g~}&VCcKIX(GwhwhE8wvi=vfAq_xH96s__!V4b(_ z+WiMv9@4`$s=#DZTH!%1>0{P>EJ=CmzqBBTgaO%1=_$sCN8j$p;-fj?^`5(c1G?Jchn|nUv*Hsa2=2=Iheb zQqm^Z616xHS`f2jkn4Ec=j1T^N=oRKX?1ed`dhA^^Jautw9e7PT|vjG)Tim9ccJj5 zYK*OWl**j~1vpjAG?<)>=6Z%Rf+vrHyPeSEPGjBuuto;T*eosC>|6VkJ7Ye!I1De`NVrpMClg-$!vX^v_Px7kJl z1sXsD2ar!)?2zo(u?y~s1Zr-%l~a|6Y{8P|j%;=YWM{qLv7_y!7^bxQ7U=GgUU5O5 z%{;D3iS$S&ug{pzSA&Qj#UQuqfk#H`s*2C0&jReuRyaN>725QrhwUJHJ=|em@`0`J zJ3_N3IQbOid5glmz(wgEb_cnW0AU>gFxC$%CsN}9Kz#q%#{282fguR@Tqn`jbs%=~ z_{3BIPCZD!gWX4RlAlFOp8vHkUh~cr0VU5<5RQLUt99G=@-%u9NTEiRvAETOMN^wy zyg%W#oCEtAti#OoqZPcGuX!D$8rfW+d$-S5mg@yd#3v7Mhb(Ypr7Fv}lM-Dqa9eF;hPA8Q+9qsz7C3}hCQZ1J>z0U0fg(8=|DfjC+9WX0w z^Ie(aBD&E-nd*T-Ta#uD2k&P1#enohiNT>!UnO_s5m!MbQykd8%&h#`WBS040Cwx| zNdxJe3&m!(gvu?>mtjle+$9S&{K5~ZM&&|2J6Bs#ska#&f$yH)j&sXAv-=(g+ex95 z0e7a2WpFGSq?~p%r7GdV(gk1yG%V1&uE%VnkyKOM7E_ZNX;$^&)w%oFCrk7j+itl< z-WTtYHQXJp6`8y+-bn#`Nb`)6(`m~vyRBw=O)~YO1^TGYcjq)V zWyT&W#Wwpmlu%Lc`GHuQ&_i1=W1NmP)UsHegeC=Z` zfM23Jjr&AD@}OWOm?xs^7Oe1f?8Fz^E{Q=IJ|T`w5FEKp@|-U?Y_$fS_j zyr6EX^S?JZ**u(;SOO~-v5zJuB+%Yb(!Xxr<+WOH%+^W7k-w7jzVuztl+T9*UXYSh z|G0$=N~W>vjCoei`1vUAj-(hsbV_Ps*Nw%f`WZ2WnpGTO)=M12+NNB#3l<}%a`7#` z{V&A)&@+yG`kO<{G^`)4OsbUHnk(~z!rHTWywLa>yh9(q0_gc1NINOY}Qh%FI zsQ~fHT{f?V?p!oVntALEzQU2`MQISmhAlfNuz_~$+?k$(Ojq=Yxnr(U+l&_t$`zxD zNqB2b6=Bj{U!C-=Oqtl^2@1Z}5n~c^Gq1LC<`dk5A78M|t&Dj*h1B1D;imtHlx%G( znpp{IZ>FWA%T)XtGfZhs&pduj>E&i8lXBW19Y7t$GJ_RzW;!(7L?fJf!29QU2Vj9D zM_BbfBO7%Jp?%-QJeV>mL2qPrG(3Nfx4$SFdFlX_sdZ||GulilRe@=yl+Gw9u!GL` zg$@UaHHhKoNmksZquKWosTwuL57Wc~ZwmV!0%zJ}TeWvCx#=Y{a7s0uDg=*9at=q? z2AL+4#b&>h$+A2+b)Lv1fB)46C*R`CkAr>Z)qJAqk(vA5(Cw9)bz6%?+5uryNUrL5 zhK2&4E5TO6GPtl%a3ISmpkU@i;ATH*js{Yca_W&$dk;);-|gs2n5jQ_SS`mSsFo4B z`JCt!Fs$kru1~PfHT*oD$g<}Wc2GC-%Cyd+M0*E?ap=d!?cjbKOUJf?aL1_j6C|g* zHJ@7u%dHC|y)W3WNkVZzyMAbbMfG7SG0Ox~SiqMQvr(AnRMSk!+`wb$k^-n$C6LaxZ5Oa9rJ1ivT*ORvOhV<6haUD|2&EEG$T_J4PU&d)bE&oFFjg4 zg0uBj(;X)&|HryHr=wvysjslz64_RAvi-{O9kGF4+iCUFlC*OcZ6mbjThHTeXk}rK z9SOVy3#0)(P6IcF%$Nu$+^w;AC8O&Q52;b^`wA&$zw)j@@-EG4lnY2e6RGIe=G?S5 z3Kt0S%~zX~+kKcUiKQx&9MBO}?&P`&ciC?>gAXq*N`c9nBsjUbX>P%N_Z4=_3dc1x zGN-JON0S2;+s<2?>T4V!4k+ms&|NT**sj3QT$7&xm< zJ#a&e?lC~W>SYm$T%2(5Y`Id;9aZl$)Hz2f1~3pxE!7c|6`ZIJSX(DX}+G@0UyeOg}Pd43LFMV;4^qZ}+ zJ8%_m#bn}w9x4oZC-FiPmv=yUV^`p=gyWVuhNhh#j6A^3UW`J6S+=0#aNWjS4YHwA z6lEO-GG0@tRqjuu4}%nr%}N5z*?&8JtVTfpeMk^(KB37w#h72}gcB}g^u`DrnKVqz zR>INRMYK+^b;07)?eU9gN~374G-S932$-)?RIKcG&=Q%uX`Lc@km+*!34pO z=_$?-wqRr~8bkJk_W=%jULxaJ?As+mLI(^UuI`PZ#4F~k5xOzb%V>2 zZt!}ZK-mf!T_DRrpkH^ctzZ`dbbGYvY(n4+WcR4FaL20KX+Wz)QE-=OgZqs}Ji^K< ziN!+8Il{ivt5C2~9Fzmvu_|Ig>f_y$eG~ZRlnn+|!fD>XG-1#(8(3PJ1hxw|#)R(J4rt0QMrvmF&t5}gz5jC+T&FuRSVm9u!e3LgQSC9z0$WcsT(^QM@ z_qDb)YRsJrkN2WyE4n)4dvB^jyRlt|9JB83Yn&V!Z5-#fJJcrvz@-edD0@Ps(4Yu$ zi%L5$5fS+55bS{+)_Iai)GX9yZEu>Ai!=(fQ*A|AiF6p<(atm5+Ql{-r#r7HLQwrieL#EWmD z{UO41(V-}Ka+4>I&`I_IW4vp=YSs-RY03TGm(3X1LYb^LvtMY4$~GUIw3;XL_QSB5 zFaTjxbvML%QT+@(a?C z4V);WW~I zR*{d*s}mf#7*)z>yZ?D{`W1N9x35-U3NlsyU`s_vkl(X48;g=ClxY2~NI+o@{OZT! z^glS>|Hgi)cN&7;KlB8Nus$j`?|XSW{0#^H7e>C8>z2jG<;~Y9d{?1a^RCqcV*if4 z_PyfgJgTs;nm`{B9e=yrvDQuGT zB2(dMiF2~X*FmmFbK_%TTf&6nch^ueK+tK9k-_?D;k^sCT){scKR{Mf22!tV1tBO6 z8TlJwW&myZ<$7)EKsP|7fo^#E2V>NHkW?;Sw;pN(^ahk$AGWE`WVDka*mRzT>jHFu_Yr17jG_(=m5doPcyYDeL><8;xepSr!8?Kp+bPG(T>Dr z*p%7RGt8!@CWEqjL1+JkORR^-1_-FS$-7Wlax1dJIK)GY->*eU6_*+xuQU`{-v774 z^S`9^HhSQDH>*+N>h!XI@^hiFBF6sXfvQ*EEF=C&3iEtN`iyX(*tX@}@jo~9mu4Y^ zI*!0O_Fq^M5@Yzt^TXMrpN=^Ftv{B3uJ^7;7D*>1e0@JVy58ae7UGv;xqJG!FnLuC zNnwgl0{EgN=bZ&yZmkBRU|;C6gK~|n)?$rMlPot>A?dZ2vUG9b_7zE)EHB;TSrmyu zgSD(z_r`qJV{+A)t~PO#L(0oy|LsQG(Z^ebPFAy0d=o9>SDr#NYf0J6TmDyXV>n=6 z-lM?~vxyt+J{4f_Gv!FXV&5EPF}s9xjVg>LFW$H|d#z!r4_oa&`cmp%P!&(9!7`gzS zY7pSP>wR-3vlwS~C!5?d9V{BCVvy2LMQqWQidhk+YIYGf?e}?6`TrAQ|3BMV#|D6O zu#lE`K&Q_9F(Y=j1GZ-KBFGjQ^&o5!=m6^g2Y{11AFqC%jpvI!uL{AuMDV+jjMDE2 zdhom2isg!(2S$y6)hnY@4a$-6(gbcn#B@iE_AiLMnI3-v(6ISdN_Om&=S*Czm}j2H z8y8bScJKNzHj?>_v#$lVGts?!1Ov+1L8-~NXT2pk((j@}eSkGXtI=F8T4G1w+Ww7R z_oZH&>G5|3d%z~Zz&|c@=*BA)Fm{>*O>n`+)QmO3^fZW5c|aECCd@K z^isdMe0xNlIWlOh5X)r^Az6^@NZ=@EEDeXf%u!dAEKz~~ncrQ_7-p)bpni$ClHbp| z$;~9ayiq#3i~A0T&YBU0&rwA<6yvViKVxnu+mBEZ1kUGl7ME~3*1QI*!Z+5a{E^+n zkV!V-oduPc(C@8hzYGJAy-iZCD_}=6LNp`N_Dj&#gGio8J}zMkWYToL7^)TEMDvTY zT`LTA_bOIG8%be)y*x90EYWI$MZ>nxkA5`WZ?A7uPKrs4kY{mwsodsl)^>taV0(tG ze>N#R-C{Os_z}O^4iz-*);lFJsLGr;EIePCRD&r%Q3Pn>2j#Q4S&9XMX!I?alxvcK zG8@5&*lnHFWR=n4P!oX2FXl&$t}S~QtZ98Z?}Jcn5qnH$Mv1;4awXf)4_Ub=gQ@f| zVb>LHkjA-`?s`;N30&#!`xM>HCdOpgd;MAD&ZivW4t%$^EfeivsLtB`jZI6v(^gyw z`XJ}_+w1CJj4w_D#-68AbUVX~=65$`cWqWG+1q*q?4X8DmDm6-z$a)|5Kp>N;NEqy z*5qUR<4IE|#O-tab;tc$6mtT{OywbuWt{F}d77^42!nE~r&E`4hj@( zBTHx|${-6nXa$g#$TZonZUgJt*{F5Y!Nq&+ba8(v=uhF*c3tU=u(hh){u8yhUkVhU z%_?c5@8c-LZXPol`#E&7!e*hp>mfmUfIW-``M|>4YdOpNOFgT4og!RQ!DvcT8$Pj% z&~&}}>#mf={PGiyI4j>{_T4*Txu@GfVd!?=FR?abL?H~xDX0oUT9rauXl3P#@GW;N ztD5j@fL&4#5ftb|j;YX8YR&e@=J=vDvn$X~&(0z1_{7$Rh|Y0;TWQyy6$VA$at^&+ z(W1+y*GTg*Xp(;6!vqS7jjnC?-V$Fl!X4!Yd~ECkDqZ$;leKOAvMsz#9R&ACGVnX< zer);wm=bxH1i}RCRdq3UMAv$c#Y-&JHpyS+>p#_ZC0Y{a?MkJ=6@b< z28(r?5}0z0)-{gdr^|I3yf0qqVM5X*{LF1Y43pn32U<=MSvtNmt2DyoAScuXx^}Gd zQ`6tmY|yCqQ1jz`z@B|vNJBV1tC;wy_Qew~zi%MxwlO^28N7GycALM>Zc%jWC9{2q zQU&X^c7t`oFp_*^`}$}7ngX`Z4}!{$L{s$4A%m~K{&p*PnG+z=C7?PRB&_AgkinX`&e(h>>^0t*#45;h+uteG>Wzc`Q`BznQaMwK&$ijrO%o?CP#{(Gx(dQpkEG zFf&xTn|>gh-|2Yj`X+@O20cl8W4D?|wLv(;`$2J1)X7X3!n}sAscoHM@#CdR&Lr2) zJI}5)^E2TNdD7eR?rQ;+(>8Qsx{bp+8JuJMGS+#(YC$2yjE^9lJfOWwG};eXgL=-~ zma{W>ly-?j)1m9h%h#d!$CV}M_@Oirqm<^mAC8uL_>;YahVR2acKDR7+kp;m3lP{=p zf(=u_#+7Lf>=#M3Mo)?|+eMYa;lQ!Y>y(`1+-!=y9TLvB59k6K>wJ^U5ZvuVlh}&} zHqF>`GD^@d@ysoq^_$B26hTd+3lcIg=cr)wKX2+=H&-A|s3JP*~ViA4kK(ZuQ_wO4- zBjod{Ra4w=C^C{4f~3WYb0`n`+LCqNIp01hw-t(HD)@tUg9J00gyBjD4EXo3?>^k_ zXctg4V^W}hp^Mfrx}A~#gm2%u$i?b-pO+W=<4HlDf1?t{TKg6TgUx^n+SL!yln%SQ zOTZ0|e-~LS(Ck4Hztpx<_8k*XQB>@;Wx=|e&xM+miBKVcziMy|_e{k7$>$T@;H~~)3QBfB)1^?!O>Ywop1Vs zZD94Myo*5cdtBOq(5WFRh-F9(qhsM)(Sq4k77r$quZqsi4qMS`uo}jyXm4L)G)rG0 zqinsyrvjD>d8*z!_%qdSy-*Tg9L-62sFWz;e#Q~CZyo0H*KJLa^DB;D9%nsrz)c|s zZC=GY%}0tm`n;Vwf2G24EN!7hqjYKYxqua&6}FS*b6Vq8XFB)b=oSQ(_yXA&8FDkx zyF>x8RyIU<3BSBRXE`Ny>()}%yD=}0sjiAHMNLT)rx?BCELZBIjX3|6kmhMIq=y6} zXK!}(#_MA9i9poE+k=N;;GzRk&9S4UeAvoDr6a1*C3Z`2Bf3VKv@e87 zhL@k1<3>uzmQV#b%dg$Fum$$Z4Wgra(M8X{uh|id__omp{pzz#(?=4zKW;U1EHvT{ z34g9md1|+Y@XIRjL=w7dffIIx84B7PM~2uWEnso}mE*&pZ>{{m@8y(;!I3_~zv*d*TI<1Vd28exEiINZI#p*8gx+{o+Z z7V1wNXN+H?YVmsXp2V^DvYO*;cBqjdJ`miTKBxi`D-C`#pF%7sLJVnix^5EC-0Vj{ z7L-Oz{a9CPXf;FH4ZT~e&9&%OE`#oL`ZNjlpMP->dg-fs(2}Q-=;k2$dYaR418Emy zV1bHB#rK0H1++%Ol7z=BJeEt)mP16F*eqnN#xmr|_w%s)#3@$eMV(!S-C(JzLkg3r z4J!FbT)~@({J?2PP>uS}CwcgwBXPyreFT5!F@oh(8?%_6anz$^8>IM{umG(((g4ea zDZ}+x{dP&Stt-b(AuP?SeFR@%^3%yRFKv45RI|rcXD0tUSfgD`zVM@6Gl-&xSrdGb{e%Txj~!suZcxAG8HknQ|8jHHrf{V|SbiV4?Zfv!)e zxV&(wiO9e2iP#`}edCY%CMh}<6k8`WrRg_FkERE5e(x77RoCiPVQh>U`6H)ouTmoA zR@To21n-?+!|r(6H{N0d?~`35=>``{sp}W3$gzwpl9l(?g5-aeh&rFs}Rdt7o4ggolaeUakaXPMa@qh`7wY1n5Zia)R*Sk2txR+;94g zwZa7PVva0Tz|Dx7A=M}b=|$gLCeTVeh3xsBudneOEq~=FN7l%(3u1kAg3vRbI?z)x zLHFQX-b2ZdKXsUibcic*={~TEdJ45bhUT*P?MjC2cC)tNS`FWBWPSA^u57=}!Va8= zgERw++D0?9Jl{}^s(t?RsOAG!BGrhb#q-`h+ww^lB^FR&A|6jh=yd_UXS2c#-Bv6x0o-GnJf@xS-JLTC;8kjw80V+e0j#S|qvWZ=kS71-3CgPz_ z(8N%(U1RMi=eW8OB_Mw%N-8#mOs<1FY6y#DdrF@zzaII`5+p{i!Yb@t8A|5pz0*R@ zU2nZ(1OI-6d|p8Jm6O<}$&vRoqu1dOWLNvShC+u|zV6Jv_wlO7i8;}>rcO=kINOd` z`v?)aw}n+Tf8X7pU&95j!EU=fOh?xsm8w6~pR(70olh?|89D8~uY^nx2%k9Tg6N`z+K>fmd0VY{=vj$R+soj4mev;59!t^G5% zIVT>z4J}#+3mUg&styUslnhuEze1rwHUpnkuv9ZlviclgK1fF36WGT_2mC0`;J%{G z1jlA3%^rO;{yxSLO`8_hq474|dL_=W$|PFZ3;N{s{yPPd6;FmUqYaar`rYW2Q zK3%Fe)xeqNDb3gAZ`(V5%5zb%;>3dX_tBipEFol^^Q=f?0d~nWxtRwbiG8K}>1N67 z%$>8~>F$pwep_KLVh~QzSY0}b1NVjx_&}UYIMFBFum>{}Devj#6pE=t^xZ1c7M~uk zY9|9%j&s$2VEeInWe$!z07k$q&eK@7`2yAZ%Us4yO35&Auo9%D0Z| zc~F&v&bu6af%nKr)W@ebre1l#d0g(jmAi4IhGcUOdo|Lrky2+ zoED*Mh852a>;u7#>98+2LQ(R`a2OuA-b^!7D9L1}NgBh>gUa)(ZV53lh!)5t1wq~~ z4oUY0bj7~ha(FwSs3-(E_3I@WSb2Vfhjk~ zyr6S39==((@ba)nqc{1Z|H9?fK9lF z=ATwSa{V`v*T+*Z6xXQa8Fqb7?_6=cXr_Ha>ZjTRlvWPbORblrCw8WMvH{x2!Bz3u z5y=+Mp<^{nvRS|_2b)I~Ih7&e5@xuI+iN}ZtzWr*qMZkMq?_G^jMCnBI*wA*w9X3N z&(1qn_>GaoGEoXml`CiGfUOQzQQDf%aiC^#2{DWzWB?xhoi~-!+x#B<+HgwAE2^HoxbDKBCNqe;X?CtYx^>XFzEWS@HER1`EkQ}9) z-SJ&MLxxF=8rGxV9`o0qn%$P`(`BF|CjJ$9~BwU-%km>eLcLsYLHQO#k9k-{L#L--x)N=ylJg zWI+N9el$A6(FMLIS~#2S&@OCatGOkbizu0)*#Fy)0{d<(r^&>!xP1Tas@5)kKQ*1jvO z6n#dN?{(VY-EA?ud*Nj=+`~6=o8c{Cl74fdrmyKS=~Q@gJc4zKe?;2r@{X__CNS+o zt=nl2_WlI1hh{pF!P;B=z6+l62jVFTwc{beiJk#ps4p-64qO2@Han2#igJd^LqcCul(Je5McLtmL8!z zz(e(NArEbp2up8NPBJ59j!C1_^M12L4xWT)vzc_gjI0cvOL66A7je;ywnnY*H6d0s zj|L;r>Uqd>bdWewFDhSZS@tU7RzNgEeviA~oZn9Hc|9Rd?0Qfy&mYfjp?fR&x+X#T zKEw+>=d#!!q`Z&S1|NuN1T%S0LhI9>T~GRL>uOnAc3X$1NrXS?0@mKp@N6NXZxdCk zStuBHZ2tWW=xf!ddxWDe$R~bHQ2DrCJWa(YO-!s?z?$e56L&gEgdgXU?b$m|;1V3G zNBHhu-y-`Bt{UB*;B}!MHNn%4MvfoN21xVYzP^{EUJrt8Ie0v~jt!%9%lZR5JnC@U zuDP1jgVOthUSWfh$Ew+?#{TO;?VZC{ZpBrlm@!Y$ISx_GQkU6joMn4{v3lh>`pOw|Fr3pvrsfliF4iAf{5 z+rT2!88&@V0tMa3vSL51v<(YpOdOu_QNV$nG&!p4H?gHe@p_VGK`)F+88M#V64-># zrrbz423a%M-X9s2A{%a z!4Ppy77^F53NkCsDTR*7^+Q>E;HA${=2)53noPxRU2ikl| zi5#B5>C9DKq)81=mvD+UGncw4d1z(;)ij5`0b69w{-@Wj_jU|E8 zkX!$nY~=bIW0(~=jC`JoRDtZAV)`hgcipXMlSC6`pXSnk9d1%CxH{zEtp_N#`ySC* zP<7D~>0a1p?+LBg)cH@i7z$o`3h9`j5BY}g;AtWvKO4f~NImKQ!21I^KRhy@fqC{l-?ER(=;_4T2`S093xPI%{+XE>XwGT2v|i{f0bG*V z?)0)e0f%ncy8@DaWz0$3ist!#nq;UkLm%}0(eM)SkuuJxS0h@w%N^9DhHug3xN2(s z%Zs!Jp{?1jo!0htW&*!RDCAL z9#j9(NUxCA1Du14;_xEj5}1(a=-`{!khz`vSm!<37mn3ySoPg98ebfp+fcBPGRjj- zDv<`MSjjvaHO1brr+8Kowt|Wf1F6!Z-1m8F9xO1v28FESN9^OQXoyzfQB2AUPXdP#XoBzr zo=A7p5#5~hg!N+k2g(y9+V|QdmAvR3b9E*=537DW|M@@7=R>&oZJI2*BfRjnZ{Xm^ z4AGxOsLZtSaPfmvY%)Zw8oqeI@T@wr#L4`QLy3`@eW@u!auM)4b0)!3_(* zmHv9y-!D5NnUcZDi;M^`>B;@`lb?L-R*DgU+u+Ci_`)iM`9U|&OY@cG=@WcH2t9-| zaQ72kA8kIB7X>d3dz8A>*<0)>{eofSh(_0X9A4WY9T#niG<)|B@_b#t0x2~XLFm}$ zjpaI=eJGb)p2|8!8@v2KxHK8KyP)YFtWAn>B zlOwC=ih(*=ABUax(Pf=Eou$zYCe#?By$>A(7G#;@f_sU0d zqrh=cc6>lSK|0(tRoG^_(IiW67A)O_N8f2f*vc{_#_`UPtA_oDiwmQ~mHmOiOcOwd=> zr|^*lJo`S!#H%KNZ=MR1Dg@3%M}1S zrSB*ow-kYrmKVReYk`3=Nx*QN5x^sI0W1 zAyxtMyYo-SiqDu6A7)J_G~u?S4hgj__8&c$$+Qi;AGtqks|g*>GTLw4LS#=1EDHy} zC{u7yx|GB4Q7fN1wDfBtnpC`bGh)=x(&A*IqIRJoKOFtaq%zoXZcK%mt5U7ec5E*c z?+*Mm!d$iyLO+a&l{IRB{Oy=qVLbdyCW&H3l z81UGeZdH9^4xQSvzxYu>juzd|>rlWtpMaVt*+7p3VB%l>a^m(a#7ptMI7CpgUT&=T z?1WZ1Gez$YHoGwx>_H^n=fWOdS#J1-KsTrOZfJSgT*o=b4 zbnAzZG-pQ><3Cn)YRfaL%cg!cPzSMJK-8kp?b)~)3Y6Zg7hix-(!h)b(T=_DrTJnz zr=u*fY0oJ#u2^U3O@6+V@q@Oy74?bXr>ziBV|H#`%*%D6>8d^eVgl_i%d>*oT`A20 zOD{&g3qGv*I2(lT54qGaXkcIRFA+}T4rghc0XNV=@ zeS3UN&NAG^;0&GZW3m0xk91nZiv;r1sio5jx+ShkE5#&M`{whD_`}Lk-Rbu^*B7r8 zHzFf=u7tRQ0$g!M9F+_?OF=NP^@=8Gge>S5VU#}v@6_q4P|BRdP;P)GY0v2~Nh93}UuOBWd|p>gyH2VHSZxKb zLjU!iYMHq+m)4W{m=sGB&hzxkEX`;iKU|zXhWxl7q`X??Ci*6#b`jG z-fMems$g8#4t(DZ^FGp*4k#Ats^QV880>3MVC~vbTbWerjO>&buEli-XuN1P0Cp$} zJ~%#biTu@6l(Oj}(Cx zmr+Br_Sg5taD=h~pY~91rcK$ydZI%OlOgIxiC;fYuDO0%@j58l4Gr&pQlUue*vZZ* z{Dt)i&Qgb&Ou;IK+hkgi>`Pf+`n;OZpEVptb4+PKKaMQ2W)Ffm_9!aLwuG9ezd_nH z_$k~zVC3UY$|Wm}$6#E&!MnbR1jR*-r=PN+yw-qx!l#?WB&xCtWw0%Yz#aU;`WP^; zX93Tz1FQbIg=qW;v>uyQXL>Bv^KRawvB9 zujSHH8!4;1oBO4anmaBYyygs#Y0BRoe?0Kb*W>3^a0(C`Om`y{dp@OcOWamI!rYoQ1c?%CmeL;CVEi9TuIOVu^ zGqp7FQ*+^Fe+Cs>!@S%h(gvTO7imH101Jm9= zlNFF}$1S=X5G{ph$Lnx2$@Yh9&4R~z(8Cbb?xgu2p9>*wVb_lbwh?ssx*`h_up3Xw zE_@hY9Yi0xEkf3|FEP=vnCi3Qu(CHcZ1q*^+XBPjVo^A0JN~~?=R`B48$2SOy_Nsx zU8|liJYJ)>@nk!T7EtMPyaF>S=hUn6^|3o}CR>3)X5S8Na@)A*SL)MoVLsKuUoYTy z72`1`P*Le{D}#z%bk!nkyxqOzsv)LAiNMoAJwjNT6*m1JHNF180!LauzjH8N0T0RE zb%2dOQ3&0;1C(7Tp+~oQ24mD@kepVPn|8epivyuA?^1nvpyMp8UfN_0^=e8AVE)PM z%pTTI@t4R*9$?~Fq4raZ^JUg73YPsG!>8d&EL+sih8OrGXae-O#g(o4q9dO5Cc{SG z^x4zZ1G#O7Ir|^ZL;LV70ii^rlyXJ_fvL&)V!vpjE7!IcjB~M1nt8?6f&6R{pBN#_ zW%?394s7~N_G&UR8$n$<08cems9hEiM75Ld$x^vXiI2H=?%ps& z)MHHQ+}AxB?p-4XfG+Ea%4x6p3C`@sS)j4(yDwZpaH1t}7l!y?E;R#?k_oxuv7=8m zwv@rZDii8ZGBkmqnS94>n-4H{D-1CFO(EF4=%hc>Zrt+=jU3+MCyR(;-#djPlXmq$ z`yu&d{Rg*er%ne8JaeG`w`g{qKf_u`C6P&O*cywY80XgsET;=58(Scc+jvHj3cz<(r!QMZIYn0Rp?iNSoq&js!GfG9uQIAMK5`B-i8qj1=`Yt38fW22aBf;Tuj;ZAz5-g6*}bjiY$%dl;z z`{eMDhT551NBWq5M>;|!EbL3O6`nsw;p1WLu=8u=C1Hx_`x(188+-aqlO~8+w>l`( zv5HSWJC#fK+*FPdg|EeRb>&WE(REXExXk$(f1k{$9CRe~t5lYX21I?SM!jcN-On|T z0l~B%%CLTt4P5CRP4& z%Dgn{3tf9-ANyn$Pp?nUZ3C)AYE*O?i^zoqnJq}q9R=rI=PZg*m40(qA;Nu=v}>IB z+!p&2aYHFF=Zy}#j{Ja{&A52{a*(O1Z*o8Qt$~V!`do#KW4c|CMJGkayJN@H&>Dsj z;fFPaZmsV);}%ZXNCTJ2mb=mi*ymSSf7Hxik+ERwTCV0_S57(B5j#qB*@h+weHc-r z^;!!3v#qF%cstKgxik2ywJa_>)aEvQe-K=QkRt)#k}w%o-Lld$Rrf%X%pV(kHhQ%S zXSYbg4FrX+Uap4VsrU1X{$)5;9iWhsF~Hf01Fx1@C)jo+URYY-`WL~m!9i~J;cqMY z2YPNo>#i67X=rXduUA}Vuivo`PG3!Wc0HAL8|zo4rGnwF){I8{y0X87QLSMktHlp% z@$75E?&we2sUfOCu=cCxh|forOb|*3I*$5-WN95mU<5;Kzdzco$+{!&7o@nd zl(sm**Y*3-E71&<85i0z;NyTrSTE0vZT zo%I)ZNO08{=$~3mBwmsa;wij_y)AqEkb9wWE^toh#M%F9;!6BNFUSA*_b#Z*YU+3y zCRDSws}}>Nk1I!Tu=EEj2ekcWk6r=COz(ZTv_Qe?1^c&iiv3k6Rk2F+BrNp(3h`Dh zbvp-agMFuzGNK{2XLR5~?A9J+`Y{GdTxj;}W$tNT;Q7yM&bbZ5L@WN$91==6QD%6= zQ1MTk<)pc<9lOUYxM-^!%5^@bDvj;<<5tk~(tz_$mK+NCXp}dk?N33>Wtu5|T{qA$;Sz-ajxD z-|1{s*2Png3nmfV?+H2O=SsMtFCFW}!c`TyC zkS7bYn+-boMGeKbJrKHq9Hq4e-V$K9gcG;*;bsq|@s*$3^AXGjx{A#gmh(81H0S*V z{7cev7A6AOlRne(!YC?o5iK35w*tP(`9yg3J^I2aao{8u`){k$B*k7I z+&Sn7jVd;Y+M=_ueDK2x51f>8koZ_(M?b81iLAkTzK6)6Cd!O&=?PhKjS^V5@qzTd z6W965>`s!rkH_Hz3EIl^En6aFd-(vR1o8}dNV&Y(wo%u^eCyzYIPwg81XSp-W`pEAAGXK{n5MQT|BF#M?{`4zPvA#X#%NXi zwrB8zvS_z+Iiy~&55<#;tU|;~mObKF+-M&jM{CWa4Cn91{oRr0w&ZyK-mBBGk{2Dw zeYK#F?2oiX4zBEJW;`BHHzXVP#}+}45N8^fJzffITYGO2oBi5orH+vo?aCKojxJ2G z?HOw#==OyJ=G8?diLS%_+PN?gPP1Eh`YSooB8kW)Ad$v_!VNAWyN!m3LT%$?rT7?T zO9+G-{Ul^^hcb^4@#z4Rl6NuIpNHqOOeA^}6>#QNM~QKqM54S2-mioTFnl^;9IeQg zzKxXr6znzFF!%R9Rr!!{)x5a@eVKu6{GbE#`3LOVzR{xW#yJ}j#-#&)3WB3n%hZJ= z6#OWkuB$wb5l^bRHzZ4rplq2%3*i_T)>Iw2-Vb866v|unw>R90lYX%TPUfHq*l){2 zLnq!)Du%@;%QSbP67v%<8GE%%44kbmGx91M;}+{OYGXty!h$;;T{9H$8Jd&arnPiG z!ll#)%-lB^{Z`1Ik4|qHXezj5<=k2OdYbPXGV>4b{ zT|0)P#9I)DR?Oa`$(t?v}Z|Wn#XxD|BzZp@bH- zgZoGMu<|#&?3&KMkfMi0qEVkF{Hw(5zQl1%Z0zn<^F{g5fTz1|;~dXDlH*TL-Pma` z8gZHMEYSAnRnVmw*R*#TSjmqoWPbOkw%?Vmm^)7Eg(Kin>wt=t?prVm9%W!%%460TQR0Xm1RnCJGf>$(O1P8uML$Oz*kJE<<6?Ba+jGZTA_u${l<` z*qcMKvWG7S^FEmMtW$ibLH8a{_Y%RarPiS1m6H&tu_=ydrSciQXpQ#=8HXDymEAq> zhOa-;6C4FO4>HBdY?*3B^-ar`O7{31cSor%muF!X-mHIhb3Si{)@%8We)V}>eHXFX zUEbEc$CKP5+Kbb#uc9$=V;KATxUO|V+SMf6*&^wpdAyz*VkOr1jB(n8q&CTS=zZ65 z781=v^!R>V%Ab2rQF{;eNN6Wm7yX#H1@;I&lA=LVCP5PuJiL;{w0Sv#huqv+c6kCv zqwod5_%^9R-GJ(zb=H|~K8MA-N9;6cAf2Ka8l%Wncbudo;-IvziDK;-Y>|~`~h(EWqMma=6 zddsDKr|TS6x5_XPHBPTUJaZtZdOW41aBoTUlu1SWS~B(TVIYa7L=!SO&BHwP37skX zxvG6r7(h);5F)Ut!kIsh7LuDYEp_HtDTtzWSh=r-TYuC~0tylN7vGBg}F_J|w0oG4{XHOJi&SJjel z@|@4jyZ)NI!xw^SFK3WOYm0SjSTvqv9)!(BGu7ifl98I=nubFGlvQRPpAI3E?|Br@ z`Sv}4AOgJYN60IY@m*Sxf1rjz+C)_saJ0qLn7i3jLc0}Y*y!!7uS?sa=|R?gJ1$~5 zYRz(F$wy&p*DOWy)%WWWRoP7tEK%qzL2-XLcOe8%cW16hK-;EOoMk~mRzWl8hFMm* zLo1~!L@Xj=E=b}1<~QjZ1^B)gcyc}OnU#YYgn#N*0dnS2gnX86hm3@6@dZ0?(~#z> z-(iusHkqu$0mwLx?ZSYg)XI^2**mX|t~cp)h4n<^4GhcG?dly4{4>&WjZyUDlaCd7 z{%L?T@~{)Qb@0UV2$VGqeToHErt#(Zs>~m4n;HNrBi`3fY=&FP-TslJXqG@{ zLQOJz^X6D7fxE|hw?Q2r&&{Eo#$r_qfhL~+0O2qBn|^}!^Tb^Xe_L0toEau|UK>rd zmR{!C?+ymwjn0DfKq-f>kTClrsQsfu`Zd?Z@6^ZRxuWEccN^V7Wqx7)syRmQG6R&u z8a;~ugI0z)TN}kTT~7paG=I8n{gIy(doi;)^6yAh=0n3i74Ft75Qh!x@Y(1W`R!0b z8$YJnMinBnq`BWFo_(wOA&m?$u}QVfj9v-6Hm%(K!MlpgHb)m!C_|7@KW~S(rmHH*m%>ZrDO$D-CugPqrjlML;bUmTAag~TBAe2L_gK# zqn5O))8H1#phpi*v9)OL-Px#yOHy*sqhXI8{w}Lc4{x2RvCE9xcj0zAL{&+S51sf7 zh|`F3rGU46;3Z=V{$jgb@(((*cL%sEIZKvRviI+51!p|IDw6kr;w>}LnI4VHt@%QL7Bo-% zZ{1;vBi`NvsiRdj2<(DNOo#5nFC|S*9R)fHp%EI^fm^4%=n=J5cHvbn7gDvX7p-cf zJdU?%fwuOV<~RFXHyXnZ_$_~#EYi!G4G!Qdjxd+rzk}pa#bsJsb6fpw_>}KQBurGkNe4G8kHmClmjh_qn9hrm+xa692HYG(@iSO_z9!&fr2_%XXX!Bb} zvqg^T#wSGk3pwx7(zloCgHkyB6U!YFWe5*f8p~(ubp|ummr~Q&^4ptZe_GB}qd_|y zLb)C+J9@Nov{a79K$l6^Ymw9K>mphyH8W$y3o9=5y)L(|qXH8d0TP}p&KoQL0lh0a zc`NY`K~z!>X%>d<-F}|G_?HTlo#v4+?5n4Tp*-`Vn2(*{F_JZE{A(4j#fO(@^kwG* zf`QTnCN(*G0tq+#9kg7E`Oiw%Z~y$6l6z`AR!xh%hP(Gmraos_kl2_L?v;Yxr0&@D zc8DF#6+JBU$gj?U*1uKEDa&5M+jZ|YFY&3P^g0Hn=?sRDZ8s)CW{KCkSWjy@8*N4# zi>FyP_hUtr5f{=EnU-0!McbCR_=lS3$G*+DHu>D{(nzB}FP(GuvFf^}fuaCZ(@P8& z^R%fWj8$)nsFyAFUA~LXQIvt+hoJSr4|`gJvDt!Igsd>6Uo)mqUVS1x-|@rW(hJCi z4yD`qIKO=Ef_v|St>$2D0y!|R`>w?~nUmzEC+k6yVPO8&@{EyBVBo36xD^nFL)nex zbHu&GtA`~c`08vNKwpC%i+JB;x{TMGEyxsSS=hM6o z6WxWSWXu4}N@T1W6)JK=+sv^duyz$+M1{!aYn{GjnzPg;rylJSPe26# zqs!ik5gilmm55Ys`mMu_ZL*|_UU7E$a}d|Hj;-Dv?lBi1e1(5l`G77Su_ST?k^fm` zVtC7WWYMF17mHjmJXpVW8kAPe)vkF9oLS;h=Y3$GF*?nc3eM4>&{+5>+5cPKA}G3} z*W-B@=@_pZ$Ji(CkLgB(H4R}QPVRhlVbWXCcR<7heFZM(*L0rAMvaSnXJsQT4 zmUmFALwSN9Po_Sm>Iar#qzdyV`D}l13E}7&TmUZ+O6?-2vt5#Wg>W38+u#LtmiF3< zi<;r>8`g?xv@>d_pg@=z^O7s~c?A8@O#E8EFK(a+uzpGMeZ(4Tbc0WM&$GVrq@Uz_ z*tahnkmyxr>-Z%kf|;A)w{fuq5{7J_`i?}!@{3Amosw@ZckP|AuJ2a-M^EqqKXw&2 z%ZVyadi~Qe{A1%e8P;v*R4Mb3v4JWYvq^Hs8BeDvi#!vV}U$JjBvX; zq!nYFD{Nh8LedUhlKbtUGJALh`MBVVTQs&c^emzXtq3LxU8x&1sO;nz)7WVks)*U4 zc1^4b6m86)0x3kn7=Oy^d&rBs;3BTO62&{+3q4o}yibDLjRcmI^L`6NJD@=K^a`t6 zZG!b}9(S|VSIaX;c6;ed zp4=~LC&rRXwf-h5-qM05l_}WB$Zt6Yf`%TJ#R-7HCrf>4?HF;wWE!Xsae`XEH>ZMB z4->Y-Z=%H#{f3PC5q`)DzK-ha=d(GKXk=kvu%sL&7FQvaNuV5JxjawUFrX{nm)HK5D9WBM zOk}e>@=6Py%_wTYWBqvAF*zMw+gZ2@IzJ$pI+}MalwsVBetlW`0s4OEeuo8Jy6~;5 z9`ETE5LG@ip-)lgdY2h<{@u7rO3F2Bwntz}1pN>seD}C2O zdmwI;!}iBDL2uL{Y?_S%uIf{L+@>;+_E&_h0=&62^FvIG&f@)-#2A_Px{*m0-+m{d2b;{(O_&UU~6 zGQeISEwHB8=#xn;17Figy=EcXz8czl>d@~UZl^k{6?LlmA+I7Y;)VN~{jj0ocVVyg zh~2a{MUBiUNSl8EMcZc^L-NrR!cEO#xFj2*>T^$lC!se4@<}#{p7#|Yj;2#tiP~+{ zg0<8c4o}*(G!S~G6hjyjsyvy$7L7U8Ni0FzPXOa74kB7VzFRv|6}=n;u!SHI;iz_b z$Xa_JijfGFv9hQM;?+^2=K^{JeP_`s_J}qU#cwIg<>{Jg(p@Ie>8IbVNAd8>DY)W; z&)zLR9~8of;*Pu~uEQGSdaF7K!xBkf#+G12_D|9P&M~rpoEHV!aZz^(A!IYvp}U_< zP%ZNyMaFudwuJzs_bF$Mag@M?QR<`ezCex85Bs7M>iA|1#tf-Y7U7IOJXjpsD}s~H z6ly_Msb^C=*h$LF+%V9Vn{(5^<=Z4*4xQkk2mES!Il-~=Kq>ev&cO?Hlf9JNihR3J zi|FW>(>a&m$<@Qp+Uu z-6LjWJc}L0m1WpJ`I-aSk6k>Z2HL#Py4W z+V6UDPt*K~=GrY$zgNkWBb0k~{Ne?xcC+8WirQvD09(f^25d%~VO516_%bnsRN&>lljO>`j`$bOaWyEwS(rlbtt4!xN@K1(d zF;}VoQw_4SZsrquTNI6>wc;^vLtQJX&duk?YG-B-Vj$m@G4xM0Ke@$9vAFSPzVtj36UG)MkU!pG$tzuXcln z_f%a*zKYzF=Zd?8@tBMLZWcns3Tb6C^tl8FDR<5Z4#y|HVVVtqA{Rq zKt?mvsemzlSn+~Ze5U~eTD5>&go3N-3c^6o82kLnV>oF?Y;&d0h=R|P5Zk~wIk2wy z_XbtJKviYWMV9Z^t`~dc%_O$yP$F_+Zsi&^tAZqhkRg9%$>Oj?n+}i`=9Hbqe#3&y zUwC*MOXm=->}&lJ>6zJGnm0j^dVMH~nsu;gKiM02$(v3?^Uwo&D?Ew!?lzZj^16u- z(utR)fiPEYk3jftL;x#|d@fYdPz`hUy+X#Crl^F94ECZ! z8r8rH%Q|`K#E(43UfqkSJJ=&YJ0<|AggX#DYDuZ+;)-%7tVz#spxbKw3!=3E5+Vy< zK#0IgF0mJp%T71C|B7rNETF7oKKWaO2;tO6y&9RTl`;AvzC}Cv-G42O>;T&eJHaqp z<*DJ^99d%DRH6N;w-KxNG)Qtwsrm76&igM0oY2@3IAXKJ_^*|b5UONa^MmF+0%I9y z3aZpiCkiEPdiZb7XV80vzc{}(csLYOia9o|%B2zWD67#O+QBqsTQC2YM?r0BEhVz% zb1a!f+h#e27$w0Z5#bkng5(b{p@W<^ld1YbeiSNJ0y`nihK(dbE*!<^^2s~otf}Xg zOP0bldhZLR$jweb5*Bo3y3#>#Wy`F#2(o0)_UAS=gD6m(sLx(P_BpjtdBSoK2S&n^ zwB1i;31&N?LgGNfE^We+zmbz&Lvo;$6NgJ*6ea78r65a**owfvT4 zbZr@s3}J(dHQ99)YTK*o`AZzp6@R%lD8M$!iK(kW3hj&^E7h&V%bj=NT=t|ikT8rq zf%PE5ZE9U~!SD9PMAl~4OClQV1(Y58ay|l31ccYi7$J0Ie~%>q_!__K*mADV?1I7C zynUf8cm?_j!b{Mmj`b7DjQZrPyX3wOHL@vst$6P?JQ5Oy5?XNgWfk0-_=rJ5a2{_< zCn9Mw$^DCN)bsp6AjlcFeNg`KHc?R%g&^i`V}AOW+mhwV2-YwOLq8jP7-I5L$Y(ng zVtVl4jd0?fh!?U0)}!ZwUsOsP$BS6S-^sa&TyGGaxW|_BJ$ws{^Y;x(paxim?Z%VF zy+Yx=K)sW&r?&ch{s)>-PH(|@I&Fb}{*+SZy1lShu5yl^^?E4P;D^iupfSaZO5ajK1w`sl@jmQhtV- z*%Yka&?F0D-L8+c>jQ>fhXz<4HN0@91KZpOF<)Z$-ptpV_5Q0t?gt3XCVV_+^Fi$k ztuhkRlYuj&gxtk>?f&@A(tyc$a>7WT#sYrffs2H^9IyT{y?H)w9ZPV-7X1!Kr?~p%FOYR7n

qCuyDNa;^e9~W2-?Q*em*O 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�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 literal 0 HcmV?d00001 diff --git a/docs/src/userguide/numpy_tutorial.rst b/docs/src/userguide/numpy_tutorial.rst index 86ff682783d..03db4cd1aab 100644 --- a/docs/src/userguide/numpy_tutorial.rst +++ b/docs/src/userguide/numpy_tutorial.rst @@ -6,6 +6,9 @@ Cython for NumPy users ************************** +.. include:: + ../two-syntax-variants-used + This tutorial is aimed at NumPy users who have no experience with Cython at all. If you have some knowledge of Cython you may want to skip to the ''Efficient indexing'' section. @@ -271,11 +274,22 @@ Adding types ============= To add types we use custom Cython syntax, so we are now breaking Python source -compatibility. Here's :file:`compute_typed.pyx`. *Read the comments!* +compatibility. *Read the comments!* + +.. tabs:: + + .. group-tab:: Pure Python + + .. literalinclude:: ../../examples/userguide/numpy_tutorial/compute_typed.py + :caption: compute_typed.py + .. figure:: compute_typed_py_html.png -.. literalinclude:: ../../examples/userguide/numpy_tutorial/compute_typed.pyx + .. group-tab:: Cython + + .. literalinclude:: ../../examples/userguide/numpy_tutorial/compute_typed.pyx + :caption: compute_typed.pyx + .. figure:: compute_typed_pyx_html.png -.. figure:: compute_typed_html.jpg At this point, have a look at the generated C code for :file:`compute_cy.pyx` and :file:`compute_typed.pyx`. Click on the lines to expand them and see corresponding C. @@ -334,12 +348,27 @@ the NumPy array isn't contiguous in memory. They can be indexed by C integers, thus allowing fast access to the NumPy array data. -Here is how to declare a memoryview of integers:: +Here is how to declare a memoryview of integers: + +.. tabs:: + + .. group-tab:: Pure Python - cdef int [:] foo # 1D memoryview - cdef int [:, :] foo # 2D memoryview - cdef int [:, :, :] foo # 3D memoryview - ... # You get the idea. + .. code-block:: python + + foo: cython.int [:] # 1D memoryview + foo: cython.int [:, :] # 2D memoryview + foo: cython.int [:, :, :] # 3D memoryview + ... # You get the idea. + + .. group-tab:: Cython + + .. code-block:: cython + + cdef int [:] foo # 1D memoryview + cdef int [:, :] foo # 2D memoryview + cdef int [:, :, :] foo # 3D memoryview + ... # You get the idea. No data is copied from the NumPy array to the memoryview in our example. As the name implies, it is only a "view" of the memory. So we can use the @@ -348,9 +377,18 @@ array ``result`` that holds the data that we operated on. Here is how to use them in our code: -:file:`compute_memview.pyx` -.. literalinclude:: ../../examples/userguide/numpy_tutorial/compute_memview.pyx +.. tabs:: + + .. group-tab:: Pure Python + + .. literalinclude:: ../../examples/userguide/numpy_tutorial/compute_memview.py + :caption: compute_memview.py + + .. group-tab:: Cython + + .. literalinclude:: ../../examples/userguide/numpy_tutorial/compute_memview.pyx + :caption: compute_memview.pyx Let's see how much faster accessing is now. @@ -377,14 +415,32 @@ The array lookups are still slowed down by two factors: explicitly coded so that it doesn't use negative indices, and it (hopefully) always access within bounds. -With decorators, we can deactivate those checks:: +With decorators, we can deactivate those checks: + +.. tabs:: + + .. group-tab:: Pure Python + + .. code-block:: python + + ... + import cython + @cython.boundscheck(False) # Deactivate bounds checking + @cython.wraparound(False) # Deactivate negative indexing. + def compute(array_1: cython.int[:, :], array_2: cython.int[:, :], + a: cython.int, b: cython.int, c: cython.int): + ... - ... - cimport cython - @cython.boundscheck(False) # Deactivate bounds checking - @cython.wraparound(False) # Deactivate negative indexing. - def compute(int[:, :] array_1, int[:, :] array_2, int a, int b, int c): - ... + .. group-tab:: Cython + + .. code-block:: cython + + ... + cimport cython + @cython.boundscheck(False) # Deactivate bounds checking + @cython.wraparound(False) # Deactivate negative indexing. + def compute(int[:, :] array_1, int[:, :] array_2, int a, int b, int c): + ... Now bounds checking is not performed (and, as a side-effect, if you ''do'' happen to access out of bounds you will in the best case crash your program @@ -425,14 +481,38 @@ memoryview as contiguous. We give an example on an array that has 3 dimensions. If you want to give Cython the information that the data is C-contiguous -you have to declare the memoryview like this:: +you have to declare the memoryview like this: + +.. tabs:: + + .. group-tab:: Pure Python + + .. code-block:: python + + a: cython.int[:,:,::1] - cdef int [:,:,::1] a + .. group-tab:: Cython + + .. code-block:: cython + + cdef int [:,:,::1] a If you want to give Cython the information that the data is Fortran-contiguous -you have to declare the memoryview like this:: +you have to declare the memoryview like this: + +.. tabs:: + + .. group-tab:: Pure Python + + .. code-block:: python + + a: cython.int[::1, :, :] - cdef int [::1, :, :] a + .. group-tab:: Cython + + .. code-block:: cython + + cdef int [::1, :, :] a If all this makes no sense to you, you can skip this part, declaring arrays as contiguous constrains the usage of your functions as it rejects array slices as input. @@ -468,7 +548,15 @@ our code. This is why, we must still declare manually the type of the And actually, manually giving the type of the ``tmp`` variable will be useful when using fused types. -.. literalinclude:: ../../examples/userguide/numpy_tutorial/compute_infer_types.pyx +.. tabs:: + + .. group-tab:: Pure Python + + .. literalinclude:: ../../examples/userguide/numpy_tutorial/compute_infer_types.py + + .. group-tab:: Cython + + .. literalinclude:: ../../examples/userguide/numpy_tutorial/compute_infer_types.pyx We now do a speed test: @@ -502,7 +590,15 @@ know what NumPy data type we should use for our output array. In this case, our function now works for ints, doubles and floats. -.. literalinclude:: ../../examples/userguide/numpy_tutorial/compute_fused_types.pyx +.. tabs:: + + .. group-tab:: Pure Python + + .. literalinclude:: ../../examples/userguide/numpy_tutorial/compute_fused_types.py + + .. group-tab:: Cython + + .. literalinclude:: ../../examples/userguide/numpy_tutorial/compute_fused_types.pyx We can check that the output type is the right one:: @@ -542,7 +638,25 @@ For MSVC (on Windows) you should use ``/openmp`` instead of ``-fopenmp``. The GIL must be released (see :ref:`Releasing the GIL `), so this is why we declare our :func:`clip` function ``nogil``. -.. literalinclude:: ../../examples/userguide/numpy_tutorial/compute_prange.pyx +.. tabs:: + + .. group-tab:: Pure Python + + .. literalinclude:: ../../examples/userguide/numpy_tutorial/compute_prange.py + :lines: 3- + + .. group-tab:: Cython + + .. literalinclude:: ../../examples/userguide/numpy_tutorial/compute_prange.pyx + :lines: 3- + +.. note:: + + Currently, Cython is checking whether there was a raised exception after every call of the function ``clip()``. + Checking a raised exception requires the GIL to be held which causes overhead inside a `nogil` loop. + The need to check here is a bug with functions returning a fused type (see :issue:`5586` for the details). + To avoid acquiring the GIL, the function is declared as ``noexcept`` or ``@cython.exceptval(check=False)``. See the :ref:`error_return_values` section for more details. + We can have substantial speed gains for minimal effort: From 15675a5e2ff2685b9aa1af7695fe306548361a92 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Sat, 23 Dec 2023 14:27:22 +0100 Subject: [PATCH 082/286] Provide better assignment failure diagnostics for typedef target types by printing their resolved type. --- Cython/Compiler/ExprNodes.py | 11 +++++++---- Cython/Compiler/PyrexTypes.py | 7 +++---- tests/errors/e_excvalfunctype.pyx | 4 ++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 9edac7d2fea..430945506f1 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -1068,11 +1068,14 @@ def coerce_to(self, dst_type, env): return src def fail_assignment(self, dst_type): + src_resolved = f" (alias of '{self.type.resolve()}')" if self.type.is_typedef else "" + dst_resolved = f" (alias of '{dst_type.resolve()}')" if dst_type.is_typedef else "" extra_diagnostics = dst_type.assignment_failure_extra_info(self.type) - if extra_diagnostics: - extra_diagnostics = ". " + extra_diagnostics - error(self.pos, "Cannot assign type '%s' to '%s'%s" % ( - self.type, dst_type, extra_diagnostics)) + error(self.pos, + f"Cannot assign type '{self.type}'{src_resolved}" + f" to '{dst_type}'{dst_resolved}" + f"{'.' if extra_diagnostics else ''}{extra_diagnostics}" + ) def check_for_coercion_error(self, dst_type, env, fail=False, default=None): if fail and not default: diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 91d7a6b6f37..076e85f9dc5 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -317,8 +317,7 @@ def assignable_from_resolved_type(self, src_type): return self.same_as(src_type) def assignment_failure_extra_info(self, src_type): - """Override if you can useful provide extra - information about why an assignment didn't work.""" + """Override if you can provide useful extra information about why an assignment didn't work.""" return "" def as_argument_type(self): @@ -2914,9 +2913,9 @@ def assignment_failure_extra_info(self, src_type): copied_src_type.exception_value = self.base_type.exception_value if self.base_type.pointer_assignable_from_resolved_type(copied_src_type): # the only reason we can't assign is because of exception incompatibility - msg = "Exception values are incompatible." + msg = " Exception values are incompatible." if not self.base_type.exception_check and not self.base_type.exception_value: - msg += " Suggest adding 'noexcept' to type '{}'.".format(src_type) + msg += f" Suggest adding 'noexcept' to type '{src_type}'." return msg return super().assignment_failure_extra_info(src_type) diff --git a/tests/errors/e_excvalfunctype.pyx b/tests/errors/e_excvalfunctype.pyx index fb5b4c7ce67..374edc53422 100644 --- a/tests/errors/e_excvalfunctype.pyx +++ b/tests/errors/e_excvalfunctype.pyx @@ -11,6 +11,6 @@ spam = grail # type mismatch _ERRORS = u""" -9:8: Cannot assign type 'spamfunc' to 'grailfunc'. Exception values are incompatible. Suggest adding 'noexcept' to type 'int (int, char *) except 42'. -10:7: Cannot assign type 'grailfunc' to 'spamfunc'. Exception values are incompatible. +9:8: Cannot assign type 'spamfunc' (alias of 'int (*)(int, char *) except 42') to 'grailfunc' (alias of 'int (*)(int, char *) noexcept'). Exception values are incompatible. Suggest adding 'noexcept' to type 'int (int, char *) except 42'. +10:7: Cannot assign type 'grailfunc' (alias of 'int (*)(int, char *) noexcept') to 'spamfunc' (alias of 'int (*)(int, char *) except 42'). Exception values are incompatible. """ From f32a10d9daf937bcebe4a2a7b3b62d95717ca2ef Mon Sep 17 00:00:00 2001 From: da-woods Date: Thu, 28 Dec 2023 08:15:28 +0000 Subject: [PATCH 083/286] Fixes an address-sanitizer issue in parallel sections (GH-5922) Address sanitizers consistently give an error on running the "parallel" test, saying that it ends up getting freed after releasing and reacquiring the gil, and thus we can't keep using the same threadstate pointer. Sanitizer finding: ==28037==ERROR: AddressSanitizer: heap-use-after-free on address 0x6130000325b8 at pc 0x7f9cb3123c0d bp 0x7f9cad6ddc10 sp 0x7f9cad6ddc08 READ of size 8 at 0x6130000325b8 thread T41 #0 0x7f9cb3123c0c in __Pyx__ExceptionReset /TEST_TMP/run/c/parallel/parallel.c:42891 #1 0x7f9cb3172593 in __pyx_f_8parallel_parallel_exc_nogil_swallow._omp_fn.0 /TEST_TMP/run/c/parallel/parallel.c:30050 #2 0x7f9cc66cdd2d (/lib64/libgomp.so.1+0x21d2d) (BuildId: 94e48b16f615cdab2143a0c2b3f15d0b8d81d0e6) #3 0x7f9cc688ff43 in start_thread (/lib64/libc.so.6+0x8ff43) (BuildId: 099807798c1de6cbe241dc4e3dd67a7326a2c29f) #4 0x7f9cc69184cb in __clone3 (/lib64/libc.so.6+0x1184cb) (BuildId: 099807798c1de6cbe241dc4e3dd67a7326a2c29f) --- Cython/Compiler/Nodes.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index fc4a31f180b..7b0a4e7d6c8 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -8597,6 +8597,8 @@ def put_error_uncatcher(self, code, exc_vars, exc_lineno_cnames=None, exc_filena if self.is_try_finally_in_nogil: code.put_ensure_gil(declare_gilstate=False) + # although the thread state is already assigned, that can't be trusted after releasing the GIL + code.putln("__Pyx_PyThreadState_assign") # not using preprocessor here to avoid warnings about # unused utility functions and/or temps @@ -8623,6 +8625,8 @@ def put_error_cleaner(self, code, exc_vars): code.globalstate.use_utility_code(reset_exception_utility_code) if self.is_try_finally_in_nogil: code.put_ensure_gil(declare_gilstate=False) + # although the thread state is already assigned, that can't be trusted after releasing the GIL + code.putln("__Pyx_PyThreadState_assign") # not using preprocessor here to avoid warnings about # unused utility functions and/or temps From 12324a112d3875fdb56ec908edf55b459334529c Mon Sep 17 00:00:00 2001 From: da-woods Date: Thu, 28 Dec 2023 08:15:28 +0000 Subject: [PATCH 084/286] Fixes an address-sanitizer issue in parallel sections (GH-5922) Address sanitizers consistently give an error on running the "parallel" test, saying that it ends up getting freed after releasing and reacquiring the gil, and thus we can't keep using the same threadstate pointer. Sanitizer finding: ==28037==ERROR: AddressSanitizer: heap-use-after-free on address 0x6130000325b8 at pc 0x7f9cb3123c0d bp 0x7f9cad6ddc10 sp 0x7f9cad6ddc08 READ of size 8 at 0x6130000325b8 thread T41 #0 0x7f9cb3123c0c in __Pyx__ExceptionReset /TEST_TMP/run/c/parallel/parallel.c:42891 #1 0x7f9cb3172593 in __pyx_f_8parallel_parallel_exc_nogil_swallow._omp_fn.0 /TEST_TMP/run/c/parallel/parallel.c:30050 #2 0x7f9cc66cdd2d (/lib64/libgomp.so.1+0x21d2d) (BuildId: 94e48b16f615cdab2143a0c2b3f15d0b8d81d0e6) #3 0x7f9cc688ff43 in start_thread (/lib64/libc.so.6+0x8ff43) (BuildId: 099807798c1de6cbe241dc4e3dd67a7326a2c29f) #4 0x7f9cc69184cb in __clone3 (/lib64/libc.so.6+0x1184cb) (BuildId: 099807798c1de6cbe241dc4e3dd67a7326a2c29f) --- Cython/Compiler/Nodes.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 1cd341d0d48..83f6f61f2d9 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -8610,6 +8610,8 @@ def put_error_uncatcher(self, code, exc_vars, exc_lineno_cnames=None, exc_filena if self.is_try_finally_in_nogil: code.put_ensure_gil(declare_gilstate=False) + # although the thread state is already assigned, that can't be trusted after releasing the GIL + code.putln("__Pyx_PyThreadState_assign") # not using preprocessor here to avoid warnings about # unused utility functions and/or temps @@ -8636,6 +8638,8 @@ def put_error_cleaner(self, code, exc_vars): code.globalstate.use_utility_code(reset_exception_utility_code) if self.is_try_finally_in_nogil: code.put_ensure_gil(declare_gilstate=False) + # although the thread state is already assigned, that can't be trusted after releasing the GIL + code.putln("__Pyx_PyThreadState_assign") # not using preprocessor here to avoid warnings about # unused utility functions and/or temps From a78d102201a4105dea4d129ab391e7dfb6af2807 Mon Sep 17 00:00:00 2001 From: Matus Valo Date: Fri, 29 Dec 2023 19:28:42 +0100 Subject: [PATCH 085/286] Add `use_threads_if` parameter to `parallel()` and `prange()` (#5919) Co-authored-by: da-woods Co-authored-by: scoder --- Cython/Compiler/Nodes.py | 65 +++++++++++++++---- .../userguide/parallelism/condition_sum.py | 14 ++++ .../userguide/parallelism/condition_sum.pyx | 14 ++++ docs/src/userguide/parallelism.rst | 26 ++++++-- tests/errors/e_cython_parallel.pyx | 24 +++++++ tests/run/parallel.pyx | 38 +++++++++-- 6 files changed, 161 insertions(+), 20 deletions(-) create mode 100644 docs/examples/userguide/parallelism/condition_sum.py create mode 100644 docs/examples/userguide/parallelism/condition_sum.pyx diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 7b0a4e7d6c8..8afe4e30fbf 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -9083,7 +9083,7 @@ class ParallelStatNode(StatNode, ParallelNode): construct (replaced by its compile time value) """ - child_attrs = ['body', 'num_threads'] + child_attrs = ['body', 'num_threads', 'threading_condition'] body = None @@ -9094,6 +9094,7 @@ class ParallelStatNode(StatNode, ParallelNode): num_threads = None chunksize = None + threading_condition = None parallel_exc = ( Naming.parallel_exc_type, @@ -9136,9 +9137,10 @@ def analyse_declarations(self, env): self.body.analyse_declarations(env) self.num_threads = None + self.threading_condition = None if self.kwargs: - # Try to find num_threads and chunksize keyword arguments + # Try to find known keyword arguments. pairs = [] seen = set() for dictitem in self.kwargs.key_value_pairs: @@ -9148,6 +9150,9 @@ def analyse_declarations(self, env): if dictitem.key.value == 'num_threads': if not dictitem.value.is_none: self.num_threads = dictitem.value + elif dictitem.key.value == 'use_threads_if': + if not dictitem.value.is_none: + self.threading_condition = dictitem.value elif self.is_prange and dictitem.key.value == 'chunksize': if not dictitem.value.is_none: self.chunksize = dictitem.value @@ -9174,6 +9179,12 @@ def analyse_expressions(self, env): if self.num_threads: self.num_threads = self.num_threads.analyse_expressions(env) + if self.threading_condition: + if self.is_parallel: + self.threading_condition = self.threading_condition.analyse_expressions(env) + else: + error(self.pos, "'use_threads_if' must de declared in the parent parallel section") + if self.chunksize: self.chunksize = self.chunksize.analyse_expressions(env) @@ -9777,15 +9788,23 @@ def redef_builtin_expect_apple_gcc_bug(self, code): if not self.parent: code.redef_builtin_expect(self.redef_condition) + def _parameters_nogil_check(self, env, names, nodes): + for name, node in zip(names, nodes): + if node is not None and node.type.is_pyobject: + error(node.pos, "%s may not be a Python object " + "as we don't have the GIL" % name) + + class ParallelWithBlockNode(ParallelStatNode): """ This node represents a 'with cython.parallel.parallel():' block """ - valid_keyword_arguments = ['num_threads'] + valid_keyword_arguments = ['num_threads', 'use_threads_if'] num_threads = None + threading_condition = None def analyse_declarations(self, env): super().analyse_declarations(env) @@ -9794,12 +9813,20 @@ def analyse_declarations(self, env): "positional arguments") def generate_execution_code(self, code): + + if self.threading_condition is not None: + self.threading_condition.generate_evaluation_code(code) + self.declare_closure_privates(code) self.setup_parallel_control_flow_block(code) code.putln("#ifdef _OPENMP") code.put("#pragma omp parallel ") + if self.threading_condition is not None: + code.put("if(%s) " % self.threading_condition.result()) + + if self.privates: privates = [e.cname for e in self.privates if not e.type.is_pyobject] @@ -9826,11 +9853,21 @@ def generate_execution_code(self, code): return_ = code.label_used(code.return_label) self.restore_labels(code) + + # ------ cleanup ------ self.end_parallel_control_flow_block(code, break_=break_, continue_=continue_, return_=return_) + + if self.threading_condition is not None: + self.threading_condition.generate_disposal_code(code) + self.threading_condition.free_temps(code) + self.release_closure_privates(code) + def nogil_check(self, env): + self._parameters_nogil_check(env, ['use_threads_if'], [self.threading_condition]) + class ParallelRangeNode(ParallelStatNode): """ @@ -9841,7 +9878,7 @@ class ParallelRangeNode(ParallelStatNode): """ child_attrs = ['body', 'target', 'else_clause', 'args', 'num_threads', - 'chunksize'] + 'chunksize', 'threading_condition'] body = target = else_clause = args = None @@ -9852,7 +9889,7 @@ class ParallelRangeNode(ParallelStatNode): nogil = None schedule = None - valid_keyword_arguments = ['schedule', 'nogil', 'num_threads', 'chunksize'] + valid_keyword_arguments = ['schedule', 'nogil', 'num_threads', 'chunksize', 'use_threads_if'] def __init__(self, pos, **kwds): super().__init__(pos, **kwds) @@ -9966,12 +10003,9 @@ def analyse_expressions(self, env): return node def nogil_check(self, env): - names = 'start', 'stop', 'step', 'target' - nodes = self.start, self.stop, self.step, self.target - for name, node in zip(names, nodes): - if node is not None and node.type.is_pyobject: - error(node.pos, "%s may not be a Python object " - "as we don't have the GIL" % name) + names = 'start', 'stop', 'step', 'target', 'use_threads_if' + nodes = self.start, self.stop, self.step, self.target, self.threading_condition + self._parameters_nogil_check(env, names, nodes) def generate_execution_code(self, code): """ @@ -10039,6 +10073,9 @@ def generate_execution_code(self, code): fmt_dict[name] = result + if self.threading_condition is not None: + self.threading_condition.generate_evaluation_code(code) + fmt_dict['i'] = code.funcstate.allocate_temp(self.index_type, False) fmt_dict['nsteps'] = code.funcstate.allocate_temp(self.index_type, False) @@ -10081,7 +10118,7 @@ def generate_execution_code(self, code): # And finally, release our privates and write back any closure # variables - for temp in start_stop_step + (self.chunksize,): + for temp in start_stop_step + (self.chunksize, self.threading_condition): if temp is not None: temp.generate_disposal_code(code) temp.free_temps(code) @@ -10103,6 +10140,10 @@ def generate_loop(self, code, fmt_dict): reduction_codepoint = self.parent.privatization_insertion_point else: code.put("#pragma omp parallel") + + if self.threading_condition is not None: + code.put(" if(%s)" % self.threading_condition.result()) + self.privatization_insertion_point = code.insertion_point() reduction_codepoint = self.privatization_insertion_point code.putln("") diff --git a/docs/examples/userguide/parallelism/condition_sum.py b/docs/examples/userguide/parallelism/condition_sum.py new file mode 100644 index 00000000000..88d7d2cca1b --- /dev/null +++ b/docs/examples/userguide/parallelism/condition_sum.py @@ -0,0 +1,14 @@ +from cython.parallel import prange + +def psum(n: cython.int): + + i: cython.int + sum: cython.int = 0 + + for i in prange(n, nogil=True, use_threads_if=n>1000): + sum += i + + return sum + +psum(30) # Executed sequentially +psum(10000) # Executed in parallel diff --git a/docs/examples/userguide/parallelism/condition_sum.pyx b/docs/examples/userguide/parallelism/condition_sum.pyx new file mode 100644 index 00000000000..c926d16f369 --- /dev/null +++ b/docs/examples/userguide/parallelism/condition_sum.pyx @@ -0,0 +1,14 @@ +from cython.parallel import prange + +def psum(int n): + + cdef int i + cdef int sum = 0 + + for i in prange(n, nogil=True, use_threads_if=n>1000): + sum += i + + return sum + +psum(30) # Executed sequentially +psum(10000) # Executed in parallel diff --git a/docs/src/userguide/parallelism.rst b/docs/src/userguide/parallelism.rst index 7c342022cdb..015dcfbf4a4 100644 --- a/docs/src/userguide/parallelism.rst +++ b/docs/src/userguide/parallelism.rst @@ -20,7 +20,7 @@ It currently supports OpenMP, but later on more backends might be supported. or parallel regions due to OpenMP restrictions. -.. function:: prange([start,] stop[, step][, nogil=False][, schedule=None[, chunksize=None]][, num_threads=None]) +.. function:: prange([start,] stop[, step][, nogil=False][, use_threads_if=CONDITION][, schedule=None[, chunksize=None]][, num_threads=None]) This function can be used for parallel loops. OpenMP automatically starts a thread pool and distributes the work according to the schedule @@ -52,6 +52,12 @@ It currently supports OpenMP, but later on more backends might be supported. This function can only be used with the GIL released. If ``nogil`` is true, the loop will be wrapped in a nogil section. + :param use_threads_if: The loop is run in multiple threads only if ``CONDITION`` + is evaluated as true. Otherwise the code is run sequentially. Running + the loop sequentially can be handy in the cases when the cost of spawning + threads is greater than the benefit of running the loop in parallel + (e.g. for small data sets). + :param schedule: The ``schedule`` is passed to OpenMP and can be one of the following: @@ -141,13 +147,25 @@ Example with a :term:`typed memoryview` (e.g. a NumPy array) .. literalinclude:: ../../examples/userguide/parallelism/memoryview_sum.pyx -.. function:: parallel(num_threads=None) +Example with conditional parallelism: + +.. tabs:: + + .. group-tab:: Pure Python + + .. literalinclude:: ../../examples/userguide/parallelism/condition_sum.py + + .. group-tab:: Cython + + .. literalinclude:: ../../examples/userguide/parallelism/condition_sum.pyx + +.. function:: parallel(num_threads=None, use_threads_if=CONDITION) This directive can be used as part of a ``with`` statement to execute code sequences in parallel. This is currently useful to setup thread-local - buffers used by a prange. A contained prange will be a worksharing loop + buffers used by a ``prange``. A contained ``prange`` will be a worksharing loop that is not parallel, so any variable assigned to in the parallel section - is also private to the prange. Variables that are private in the parallel + is also private to the ``prange``. Variables that are private in the parallel block are unavailable after the parallel block. Example with thread-local buffers diff --git a/tests/errors/e_cython_parallel.pyx b/tests/errors/e_cython_parallel.pyx index f727d045534..9b37758b09d 100644 --- a/tests/errors/e_cython_parallel.pyx +++ b/tests/errors/e_cython_parallel.pyx @@ -149,6 +149,26 @@ with nogil, cython.parallel.parallel(): with cython.parallel.parallel(): pass +cdef bint gil_function(): + return True + +for i in prange(10, nogil=True, use_threads_if=gil_function()): + pass + +with nogil, parallel.parallel(use_threads_if=gil_function()): + pass + +def bar(): + + python_var = object() + + cdef int i + + for i in prange(10, nogil=True, use_threads_if=python_var): + pass + + with nogil, parallel.parallel(use_threads_if=python_var): + pass _ERRORS = u""" 3:8: cython.parallel.parallel is not a module @@ -184,4 +204,8 @@ _ERRORS = u""" 139:62: Chunksize not valid for the schedule runtime 145:70: Calling gil-requiring function not allowed without gil 149:33: Nested parallel with blocks are disallowed +155:59: Calling gil-requiring function not allowed without gil +158:57: Calling gil-requiring function not allowed without gil +167:51: use_threads_if may not be a Python object as we don't have the GIL +170:49: use_threads_if may not be a Python object as we don't have the GIL """ diff --git a/tests/run/parallel.pyx b/tests/run/parallel.pyx index 44a3a38f2dc..acdf12fe014 100644 --- a/tests/run/parallel.pyx +++ b/tests/run/parallel.pyx @@ -36,14 +36,31 @@ cdef int get_num_threads() noexcept with gil: print "get_num_threads called" return 3 -def test_num_threads(): +cdef bint check_size(int size) nogil: + return size > 5 + +def test_num_threads(int size): """ - >>> test_num_threads() + >>> test_num_threads(6) 1 get_num_threads called 3 get_num_threads called 3 + get_num_threads called + 3 + get_num_threads called + 3 + >>> test_num_threads(4) + 1 + get_num_threads called + 1 + get_num_threads called + 1 + get_num_threads called + 1 + get_num_threads called + 1 """ cdef int dyn = openmp.omp_get_dynamic() cdef int num_threads @@ -56,14 +73,27 @@ def test_num_threads(): print num_threads - with nogil, cython.parallel.parallel(num_threads=get_num_threads()): + with nogil, cython.parallel.parallel(num_threads=get_num_threads(), use_threads_if=size > 5): + p[0] = openmp.omp_get_num_threads() + + print num_threads + + # Checks that temporary variables are released properly + with nogil, cython.parallel.parallel(num_threads=get_num_threads(), use_threads_if=check_size(size)): p[0] = openmp.omp_get_num_threads() print num_threads cdef int i + # Checks that temporary variables are released properly + for i in prange(1, nogil=True, num_threads=get_num_threads(), use_threads_if=check_size(size)): + p[0] = openmp.omp_get_num_threads() + break + + print num_threads + num_threads = 0xbad - for i in prange(1, nogil=True, num_threads=get_num_threads()): + for i in prange(1, nogil=True, num_threads=get_num_threads(), use_threads_if=size > 5): p[0] = openmp.omp_get_num_threads() break From 8d56987b7d5829970d2c7cdb4d439155679bc9bc Mon Sep 17 00:00:00 2001 From: da-woods Date: Sat, 30 Dec 2023 17:14:54 +0000 Subject: [PATCH 086/286] Add extra info on annotation inexact type (#5915) For most builtin types, Cython interprets an annotation to mean that an exact type is required. This is stricter than PEP-484 and occassionally causes confusion. 1. Adds a bit more detail to the exception message in these cases (or to __notes__ in Python 3.12+) to explain. 2. Add a note in the migration guide. I've only applied this to function arguments. Applying it to general assignments would be possible but looks significantly more complicated so I thought it better to cover the main case. Related issue: https://github.com/cython/cython/issues/5908 --- Cython/Compiler/Nodes.py | 11 +++++++- Cython/Utility/FunctionArguments.c | 35 +++++++++++++++++++++++- docs/src/userguide/migrating_to_cy30.rst | 5 ++++ tests/run/annotation_typing.pyx | 18 ++++++++++++ 4 files changed, 67 insertions(+), 2 deletions(-) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 8afe4e30fbf..8d56c0c087d 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -871,6 +871,7 @@ class CArgDeclNode(Node): # kw_only boolean Is a keyword-only argument # is_dynamic boolean Non-literal arg stored inside CyFunction # pos_only boolean Is a positional-only argument + # type_from_annotation boolean Was the type deduced from an annotation # # name_cstring property that converts the name to a cstring taking care of unicode # and quoting it @@ -891,6 +892,7 @@ class CArgDeclNode(Node): default_value = None annotation = None is_dynamic = 0 + type_from_annotation = False def declared_name(self): return self.declarator.declared_name() @@ -992,6 +994,8 @@ def inject_type_from_annotations(self, env): elif not self.or_none and arg_type.can_be_optional(): self.not_none = True + if arg_type: + self.type_from_annotation = True return arg_type def calculate_default_value_code(self, code): @@ -2441,13 +2445,18 @@ def generate_arg_type_test(self, arg, code): UtilityCode.load_cached("ArgTypeTest", "FunctionArguments.c")) typeptr_cname = arg.type.typeptr_cname arg_code = "((PyObject *)%s)" % arg.entry.cname + exact = 0 + if arg.type.is_builtin_type and arg.type.require_exact: + # 2 is used to indicate that the type is from the annotation + # and provide a little extra info on failure. + exact = 2 if arg.type_from_annotation else 1 code.putln( 'if (unlikely(!__Pyx_ArgTypeTest(%s, %s, %d, %s, %s))) %s' % ( arg_code, typeptr_cname, arg.accept_none, arg.name_cstring, - arg.type.is_builtin_type and arg.type.require_exact, + exact, code.error_goto(arg.pos))) else: error(arg.pos, "Cannot test type of extern C class without type object name specification") diff --git a/Cython/Utility/FunctionArguments.c b/Cython/Utility/FunctionArguments.c index 2f3bab8744f..8c11a76d14f 100644 --- a/Cython/Utility/FunctionArguments.c +++ b/Cython/Utility/FunctionArguments.c @@ -1,6 +1,8 @@ //////////////////// ArgTypeTest.proto //////////////////// +// Exact is 0 (False), 1 (True) or 2 (True and from annotation) +// The latter gives a small amount of extra error diagnostics #define __Pyx_ArgTypeTest(obj, type, none_allowed, name, exact) \ ((likely(__Pyx_IS_TYPE(obj, type) | (none_allowed && (obj == Py_None)))) ? 1 : \ __Pyx__ArgTypeTest(obj, type, name, exact)) @@ -8,23 +10,54 @@ static int __Pyx__ArgTypeTest(PyObject *obj, PyTypeObject *type, const char *name, int exact); /*proto*/ //////////////////// ArgTypeTest //////////////////// +//@substitute: naming static int __Pyx__ArgTypeTest(PyObject *obj, PyTypeObject *type, const char *name, int exact) { __Pyx_TypeName type_name; __Pyx_TypeName obj_type_name; + PyObject *extra_info = $empty_unicode; + int from_annotation_subclass = 0; if (unlikely(!type)) { PyErr_SetString(PyExc_SystemError, "Missing type object"); return 0; } else if (!exact) { if (likely(__Pyx_TypeCheck(obj, type))) return 1; + } else if (exact == 2) { + // type from annotation + if (__Pyx_TypeCheck(obj, type)) { + from_annotation_subclass = 1; + extra_info = PYUNICODE("Note that Cython is deliberately stricter than PEP-484 and rejects subclasses of builtin types. If you need to pass subclasses then set the 'annotation_typing' directive to False."); + } } type_name = __Pyx_PyType_GetName(type); obj_type_name = __Pyx_PyType_GetName(Py_TYPE(obj)); PyErr_Format(PyExc_TypeError, "Argument '%.200s' has incorrect type (expected " __Pyx_FMT_TYPENAME - ", got " __Pyx_FMT_TYPENAME ")", name, type_name, obj_type_name); + ", got " __Pyx_FMT_TYPENAME ")" +#if __PYX_LIMITED_VERSION_HEX < 0x030C0000 + "%s%U" +#endif + , name, type_name, obj_type_name +#if __PYX_LIMITED_VERSION_HEX < 0x030C0000 + , (from_annotation_subclass ? ". " : ""), extra_info +#endif + ); +#if __PYX_LIMITED_VERSION_HEX >= 0x030C0000 + // Set the extra_info as a note instead. In principle it'd be possible to do this + // from Python 3.11 up, but PyErr_GetRaisedException makes it much easier so do it + // from Python 3.12 instead. + if (exact == 2 && from_annotation_subclass) { + PyObject *res; + PyObject *vargs[2]; + vargs[0] = PyErr_GetRaisedException(); + vargs[1] = extra_info; + res = PyObject_VectorcallMethod(PYUNICODE("add_note"), vargs, 2, NULL); + Py_XDECREF(res); + PyErr_SetRaisedException(vargs[0]); + } +#endif __Pyx_DECREF_TypeName(type_name); __Pyx_DECREF_TypeName(obj_type_name); return 0; diff --git a/docs/src/userguide/migrating_to_cy30.rst b/docs/src/userguide/migrating_to_cy30.rst index 77c768a6ecb..f427e84dc94 100644 --- a/docs/src/userguide/migrating_to_cy30.rst +++ b/docs/src/userguide/migrating_to_cy30.rst @@ -247,6 +247,11 @@ any Python object for ``x``), unless the language level is explicitly set to 2. To mitigate the effect, Cython 3.0 still accepts both Python ``int`` and ``long`` values under Python 2.x. +One potential issue you may encounter is that types like ``typing.List`` +are now understood in annotations (where previously they were ignored) +and are interpreted to mean *exact* ``list``. This is stricter than +the interpretation specified in PEP-484, which also allows subclasses. + To make it easier to handle cases where your interpretation of type annotations differs from Cython's, Cython 3 now supports setting the ``annotation_typing`` :ref:`directive ` on a diff --git a/tests/run/annotation_typing.pyx b/tests/run/annotation_typing.pyx index fbdd62b0498..e6f5caac1ce 100644 --- a/tests/run/annotation_typing.pyx +++ b/tests/run/annotation_typing.pyx @@ -391,6 +391,24 @@ def int_alias(a: cython.int, b: cy_i): print(cython.typeof(b)) +def test_inexact_types(d: dict): + """ + >>> test_inexact_types({}) # good + + Check that our custom pep484 warning is in either the error message + or the exception notes + >>> from collections import OrderedDict + >>> try: + ... test_inexact_types(OrderedDict()) + ... except TypeError as e: + ... assert ("Cython is deliberately stricter than PEP-484" in e.args[0] or + ... any("Cython is deliberately stricter than PEP-484" in note for note in getattr(e, "__notes__", []))), e + ... else: + ... assert False + """ + pass + + _WARNINGS = """ 15:32: Strings should no longer be used for type declarations. Use 'cython.int' etc. directly. 15:47: Dicts should no longer be used as type annotations. Use 'cython.int' etc. directly. From 04502f6afff6dca65ad0b0da857b21dcfa8a9b6a Mon Sep 17 00:00:00 2001 From: da-woods Date: Sat, 30 Dec 2023 17:14:54 +0000 Subject: [PATCH 087/286] Add extra info on annotation inexact type (#5915) For most builtin types, Cython interprets an annotation to mean that an exact type is required. This is stricter than PEP-484 and occassionally causes confusion. 1. Adds a bit more detail to the exception message in these cases (or to __notes__ in Python 3.12+) to explain. 2. Add a note in the migration guide. I've only applied this to function arguments. Applying it to general assignments would be possible but looks significantly more complicated so I thought it better to cover the main case. Related issue: https://github.com/cython/cython/issues/5908 --- Cython/Compiler/Nodes.py | 11 +++++++- Cython/Utility/FunctionArguments.c | 35 +++++++++++++++++++++++- docs/src/userguide/migrating_to_cy30.rst | 5 ++++ tests/run/annotation_typing.pyx | 18 ++++++++++++ 4 files changed, 67 insertions(+), 2 deletions(-) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 83f6f61f2d9..7eb56922372 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -878,6 +878,7 @@ class CArgDeclNode(Node): # kw_only boolean Is a keyword-only argument # is_dynamic boolean Non-literal arg stored inside CyFunction # pos_only boolean Is a positional-only argument + # type_from_annotation boolean Was the type deduced from an annotation # # name_cstring property that converts the name to a cstring taking care of unicode # and quoting it @@ -898,6 +899,7 @@ class CArgDeclNode(Node): default_value = None annotation = None is_dynamic = 0 + type_from_annotation = False def declared_name(self): return self.declarator.declared_name() @@ -999,6 +1001,8 @@ def inject_type_from_annotations(self, env): elif not self.or_none and arg_type.can_be_optional(): self.not_none = True + if arg_type: + self.type_from_annotation = True return arg_type def calculate_default_value_code(self, code): @@ -2455,13 +2459,18 @@ def generate_arg_type_test(self, arg, code): UtilityCode.load_cached("ArgTypeTest", "FunctionArguments.c")) typeptr_cname = arg.type.typeptr_cname arg_code = "((PyObject *)%s)" % arg.entry.cname + exact = 0 + if arg.type.is_builtin_type and arg.type.require_exact: + # 2 is used to indicate that the type is from the annotation + # and provide a little extra info on failure. + exact = 2 if arg.type_from_annotation else 1 code.putln( 'if (unlikely(!__Pyx_ArgTypeTest(%s, %s, %d, %s, %s))) %s' % ( arg_code, typeptr_cname, arg.accept_none, arg.name_cstring, - arg.type.is_builtin_type and arg.type.require_exact, + exact, code.error_goto(arg.pos))) else: error(arg.pos, "Cannot test type of extern C class without type object name specification") diff --git a/Cython/Utility/FunctionArguments.c b/Cython/Utility/FunctionArguments.c index 0dc89c638ab..822ccdcb4e2 100644 --- a/Cython/Utility/FunctionArguments.c +++ b/Cython/Utility/FunctionArguments.c @@ -1,6 +1,8 @@ //////////////////// ArgTypeTest.proto //////////////////// +// Exact is 0 (False), 1 (True) or 2 (True and from annotation) +// The latter gives a small amount of extra error diagnostics #define __Pyx_ArgTypeTest(obj, type, none_allowed, name, exact) \ ((likely(__Pyx_IS_TYPE(obj, type) | (none_allowed && (obj == Py_None)))) ? 1 : \ __Pyx__ArgTypeTest(obj, type, name, exact)) @@ -8,11 +10,14 @@ static int __Pyx__ArgTypeTest(PyObject *obj, PyTypeObject *type, const char *name, int exact); /*proto*/ //////////////////// ArgTypeTest //////////////////// +//@substitute: naming static int __Pyx__ArgTypeTest(PyObject *obj, PyTypeObject *type, const char *name, int exact) { __Pyx_TypeName type_name; __Pyx_TypeName obj_type_name; + PyObject *extra_info = $empty_unicode; + int from_annotation_subclass = 0; if (unlikely(!type)) { PyErr_SetString(PyExc_SystemError, "Missing type object"); return 0; @@ -24,12 +29,40 @@ static int __Pyx__ArgTypeTest(PyObject *obj, PyTypeObject *type, const char *nam } else { if (likely(__Pyx_TypeCheck(obj, type))) return 1; + } else if (exact == 2) { + // type from annotation + if (__Pyx_TypeCheck(obj, type)) { + from_annotation_subclass = 1; + extra_info = PYUNICODE("Note that Cython is deliberately stricter than PEP-484 and rejects subclasses of builtin types. If you need to pass subclasses then set the 'annotation_typing' directive to False."); + } } type_name = __Pyx_PyType_GetName(type); obj_type_name = __Pyx_PyType_GetName(Py_TYPE(obj)); PyErr_Format(PyExc_TypeError, "Argument '%.200s' has incorrect type (expected " __Pyx_FMT_TYPENAME - ", got " __Pyx_FMT_TYPENAME ")", name, type_name, obj_type_name); + ", got " __Pyx_FMT_TYPENAME ")" +#if __PYX_LIMITED_VERSION_HEX < 0x030C0000 + "%s%U" +#endif + , name, type_name, obj_type_name +#if __PYX_LIMITED_VERSION_HEX < 0x030C0000 + , (from_annotation_subclass ? ". " : ""), extra_info +#endif + ); +#if __PYX_LIMITED_VERSION_HEX >= 0x030C0000 + // Set the extra_info as a note instead. In principle it'd be possible to do this + // from Python 3.11 up, but PyErr_GetRaisedException makes it much easier so do it + // from Python 3.12 instead. + if (exact == 2 && from_annotation_subclass) { + PyObject *res; + PyObject *vargs[2]; + vargs[0] = PyErr_GetRaisedException(); + vargs[1] = extra_info; + res = PyObject_VectorcallMethod(PYUNICODE("add_note"), vargs, 2, NULL); + Py_XDECREF(res); + PyErr_SetRaisedException(vargs[0]); + } +#endif __Pyx_DECREF_TypeName(type_name); __Pyx_DECREF_TypeName(obj_type_name); return 0; diff --git a/docs/src/userguide/migrating_to_cy30.rst b/docs/src/userguide/migrating_to_cy30.rst index 77c768a6ecb..f427e84dc94 100644 --- a/docs/src/userguide/migrating_to_cy30.rst +++ b/docs/src/userguide/migrating_to_cy30.rst @@ -247,6 +247,11 @@ any Python object for ``x``), unless the language level is explicitly set to 2. To mitigate the effect, Cython 3.0 still accepts both Python ``int`` and ``long`` values under Python 2.x. +One potential issue you may encounter is that types like ``typing.List`` +are now understood in annotations (where previously they were ignored) +and are interpreted to mean *exact* ``list``. This is stricter than +the interpretation specified in PEP-484, which also allows subclasses. + To make it easier to handle cases where your interpretation of type annotations differs from Cython's, Cython 3 now supports setting the ``annotation_typing`` :ref:`directive ` on a diff --git a/tests/run/annotation_typing.pyx b/tests/run/annotation_typing.pyx index 7c49fea40f1..e6f2405d510 100644 --- a/tests/run/annotation_typing.pyx +++ b/tests/run/annotation_typing.pyx @@ -390,6 +390,24 @@ def int_alias(a: cython.int, b: cy_i): print(cython.typeof(b)) +def test_inexact_types(d: dict): + """ + >>> test_inexact_types({}) # good + + Check that our custom pep484 warning is in either the error message + or the exception notes + >>> from collections import OrderedDict + >>> try: + ... test_inexact_types(OrderedDict()) + ... except TypeError as e: + ... assert ("Cython is deliberately stricter than PEP-484" in e.args[0] or + ... any("Cython is deliberately stricter than PEP-484" in note for note in getattr(e, "__notes__", []))), e + ... else: + ... assert False + """ + pass + + _WARNINGS = """ 14:32: Strings should no longer be used for type declarations. Use 'cython.int' etc. directly. 14:47: Dicts should no longer be used as type annotations. Use 'cython.int' etc. directly. From 219c57eef3866daa96d2b7a2f9eb4bb1a8fb75db Mon Sep 17 00:00:00 2001 From: Kent Slaney Date: Sat, 30 Dec 2023 09:21:02 -0800 Subject: [PATCH 088/286] Fix error in auto-completion in Cygdb (#5924) --- Cython/Debugger/libcython.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cython/Debugger/libcython.py b/Cython/Debugger/libcython.py index a513c989802..b330ca76339 100644 --- a/Cython/Debugger/libcython.py +++ b/Cython/Debugger/libcython.py @@ -838,6 +838,9 @@ def complete(self, text, word): else: all_names = qnames + if word is None: + return all_names + words = text.strip().split() if not words or '.' not in words[-1]: # complete unqualified From 30e5861accbf7af23393a646e09a4693b490f2f7 Mon Sep 17 00:00:00 2001 From: da-woods Date: Sat, 30 Dec 2023 19:04:11 +0000 Subject: [PATCH 089/286] Revert "Add extra info on annotation inexact type (#5915)" This reverts commit 04502f6afff6dca65ad0b0da857b21dcfa8a9b6a. --- Cython/Compiler/Nodes.py | 11 +------- Cython/Utility/FunctionArguments.c | 35 +----------------------- docs/src/userguide/migrating_to_cy30.rst | 5 ---- tests/run/annotation_typing.pyx | 18 ------------ 4 files changed, 2 insertions(+), 67 deletions(-) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 7eb56922372..83f6f61f2d9 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -878,7 +878,6 @@ class CArgDeclNode(Node): # kw_only boolean Is a keyword-only argument # is_dynamic boolean Non-literal arg stored inside CyFunction # pos_only boolean Is a positional-only argument - # type_from_annotation boolean Was the type deduced from an annotation # # name_cstring property that converts the name to a cstring taking care of unicode # and quoting it @@ -899,7 +898,6 @@ class CArgDeclNode(Node): default_value = None annotation = None is_dynamic = 0 - type_from_annotation = False def declared_name(self): return self.declarator.declared_name() @@ -1001,8 +999,6 @@ def inject_type_from_annotations(self, env): elif not self.or_none and arg_type.can_be_optional(): self.not_none = True - if arg_type: - self.type_from_annotation = True return arg_type def calculate_default_value_code(self, code): @@ -2459,18 +2455,13 @@ def generate_arg_type_test(self, arg, code): UtilityCode.load_cached("ArgTypeTest", "FunctionArguments.c")) typeptr_cname = arg.type.typeptr_cname arg_code = "((PyObject *)%s)" % arg.entry.cname - exact = 0 - if arg.type.is_builtin_type and arg.type.require_exact: - # 2 is used to indicate that the type is from the annotation - # and provide a little extra info on failure. - exact = 2 if arg.type_from_annotation else 1 code.putln( 'if (unlikely(!__Pyx_ArgTypeTest(%s, %s, %d, %s, %s))) %s' % ( arg_code, typeptr_cname, arg.accept_none, arg.name_cstring, - exact, + arg.type.is_builtin_type and arg.type.require_exact, code.error_goto(arg.pos))) else: error(arg.pos, "Cannot test type of extern C class without type object name specification") diff --git a/Cython/Utility/FunctionArguments.c b/Cython/Utility/FunctionArguments.c index 822ccdcb4e2..0dc89c638ab 100644 --- a/Cython/Utility/FunctionArguments.c +++ b/Cython/Utility/FunctionArguments.c @@ -1,8 +1,6 @@ //////////////////// ArgTypeTest.proto //////////////////// -// Exact is 0 (False), 1 (True) or 2 (True and from annotation) -// The latter gives a small amount of extra error diagnostics #define __Pyx_ArgTypeTest(obj, type, none_allowed, name, exact) \ ((likely(__Pyx_IS_TYPE(obj, type) | (none_allowed && (obj == Py_None)))) ? 1 : \ __Pyx__ArgTypeTest(obj, type, name, exact)) @@ -10,14 +8,11 @@ static int __Pyx__ArgTypeTest(PyObject *obj, PyTypeObject *type, const char *name, int exact); /*proto*/ //////////////////// ArgTypeTest //////////////////// -//@substitute: naming static int __Pyx__ArgTypeTest(PyObject *obj, PyTypeObject *type, const char *name, int exact) { __Pyx_TypeName type_name; __Pyx_TypeName obj_type_name; - PyObject *extra_info = $empty_unicode; - int from_annotation_subclass = 0; if (unlikely(!type)) { PyErr_SetString(PyExc_SystemError, "Missing type object"); return 0; @@ -29,40 +24,12 @@ static int __Pyx__ArgTypeTest(PyObject *obj, PyTypeObject *type, const char *nam } else { if (likely(__Pyx_TypeCheck(obj, type))) return 1; - } else if (exact == 2) { - // type from annotation - if (__Pyx_TypeCheck(obj, type)) { - from_annotation_subclass = 1; - extra_info = PYUNICODE("Note that Cython is deliberately stricter than PEP-484 and rejects subclasses of builtin types. If you need to pass subclasses then set the 'annotation_typing' directive to False."); - } } type_name = __Pyx_PyType_GetName(type); obj_type_name = __Pyx_PyType_GetName(Py_TYPE(obj)); PyErr_Format(PyExc_TypeError, "Argument '%.200s' has incorrect type (expected " __Pyx_FMT_TYPENAME - ", got " __Pyx_FMT_TYPENAME ")" -#if __PYX_LIMITED_VERSION_HEX < 0x030C0000 - "%s%U" -#endif - , name, type_name, obj_type_name -#if __PYX_LIMITED_VERSION_HEX < 0x030C0000 - , (from_annotation_subclass ? ". " : ""), extra_info -#endif - ); -#if __PYX_LIMITED_VERSION_HEX >= 0x030C0000 - // Set the extra_info as a note instead. In principle it'd be possible to do this - // from Python 3.11 up, but PyErr_GetRaisedException makes it much easier so do it - // from Python 3.12 instead. - if (exact == 2 && from_annotation_subclass) { - PyObject *res; - PyObject *vargs[2]; - vargs[0] = PyErr_GetRaisedException(); - vargs[1] = extra_info; - res = PyObject_VectorcallMethod(PYUNICODE("add_note"), vargs, 2, NULL); - Py_XDECREF(res); - PyErr_SetRaisedException(vargs[0]); - } -#endif + ", got " __Pyx_FMT_TYPENAME ")", name, type_name, obj_type_name); __Pyx_DECREF_TypeName(type_name); __Pyx_DECREF_TypeName(obj_type_name); return 0; diff --git a/docs/src/userguide/migrating_to_cy30.rst b/docs/src/userguide/migrating_to_cy30.rst index f427e84dc94..77c768a6ecb 100644 --- a/docs/src/userguide/migrating_to_cy30.rst +++ b/docs/src/userguide/migrating_to_cy30.rst @@ -247,11 +247,6 @@ any Python object for ``x``), unless the language level is explicitly set to 2. To mitigate the effect, Cython 3.0 still accepts both Python ``int`` and ``long`` values under Python 2.x. -One potential issue you may encounter is that types like ``typing.List`` -are now understood in annotations (where previously they were ignored) -and are interpreted to mean *exact* ``list``. This is stricter than -the interpretation specified in PEP-484, which also allows subclasses. - To make it easier to handle cases where your interpretation of type annotations differs from Cython's, Cython 3 now supports setting the ``annotation_typing`` :ref:`directive ` on a diff --git a/tests/run/annotation_typing.pyx b/tests/run/annotation_typing.pyx index e6f2405d510..7c49fea40f1 100644 --- a/tests/run/annotation_typing.pyx +++ b/tests/run/annotation_typing.pyx @@ -390,24 +390,6 @@ def int_alias(a: cython.int, b: cy_i): print(cython.typeof(b)) -def test_inexact_types(d: dict): - """ - >>> test_inexact_types({}) # good - - Check that our custom pep484 warning is in either the error message - or the exception notes - >>> from collections import OrderedDict - >>> try: - ... test_inexact_types(OrderedDict()) - ... except TypeError as e: - ... assert ("Cython is deliberately stricter than PEP-484" in e.args[0] or - ... any("Cython is deliberately stricter than PEP-484" in note for note in getattr(e, "__notes__", []))), e - ... else: - ... assert False - """ - pass - - _WARNINGS = """ 14:32: Strings should no longer be used for type declarations. Use 'cython.int' etc. directly. 14:47: Dicts should no longer be used as type annotations. Use 'cython.int' etc. directly. From c22c4e2d14d8a69b90c5ea1298d3976ae3bdbec1 Mon Sep 17 00:00:00 2001 From: da-woods Date: Sun, 31 Dec 2023 18:48:33 +0000 Subject: [PATCH 090/286] Revert "Fix error in auto-completion in Cygdb (#5924)" This reverts commit 219c57eef3866daa96d2b7a2f9eb4bb1a8fb75db. --- Cython/Debugger/libcython.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/Cython/Debugger/libcython.py b/Cython/Debugger/libcython.py index b330ca76339..a513c989802 100644 --- a/Cython/Debugger/libcython.py +++ b/Cython/Debugger/libcython.py @@ -838,9 +838,6 @@ def complete(self, text, word): else: all_names = qnames - if word is None: - return all_names - words = text.strip().split() if not words or '.' not in words[-1]: # complete unqualified From a0b759aab3849eaa97b7a548a6912cd3f3ff4911 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Mon, 1 Jan 2024 16:34:07 +0100 Subject: [PATCH 091/286] docs: Add a note that global variable annotations are ignored. --- docs/src/tutorial/pure.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/src/tutorial/pure.rst b/docs/src/tutorial/pure.rst index 32a7fa0cae8..438741ac8d1 100644 --- a/docs/src/tutorial/pure.rst +++ b/docs/src/tutorial/pure.rst @@ -364,6 +364,11 @@ Note the use of ``cython.int`` rather than ``int`` - Cython does not translate an ``int`` annotation to a C integer by default since the behaviour can be quite different with respect to overflow and division. +Annotations on global variables are currently ignored. This is because we expect +annotation-typed code to be in majority written for Python, and global type annotations +would turn the Python variable into an internal C variable, thus removing it from the +module dict. To declare global variables as typed C variables, use ``@cython.declare()``. + Annotations can be combined with the ``@cython.exceptval()`` decorator for non-Python return types: From 7bc98636c684863bb3c6da6cb946810ea4811ed8 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Mon, 1 Jan 2024 17:13:08 +0100 Subject: [PATCH 092/286] Add "p_..." pointer types of signed/unsigned integer types to Shadow.py. Closes https://github.com/cython/cython/issues/5934 --- Cython/Shadow.py | 11 ++++++++- Cython/Tests/TestShadow.py | 47 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 Cython/Tests/TestShadow.py diff --git a/Cython/Shadow.py b/Cython/Shadow.py index b37949a5762..0f87ced2fc6 100644 --- a/Cython/Shadow.py +++ b/Cython/Shadow.py @@ -505,9 +505,18 @@ def _specialized_from_args(signatures, args, kwargs): void = typedef(None, "void") Py_tss_t = typedef(None, "Py_tss_t") -for t in int_types + float_types + complex_types + other_types: +for t in int_types: for i in range(1, 4): gs["%s_%s" % ('p'*i, t)] = gs[t]._pointer(i) + if 'u'+t in gs: + gs["%s_u%s" % ('p'*i, t)] = gs['u'+t]._pointer(i) + gs["%s_s%s" % ('p'*i, t)] = gs['s'+t]._pointer(i) + +for t in float_types + complex_types + other_types: + for i in range(1, 4): + gs["%s_%s" % ('p'*i, t)] = gs[t]._pointer(i) + +del t, i NULL = gs['p_void'](0) diff --git a/Cython/Tests/TestShadow.py b/Cython/Tests/TestShadow.py new file mode 100644 index 00000000000..df5e849be28 --- /dev/null +++ b/Cython/Tests/TestShadow.py @@ -0,0 +1,47 @@ +import unittest + +from Cython import Shadow +from Cython.Compiler import Options, CythonScope + +class TestShadow(unittest.TestCase): + def test_all_types_in_shadow(self): + cython_scope = CythonScope.create_cython_scope(None) + # Not doing load_cythonscope at this stage because it requires a proper context and + # Errors.py to be set up + + missing_types = [] + for key in cython_scope.entries.keys(): + if key.startswith('__') and key.endswith('__'): + continue + if key in ('PyTypeObject', 'PyObject_TypeCheck'): + # These are declared in Shadow.py for reasons that look to + # be an implementation detail, but it isn't our intention for + # users to access them from Pure Python mode. + continue + if not hasattr(Shadow, key): + missing_types.append(key) + self.assertEqual(missing_types, []) + + def test_int_types_in_shadow(self): + missing_types = [] + for int_name in Shadow.int_types: + for sign in ['', 'u', 's']: + name = sign + int_name + + if sign and int_name in ['Py_UNICODE', 'Py_UCS4', 'Py_ssize_t', 'size_t']: + self.assertNotIn(name, dir(Shadow)) + self.assertNotIn('p_' + name, dir(Shadow)) + continue + + if not hasattr(Shadow, name): + missing_types.append(name) + + for ptr in range(1, 4): + ptr_name = 'p' * ptr + '_' + name + if not hasattr(Shadow, ptr_name): + missing_types.append(ptr_name) + self.assertEqual(missing_types, []) + + # TODO - there's a lot of types that are looked up by `cython_scope.lookup_type` that + # it's unfortunately hard to get a definite list of to confirm that they're present + # (because they're obtained by on-the-fly string parsing) From 513ecb645df8dfd9675a1f0ac2956a129fd45b54 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Mon, 8 Jan 2024 09:57:18 +0100 Subject: [PATCH 093/286] Avoid C99-ism in 3.0.x branch which still needs to support old Py2.7 MSVC builds. --- Cython/Utility/ObjectHandling.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 91b4024232c..743a8f1b02a 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1999,10 +1999,11 @@ typedef struct { static PyObject *__Pyx_SelflessCall(PyObject *method, PyObject *args, PyObject *kwargs) { // NOTE: possible optimization - use vectorcall + PyObject *result; PyObject *selfless_args = PyTuple_GetSlice(args, 1, PyTuple_Size(args)); if (unlikely(!selfless_args)) return NULL; - PyObject *result = PyObject_Call(method, selfless_args, kwargs); + result = PyObject_Call(method, selfless_args, kwargs); Py_DECREF(selfless_args); return result; } From 71baefae2479b82c6d2d364b8c551921a461f4f7 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Mon, 8 Jan 2024 20:36:27 +0100 Subject: [PATCH 094/286] Avoid C99-ism. --- Cython/Utility/ObjectHandling.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 743a8f1b02a..7c4e9232b2e 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -242,8 +242,8 @@ static CYTHON_INLINE int __Pyx_IterFinish(void); /*proto*/ static CYTHON_INLINE int __Pyx_IterFinish(void) { __Pyx_PyThreadState_declare - __Pyx_PyThreadState_assign PyObject* exc_type = __Pyx_PyErr_CurrentExceptionType(); + __Pyx_PyThreadState_assign if (unlikely(exc_type)) { if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) return -1; From 7d976cbe9cc8823b0ef2aaeb354647cc8664eb4e Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Mon, 8 Jan 2024 20:36:27 +0100 Subject: [PATCH 095/286] Avoid C99-ism. --- Cython/Utility/ObjectHandling.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 7c4e9232b2e..b0a8554a7c5 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -241,9 +241,10 @@ static CYTHON_INLINE int __Pyx_IterFinish(void); /*proto*/ // detects an error that occurred in the iterator, it returns -1. static CYTHON_INLINE int __Pyx_IterFinish(void) { + PyObject* exc_type; __Pyx_PyThreadState_declare - PyObject* exc_type = __Pyx_PyErr_CurrentExceptionType(); __Pyx_PyThreadState_assign + exc_type = __Pyx_PyErr_CurrentExceptionType(); if (unlikely(exc_type)) { if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) return -1; From f08dbc3013118b972c0d26b241f0f3bda1559379 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Mon, 8 Jan 2024 21:14:36 +0100 Subject: [PATCH 096/286] Actually test the 3.0.x branch with C89 since it should continue to support Py2.7 and old MSVC versions that go with it. --- .github/workflows/ci.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a5771df3c30..be82f64fb6b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,7 +62,12 @@ jobs: # Ubuntu sub-jobs: # ================ - # GCC 11 (with latest language standards) + # GCC 11 (with broad language standards) + - os: ubuntu-20.04 + python-version: 3.9 + backend: c + env: { GCC_VERSION: 11, EXTRA_CFLAGS: "-std=c89" } + extra_hash: "-gcc11-c89" - os: ubuntu-20.04 python-version: 3.9 backend: c From 8aa863537b155c9162056f03d3f7d3f6dc1fa5b3 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Mon, 8 Jan 2024 21:21:30 +0100 Subject: [PATCH 097/286] Cleanups in master branch (which actually uses C99, not C89). --- .github/workflows/ci.yml | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dd713b68de8..6108bc7ca52 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,33 +61,28 @@ jobs: # ================ # GCC 11 (with broad language standards) - os: ubuntu-20.04 - python-version: 3.9 - backend: c - env: { GCC_VERSION: 11, EXTRA_CFLAGS: "-std=c89" } - extra_hash: "-gcc11-c89" - - os: ubuntu-20.04 - python-version: 3.8 + python-version: "3.9" backend: c env: { GCC_VERSION: 11, EXTRA_CFLAGS: "-std=c99" } extra_hash: "-c99" - os: ubuntu-20.04 - python-version: 3.11 + python-version: "3.11" backend: c env: { GCC_VERSION: 11, EXTRA_CFLAGS: "-std=c17" } extra_hash: "-gcc11" - os: ubuntu-20.04 - python-version: 3.11 + python-version: "3.11" backend: cpp env: { GCC_VERSION: 11, EXTRA_CFLAGS: "-std=c++20" } extra_hash: "-gcc11" # compile all modules - os: ubuntu-20.04 - python-version: 3.7 + python-version: "3.7" backend: c env: { CYTHON_COMPILE_ALL: 1 } extra_hash: "-all" - os: ubuntu-20.04 - python-version: 3.7 + python-version: "3.7" backend: cpp env: { CYTHON_COMPILE_ALL: 1 } extra_hash: "-all" @@ -103,18 +98,18 @@ jobs: extra_hash: "-all" # Linting - os: ubuntu-20.04 - python-version: 3.9 + python-version: "3.9" backend: "c,cpp" env: { TEST_CODE_STYLE: 1, NO_CYTHON_COMPILE: 1 } extra_hash: "-codestyle" # Limited API - os: ubuntu-20.04 - python-version: 3.7 + python-version: "3.7" backend: "c,cpp" env: { LIMITED_API: "--limited-api", EXCLUDE: "--no-file" } extra_hash: "-limited_api" - os: ubuntu-20.04 - python-version: 3.8 + python-version: "3.8" backend: "c,cpp" env: { LIMITED_API: "--limited-api", EXCLUDE: "--no-file" } extra_hash: "-limited_api" @@ -137,23 +132,23 @@ jobs: extra_hash: "-limited_api" # Type specs - os: ubuntu-20.04 - python-version: 3.9 + python-version: "3.9" backend: c env: { EXTRA_CFLAGS: "-DCYTHON_USE_TYPE_SPECS=1" } extra_hash: "-typespecs" - os: ubuntu-20.04 - python-version: 3.8 + python-version: "3.8" backend: c env: { EXTRA_CFLAGS: "-DCYTHON_USE_TYPE_SPECS=1" } extra_hash: "-typespecs" - os: ubuntu-20.04 - python-version: 3.7 + python-version: "3.7" backend: c env: { EXTRA_CFLAGS: "-DCYTHON_USE_TYPE_SPECS=1" } extra_hash: "-typespecs" # Stackless - os: ubuntu-20.04 - python-version: 3.8 + python-version: "3.8" backend: c env: { STACKLESS: true, PY: 3 } extra_hash: "-stackless" From 46a768e66724d2bf0dc6a64e9bce43dea6ffadc5 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Tue, 9 Jan 2024 09:58:55 +0100 Subject: [PATCH 098/286] Fix some C99-isms in 3.0.x branch. --- Cython/Utility/FunctionArguments.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cython/Utility/FunctionArguments.c b/Cython/Utility/FunctionArguments.c index 0dc89c638ab..becef68bc3c 100644 --- a/Cython/Utility/FunctionArguments.c +++ b/Cython/Utility/FunctionArguments.c @@ -492,8 +492,8 @@ static int __Pyx_MergeKeywords(PyObject *kwdict, PyObject *source_mapping) { #define __Pyx_Arg_NewRef_VARARGS(arg) __Pyx_NewRef(arg) #define __Pyx_Arg_XDECREF_VARARGS(arg) Py_XDECREF(arg) #else - #define __Pyx_Arg_NewRef_VARARGS(arg) arg // no-op - #define __Pyx_Arg_XDECREF_VARARGS(arg) // no-op - arg is borrowed + #define __Pyx_Arg_NewRef_VARARGS(arg) arg /* no-op */ + #define __Pyx_Arg_XDECREF_VARARGS(arg) /* no-op - arg is borrowed */ #endif #define __Pyx_NumKwargs_VARARGS(kwds) PyDict_Size(kwds) #define __Pyx_KwValues_VARARGS(args, nargs) NULL @@ -509,9 +509,9 @@ static int __Pyx_MergeKeywords(PyObject *kwdict, PyObject *source_mapping) { #else #define __Pyx_KwargsAsDict_FASTCALL(kw, kwvalues) _PyStack_AsDict(kwvalues, kw) #endif - #define __Pyx_Arg_NewRef_FASTCALL(arg) arg // no-op, __Pyx_Arg_FASTCALL is direct and this needs - // to have the same reference counting - #define __Pyx_Arg_XDECREF_FASTCALL(arg) // no-op - arg was returned from array + #define __Pyx_Arg_NewRef_FASTCALL(arg) arg /* no-op, __Pyx_Arg_FASTCALL is direct and this needs + to have the same reference counting */ + #define __Pyx_Arg_XDECREF_FASTCALL(arg) /* no-op - arg was returned from array */ #else #define __Pyx_Arg_FASTCALL __Pyx_Arg_VARARGS #define __Pyx_NumKwargs_FASTCALL __Pyx_NumKwargs_VARARGS @@ -555,7 +555,7 @@ static CYTHON_INLINE PyObject * __Pyx_GetKwValue_FASTCALL(PyObject *kwnames, PyO { int eq = __Pyx_PyUnicode_Equals(s, PyTuple_GET_ITEM(kwnames, i), Py_EQ); if (unlikely(eq != 0)) { - if (unlikely(eq < 0)) return NULL; // error + if (unlikely(eq < 0)) return NULL; /* error */ return kwvalues[i]; } } From d3b92b0a4207ee070405d6f1ab8a75e95b53c56a Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Tue, 9 Jan 2024 10:10:10 +0100 Subject: [PATCH 099/286] Use Py3.7 instead of Py3.9 for the C89 build since CPython switched to C99 at some point (e.g. "inline"). --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index be82f64fb6b..8bb5d1ce397 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,7 +64,7 @@ jobs: # ================ # GCC 11 (with broad language standards) - os: ubuntu-20.04 - python-version: 3.9 + python-version: "3.7" backend: c env: { GCC_VERSION: 11, EXTRA_CFLAGS: "-std=c89" } extra_hash: "-gcc11-c89" From 6990d6e24f89949c4cf32d143d8c20e516d93756 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Tue, 9 Jan 2024 10:19:46 +0100 Subject: [PATCH 100/286] Use Py3.6 instead of Py3.7/8/9 for the C89 build since CPython switched to C99 at some point (e.g. "inline"). --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8bb5d1ce397..9a5f015f747 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,7 +64,7 @@ jobs: # ================ # GCC 11 (with broad language standards) - os: ubuntu-20.04 - python-version: "3.7" + python-version: "3.6" backend: c env: { GCC_VERSION: 11, EXTRA_CFLAGS: "-std=c89" } extra_hash: "-gcc11-c89" From 9866ce478fa76a07babff8a5d9d53a4030eaf9e7 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Tue, 9 Jan 2024 10:21:17 +0100 Subject: [PATCH 101/286] Avoid C99-ism. --- Cython/Utility/FunctionArguments.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Utility/FunctionArguments.c b/Cython/Utility/FunctionArguments.c index becef68bc3c..b88b566a759 100644 --- a/Cython/Utility/FunctionArguments.c +++ b/Cython/Utility/FunctionArguments.c @@ -559,7 +559,7 @@ static CYTHON_INLINE PyObject * __Pyx_GetKwValue_FASTCALL(PyObject *kwnames, PyO return kwvalues[i]; } } - return NULL; // not found (no exception set) + return NULL; /* not found (no exception set) */ } #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000 From 30a6534a2279eddfd7a8b75de94897357eeaba2b Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Tue, 9 Jan 2024 10:24:08 +0100 Subject: [PATCH 102/286] Fix some C99-isms in 3.0.x branch. --- Cython/Utility/ModuleSetupCode.c | 7 ++++--- Cython/Utility/TypeConversion.c | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 9fa0a7bc615..80492466d5a 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -690,8 +690,9 @@ class __Pyx_FakeReference { PyErr_Fetch(&type, &value, &traceback); #if __PYX_LIMITED_VERSION_HEX >= 0x030B0000 - minor_version = 11; // we don't yet need to distinguish between versions > 11 - // Note that from 3.13, when we do we can use Py_Version + minor_version = 11; + // we don't yet need to distinguish between versions > 11 + // Note that from 3.13, when we do, we can use Py_Version #else if (!(version_info = PySys_GetObject("version_info"))) goto end; if (!(py_minor_version = PySequence_GetItem(version_info, 1))) goto end; @@ -772,7 +773,7 @@ class __Pyx_FakeReference { // 1. pass an empty bytes string as exception_table // 2. pass name as qualname (TODO this might implementing properly in future) PyCodeObject *result; - PyObject *empty_bytes = PyBytes_FromStringAndSize("", 0); // we don't have access to __pyx_empty_bytes here + PyObject *empty_bytes = PyBytes_FromStringAndSize("", 0); /* we don't have access to __pyx_empty_bytes here */ if (!empty_bytes) return NULL; result = #if PY_VERSION_HEX >= 0x030C0000 diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index 93c59e50db3..d2f62e67381 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -162,7 +162,7 @@ static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject*); typedef Py_ssize_t __Pyx_compact_pylong; typedef size_t __Pyx_compact_upylong; - #else // Py < 3.12 + #else /* Py < 3.12 */ #define __Pyx_PyLong_IsNeg(x) (Py_SIZE(x) < 0) #define __Pyx_PyLong_IsNonNeg(x) (Py_SIZE(x) >= 0) #define __Pyx_PyLong_IsZero(x) (Py_SIZE(x) == 0) From 914d7be4da613d95e55cce604a68a415960612e1 Mon Sep 17 00:00:00 2001 From: da-woods Date: Tue, 9 Jan 2024 10:27:20 +0000 Subject: [PATCH 103/286] Fix parsing of ptrdiff_t in PyrexTypes and add another "all types in Shadow.py?" test (GH-5938) --- Cython/Compiler/PyrexTypes.py | 2 ++ Cython/Shadow.py | 4 +++- Cython/Tests/TestShadow.py | 42 ++++++++++++++++++++++++++++++----- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 076e85f9dc5..dd62d4c5d0c 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -5342,6 +5342,8 @@ def parse_basic_type(name): signed = 2 elif name == 'size_t': signed = 0 + elif name == 'ptrdiff_t': + signed = 2 else: if name.startswith('u'): name = name[1:] diff --git a/Cython/Shadow.py b/Cython/Shadow.py index f7039f1dfa0..87e2f0412df 100644 --- a/Cython/Shadow.py +++ b/Cython/Shadow.py @@ -442,6 +442,8 @@ def _specialized_from_args(signatures, args, kwargs): 'Py_hash_t', 'Py_ssize_t', 'size_t', + 'ssize_t', + 'ptrdiff_t', ] float_types = [ 'longdouble', @@ -478,7 +480,7 @@ def _specialized_from_args(signatures, args, kwargs): for name in int_types: reprname = to_repr(name, name) gs[name] = typedef(py_int, reprname) - if name not in ('Py_UNICODE', 'Py_UCS4') and not name.endswith('size_t'): + if name not in ('Py_UNICODE', 'Py_UCS4', 'Py_hash_t', 'ptrdiff_t') and not name.endswith('size_t'): gs['u'+name] = typedef(py_int, "unsigned " + reprname) gs['s'+name] = typedef(py_int, "signed " + reprname) diff --git a/Cython/Tests/TestShadow.py b/Cython/Tests/TestShadow.py index 220a403df2c..329c3a51731 100644 --- a/Cython/Tests/TestShadow.py +++ b/Cython/Tests/TestShadow.py @@ -1,7 +1,7 @@ import unittest from Cython import Shadow -from Cython.Compiler import Options, CythonScope +from Cython.Compiler import Options, CythonScope, PyrexTypes, Errors class TestShadow(unittest.TestCase): def test_all_directives_in_shadow(self): @@ -63,7 +63,12 @@ def test_int_types_in_shadow(self): for sign in ['', 'u', 's']: name = sign + int_name - if sign and int_name in ['Py_UNICODE', 'Py_UCS4', 'Py_ssize_t', 'size_t']: + if sign and ( + int_name in ['Py_UNICODE', 'Py_UCS4', 'Py_ssize_t', + 'ssize_t', 'ptrdiff_t', 'Py_hash_t'] or + name == "usize_t"): + # size_t is special-cased here a little since ssize_t legitimate + # but usize_t isn't self.assertNotIn(name, dir(Shadow)) self.assertNotIn('p_' + name, dir(Shadow)) continue @@ -77,6 +82,33 @@ def test_int_types_in_shadow(self): missing_types.append(ptr_name) self.assertEqual(missing_types, []) - # TODO - there's a lot of types that are looked up by `cython_scope.lookup_type` that - # it's unfortunately hard to get a definite list of to confirm that they're present - # (because they're obtained by on-the-fly string parsing) + def test_most_types(self): + # TODO it's unfortunately hard to get a definite list of types to confirm that they're + # present (because they're obtained by on-the-fly string parsing in `cython_scope.lookup_type`) + + cython_scope = CythonScope.create_cython_scope(None) + # Set up just enough of "Context" and "Errors" that CythonScope.lookup_type can fail + class Context: + cpp = False + language_level = 3 + future_directives = [] + cython_scope.context = Context + Errors.init_thread() + + missing_types = [] + missing_lookups = [] + for (signed, longness, name), type_ in PyrexTypes.modifiers_and_name_to_type.items(): + if name == 'object': + continue # This probably shouldn't be in Shadow + if not hasattr(Shadow, name): + missing_types.append(name) + if not cython_scope.lookup_type(name): + missing_lookups.append(name) + for ptr in range(1, 4): + ptr_name = 'p' * ptr + '_' + name + if not hasattr(Shadow, ptr_name): + missing_types.append(ptr_name) + if not cython_scope.lookup_type(ptr_name): + missing_lookups.append(ptr_name) + self.assertEqual(missing_types, []) + self.assertEqual(missing_lookups, []) From b893b5733df0f579b5a52a94664a26e3fb4f48e8 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Tue, 9 Jan 2024 11:36:35 +0100 Subject: [PATCH 104/286] Clean up a few temporary variables in Shadow.py and add a warning when users access 'cython.gs'. --- Cython/Shadow.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Cython/Shadow.py b/Cython/Shadow.py index 87e2f0412df..1d1283e687d 100644 --- a/Cython/Shadow.py +++ b/Cython/Shadow.py @@ -490,6 +490,8 @@ def _specialized_from_args(signatures, args, kwargs): for name in complex_types: gs[name] = typedef(py_complex, to_repr(name, name)) +del name, reprname + bint = typedef(bool, "bint") void = typedef(None, "void") Py_tss_t = typedef(None, "Py_tss_t") @@ -509,8 +511,19 @@ def _specialized_from_args(signatures, args, kwargs): NULL = gs['p_void'](0) -# looks like 'gs' has some users out there by now... -#del gs +del gs + + +def __getattr__(name): + # looks like 'gs' has some users out there by now... + if name == 'gs': + import warnings + warnings.warn( + "'gs' is not a publicly exposed name in cython.*. Use vars() or globals() instead.", + DeprecationWarning) + return globals() + raise AttributeError(f"'cython' has no attribute {name!r}") + integral = floating = numeric = _FusedType() From dd5e03ef872786f3fd6b660cb8e609e0d20f39ef Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Tue, 9 Jan 2024 13:36:21 +0100 Subject: [PATCH 105/286] Extract a list of fixed-sign integer types and reuse them where possible. --- Cython/Compiler/PyrexTypes.py | 66 +++++++++++++++++------------------ 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index dd62d4c5d0c..95ee1a9c6f2 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -4862,6 +4862,17 @@ def specialize_here(self, pos, env, template_values=None): memoryviewslice_type = CStructOrUnionType("memoryviewslice", "struct", None, 1, "__Pyx_memviewslice") +fixed_sign_int_types = { + "bint": (1, c_bint_type), + "Py_UNICODE": (0, c_py_unicode_type), + "Py_UCS4": (0, c_py_ucs4_type), + "Py_hash_t": (2, c_py_hash_t_type), + "Py_ssize_t": (2, c_py_ssize_t_type), + "ssize_t": (2, c_ssize_t_type), + "size_t": (0, c_size_t_type), + "ptrdiff_t": (2, c_ptrdiff_t_type), +} + modifiers_and_name_to_type = { #(signed, longness, name) : type (0, 0, "char"): c_uchar_type, @@ -4896,18 +4907,14 @@ def specialize_here(self, pos, env, template_values=None): (1, 0, "void"): c_void_type, (1, 0, "Py_tss_t"): c_pytss_t_type, - (1, 0, "bint"): c_bint_type, - (0, 0, "Py_UNICODE"): c_py_unicode_type, - (0, 0, "Py_UCS4"): c_py_ucs4_type, - (2, 0, "Py_hash_t"): c_py_hash_t_type, - (2, 0, "Py_ssize_t"): c_py_ssize_t_type, - (2, 0, "ssize_t") : c_ssize_t_type, - (0, 0, "size_t") : c_size_t_type, - (2, 0, "ptrdiff_t") : c_ptrdiff_t_type, - (1, 0, "object"): py_object_type, } +modifiers_and_name_to_type.update({ + (signed, 0, name): tp + for name, (signed, tp) in fixed_sign_int_types.items() +}) + def is_promotion(src_type, dst_type): # It's hard to find a hard definition of promotion, but empirical # evidence suggests that the below is all that's allowed. @@ -5328,22 +5335,9 @@ def parse_basic_type(name): if basic_type: return basic_type # - signed = 1 longness = 0 - if name == 'Py_UNICODE': - signed = 0 - elif name == 'Py_UCS4': - signed = 0 - elif name == 'Py_hash_t': - signed = 2 - elif name == 'Py_ssize_t': - signed = 2 - elif name == 'ssize_t': - signed = 2 - elif name == 'size_t': - signed = 0 - elif name == 'ptrdiff_t': - signed = 2 + if name in fixed_sign_int_types: + signed, _ = fixed_sign_int_types[name] else: if name.startswith('u'): name = name[1:] @@ -5352,15 +5346,21 @@ def parse_basic_type(name): not name.startswith('short')): name = name[1:] signed = 2 - longness = 0 - while name.startswith('short'): - name = name.replace('short', '', 1).strip() - longness -= 1 - while name.startswith('long'): - name = name.replace('long', '', 1).strip() - longness += 1 - if longness != 0 and not name: - name = 'int' + else: + signed = 1 + + name_parts = iter(name.split()) + for modifier in name_parts: + if modifier == 'long': + longness += 1 + elif modifier == 'short': + longness -= 1 + else: + name = ' '.join([modifier, *name_parts]) + break + else: + if longness != 0: + name = 'int' # long/short [int] return simple_c_type(signed, longness, name) From b85be7e838318d251fc3d3fbfdf1a1ecf5a515fd Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Tue, 9 Jan 2024 13:38:34 +0100 Subject: [PATCH 106/286] Avoid C99-ism. --- Cython/Utility/Exceptions.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Utility/Exceptions.c b/Cython/Utility/Exceptions.c index 9a8261cae49..daf6578eb96 100644 --- a/Cython/Utility/Exceptions.c +++ b/Cython/Utility/Exceptions.c @@ -1083,7 +1083,7 @@ static PyCodeObject* __Pyx_CreateCodeObjectForTraceback( #else py_code = PyCode_NewEmpty(filename, funcname, py_line); #endif - Py_XDECREF(py_funcname); // XDECREF since it's only set on Py3 if cline + Py_XDECREF(py_funcname); /* XDECREF since it's only set on Py3 if cline */ return py_code; bad: Py_XDECREF(py_funcname); From e39e52c90b3aa14abe012e313ffd4edb6897df61 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Tue, 9 Jan 2024 15:06:36 +0100 Subject: [PATCH 107/286] Fix parsing of "longlong" etc. after the last change, i.e. Python style type names without spaces. --- Cython/Compiler/PyrexTypes.py | 44 +++++++++++++++++------------------ 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 95ee1a9c6f2..b41b1ec612c 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -5331,36 +5331,34 @@ def parse_basic_type(name): if base: return CPtrType(base) # + if name in fixed_sign_int_types: + return fixed_sign_int_types[name][1] basic_type = simple_c_type(1, 0, name) if basic_type: return basic_type # - longness = 0 - if name in fixed_sign_int_types: - signed, _ = fixed_sign_int_types[name] + if name.startswith('u'): + name = name[1:] + signed = 0 + elif (name.startswith('s') and + not name.startswith('short')): + name = name[1:] + signed = 2 else: - if name.startswith('u'): - name = name[1:] - signed = 0 - elif (name.startswith('s') and - not name.startswith('short')): - name = name[1:] - signed = 2 - else: - signed = 1 + signed = 1 - name_parts = iter(name.split()) - for modifier in name_parts: - if modifier == 'long': - longness += 1 - elif modifier == 'short': - longness -= 1 - else: - name = ' '.join([modifier, *name_parts]) - break + # We parse both (cy) 'long long' and (py) 'longlong' style names here. + longness = 0 + while name.startswith(('long', 'short')): + if name.startswith('long'): + name = name[4:].lstrip() + longness += 1 else: - if longness != 0: - name = 'int' # long/short [int] + name = name[5:].lstrip() + longness -= 1 + if longness != 0 and not name: + name = 'int' # long/short [int] + return simple_c_type(signed, longness, name) From 356495be50773262b09e158115aba0afe167cb97 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Tue, 9 Jan 2024 20:39:23 +0100 Subject: [PATCH 108/286] Avoid C99-ism. --- Cython/Utility/CythonFunction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index dbd9547eb2b..2f8efc9e8b5 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -58,7 +58,7 @@ typedef struct { // Dynamic default args and annotations void *defaults; int defaults_pyobjects; - size_t defaults_size; // used by FusedFunction for copying defaults + size_t defaults_size; /* used by FusedFunction for copying defaults */ int flags; // Defaults info From ffe6fa7fe47e6b5005c0aad7c6ae7144b3402f33 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Tue, 9 Jan 2024 20:59:03 +0100 Subject: [PATCH 109/286] Avoid C99-isms. --- Cython/Utility/Dataclasses.c | 10 +++++----- Cython/Utility/FunctionArguments.c | 6 +++--- Cython/Utility/ModuleSetupCode.c | 2 +- Cython/Utility/UFuncs_C.c | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cython/Utility/Dataclasses.c b/Cython/Utility/Dataclasses.c index 57806a7c4b3..0b69049ab22 100644 --- a/Cython/Utility/Dataclasses.c +++ b/Cython/Utility/Dataclasses.c @@ -39,7 +39,7 @@ static PyObject* __Pyx_LoadInternalModule(const char* name, const char* fallback if (!module) { PyObject *localDict, *runValue, *builtins, *modulename; if (!PyErr_ExceptionMatches(PyExc_ImportError)) goto bad; - PyErr_Clear(); // this is reasonably likely (especially on older versions of Python) + PyErr_Clear(); /* this is reasonably likely (especially on older versions of Python) */ #if PY_MAJOR_VERSION < 3 modulename = PyBytes_FromFormat("_cython_" CYTHON_ABI ".%s", name); #else @@ -47,17 +47,17 @@ static PyObject* __Pyx_LoadInternalModule(const char* name, const char* fallback #endif if (!modulename) goto bad; #if PY_MAJOR_VERSION >= 3 && CYTHON_COMPILING_IN_CPYTHON - module = PyImport_AddModuleObject(modulename); // borrowed + module = PyImport_AddModuleObject(modulename); /* borrowed */ #else - module = PyImport_AddModule(PyBytes_AsString(modulename)); // borrowed + module = PyImport_AddModule(PyBytes_AsString(modulename)); /* borrowed */ #endif Py_DECREF(modulename); if (!module) goto bad; Py_INCREF(module); if (PyObject_SetAttrString(shared_abi_module, name, module) < 0) goto bad; - localDict = PyModule_GetDict(module); // borrowed + localDict = PyModule_GetDict(module); /* borrowed */ if (!localDict) goto bad; - builtins = PyEval_GetBuiltins(); // borrowed + builtins = PyEval_GetBuiltins(); /* borrowed */ if (!builtins) goto bad; if (PyDict_SetItemString(localDict, "__builtins__", builtins) <0) goto bad; diff --git a/Cython/Utility/FunctionArguments.c b/Cython/Utility/FunctionArguments.c index b88b566a759..961fbc26ef8 100644 --- a/Cython/Utility/FunctionArguments.c +++ b/Cython/Utility/FunctionArguments.c @@ -301,7 +301,7 @@ static int __Pyx_ParseOptionalKeywords( if (*name) { values[name-argnames] = value; #if CYTHON_AVOID_BORROWED_REFS - Py_INCREF(value); // transfer ownership of value to values + Py_INCREF(value); /* transfer ownership of value to values */ Py_DECREF(key); #endif key = NULL; @@ -323,7 +323,7 @@ static int __Pyx_ParseOptionalKeywords( && _PyString_Eq(**name, key)) { values[name-argnames] = value; #if CYTHON_AVOID_BORROWED_REFS - value = NULL; // ownership transferred to values + value = NULL; /* ownership transferred to values */ #endif break; } @@ -357,7 +357,7 @@ static int __Pyx_ParseOptionalKeywords( if (cmp == 0) { values[name-argnames] = value; #if CYTHON_AVOID_BORROWED_REFS - value = NULL; // ownership transferred to values + value = NULL; /* ownership transferred to values */ #endif break; } diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 80492466d5a..33e7abe0614 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -680,7 +680,7 @@ class __Pyx_FakeReference { PyObject *exception_table = NULL; PyObject *types_module=NULL, *code_type=NULL, *result=NULL; #if __PYX_LIMITED_VERSION_HEX < 0x030B0000 - PyObject *version_info; // borrowed + PyObject *version_info; /* borrowed */ PyObject *py_minor_version = NULL; #endif long minor_version = 0; diff --git a/Cython/Utility/UFuncs_C.c b/Cython/Utility/UFuncs_C.c index e7ce8812ed8..2115e4b2b52 100644 --- a/Cython/Utility/UFuncs_C.c +++ b/Cython/Utility/UFuncs_C.c @@ -19,7 +19,7 @@ // getter functions because we can't forward-declare arrays static PyUFuncGenericFunction* {{ufunc_funcs_name}}(void); /* proto */ static char* {{ufunc_types_name}}(void); /* proto */ -static void* {{ufunc_data_name}}[] = {NULL}; // always null +static void* {{ufunc_data_name}}[] = {NULL}; /* always null */ /////////////////////// UFuncConsts ///////////////////////// From f974ec15b643dfb6338c0aef90976424d5a6bd2c Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Tue, 9 Jan 2024 22:26:45 +0100 Subject: [PATCH 110/286] Update changelog. --- CHANGES.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 85b5449b7e1..38daa78cc2d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,25 @@ Cython Changelog ================ +3.0.8 (2024-??-??) +================== + +Bugs fixed +---------- + +* Using ``const`` together with defined fused types could fail to compile. + (Github issue :issue:`5230`) + +* A "use after free" bug was fixed in parallel sections. + (Github issue :issue:`5922`) + +* Several types were not available as ``cython.*`` types in pure Python code. + +* The generated code is now correct C89 again, removing some C++ style ``//`` comments + and C99-style declaration-after-code code ordering. This is still relevant for some + ols C compilers, specifically ones that match old Python 2.7 installations. + + 3.0.7 (2023-12-19) ================== From eec604ba90e68df34bb4fc0ca3516630b97c1f0f Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Tue, 9 Jan 2024 22:41:21 +0100 Subject: [PATCH 111/286] Update changelog. --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 66827c726ef..63147b4601d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -18,9 +18,15 @@ Features added * Dataclasses support the ``match_args`` option. (Github issue :issue:`5381`) +* Threading in parallel sections can now be disabled with a new ``use_threads_if`` condition. + (Github issue :issue:`5919`) + * f-strings are slightly faster. (Github issue :issue:`5866`) +* ``dict.pop()`` is faster in some cases. + (Github issue :issue:`5911`) + * Most builtin methods now provide their return type for type inference. (Github issue :issue:`5865`) From b9bfa7f0492f4f71af1f034822fd90dd4ed3638e Mon Sep 17 00:00:00 2001 From: da-woods Date: Tue, 9 Jan 2024 10:27:20 +0000 Subject: [PATCH 112/286] Fix parsing of ptrdiff_t in PyrexTypes and add another "all types in Shadow.py?" test (GH-5938) --- Cython/Compiler/PyrexTypes.py | 2 ++ Cython/Shadow.py | 4 +++- Cython/Tests/TestShadow.py | 42 ++++++++++++++++++++++++++++++----- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 91ded10e50b..6188fad0b88 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -5321,6 +5321,8 @@ def parse_basic_type(name): signed = 2 elif name == 'size_t': signed = 0 + elif name == 'ptrdiff_t': + signed = 2 else: if name.startswith('u'): name = name[1:] diff --git a/Cython/Shadow.py b/Cython/Shadow.py index 0f87ced2fc6..148642abc89 100644 --- a/Cython/Shadow.py +++ b/Cython/Shadow.py @@ -451,6 +451,8 @@ def _specialized_from_args(signatures, args, kwargs): 'Py_hash_t', 'Py_ssize_t', 'size_t', + 'ssize_t', + 'ptrdiff_t', ] float_types = [ 'longdouble', @@ -491,7 +493,7 @@ def _specialized_from_args(signatures, args, kwargs): for name in int_types: reprname = to_repr(name, name) gs[name] = typedef(py_int, reprname) - if name not in ('Py_UNICODE', 'Py_UCS4') and not name.endswith('size_t'): + if name not in ('Py_UNICODE', 'Py_UCS4', 'Py_hash_t', 'ptrdiff_t') and not name.endswith('size_t'): gs['u'+name] = typedef(py_int, "unsigned " + reprname) gs['s'+name] = typedef(py_int, "signed " + reprname) diff --git a/Cython/Tests/TestShadow.py b/Cython/Tests/TestShadow.py index df5e849be28..83abdcec2bf 100644 --- a/Cython/Tests/TestShadow.py +++ b/Cython/Tests/TestShadow.py @@ -1,7 +1,7 @@ import unittest from Cython import Shadow -from Cython.Compiler import Options, CythonScope +from Cython.Compiler import Options, CythonScope, PyrexTypes, Errors class TestShadow(unittest.TestCase): def test_all_types_in_shadow(self): @@ -28,7 +28,12 @@ def test_int_types_in_shadow(self): for sign in ['', 'u', 's']: name = sign + int_name - if sign and int_name in ['Py_UNICODE', 'Py_UCS4', 'Py_ssize_t', 'size_t']: + if sign and ( + int_name in ['Py_UNICODE', 'Py_UCS4', 'Py_ssize_t', + 'ssize_t', 'ptrdiff_t', 'Py_hash_t'] or + name == "usize_t"): + # size_t is special-cased here a little since ssize_t legitimate + # but usize_t isn't self.assertNotIn(name, dir(Shadow)) self.assertNotIn('p_' + name, dir(Shadow)) continue @@ -42,6 +47,33 @@ def test_int_types_in_shadow(self): missing_types.append(ptr_name) self.assertEqual(missing_types, []) - # TODO - there's a lot of types that are looked up by `cython_scope.lookup_type` that - # it's unfortunately hard to get a definite list of to confirm that they're present - # (because they're obtained by on-the-fly string parsing) + def test_most_types(self): + # TODO it's unfortunately hard to get a definite list of types to confirm that they're + # present (because they're obtained by on-the-fly string parsing in `cython_scope.lookup_type`) + + cython_scope = CythonScope.create_cython_scope(None) + # Set up just enough of "Context" and "Errors" that CythonScope.lookup_type can fail + class Context: + cpp = False + language_level = 3 + future_directives = [] + cython_scope.context = Context + Errors.init_thread() + + missing_types = [] + missing_lookups = [] + for (signed, longness, name), type_ in PyrexTypes.modifiers_and_name_to_type.items(): + if name == 'object': + continue # This probably shouldn't be in Shadow + if not hasattr(Shadow, name): + missing_types.append(name) + if not cython_scope.lookup_type(name): + missing_lookups.append(name) + for ptr in range(1, 4): + ptr_name = 'p' * ptr + '_' + name + if not hasattr(Shadow, ptr_name): + missing_types.append(ptr_name) + if not cython_scope.lookup_type(ptr_name): + missing_lookups.append(ptr_name) + self.assertEqual(missing_types, []) + self.assertEqual(missing_lookups, []) From a1b79a6bc5326406ad73af73f5b41e3bb5f8da6e Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Wed, 10 Jan 2024 09:12:41 +0100 Subject: [PATCH 113/286] Prepare release of 3.0.8. --- CHANGES.rst | 2 +- Cython/Shadow.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 38daa78cc2d..bdee645a3a4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,7 +2,7 @@ Cython Changelog ================ -3.0.8 (2024-??-??) +3.0.8 (2024-01-10) ================== Bugs fixed diff --git a/Cython/Shadow.py b/Cython/Shadow.py index 148642abc89..c474813c7ec 100644 --- a/Cython/Shadow.py +++ b/Cython/Shadow.py @@ -2,7 +2,7 @@ from __future__ import absolute_import # Possible version formats: "3.1.0", "3.1.0a1", "3.1.0a1.dev0" -__version__ = "3.0.7" +__version__ = "3.0.8" try: from __builtin__ import basestring From 6f7a949303b2e067aad9f808a392754758d9967e Mon Sep 17 00:00:00 2001 From: da-woods Date: Thu, 11 Jan 2024 19:01:07 +0000 Subject: [PATCH 114/286] Fix types in cpdef_extern_func (#5940) I don't think the test is really interested in the exact type of strchr, so instead wrap a function with a known function type. Fixes #5939 --- tests/run/cpdef_extern_func.pyx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/run/cpdef_extern_func.pyx b/tests/run/cpdef_extern_func.pyx index e1ba3d09617..5325b39c88c 100644 --- a/tests/run/cpdef_extern_func.pyx +++ b/tests/run/cpdef_extern_func.pyx @@ -1,6 +1,5 @@ # cython: c_string_type=str # cython: c_string_encoding=ascii -# distutils: extra_compile_args=-fpermissive __doc__ = """ >>> sqrt(1) @@ -12,9 +11,9 @@ __doc__ = """ >>> log(10) # doctest: +ELLIPSIS Traceback (most recent call last): NameError: ...name 'log' is not defined ->>> strchr('abcabc', ord('c')) +>>> my_strchr('abcabc', ord('c')) 'cabc' ->>> strchr(needle=ord('c'), haystack='abcabc') +>>> my_strchr(needle=ord('c'), haystack='abcabc') 'cabc' """ @@ -24,5 +23,12 @@ cdef extern from "math.h": cdef double log(double) # not wrapped cdef extern from "string.h": - # signature must be exact in C++, disagrees with C - cpdef const char* strchr(const char *haystack, int needle); + """ + /* the return type of strchr differs between C and C++. This + test is not interested in that, so create a wrapper function with + a known return type */ + const char* my_strchr(const char *haystack, int needle) { + return strchr(haystack, needle); + } + """ + cpdef const char* my_strchr(const char *haystack, int needle); From fe59ead44dd682d01cb01796afff512a5e76856e Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Fri, 12 Jan 2024 09:52:30 +0100 Subject: [PATCH 115/286] Build: Let cibuildwheel decide itself which wheels to build. Closes https://github.com/cython/cython/issues/5904 --- .github/workflows/wheels.yml | 92 ++++++++++++++++++++++++------------ pyproject.toml | 30 ++++++++++++ 2 files changed, 93 insertions(+), 29 deletions(-) create mode 100644 pyproject.toml diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index b402f7387c3..5319756c410 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -26,6 +26,14 @@ on: paths: #- Cython/Build/** - .github/workflows/wheels.yml + - pyproject.toml + - MANIFEST.in + - setup.* + push: + paths: + #- Cython/Build/** + - .github/workflows/wheels.yml + - pyproject.toml - MANIFEST.in - setup.* workflow_dispatch: @@ -38,52 +46,77 @@ permissions: contents: write # to create GitHub release (softprops/action-gh-release) jobs: + generate-wheels-matrix: + # Create a matrix of all architectures & versions to build. + # This enables the next step to run cibuildwheel in parallel. + # From https://iscinumpy.dev/post/cibuildwheel-2-10-0/#only-210 + name: Generate wheels matrix + if: >- + github.event_name == 'push' || + github.event_name == 'release' || + github.event_name == 'schedule' || + github.event_name == 'workflow_dispatch' || + (github.event_name == 'pull_request' && + contains(github.event.pull_request.labels.*.name, 'Build System')) + runs-on: ubuntu-latest + outputs: + include: ${{ steps.set-matrix.outputs.include }} + steps: + - uses: actions/checkout@v4 + - name: Install cibuildwheel + # Nb. keep cibuildwheel version pin consistent with job below + run: pipx install cibuildwheel==2.16.2 + - id: set-matrix + run: | + MATRIX=$( + { + cibuildwheel --print-build-identifiers --prerelease-pythons --platform linux \ + | jq -nRc '{"only": inputs, "os": "ubuntu-latest"}' \ + && cibuildwheel --print-build-identifiers --prerelease-pythons --platform macos \ + | jq -nRc '{"only": inputs, "os": "macos-latest"}' \ + && cibuildwheel --print-build-identifiers --prerelease-pythons --platform windows \ + | jq -nRc '{"only": inputs, "os": "windows-2019"}' + } | jq -sc + ) + echo "$MATRIX" + echo "include=$MATRIX" >> $GITHUB_OUTPUT + build_wheels: - name: Build wheel for ${{ matrix.python }}-${{ matrix.buildplat[1] }} + name: Wheel ${{ matrix.only }} if: >- + github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'Build System')) - runs-on: ${{ matrix.buildplat[0] }} + needs: generate-wheels-matrix + runs-on: ${{ matrix.os }} + strategy: # Ensure that a wheel builder finishes even if another fails fail-fast: false matrix: - # Github Actions doesn't support pairing matrix values together, let's improvise - # https://github.com/github/feedback/discussions/7835#discussioncomment-1769026 - buildplat: - - [ubuntu-20.04, manylinux_x86_64] - - [ubuntu-20.04, manylinux_aarch64] - - [ubuntu-20.04, manylinux_i686] - - [ubuntu-20.04, musllinux_x86_64] - - [ubuntu-20.04, musllinux_aarch64] - - [macos-11, macosx_*] - - [windows-2019, win_amd64] - - [windows-2019, win32] - python: ["cp37", "cp38", "cp39", "cp310", "cp311", "cp312"] # Note: Wheels not needed for PyPy + include: ${{ fromJson(needs.generate-wheels-matrix.outputs.include) }} + steps: - name: Checkout Cython uses: actions/checkout@v3 - name: Set up QEMU - if: contains(matrix.buildplat[1], '_aarch64') - uses: docker/setup-qemu-action@v1 + if: runner.os == 'Linux' && !contains(matrix.only, 'x86') && !contains(matrix.only, 'i686') + uses: docker/setup-qemu-action@v3 with: platforms: all - + - name: Build wheels - uses: pypa/cibuildwheel@v2.13.0 - env: - # TODO: Build Cython with the compile-all flag? - CIBW_BUILD: ${{ matrix.python }}-${{ matrix.buildplat[1] }} - CIBW_PRERELEASE_PYTHONS: True - CIBW_ARCHS_LINUX: auto aarch64 - CIBW_ENVIRONMENT: CFLAGS='-O3 -g0 -mtune=generic -pipe -fPIC' LDFLAGS='-fPIC' - # TODO: Cython tests take a long time to complete - # consider running a subset in the future? - #CIBW_TEST_COMMAND: python {project}/runtests.py -vv --no-refnanny + # Nb. keep cibuildwheel version pin consistent with generate-matrix job above + uses: pypa/cibuildwheel@v2.16.2 + with: + only: ${{ matrix.only }} + # TODO: Cython tests take a long time to complete + # consider running a subset in the future? + #CIBW_TEST_COMMAND: python {project}/runtests.py -vv - name: Release uses: softprops/action-gh-release@v1 @@ -96,12 +129,13 @@ jobs: - uses: actions/upload-artifact@v3 with: - name: ${{ matrix.python }}-${{ startsWith(matrix.buildplat[1], 'macosx') && 'macosx' || matrix.buildplat[1] }} + name: ${{ matrix.only }} path: ./wheelhouse/*.whl build_sdist_pure_wheel: name: Build sdist and pure wheel if: >- + github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || @@ -122,7 +156,7 @@ jobs: run: | pip install --upgrade wheel setuptools python setup.py sdist - python setup.py bdist_wheel --no-cython-compile --universal + python setup.py bdist_wheel --no-cython-compile - uses: actions/upload-artifact@v3 with: diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000000..4561320c9dd --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,30 @@ +[build-system] +requires = ["setuptools", "wheel"] + +[tool.cibuildwheel] +build-verbosity = 2 +skip = ["pp*", "cp36*"] +# test-command = "make test" + +[tool.cibuildwheel.linux] +archs = ["x86_64", "aarch64", "i686"] +repair-wheel-command = "auditwheel repair --strip -w {dest_dir} {wheel}" + +[tool.cibuildwheel.linux.environment] +CFLAGS = "-O3 -g0 -pipe -fPIC -march=core2" +AR = "gcc-ar" +NM = "gcc-nm" +RANLIB = "gcc-ranlib" + +[[tool.cibuildwheel.overrides]] +select = "*aarch64" +environment = {CFLAGS = "-O3 -g0 -pipe -fPIC -march=armv8-a -mtune=cortex-a72", AR = "gcc-ar", NM = "gcc-nm", RANLIB = "gcc-ranlib" } + +[tool.cibuildwheel.windows] +archs = ["AMD64", "x86"] + +[tool.cibuildwheel.macos] +# https://cibuildwheel.readthedocs.io/en/stable/faq/#what-to-provide suggests to provide +# x86_64 and one of universal2 or arm64 wheels. x86_64 is still required by older pips, +# so additional arm64 wheels should suffice. +archs = ["x86_64", "arm64"] From f2d564a06b775bd1cedfc36f5fc8996865ad3675 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Fri, 12 Jan 2024 10:42:01 +0100 Subject: [PATCH 116/286] Extend and clean up test. --- tests/run/cpdef_extern_func.pyx | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/tests/run/cpdef_extern_func.pyx b/tests/run/cpdef_extern_func.pyx index 5325b39c88c..4b708e03e40 100644 --- a/tests/run/cpdef_extern_func.pyx +++ b/tests/run/cpdef_extern_func.pyx @@ -8,13 +8,20 @@ __doc__ = """ 2.0 >>> pxd_sqrt(9) 3.0 + >>> log(10) # doctest: +ELLIPSIS Traceback (most recent call last): NameError: ...name 'log' is not defined + >>> my_strchr('abcabc', ord('c')) 'cabc' >>> my_strchr(needle=ord('c'), haystack='abcabc') 'cabc' + +>>> strchr('abcabc', ord('c')) +'cabc' +>>> strchr(needle=ord('c'), haystack='abcabc') +'cabc' """ cdef extern from "math.h": @@ -24,11 +31,13 @@ cdef extern from "math.h": cdef extern from "string.h": """ - /* the return type of strchr differs between C and C++. This - test is not interested in that, so create a wrapper function with - a known return type */ - const char* my_strchr(const char *haystack, int needle) { + /* The return type of strchr differs between C and C++. + This test is not interested in that, so create a wrapper function + with a known return type. + */ + static const char* my_strchr(const char *haystack, int needle) { return strchr(haystack, needle); } """ - cpdef const char* my_strchr(const char *haystack, int needle); + cpdef const char* my_strchr(const char *haystack, int needle) + cpdef const char* strchr "my_strchr" (const char *haystack, int needle) From 7b8ad1c65bf27dc558e19a9191f09eac7749f490 Mon Sep 17 00:00:00 2001 From: da-woods Date: Sun, 14 Jan 2024 09:34:42 +0000 Subject: [PATCH 117/286] Use vectorcall by default (#5804) f(a, b) goes to vectorcall (as before) f(a, b, **kwds) now goes to "vectorcall_dict f(a, b, c=c) now goes to vectorcall with kwnames --- Cython/Compiler/ExprNodes.py | 196 +++++++++++++++++++++++++------ Cython/Compiler/Optimize.py | 56 +++++---- Cython/Utility/ModuleSetupCode.c | 9 ++ Cython/Utility/ObjectHandling.c | 66 ++++++++++- Cython/Utility/TypeConversion.c | 22 ++-- tests/run/call_py_cy.pyx | 36 ++++++ 6 files changed, 312 insertions(+), 73 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 430945506f1..e6f37e74556 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -5987,6 +5987,16 @@ def analyse_as_type_constructor(self, env): self.type = type return True + def function_type(self): + # Return the type of the function being called, coercing a function + # pointer to a function if necessary. + func_type = self.function.type + + if func_type.is_ptr: + func_type = func_type.base_type + + return func_type + def is_lvalue(self): return self.type.is_reference @@ -6109,17 +6119,6 @@ def analyse_types(self, env): return self - def function_type(self): - # Return the type of the function being called, coercing a function - # pointer to a function if necessary. If the function has fused - # arguments, return the specific type. - func_type = self.function.type - - if func_type.is_ptr: - func_type = func_type.base_type - - return func_type - def analyse_c_function_call(self, env): func_type = self.function.type if func_type is error_type: @@ -6528,15 +6527,18 @@ def generate_evaluation_code(self, code): ", ".join(a.pythran_result() for a in args))) -class PyMethodCallNode(SimpleCallNode): +class PyMethodCallNode(CallNode): # Specialised call to a (potential) PyMethodObject with non-constant argument tuple. # Allows the self argument to be injected directly instead of repacking a tuple for it. # # function ExprNode the function/method object to call # arg_tuple TupleNode the arguments for the args tuple + # kwdict ExprNode or None keyword dictionary (if present) + # unpack bool - subexprs = ['function', 'arg_tuple'] + subexprs = ['function', 'arg_tuple', 'kwdict'] is_temp = True + kwdict = None def generate_evaluation_code(self, code): code.mark_pos(self.pos) @@ -6545,8 +6547,15 @@ def generate_evaluation_code(self, code): self.function.generate_evaluation_code(code) assert self.arg_tuple.mult_factor is None args = self.arg_tuple.args + kwargs_key_value_pairs = None for arg in args: arg.generate_evaluation_code(code) + if isinstance(self.kwdict, DictNode): + kwargs_key_value_pairs = self.kwdict.key_value_pairs + for keyvalue in kwargs_key_value_pairs: + keyvalue.generate_evaluation_code(code) + elif self.kwdict: + self.kwdict.generate_evaluation_code(code) # make sure function is in temp so that we can replace the reference below if it's a method reuse_function_temp = self.function.is_temp @@ -6585,42 +6594,90 @@ def attribute_is_likely_method(attr): else: likely_method = 'unlikely' - code.putln("#if CYTHON_UNPACK_METHODS") - code.putln("if (%s(PyMethod_Check(%s))) {" % (likely_method, function)) - code.putln("%s = PyMethod_GET_SELF(%s);" % (self_arg, function)) - # the following is always true in Py3 (kept only for safety), - # but is false for unbound methods in Py2 - code.putln("if (likely(%s)) {" % self_arg) - code.putln("PyObject* function = PyMethod_GET_FUNCTION(%s);" % function) - code.put_incref(self_arg, py_object_type) - code.put_incref("function", py_object_type) - # free method object as early to possible to enable reuse from CPython's freelist - code.put_decref_set(function, py_object_type, "function") - code.putln("%s = 1;" % arg_offset_cname) - code.putln("}") - code.putln("}") - code.putln("#endif") # CYTHON_UNPACK_METHODS - # TODO may need to deal with unused variables in the #else case + if self.unpack: + # unpack is ultimately governed by optimize.unpack_method_calls + # and is a separate decision to whether we want vectorcall-type behaviour + code.putln("#if CYTHON_UNPACK_METHODS") + code.putln("if (%s(PyMethod_Check(%s))) {" % (likely_method, function)) + code.putln("%s = PyMethod_GET_SELF(%s);" % (self_arg, function)) + # the result of PyMethod_GET_SELF is always true in Py3. + code.putln(f"assert({self_arg});") + code.putln("PyObject* function = PyMethod_GET_FUNCTION(%s);" % function) + code.put_incref(self_arg, py_object_type) + code.put_incref("function", py_object_type) + # free method object as early to possible to enable reuse from CPython's freelist + code.put_decref_set(function, py_object_type, "function") + code.putln("%s = 1;" % arg_offset_cname) + code.putln("}") + code.putln("#endif") # CYTHON_UNPACK_METHODS + # TODO may need to deal with unused variables in the #else case + kwnames_temp = None + if kwargs_key_value_pairs: + code.globalstate.use_utility_code( + UtilityCode.load_cached("PyObjectVectorCallKwBuilder", "ObjectHandling.c")) + function_caller = "__Pyx_Object_Vectorcall_CallFromBuilder" + kwnames_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True) + code.putln("%s = __Pyx_MakeVectorcallBuilderKwds(%s); %s" % ( + kwnames_temp, len(kwargs_key_value_pairs), + code.error_goto_if_null(kwnames_temp, self.pos) + )) + code.put_gotref(kwnames_temp, py_object_type) + elif self.kwdict: + code.globalstate.use_utility_code( + UtilityCode.load_cached("PyObjectFastCall", "ObjectHandling.c")) + function_caller = "__Pyx_PyObject_FastCallDict" + else: + code.globalstate.use_utility_code( + UtilityCode.load_cached("PyObjectFastCall", "ObjectHandling.c")) + function_caller = "__Pyx_PyObject_FastCall" # actually call the function - code.globalstate.use_utility_code( - UtilityCode.load_cached("PyObjectFastCall", "ObjectHandling.c")) + code.putln("{") + extra_keyword_args = "" + if kwargs_key_value_pairs: + extra_keyword_args = f"+ ((CYTHON_VECTORCALL) ? {len(kwargs_key_value_pairs)} : 0)" # To avoid passing an out-of-bounds argument pointer in the no-args case, # we need at least two entries, so we pad with NULL and point to that. # See https://github.com/cython/cython/issues/5668 - code.putln("PyObject *__pyx_callargs[%d] = {%s, %s};" % ( + code.putln("PyObject *__pyx_callargs[%d%s] = {%s, %s};" % ( (len(args) + 1) if args else 2, + extra_keyword_args, self_arg, ', '.join(arg.py_result() for arg in args) if args else "NULL", )) - code.putln("%s = __Pyx_PyObject_FastCall(%s, __pyx_callargs+1-%s, %d+%s);" % ( + if kwargs_key_value_pairs: + for n, keyvalue in enumerate(kwargs_key_value_pairs): + key_is_str = ( + (keyvalue.key.type is Builtin.str_type or keyvalue.key.type is Builtin.unicode_type) + and not keyvalue.key.may_be_none() + ) + code.put_error_if_neg( + self.pos, + "__Pyx_VectorcallBuilder_AddArg%s(%s, %s, %s, __pyx_callargs+%d, %d)" % ( + "" if key_is_str else "_Check", + keyvalue.key.py_result(), + keyvalue.value.py_result(), + kwnames_temp, + len(args) + 1, + n + )) + + if kwnames_temp: + keyword_variable = f", {kwnames_temp}" + elif self.kwdict: + keyword_variable = f", {self.kwdict.result()}" + else: + keyword_variable = "" + code.putln("%s = %s(%s, __pyx_callargs+1-%s, %d+%s%s);" % ( self.result(), + function_caller, function, arg_offset_cname, len(args), - arg_offset_cname)) + arg_offset_cname, + keyword_variable)) code.put_xdecref_clear(self_arg, py_object_type) code.funcstate.release_temp(self_arg) @@ -6628,6 +6685,15 @@ def attribute_is_likely_method(attr): for arg in args: arg.generate_disposal_code(code) arg.free_temps(code) + if kwargs_key_value_pairs: + for keyvalue in kwargs_key_value_pairs: + keyvalue.generate_disposal_code(code) + keyvalue.free_temps(code) + code.put_decref_clear(kwnames_temp, py_object_type) + code.funcstate.release_temp(kwnames_temp) + elif self.kwdict: + self.kwdict.generate_disposal_code(code) + self.kwdict.free_temps(code) code.putln(code.error_goto_if_null(self.result(), self.pos)) self.generate_gotref(code) @@ -6639,6 +6705,51 @@ def attribute_is_likely_method(attr): code.funcstate.release_temp(function) code.putln("}") + @staticmethod + def can_be_used_for_posargs(positional_args, has_kwargs, kwds_is_dict_node=None): + """ + Test whether the positional args given are compatible with + being translated into a PyMethodCallNode + """ + if not isinstance(positional_args, TupleNode): + return False + if positional_args.mult_factor: + return False + if positional_args.is_literal and len(positional_args.args) > 1: + return False + if not len(positional_args.args): + # If positional_args is an empty tuple, it's probably only + # worth optimizing if the kwds are f(a=1, b=2) and not + # if they're f(**kwds) + return has_kwargs and kwds_is_dict_node + return True + + + @staticmethod + def can_be_used_for_function(function): + """ + Test whether the function passed is suitable to be translated + into a PyMethodCallNode + """ + may_be_a_method = True + if function.type is Builtin.type_type: + may_be_a_method = False + elif function.is_attribute: + if function.entry and function.entry.type.is_cfunction: + # optimised builtin method + may_be_a_method = False + elif function.is_name: + entry = function.entry + if entry.is_builtin or entry.type.is_cfunction: + may_be_a_method = False + elif entry.cf_assignments: + # local functions/classes are definitely not methods + non_method_nodes = (PyCFunctionNode, ClassNode, Py3ClassNode) + may_be_a_method = any( + assignment.rhs and not isinstance(assignment.rhs, non_method_nodes) + for assignment in entry.cf_assignments) + return may_be_a_method + class InlinedDefNodeCallNode(CallNode): # Inline call to defnode @@ -7156,12 +7267,25 @@ def generate_evaluation_code(self, code): code.putln("%s = %s;" % (self.result(), item.py_result())) item.generate_post_assignment_code(code) else: + if item.is_temp: + # For the fairly plausible special case where item is a temporary + # with a refcount of 1 (so created specifically for us), + # avoid making a copy + code.putln("#if CYTHON_COMPILING_IN_CPYTHON") + code.putln("if (Py_REFCNT(%s) == 1) {" % item.py_result()) + code.putln("%s = %s;" % (self.result(), item.py_result())) + item.generate_post_assignment_code(code) + code.putln("} else") + code.putln("#endif") + code.putln("{") code.putln("%s = PyDict_Copy(%s); %s" % ( self.result(), item.py_result(), code.error_goto_if_null(self.result(), item.pos))) self.generate_gotref(code) item.generate_disposal_code(code) + if item.is_temp: + code.putln("}") if item.type is not dict_type: code.putln('} else {') @@ -9156,9 +9280,9 @@ class DictNode(ExprNode): # Dictionary constructor. # # key_value_pairs [DictItemNode] - # exclude_null_values [boolean] Do not add NULL values to dict + # exclude_null_values boolean Do not add NULL values to dict # - # obj_conversion_errors [PyrexError] used internally + # obj_conversion_errors PyrexError used internally subexprs = ['key_value_pairs'] is_temp = 1 diff --git a/Cython/Compiler/Optimize.py b/Cython/Compiler/Optimize.py index 6068e9bef24..f5c5b8b71f6 100644 --- a/Cython/Compiler/Optimize.py +++ b/Cython/Compiler/Optimize.py @@ -5067,6 +5067,13 @@ def visit_SingleAssignmentNode(self, node): lhs.lhs_of_first_assignment = True return node + def _check_optimize_method_calls(self, node): + function = node.function + return (node.is_temp and function.type.is_pyobject and self.current_directives.get( + "optimize.unpack_method_calls_in_pyinit" + if not self.in_loop and self.current_env().is_module_scope + else "optimize.unpack_method_calls")) + def visit_SimpleCallNode(self, node): """ Replace generic calls to isinstance(x, type) by a more efficient type check. @@ -5083,38 +5090,37 @@ def visit_SimpleCallNode(self, node): function.type = function.entry.type PyTypeObjectPtr = PyrexTypes.CPtrType(cython_scope.lookup('PyTypeObject').type) node.args[1] = ExprNodes.CastNode(node.args[1], PyTypeObjectPtr) - elif (node.is_temp and function.type.is_pyobject and self.current_directives.get( - "optimize.unpack_method_calls_in_pyinit" - if not self.in_loop and self.current_env().is_module_scope - else "optimize.unpack_method_calls")): + else: # optimise simple Python methods calls - if isinstance(node.arg_tuple, ExprNodes.TupleNode) and not ( - node.arg_tuple.mult_factor or (node.arg_tuple.is_literal and len(node.arg_tuple.args) > 1)): + if ExprNodes.PyMethodCallNode.can_be_used_for_posargs(node.arg_tuple, has_kwargs=False): # simple call, now exclude calls to objects that are definitely not methods - may_be_a_method = True - if function.type is Builtin.type_type: - may_be_a_method = False - elif function.is_attribute: - if function.entry and function.entry.type.is_cfunction: - # optimised builtin method - may_be_a_method = False - elif function.is_name: - entry = function.entry - if entry.is_builtin or entry.type.is_cfunction: - may_be_a_method = False - elif entry.cf_assignments: - # local functions/classes are definitely not methods - non_method_nodes = (ExprNodes.PyCFunctionNode, ExprNodes.ClassNode, ExprNodes.Py3ClassNode) - may_be_a_method = any( - assignment.rhs and not isinstance(assignment.rhs, non_method_nodes) - for assignment in entry.cf_assignments) - if may_be_a_method: + if ExprNodes.PyMethodCallNode.can_be_used_for_function(function): if (node.self and function.is_attribute and isinstance(function.obj, ExprNodes.CloneNode) and function.obj.arg is node.self): # function self object was moved into a CloneNode => undo function.obj = function.obj.arg node = self.replace(node, ExprNodes.PyMethodCallNode.from_node( - node, function=function, arg_tuple=node.arg_tuple, type=node.type)) + node, function=function, arg_tuple=node.arg_tuple, type=node.type, + unpack=self._check_optimize_method_calls(node))) + return node + + def visit_GeneralCallNode(self, node): + """ + Replace likely Python method calls by a specialised PyMethodCallNode. + """ + self.visitchildren(node) + has_kwargs = bool(node.keyword_args) + kwds_is_dict_node = isinstance(node.keyword_args, ExprNodes.DictNode) + if not ExprNodes.PyMethodCallNode.can_be_used_for_posargs( + node.positional_args, has_kwargs=has_kwargs, kwds_is_dict_node=kwds_is_dict_node): + return node + function = node.function + if not ExprNodes.PyMethodCallNode.can_be_used_for_function(function): + return node + + node = self.replace(node, ExprNodes.PyMethodCallNode.from_node( + node, function=function, arg_tuple=node.positional_args, kwdict=node.keyword_args, + type=node.type, unpack=self._check_optimize_method_calls(node))) return node def visit_NumPyMethodCallNode(self, node): diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 616ae3daa26..d62e1b6aba6 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -432,8 +432,17 @@ #endif #ifndef CYTHON_VECTORCALL +#if CYTHON_COMPILING_IN_LIMITED_API +// Possibly needs a bit of clearing up, however: +// the limited API doesn't define CYTHON_FAST_PYCCALL (because that involves +// a lot of access to internals) but does define CYTHON_VECTORCALL because +// that's available cleanly from Python 3.12. Note that only VectorcallDict isn't +// available though. +#define CYTHON_VECTORCALL (__PYX_LIMITED_VERSION_HEX >= 0x030C0000) +#else #define CYTHON_VECTORCALL (CYTHON_FAST_PYCCALL && PY_VERSION_HEX >= 0x030800B1) #endif +#endif /* Whether to use METH_FASTCALL with a fake backported implementation of vectorcall */ #define CYTHON_BACKPORT_VECTORCALL (CYTHON_METH_FASTCALL && PY_VERSION_HEX < 0x030800B1) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 9bf7ba831e9..89d461d1b71 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -2023,7 +2023,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObj #endif if (kwargs == NULL) { - #if CYTHON_VECTORCALL + #if CYTHON_VECTORCALL && !CYTHON_COMPILING_IN_LIMITED_API #if PY_VERSION_HEX < 0x03090000 vectorcallfunc f = _PyVectorcall_Function(func); #else @@ -2051,6 +2051,70 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObj #endif } +/////////////// PyObjectVectorCallKwBuilder.proto //////////////// +//@requires: PyObjectFastCall +// For versions that define PyObject_Vectorcall, use PyObject_Vectorcall and define functions to build a kwnames tuple and add arguments to args. +// For versions that don't, use __Pyx_PyObject_FastCallDict and functions to build a keyword dictionary + +CYTHON_UNUSED static int __Pyx_VectorcallBuilder_AddArg_Check(PyObject *key, PyObject *value, PyObject *builder, PyObject **args, int n); /* proto */ + +#if CYTHON_VECTORCALL +#if __Pyx > 0x03080000 +#define __Pyx_Object_Vectorcall_CallFromBuilder PyObject_Vectorcall +#else +#define __Pyx_Object_Vectorcall_CallFromBuilder _PyObject_Vectorcall +#endif + +#define __Pyx_MakeVectorcallBuilderKwds(n) PyTuple_New(n) + +static int __Pyx_VectorcallBuilder_AddArg(PyObject *key, PyObject *value, PyObject *builder, PyObject **args, int n); /* proto */ +static int __Pyx_VectorcallBuilder_AddArgStr(const char *key, PyObject *value, PyObject *builder, PyObject **args, int n); /* proto */ +#else +#define __Pyx_Object_Vectorcall_CallFromBuilder __Pyx_PyObject_FastCallDict + +#define __Pyx_MakeVectorcallBuilderKwds(n) PyDict_New() + +#define __Pyx_VectorcallBuilder_AddArg(key, value, builder, args, n) PyDict_SetItem(builder, key, value) +#define __Pyx_VectorcallBuilder_AddArgStr(key, value, builder, args, n) PyDict_SetItemString(builder, key, value) +#endif + + + +/////////////// PyObjectVectorCallKwBuilder //////////////// + +#if CYTHON_VECTORCALL +static int __Pyx_VectorcallBuilder_AddArg(PyObject *key, PyObject *value, PyObject *builder, PyObject **args, int n) { + (void)__Pyx_PyObject_FastCallDict; + + if (unlikely(__Pyx_PyTuple_SET_ITEM(builder, n, key))) return -1; + Py_INCREF(key); + args[n] = value; + return 0; +} + +CYTHON_UNUSED static int __Pyx_VectorcallBuilder_AddArg_Check(PyObject *key, PyObject *value, PyObject *builder, PyObject **args, int n) { + (void)__Pyx_VectorcallBuilder_AddArgStr; + if (unlikely(!PyUnicode_Check(key))) { + PyErr_SetString(PyExc_TypeError, "keywords must be strings"); + return -1; + } + return __Pyx_VectorcallBuilder_AddArg(key, value, builder, args, n); +} + +static int __Pyx_VectorcallBuilder_AddArgStr(const char *key, PyObject *value, PyObject *builder, PyObject **args, int n) { + PyObject *pyKey = PyUnicode_FromString(key); + if (!pyKey) return -1; + return __Pyx_VectorcallBuilder_AddArg(pyKey, value, builder, args, n); +} +#else // CYTHON_VECTORCALL +CYTHON_UNUSED static int __Pyx_VectorcallBuilder_AddArg_Check(PyObject *key, PyObject *value, PyObject *builder, PyObject **args, int n) { + if (unlikely(!PyUnicode_Check(key))) { + PyErr_SetString(PyExc_TypeError, "keywords must be strings"); + return -1; + } + return PyDict_SetItem(builder, key, value); +} +#endif /////////////// PyObjectCallMethod0.proto /////////////// diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index 6476d6afa73..17b8f59f904 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -680,6 +680,7 @@ static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value); /////////////// CIntToPy /////////////// //@requires: GCCDiagnostics +//@requires: ObjectHandling.c::PyObjectVectorCallKwBuilder static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value) { #ifdef __Pyx_HAS_GCC_DIAGNOSTIC @@ -718,8 +719,8 @@ static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value) { little, !is_unsigned); #else // call int.from_bytes() - PyObject *from_bytes, *result = NULL; - PyObject *py_bytes = NULL, *arg_tuple = NULL, *kwds = NULL, *order_str = NULL; + PyObject *from_bytes, *result = NULL, *kwds = NULL; + PyObject *py_bytes = NULL, *order_str = NULL; from_bytes = PyObject_GetAttrString((PyObject*)&PyLong_Type, "from_bytes"); if (!from_bytes) return NULL; py_bytes = PyBytes_FromStringAndSize((char*)bytes, sizeof({{TYPE}})); @@ -728,19 +729,18 @@ static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value) { // to ever run so it seems a pessimization mostly. order_str = PyUnicode_FromString(little ? "little" : "big"); if (!order_str) goto limited_bad; - arg_tuple = PyTuple_Pack(2, py_bytes, order_str); - if (!arg_tuple) goto limited_bad; - if (!is_unsigned) { - // default is signed=False - kwds = PyDict_New(); - if (!kwds) goto limited_bad; - if (PyDict_SetItemString(kwds, "signed", __Pyx_NewRef(Py_True))) goto limited_bad; + { + PyObject *args[3+(CYTHON_VECTORCALL ? 1 : 0)] = { NULL, py_bytes, order_str }; + if (!is_unsigned) { + kwds = __Pyx_MakeVectorcallBuilderKwds(1); + if (!kwds) goto limited_bad; + if (__Pyx_VectorcallBuilder_AddArgStr("signed", __Pyx_NewRef(Py_True), kwds, args+3, 0) < 0) goto limited_bad; + } + result = __Pyx_Object_Vectorcall_CallFromBuilder(from_bytes, args+1, 2 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET, kwds); } - result = PyObject_Call(from_bytes, arg_tuple, kwds); limited_bad: Py_XDECREF(kwds); - Py_XDECREF(arg_tuple); Py_XDECREF(order_str); Py_XDECREF(py_bytes); Py_XDECREF(from_bytes); diff --git a/tests/run/call_py_cy.pyx b/tests/run/call_py_cy.pyx index 0bacd2cf593..ec6ff35ef14 100644 --- a/tests/run/call_py_cy.pyx +++ b/tests/run/call_py_cy.pyx @@ -4,8 +4,11 @@ ####### # Test that Cython and Python functions can call each other in various signature combinations. +# and check that the right calls use vectorcall (PyMethodCallNode) ####### +cimport cython + py_call_noargs = eval("lambda: 'noargs'") py_call_onearg = eval("lambda arg: arg") py_call_twoargs = eval("lambda arg, arg2: (arg, arg2)") @@ -15,6 +18,7 @@ py_call_starstarargs = eval("lambda **kw: sorted(kw.items())") py_call_args_and_starstarargs = eval("lambda *args, **kw: (args, sorted(kw.items()))") +#@cython.test_fail_if_path_exists("//PyMethodCallNode") def cy_call_noargs(): """ >>> cy_call_noargs() @@ -23,6 +27,7 @@ def cy_call_noargs(): return py_call_noargs() +@cython.test_assert_path_exists("//PyMethodCallNode") def cy_call_onearg(f): """ >>> cy_call_onearg(py_call_onearg) @@ -43,6 +48,7 @@ def cy_call_onearg(f): return f('onearg') +@cython.test_assert_path_exists("//PyMethodCallNode") def cy_call_twoargs(f, arg): """ >>> cy_call_twoargs(py_call_twoargs, 132) @@ -61,6 +67,25 @@ def cy_call_twoargs(f, arg): return f(arg, 'twoargs') +@cython.test_assert_path_exists("//PyMethodCallNode") +def cy_call_arg_and_kwarg(f, arg): + """ + >>> cy_call_arg_and_kwarg(py_call_twoargs, 123) + (123, 'twoargs') + + + >>> class Class(object): + ... def method1(self, arg, arg2): return arg, arg2 + ... def method2(self, arg): return arg + >>> cy_call_arg_and_kwarg(Class().method1, 123) + (123, 'twoargs') + >>> cy_call_twoargs(Class.method2, Class()) + 'twoargs' + """ + return f(arg, arg2='twoargs') + + +@cython.test_assert_path_exists("//PyMethodCallNode") def cy_call_two_kwargs(f, arg): """ >>> cy_call_two_kwargs(py_call_twoargs, arg=132) @@ -79,6 +104,7 @@ def cy_call_two_kwargs(f, arg): return f(arg2='two-kwargs', arg=arg) +@cython.test_fail_if_path_exists("//PyMethodCallNode") def cy_call_starargs(*args): """ >>> cy_call_starargs() @@ -93,6 +119,7 @@ def cy_call_starargs(*args): return py_call_starargs(*args) +@cython.test_fail_if_path_exists("//PyMethodCallNode") def cy_call_pos_and_starargs(f, *args): """ >>> cy_call_pos_and_starargs(py_call_onearg) @@ -127,6 +154,10 @@ def cy_call_pos_and_starargs(f, *args): return f(args[0] if args else 'no-arg', *args[1:]) +# Choice of whether to use PyMethodCallNode here is pretty arbitrary - +# vectorcall_dict or PyObject_Call are likely to be fairly similar cost. +# The test is for the current behaviour but it isn't a big issue if it changes +@cython.test_fail_if_path_exists("//PyMethodCallNode") def cy_call_starstarargs(**kw): """ >>> kw = {} @@ -142,6 +173,10 @@ def cy_call_starstarargs(**kw): return py_call_starstarargs(**kw) +# Choice of whether to use PyMethodCallNode here is pretty arbitrary - +# vectorcall_dict or PyObject_Call are likely to be fairly similar cost. +# The test is for the current behaviour but it isn't a big issue if it changes +@cython.test_fail_if_path_exists("//PyMethodCallNode") def cy_call_kw_and_starstarargs(f=None, arg1=None, **kw): """ >>> kw = {} @@ -202,6 +237,7 @@ def cy_call_kw_and_starstarargs(f=None, arg1=None, **kw): return (f or py_call_starstarargs)(arg=arg1, **kw) +@cython.test_assert_path_exists("//PyMethodCallNode") def cy_call_pos_and_starstarargs(f=None, arg1=None, **kw): """ >>> cy_call_pos_and_starstarargs(arg=123) From 2f1a9bafec62a63cabf1268e82cdca7b86395a32 Mon Sep 17 00:00:00 2001 From: da-woods Date: Sun, 14 Jan 2024 12:28:07 +0000 Subject: [PATCH 118/286] Force closing gzip file in TestCyCache (#5945) instead of relying on the destructor to do it. I'm not sure if this'll fix it, but it's maybe worth a try. If it works, fixes https://github.com/cython/cython/issues/5825 --- Cython/Build/Tests/TestCyCache.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Cython/Build/Tests/TestCyCache.py b/Cython/Build/Tests/TestCyCache.py index 5a94edc19ef..e2e241f1641 100644 --- a/Cython/Build/Tests/TestCyCache.py +++ b/Cython/Build/Tests/TestCyCache.py @@ -5,6 +5,7 @@ import sys import tempfile import unittest +from contextlib import closing import Cython.Build.Dependencies import Cython.Utils @@ -65,9 +66,6 @@ def test_cycache_switch(self): msg='\n'.join(list(difflib.unified_diff( a_contents.split('\n'), a_contents1.split('\n')))[:10])) - @unittest.skipIf(sys.version_info[:2] == (3, 12) and sys.platform == "win32", - "This test is mysteriously broken on Windows on the CI only " - "(https://github.com/cython/cython/issues/5825)") def test_cycache_uses_cache(self): a_pyx = os.path.join(self.src_dir, 'a.pyx') a_c = a_pyx[:-4] + '.c' @@ -75,7 +73,8 @@ def test_cycache_uses_cache(self): f.write('pass') self.fresh_cythonize(a_pyx, cache=self.cache_dir) a_cache = os.path.join(self.cache_dir, os.listdir(self.cache_dir)[0]) - gzip.GzipFile(a_cache, 'wb').write(b'fake stuff') + with closing(gzip.GzipFile(a_cache, 'wb')) as gzipfile: + gzipfile.write(b'fake stuff') os.unlink(a_c) self.fresh_cythonize(a_pyx, cache=self.cache_dir) with open(a_c) as f: From 1e5329211e471de837b6c6be2da0e1796c6a6a03 Mon Sep 17 00:00:00 2001 From: da-woods Date: Sun, 14 Jan 2024 13:02:08 +0000 Subject: [PATCH 119/286] Try disabling gc for line-trace tests (#5947) This is something of a wild guess, to see if it fixes the tests on Windows 3.12. --- tests/run/line_trace.pyx | 133 +++++++++++++++++++++------------------ 1 file changed, 71 insertions(+), 62 deletions(-) diff --git a/tests/run/line_trace.pyx b/tests/run/line_trace.pyx index aee352335a7..4bc5ab56e1f 100644 --- a/tests/run/line_trace.pyx +++ b/tests/run/line_trace.pyx @@ -4,6 +4,8 @@ # tag: trace import sys +import gc +from contextlib import contextmanager from cpython.ref cimport PyObject, Py_INCREF, Py_XDECREF @@ -180,6 +182,17 @@ def py_return(retval=123): return retval """, plain_python_functions) +@contextmanager +def gc_off(): + was_enabled = gc.isenabled() + gc.disable() + try: + yield + finally: + if was_enabled: + gc.enable() + + def run_trace(func, *args, bint with_sys=False): """ >>> py_add = plain_python_functions['py_add'] @@ -229,17 +242,18 @@ def run_trace(func, *args, bint with_sys=False): """ trace = [] trace_func = _create_trace_func(trace) - if with_sys: - sys.settrace(trace_func) - else: - PyEval_SetTrace(trace_trampoline, trace_func) - try: - func(*args) - finally: + with gc_off(): if with_sys: - sys.settrace(None) + sys.settrace(trace_func) else: - PyEval_SetTrace(NULL, NULL) + PyEval_SetTrace(trace_trampoline, trace_func) + try: + func(*args) + finally: + if with_sys: + sys.settrace(None) + else: + PyEval_SetTrace(NULL, NULL) return trace @@ -278,24 +292,25 @@ def run_trace_with_exception(func, bint with_sys=False, bint fail=False): """ trace = ['cy_try_except' if fail else 'NO ERROR'] trace_func = _create__failing_line_trace_func(trace) if fail else _create_trace_func(trace) - if with_sys: - sys.settrace(trace_func) - else: - PyEval_SetTrace(trace_trampoline, trace_func) - try: - try: - retval = cy_try_except(func) - except ValueError as exc: - print("%s(%r)" % (type(exc).__name__, str(exc))) - except AttributeError as exc: - print("%s(%r)" % (type(exc).__name__, str(exc))) - else: - print('OK: %r' % retval) - finally: + with gc_off(): if with_sys: - sys.settrace(None) + sys.settrace(trace_func) else: - PyEval_SetTrace(NULL, NULL) + PyEval_SetTrace(trace_trampoline, trace_func) + try: + try: + retval = cy_try_except(func) + except ValueError as exc: + print("%s(%r)" % (type(exc).__name__, str(exc))) + except AttributeError as exc: + print("%s(%r)" % (type(exc).__name__, str(exc))) + else: + print('OK: %r' % retval) + finally: + if with_sys: + sys.settrace(None) + else: + PyEval_SetTrace(NULL, NULL) return trace[1:] @@ -312,11 +327,12 @@ def fail_on_call_trace(func, *args): """ trace = [] trace_func = _create_failing_call_trace_func(trace) - PyEval_SetTrace(trace_trampoline, trace_func) - try: - func(*args) - finally: - PyEval_SetTrace(NULL, NULL) + with gc_off(): + PyEval_SetTrace(trace_trampoline, trace_func) + try: + func(*args) + finally: + PyEval_SetTrace(NULL, NULL) assert not trace @@ -376,20 +392,21 @@ def fail_on_line_trace(fail_func, add_func, nogil_add_func): trace = ['NO ERROR'] exception = None trace_func = _create__failing_line_trace_func(trace) - PyEval_SetTrace(trace_trampoline, trace_func) - try: - x += 1 - add_func(1, 2) - x += 1 - if fail_func: - trace[0] = fail_func # trigger error on first line - x += 1 - nogil_add_func(3, 4) - x += 1 - except Exception as exc: - exception = str(exc) - finally: - PyEval_SetTrace(NULL, NULL) + with gc_off(): + PyEval_SetTrace(trace_trampoline, trace_func) + try: + x += 1 + add_func(1, 2) + x += 1 + if fail_func: + trace[0] = fail_func # trigger error on first line + x += 1 + nogil_add_func(3, 4) + x += 1 + except Exception as exc: + exception = str(exc) + finally: + PyEval_SetTrace(NULL, NULL) if exception: print(exception) else: @@ -397,15 +414,6 @@ def fail_on_line_trace(fail_func, add_func, nogil_add_func): return trace -def skip_on_win_py3_12_0(func): - if sys.version_info[:3] == (3, 12, 0) and sys.platform == "win32": - # This test is mysteriously failing on the CI only. Disable - # it for now and hope that the next minor release fixes it. - return None - return func - - -@skip_on_win_py3_12_0 def disable_trace(func, *args, bint with_sys=False): """ >>> py_add = plain_python_functions["py_add"] @@ -426,15 +434,16 @@ def disable_trace(func, *args, bint with_sys=False): """ trace = [] trace_func = _create_disable_tracing(trace) - if with_sys: - sys.settrace(trace_func) - else: - PyEval_SetTrace(trace_trampoline, trace_func) - try: - func(*args) - finally: + with gc_off(): if with_sys: - sys.settrace(None) + sys.settrace(trace_func) else: - PyEval_SetTrace(NULL, NULL) + PyEval_SetTrace(trace_trampoline, trace_func) + try: + func(*args) + finally: + if with_sys: + sys.settrace(None) + else: + PyEval_SetTrace(NULL, NULL) return trace From 2f314fb1b1fd0a93eca08d842a7616e5c804105d Mon Sep 17 00:00:00 2001 From: da-woods Date: Sun, 14 Jan 2024 13:20:08 +0000 Subject: [PATCH 120/286] Don't use contextlib in TestCyCache just use the gzipfile in the with statement directly. --- Cython/Build/Tests/TestCyCache.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cython/Build/Tests/TestCyCache.py b/Cython/Build/Tests/TestCyCache.py index e2e241f1641..f4d9dae9906 100644 --- a/Cython/Build/Tests/TestCyCache.py +++ b/Cython/Build/Tests/TestCyCache.py @@ -5,7 +5,6 @@ import sys import tempfile import unittest -from contextlib import closing import Cython.Build.Dependencies import Cython.Utils @@ -73,7 +72,7 @@ def test_cycache_uses_cache(self): f.write('pass') self.fresh_cythonize(a_pyx, cache=self.cache_dir) a_cache = os.path.join(self.cache_dir, os.listdir(self.cache_dir)[0]) - with closing(gzip.GzipFile(a_cache, 'wb')) as gzipfile: + with gzip.GzipFile(a_cache, 'wb') as gzipfile: gzipfile.write(b'fake stuff') os.unlink(a_c) self.fresh_cythonize(a_pyx, cache=self.cache_dir) From 0e1026cb7c767450a88ab822e8eda2b451dee3c6 Mon Sep 17 00:00:00 2001 From: da-woods Date: Mon, 15 Jan 2024 18:19:55 +0000 Subject: [PATCH 121/286] Update doc requirements and pin them to recent versions (#5946) Also fix a few small issues in Changes.rst (the PEP link was a "duplicate definition" error - that target is already defined elsewhere in the file). --- CHANGES.rst | 4 +- doc-requirements.txt | 115 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 113 insertions(+), 6 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index aafd5bafd0e..96db40d7e59 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -13,7 +13,7 @@ Features added * Many issues with the Limited C-API were resolved. (Github issues :issue:`5697`, :issue:`5798`, :issue:`5845`, :issue:`5846`, - :issue:`5885`, :issue:`5886`, :issue:`5888`) + :issue:`5885`, :issue:`5886`, :issue:`5888`) * Dataclasses support the ``match_args`` option. (Github issue :issue:`5381`) @@ -2186,7 +2186,7 @@ Features added both Python and C semantics of enums. (Github issue :issue:`2732`) -* `PEP-614 `_: +* `PEP-614`_: decorators can now be arbitrary Python expressions. (Github issue :issue:`4570`) diff --git a/doc-requirements.txt b/doc-requirements.txt index fe0a95fae23..70ac73b6b84 100644 --- a/doc-requirements.txt +++ b/doc-requirements.txt @@ -1,5 +1,112 @@ -sphinx==4.5.0 +sphinx==7.2.6 sphinx-issues==3.0.1 -sphinx-tabs==3.4.0 -Jinja2==3.0.3 -jupyter +sphinx-tabs==3.4.4 +Jinja2==3.1.3 +jupyter==1.0.0 +# automatic requirements from pip freeze below: +alabaster==0.7.16 +anyio==4.2.0 +argon2-cffi==23.1.0 +argon2-cffi-bindings==21.2.0 +arrow==1.3.0 +asttokens==2.4.1 +async-lru==2.0.4 +attrs==23.2.0 +Babel==2.14.0 +beautifulsoup4==4.12.2 +bleach==6.1.0 +certifi==2023.11.17 +cffi==1.16.0 +charset-normalizer==3.3.2 +comm==0.2.1 +debugpy==1.8.0 +decorator==5.1.1 +defusedxml==0.7.1 +docutils==0.18.1 +exceptiongroup==1.2.0 +executing==2.0.1 +fastjsonschema==2.19.1 +fqdn==1.5.1 +idna==3.6 +imagesize==1.4.1 +importlib-metadata==7.0.1 +ipykernel==6.28.0 +ipython==8.18.1 +ipywidgets==8.1.1 +isoduration==20.11.0 +jedi==0.19.1 +json5==0.9.14 +jsonpointer==2.4 +jsonschema==4.20.0 +jsonschema-specifications==2023.12.1 +jupyter-console==6.6.3 +jupyter-events==0.9.0 +jupyter-lsp==2.2.1 +jupyter_client==8.6.0 +jupyter_core==5.7.1 +jupyter_server==2.12.4 +jupyter_server_terminals==0.5.1 +jupyterlab==4.0.10 +jupyterlab-widgets==3.0.9 +jupyterlab_pygments==0.3.0 +jupyterlab_server==2.25.2 +MarkupSafe==2.1.3 +matplotlib-inline==0.1.6 +mistune==3.0.2 +nbclient==0.9.0 +nbconvert==7.14.1 +nbformat==5.9.2 +nest-asyncio==1.5.8 +notebook==7.0.6 +notebook_shim==0.2.3 +overrides==7.4.0 +packaging==23.2 +pandocfilters==1.5.0 +parso==0.8.3 +pexpect==4.9.0 +platformdirs==4.1.0 +prometheus-client==0.19.0 +prompt-toolkit==3.0.43 +psutil==5.9.7 +ptyprocess==0.7.0 +pure-eval==0.2.2 +pycparser==2.21 +Pygments==2.17.2 +python-dateutil==2.8.2 +python-json-logger==2.0.7 +PyYAML==6.0.1 +pyzmq==25.1.2 +qtconsole==5.5.1 +QtPy==2.4.1 +referencing==0.32.1 +requests==2.31.0 +rfc3339-validator==0.1.4 +rfc3986-validator==0.1.1 +rpds-py==0.17.1 +Send2Trash==1.8.2 +six==1.16.0 +sniffio==1.3.0 +snowballstemmer==2.2.0 +soupsieve==2.5 +sphinxcontrib-applehelp==1.0.8 +sphinxcontrib-devhelp==1.0.6 +sphinxcontrib-htmlhelp==2.0.5 +sphinxcontrib-jsmath==1.0.1 +sphinxcontrib-qthelp==1.0.7 +sphinxcontrib-serializinghtml==1.1.10 +stack-data==0.6.3 +terminado==0.18.0 +tinycss2==1.2.1 +tomli==2.0.1 +tornado==6.4 +traitlets==5.14.1 +types-python-dateutil==2.8.19.20240106 +typing_extensions==4.9.0 +uri-template==1.3.0 +urllib3==2.1.0 +wcwidth==0.2.13 +webcolors==1.13 +webencodings==0.5.1 +websocket-client==1.7.0 +widgetsnbextension==4.0.9 +zipp==3.17.0 From d633c252bc7c3e885c5ec4ea72fef2ebaecdb6cf Mon Sep 17 00:00:00 2001 From: dutoit Date: Thu, 8 Jun 2023 14:50:09 +0200 Subject: [PATCH 122/286] Making HPy test works - from PR 4490 --- Cython/Compiler/ModuleNode.py | 12 ++++++++++- Cython/Compiler/Options.py | 3 +++ Cython/Compiler/ParseTreeTransforms.py | 15 +++++++++++++ Cython/Compiler/Pipeline.py | 3 ++- Cython/Compiler/Symtab.py | 1 - Cython/Shadow.py | 4 ++-- runtests.py | 30 +++++++++++++++++++++++--- tests/run/hpy_basic.pyx | 22 +++++++++++++++++++ 8 files changed, 82 insertions(+), 8 deletions(-) create mode 100644 tests/run/hpy_basic.pyx diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 3014d729421..98c26387583 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -6,13 +6,14 @@ import cython cython.declare(Naming=object, Options=object, PyrexTypes=object, TypeSlots=object, error=object, warning=object, py_object_type=object, UtilityCode=object, - EncodedString=object, re=object) + EncodedString=object, re=object, textwrap=object) from collections import defaultdict import json import operator import os import re +import textwrap import sys from .PyrexTypes import CPtrType @@ -754,6 +755,15 @@ def _put_setup_code(code, name): code.put(UtilityCode.load_as_string(name, "ModuleSetupCode.c")[1]) def generate_module_preamble(self, env, options, cimported_modules, metadata, code): + inc = Code.IncludeCode(initial=True, verbatim=textwrap.dedent(""" + #ifdef HPY + #include "Python.h" + #include "hpy.h" + #else + #include "Python.h" + #endif + """)) + env.process_include(inc) code.put_generated_by() if metadata: code.putln("/* BEGIN: Cython Metadata") diff --git a/Cython/Compiler/Options.py b/Cython/Compiler/Options.py index 25dc7147fc4..dea124b02d8 100644 --- a/Cython/Compiler/Options.py +++ b/Cython/Compiler/Options.py @@ -350,6 +350,7 @@ class DEFER_ANALYSIS_OF_ARGUMENTS: 'dataclasses.dataclass': DEFER_ANALYSIS_OF_ARGUMENTS, 'dataclasses.field': DEFER_ANALYSIS_OF_ARGUMENTS, 'embedsignature.format': one_of('c', 'clinic', 'python'), + 'hpy': bool, } for key, val in _directive_defaults.items(): @@ -405,6 +406,7 @@ class DEFER_ANALYSIS_OF_ARGUMENTS: 'cpp_locals': ('module', 'function', 'cclass'), # I don't think they make sense in a with_statement 'ufunc': ('function',), 'legacy_implicit_noexcept': ('module', ), + 'hpy': ('module', 'function',), 'control_flow.dot_output': ('module',), 'control_flow.dot_annotate_defs': ('module',), } @@ -800,4 +802,5 @@ def to_fingerprint(item): create_extension=None, np_pythran=False, legacy_implicit_noexcept=None, + hpy=False, ) diff --git a/Cython/Compiler/ParseTreeTransforms.py b/Cython/Compiler/ParseTreeTransforms.py index f98d88ad635..3a190e4f2fa 100644 --- a/Cython/Compiler/ParseTreeTransforms.py +++ b/Cython/Compiler/ParseTreeTransforms.py @@ -4229,3 +4229,18 @@ def serialize_local_variables(self, entries): self.tb.start('LocalVar', attrs) self.tb.end('LocalVar') + +class HPy(CythonTransform): + """ + If a @cpython.hyp decorator is used + - set the global context option + - set a node attribute + In the future, this could replace a cpython node with an hpy node + """ + + def visit_Node(self, node): + if self.current_directives.get('hpy', False): + node.is_hpy = 1 + self.context.options.hpy = True + self.visitchildren(node) + return node \ No newline at end of file diff --git a/Cython/Compiler/Pipeline.py b/Cython/Compiler/Pipeline.py index 05d3735ffc8..d5b3ea320c8 100644 --- a/Cython/Compiler/Pipeline.py +++ b/Cython/Compiler/Pipeline.py @@ -151,7 +151,7 @@ def create_pipeline(context, mode, exclude_classes=()): from .ParseTreeTransforms import CalculateQualifiedNamesTransform from .TypeInference import MarkParallelAssignments, MarkOverflowingArithmetic from .ParseTreeTransforms import AdjustDefByDirectives, AlignFunctionDefinitions, AutoCpdefFunctionDefinitions - from .ParseTreeTransforms import RemoveUnreachableCode, GilCheck, CoerceCppTemps + from .ParseTreeTransforms import RemoveUnreachableCode, GilCheck, CoerceCppTemps, HPy from .FlowControl import ControlFlowAnalysis from .AnalysedTreeTransforms import AutoTestDictTransform from .AutoDocTransforms import EmbedSignature @@ -214,6 +214,7 @@ def create_pipeline(context, mode, exclude_classes=()): AnalyseExpressionsTransform(context), FindInvalidUseOfFusedTypes(), ExpandInplaceOperators(context), + HPy(context), IterationTransform(context), SwitchTransform(context), OptimizeBuiltinCalls(context), ## Necessary? diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index 62c8a68a2ec..c9127d28349 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -1316,7 +1316,6 @@ def __init__(self, name, parent_module, context, is_package=False): self.undeclared_cached_builtins = [] self.namespace_cname = self.module_cname self._cached_tuple_types = {} - self.process_include(Code.IncludeCode("Python.h", initial=True)) def qualifying_scope(self): return self.parent_module diff --git a/Cython/Shadow.py b/Cython/Shadow.py index 1d1283e687d..977f8454590 100644 --- a/Cython/Shadow.py +++ b/Cython/Shadow.py @@ -123,8 +123,8 @@ class _Optimization: embedsignature.format = overflowcheck.fold = optimize.use_switch = \ optimize.unpack_method_calls = lambda arg: _EmptyDecoratorAndManager() -final = internal = type_version_tag = no_gc_clear = no_gc = total_ordering = \ - ufunc = _empty_decorator +final = internal = type_version_tag = no_gc_clear = no_gc = \ +total_ordering = hpy = ufunc = _empty_decorator binding = lambda _: _empty_decorator diff --git a/runtests.py b/runtests.py index 484252e6b40..f8c0e64603c 100755 --- a/runtests.py +++ b/runtests.py @@ -135,6 +135,7 @@ def get_distutils_distro(_cache=[]): 'tag:ipython': 'IPython.testing.globalipapp', 'tag:jedi': 'jedi_BROKEN_AND_DISABLED', 'tag:test.support': 'test.support', # support module for CPython unit tests + 'tag:hpy': 'hpy.devel', } def patch_inspect_isfunction(): @@ -242,6 +243,24 @@ def update_linetrace_extension(ext): ext.define_macros.append(('CYTHON_TRACE', 1)) return ext +def update_hpy_extension(ext): + ext.define_macros.append(('HPY', 1)) + import hpy.devel + # This should be set up by using the setuptools entrypoint + # hpy.devel.handle_hpy_ext_module via a + # setup(setup_requires=['hpy.devel'], ...) + # but this testrunner does not support that yet? + # so instead monkeypatch around to get hpy_ext._finalize_hpy_ext to work + hpy_ext = hpy.devel.build_hpy_ext_mixin() + hpy_ext.hpydevel = hpy.devel.HPyDevel() + dist = get_distutils_distro() + if not hasattr(dist, 'hpy_abi'): + # can be 'cpython' or 'universal' + # for now, always use 'cpython' + dist.hpy_abi = 'cpython' + hpy_ext.distribution = dist + hpy_ext._finalize_hpy_ext(ext) + return ext def update_numpy_extension(ext, set_api17_macro=True): import numpy as np @@ -454,6 +473,7 @@ def get_openmp_compiler_flags(language): 'tag:trace' : update_linetrace_extension, 'tag:no-macos': exclude_extension_on_platform('darwin'), 'tag:cppexecpolicies': require_gcc("9.1"), + 'tag:hpy': update_hpy_extension, } # TODO: use tags @@ -854,13 +874,14 @@ def build_tests(self, test_class, path, workdir, module, module_path, expect_err add_cython_import = self.add_cython_import and module_path.endswith('.py') preparse_list = tags.get('preparse', ['id']) + hpy = 'hpy' in tags.get('tag', '') tests = [ self.build_test(test_class, path, workdir, module, module_path, tags, language, language_level, expect_log, warning_errors, preparse, pythran_dir if language == "cpp" else None, add_cython_import=add_cython_import, - extra_directives=extra_directives, + extra_directives=extra_directives, hpy=hpy, full_limited_api_mode=full_limited_api_mode) for language in languages for preparse in preparse_list @@ -871,7 +892,7 @@ def build_tests(self, test_class, path, workdir, module, module_path, expect_err def build_test(self, test_class, path, workdir, module, module_path, tags, language, language_level, expect_log, warning_errors, preparse, pythran_dir, add_cython_import, - extra_directives, full_limited_api_mode): + extra_directives, hpy, full_limited_api_mode): language_workdir = os.path.join(workdir, language) if not os.path.exists(language_workdir): os.makedirs(language_workdir) @@ -901,6 +922,7 @@ def build_test(self, test_class, path, workdir, module, module_path, tags, langu pythran_dir=pythran_dir, stats=self.stats, add_cython_import=add_cython_import, + hpy=hpy, full_limited_api_mode=full_limited_api_mode, ) @@ -957,7 +979,7 @@ def __init__(self, test_directory, workdir, module, module_path, tags, language= expect_log=(), annotate=False, cleanup_workdir=True, cleanup_sharedlibs=True, cleanup_failures=True, cython_only=False, test_selector=None, - fork=True, language_level=2, warning_errors=False, + fork=True, language_level=2, warning_errors=False, hpy=False, test_determinism=False, shard_num=0, common_utility_dir=None, pythran_dir=None, stats=None, add_cython_import=False, extra_directives=None, full_limited_api_mode=False): @@ -985,6 +1007,7 @@ def __init__(self, test_directory, workdir, module, module_path, tags, language= self.test_determinism = test_determinism self.common_utility_dir = common_utility_dir self.pythran_dir = pythran_dir + self.hpy = hpy self.stats = stats self.add_cython_import = add_cython_import self.extra_directives = extra_directives @@ -1227,6 +1250,7 @@ def run_cython(self, test_directory, module, module_path, targetdir, incdir, ann generate_pxi = False, evaluate_tree_assertions = True, common_utility_include_dir = common_utility_include_dir, + hpy = self.hpy, **extra_compile_options ) cython_compile(module_path, options=options, full_module_name=module) diff --git a/tests/run/hpy_basic.pyx b/tests/run/hpy_basic.pyx new file mode 100644 index 00000000000..f41bb98be30 --- /dev/null +++ b/tests/run/hpy_basic.pyx @@ -0,0 +1,22 @@ +#mode: run +#tag: hpy + +import cython + +@cython.hpy +def add_int_hpy(a:int, b:int): + """ + >>> add_int_hpy(1, 2) + 3 + """ + return a + b + +def add_int_capi(a:int, b:int): + """ + >>> add_int_capi(1, 2) + 3 + """ + return a + b + +assert add_int_hpy(22, 33) == 55 +assert add_int_capi(44, 55) == 99 \ No newline at end of file From 6f0635457c40051f4555126118b8a676e4e0721d Mon Sep 17 00:00:00 2001 From: dutoit Date: Wed, 14 Jun 2023 16:26:08 +0200 Subject: [PATCH 123/286] Small change to runtests --- runtests.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtests.py b/runtests.py index f8c0e64603c..879da0f2257 100755 --- a/runtests.py +++ b/runtests.py @@ -251,13 +251,14 @@ def update_hpy_extension(ext): # setup(setup_requires=['hpy.devel'], ...) # but this testrunner does not support that yet? # so instead monkeypatch around to get hpy_ext._finalize_hpy_ext to work - hpy_ext = hpy.devel.build_hpy_ext_mixin() + hpy_ext = hpy.devel.build_ext_hpy_mixin() hpy_ext.hpydevel = hpy.devel.HPyDevel() dist = get_distutils_distro() if not hasattr(dist, 'hpy_abi'): # can be 'cpython' or 'universal' # for now, always use 'cpython' dist.hpy_abi = 'cpython' + dist.hpy_use_static_libs = False hpy_ext.distribution = dist hpy_ext._finalize_hpy_ext(ext) return ext From cd3cb0fcf417f3f7611c731cf0569a087725cc58 Mon Sep 17 00:00:00 2001 From: dutoit Date: Tue, 4 Jul 2023 13:46:33 +0200 Subject: [PATCH 124/286] Started port of module initialisation functions --- Cython/Compiler/ModuleNode.py | 63 +++++++++++++++++++++++++++++--- Cython/Compiler/Naming.py | 2 + Cython/Compiler/PyrexTypes.py | 4 +- Cython/Compiler/Symtab.py | 4 ++ Cython/Utility/ModuleSetupCode.c | 58 +++++++++++++++++++++++++++++ Cython/Utility/TypeConversion.c | 1 + 6 files changed, 125 insertions(+), 7 deletions(-) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 98c26387583..46c43e621d1 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -509,6 +509,7 @@ def generate_c_code(self, env, options, result): code.mark_pos(None) self.generate_typeobj_definitions(env, code) self.generate_method_table(env, code) + self.generate_hpy_define_array(env, code) if env.has_import_star: self.generate_import_star(env, code) @@ -809,6 +810,7 @@ def generate_module_preamble(self, env, options, cimported_modules, metadata, co self._put_setup_code(code, "CppInitCode") else: self._put_setup_code(code, "CInitCode") + self._put_setup_code(code, "HPyInitCode") self._put_setup_code(code, "PythonCompatibility") self._put_setup_code(code, "MathInitCode") @@ -879,7 +881,7 @@ def generate_module_preamble(self, env, options, cimported_modules, metadata, co code.putln('static CYTHON_INLINE void __Pyx_pretend_to_initialize(void* ptr) { (void)ptr; }') code.putln('') code.putln('#if !CYTHON_USE_MODULE_STATE') - code.putln('static PyObject *%s = NULL;' % env.module_cname) + code.putln('static PYOBJECT_TYPE %s = API_NULL_VALUE;' % env.module_cname) if Options.pre_import is not None: code.putln('static PyObject *%s;' % Naming.preimport_cname) code.putln('#endif') @@ -1482,6 +1484,7 @@ def generate_typeobj_definitions(self, env, code): self.generate_property_accessors(scope, code) self.generate_method_table(scope, code) + self.generate_hpy_define_array(scope, code) self.generate_getset_table(scope, code) code.putln("#if CYTHON_USE_TYPE_SPECS") self.generate_typeobj_spec(entry, code) @@ -2709,6 +2712,34 @@ def generate_method_table(self, env, code): if wrapper_code_writer.getvalue(): wrapper_code_writer.putln("") + def generate_hpy_define_array(self, env, code): + if env.is_c_class_scope: + return + + code.putln("") + code.putln("#if CYTHON_USING_HPY") + wrapper_code_writer = code.insertion_point() + + code.putln( + "static HPyDef *%s[] = {" % ( + env.hpy_defines_cname)) + if env.hpyfunc_entries: + for entry in env.hpyfunc_entries: + code.put_hpydef(entry) + code.putln(","); + for entry in env.pyfunc_entries: + if not entry.fused_cfunction and not entry.is_overridable: + code.put_hpydef(entry) + code.putln(",") + code.putln("NULL") + code.putln( + "};") + code.putln("#endif") + code.putln("") + + if wrapper_code_writer.getvalue(): + wrapper_code_writer.putln("") + def generate_dict_getter_function(self, scope, code): dict_attr = scope.lookup_here("__dict__") if not dict_attr or not dict_attr.is_variable: @@ -3001,6 +3032,7 @@ def generate_module_init_func(self, imported_modules, env, code): header3 = "__Pyx_PyMODINIT_FUNC %s(void)" % self.mod_init_func_cname('PyInit', env) header3 = EncodedString(header3) # Optimise for small code size as the module init function is only executed once. + code.putln("#if !CYTHON_USING_HPY") code.putln("%s CYTHON_SMALL_CODE; /*proto*/" % header3) if self.scope.is_package: code.putln("#if !defined(CYTHON_NO_PYINIT_EXPORT) && (defined(_WIN32) || defined(WIN32) || defined(MS_WINDOWS))") @@ -3016,6 +3048,10 @@ def generate_module_init_func(self, imported_modules, env, code): code.putln("void %s(void) {} /* workaround for https://bugs.python.org/issue39432 */" % wrong_punycode_module_name) code.putln("#endif") code.putln(header3) + code.putln("#else") + code.putln("HPy_MODINIT(%s, %s)" % (env.module_name, Naming.pymoduledef_cname)) + code.putln("static HPy init_%s_impl(HPY_CONTEXT_TYPE %s)" % (env.module_name, Naming.hpy_context_cname)) + code.putln("#endif") # CPython 3.5+ supports multi-phase module initialisation (gives access to __spec__, __file__, etc.) code.putln("#if CYTHON_PEP489_MULTI_PHASE_INIT") @@ -3028,7 +3064,7 @@ def generate_module_init_func(self, imported_modules, env, code): code.putln("") # main module init code lives in Py_mod_exec function, not in PyInit function - code.putln("static CYTHON_SMALL_CODE int %s(PyObject *%s)" % ( + code.putln("static CYTHON_SMALL_CODE int %s(PYOBJECT_TYPE %s)" % ( self.module_init_func_cname(), Naming.pymodinit_module_arg)) code.putln("#endif") # PEP489 @@ -3066,10 +3102,17 @@ def generate_module_init_func(self, imported_modules, env, code): code.putln("}") code.putln("#else") # Hack: enforce single initialisation also on reimports under different names (with PEP 3121/489). + code.putln("#if !CYTHON_USING_HPY") code.putln("if (%s) return __Pyx_NewRef(%s);" % ( Naming.module_cname, Naming.module_cname, )) + code.putln("#else") + code.putln("if (%s) return __Pyx_hNewRef(%s);" % ( + Naming.module_cname, + Naming.module_cname, + )) + code.putln("#endif") code.putln("#endif") code.putln("/*--- Module creation code ---*/") @@ -3502,7 +3545,7 @@ def generate_pymoduledef_struct(self, env, code): exec_func_cname = self.module_init_func_cname() code.putln("static PyObject* %s(PyObject *spec, PyModuleDef *def); /*proto*/" % Naming.pymodule_create_func_cname) - code.putln("static int %s(PyObject* module); /*proto*/" % exec_func_cname) + code.putln("static int %s(PYOBJECT_TYPE module); /*proto*/" % exec_func_cname) code.putln("static PyModuleDef_Slot %s[] = {" % Naming.pymoduledef_slots_cname) code.putln("{Py_mod_create, (void*)%s}," % Naming.pymodule_create_func_cname) @@ -3518,11 +3561,12 @@ def generate_pymoduledef_struct(self, env, code): code.putln("") code.putln('#ifdef __cplusplus') code.putln('namespace {') - code.putln("struct PyModuleDef %s =" % Naming.pymoduledef_cname) + code.putln("PYMODULEDEF_TYPE %s =" % Naming.pymoduledef_cname) code.putln('#else') - code.putln("static struct PyModuleDef %s =" % Naming.pymoduledef_cname) + code.putln("static PYMODULEDEF_TYPE %s =" % Naming.pymoduledef_cname) code.putln('#endif') code.putln('{') + code.putln("#if !CYTHON_USING_HPY") code.putln(" PyModuleDef_HEAD_INIT,") code.putln(' %s,' % env.module_name.as_c_string_literal()) code.putln(" %s, /* m_doc */" % doc) @@ -3548,6 +3592,15 @@ def generate_pymoduledef_struct(self, env, code): code.putln(" NULL, /* m_clear */") code.putln(" %s /* m_free */" % cleanup_func) code.putln("#endif") + code.putln("#else") + code.putln(" .doc = %s," % doc) + code.putln(" .size = -1,") + code.putln(" .legacy_methods = 0,") + if env.is_c_class_scope and not env.hpyfunc_entries: + code.putln(" .defines = 0,") + else: + code.putln(" .defines = %s," % env.hpy_defines_cname) + code.putln("#endif") code.putln("};") code.putln('#ifdef __cplusplus') code.putln('} /* anonymous namespace */') diff --git a/Cython/Compiler/Naming.py b/Cython/Compiler/Naming.py index ed88a7f18b0..ecb928662d2 100644 --- a/Cython/Compiler/Naming.py +++ b/Cython/Compiler/Naming.py @@ -76,6 +76,7 @@ 'umethod': pyrex_prefix + "umethod_", } +hpy_context_cname = pyrex_prefix + "hpy_ctx" ctuple_type_prefix = pyrex_prefix + "ctuple_" args_cname = pyrex_prefix + "args" nargs_cname = pyrex_prefix + "nargs" @@ -102,6 +103,7 @@ modulestateglobal_cname = pyrex_prefix + "mstate_global" moddoc_cname = pyrex_prefix + "mdoc" methtable_cname = pyrex_prefix + "methods" +hpy_defines_cname= pyrex_prefix + "hpy_defines" retval_cname = pyrex_prefix + "r" reqd_kwds_cname = pyrex_prefix + "reqd_kwds" self_cname = pyrex_prefix + "self" diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index b41b1ec612c..468c3f924ad 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -1311,14 +1311,14 @@ def generate_incref(self, code, cname, nanny): code.funcstate.needs_refnanny = True code.putln("__Pyx_INCREF(%s);" % self.as_pyobject(cname)) else: - code.putln("Py_INCREF(%s);" % self.as_pyobject(cname)) + code.putln("PYOBJECT_ALLOC(%s);" % self.as_pyobject(cname)) def generate_xincref(self, code, cname, nanny): if nanny: code.funcstate.needs_refnanny = True code.putln("__Pyx_XINCREF(%s);" % self.as_pyobject(cname)) else: - code.putln("Py_XINCREF(%s);" % self.as_pyobject(cname)) + code.putln("PYOJBECT_XALLOC(%s);" % self.as_pyobject(cname)) def generate_decref(self, code, cname, nanny, have_gil): # have_gil is for the benefit of memoryviewslice - it's ignored here diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index c9127d28349..f8706f6268b 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -330,6 +330,7 @@ class Scope: # arg_entries [Entry] Function argument entries # var_entries [Entry] User-defined variable entries # pyfunc_entries [Entry] Python function entries + # hpyfunc_entries [Entry] HPy function entries # cfunc_entries [Entry] C function entries # c_class_entries [Entry] All extension type entries # cname_to_entry {string : Entry} Temp cname to entry mapping @@ -399,6 +400,7 @@ def __init__(self, name, outer_scope, parent_scope): self.arg_entries = [] self.var_entries = [] self.pyfunc_entries = [] + self.hpyfunc_entries = [] self.cfunc_entries = [] self.c_class_entries = [] self.defined_c_classes = [] @@ -1261,6 +1263,7 @@ class ModuleScope(Scope): # module_cname string C name of Python module object # #module_dict_cname string C name of module dict object # method_table_cname string C name of method table + # hpy_defines_cname string C name of HPy defines table # doc string Module doc string # doc_cname string C name of module doc string # utility_code_list [UtilityCode] Queuing utility codes for forwarding to Code.py @@ -1301,6 +1304,7 @@ def __init__(self, name, parent_module, context, is_package=False): self.module_cname = Naming.module_cname self.module_dict_cname = Naming.moddict_cname self.method_table_cname = Naming.methtable_cname + self.hpy_defines_cname = Naming.hpy_defines_cname self.doc = "" self.doc_cname = Naming.moddoc_cname self.utility_code_list = [] diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index d62e1b6aba6..2f0f717aed7 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -427,6 +427,33 @@ #endif #endif +#if defined(HPY) + #define CYTHON_USING_HPY 1 + #if PY_MAJOR_VERSION < 3 + #error "Cython/HPy requires Python 3 or newer" + #endif + #undef CYTHON_PEP489_MULTI_PHASE_INIT + #define CYTHON_PEP489_MULTI_PHASE_INIT 0 + /* Module state is not yet supported in HPy */ + #define CYTHON_USE_MODULE_STATE 0 + /* Any Python objects are generally opaque in HPy */ + #undef CYTHON_USE_PYLONG_INTERNALS + #define CYTHON_USE_PYLONG_INTERNALS 0 + #undef CYTHON_USE_DICT_VERSIONS + #define CYTHON_USE_DICT_VERSIONS 0 + #undef CYTHON_FAST_THREAD_STATE + #define CYTHON_FAST_THREAD_STATE 0 + #undef CYTHON_USE_TYPE_SLOTS + #define CYTHON_USE_TYPE_SLOTS 0 + /* We don't use refnanny in HPy since it has the debug mode */ + #undef CYTHON_REFNANNY + #define CYTHON_REFNANNY 0 + #undef CYTHON_METH_FASTCALL + #define CYTHON_METH_FASTCALL 1 +#else + #define CYTHON_USING_HPY 0 +#endif + #ifndef CYTHON_FAST_PYCCALL #define CYTHON_FAST_PYCCALL CYTHON_FAST_PYCALL #endif @@ -684,6 +711,37 @@ class __Pyx_FakeReference { }; +/////////////// HPyInitCode /////////////// + +#if CYTHON_USING_HPY + #define PYOBJECT_TYPE HPy + #define CAPI_IS_POINTER + #define PYOBJECT_ALLOC(h) HPy_Dup(*$hpy_context_cname, h) + #define PYOBJECT_XALLOC(h) HPy_Dup(*$hpy_context_cname, h) + #define PYOBJECT_DEALLOC(h) HPy_Close(*$hpy_context_cname, h) + #define PYOBJECT_XDEALLOC(h) HPy_Close(*$hpy_context_cname, h) + #define HPY_CONTEXT_TYPE HPyContext * + + #define API_NULL_VALUE HPy_NULL + + #define PYMODULEDEF_TYPE HPyModuleDef + + #define PYOBJECT_GET_ATTR_STR(o, attr_name) HPyObject_GetAttrString(*$hpy_context_name, o, attr_name) +#else + #define PYOBJECT_TYPE PyObject * + #define CAPI_IS_POINTER * //Some types are sometimes pointers and sometimes not (i.e. PyModuleDef) where the type is always the same in HPy + #define PYOBJECT_ALLOC(h) Py_INCREF(h) + #define PYOBJECT_XALLOC(h) Py_XINCREF(h) + #define PYOBJECT_DEALLOC(h) Py_DECREF(h) + #define PYOBJECT_XDEALLOC(h) Py_XDECREF(h) + + #define API_NULL_VALUE NULL + + #define PYMODULEDEF_TYPE struct PyModuleDef + + #define PYOBJECT_GET_ATTR_STR(o, attr_name) PyObject_GetAttrString(o, attr_name) +#endif + /////////////// PythonCompatibility /////////////// #define __PYX_BUILD_PY_SSIZE_T "n" diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index 17b8f59f904..0708cdf9044 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -108,6 +108,7 @@ static CYTHON_INLINE size_t __Pyx_Py_UNICODE_strlen(const Py_UNICODE *u) #define __Pyx_PyUnicode_FromUnicodeAndLength PyUnicode_FromUnicode #define __Pyx_PyUnicode_AsUnicode PyUnicode_AsUnicode +#define __Pyx_hNewRef(obj) HPy_Dup(*$hpy_context_cname, obj) //This will be merged into Pyx_NewRef eventually, but #if CYTHON_USING_HPY will make all calls to this macro use HPy, even if it hasn't been ported yet #define __Pyx_NewRef(obj) (Py_INCREF(obj), obj) #define __Pyx_Owned_Py_None(b) __Pyx_NewRef(Py_None) static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b); From dca38d2c51c771de6c2be53fda266f2630bb5a82 Mon Sep 17 00:00:00 2001 From: dutoit Date: Wed, 5 Jul 2023 16:36:31 +0200 Subject: [PATCH 125/286] Removed wrong HPy changes, added some more HPy APIs, possibly made context work --- Cython/Compiler/ModuleNode.py | 12 +++++------- Cython/Utility/ModuleSetupCode.c | 16 ++++++++++------ Cython/Utility/TypeConversion.c | 2 +- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 46c43e621d1..c7c132c0efc 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -3048,15 +3048,13 @@ def generate_module_init_func(self, imported_modules, env, code): code.putln("void %s(void) {} /* workaround for https://bugs.python.org/issue39432 */" % wrong_punycode_module_name) code.putln("#endif") code.putln(header3) - code.putln("#else") - code.putln("HPy_MODINIT(%s, %s)" % (env.module_name, Naming.pymoduledef_cname)) - code.putln("static HPy init_%s_impl(HPY_CONTEXT_TYPE %s)" % (env.module_name, Naming.hpy_context_cname)) - code.putln("#endif") # CPython 3.5+ supports multi-phase module initialisation (gives access to __spec__, __file__, etc.) code.putln("#if CYTHON_PEP489_MULTI_PHASE_INIT") code.putln("{") + code.putln("#if !CYTHON_USING_HPY") code.putln("return PyModuleDef_Init(&%s);" % Naming.pymoduledef_cname) + code.putln("#endif") code.putln("}") mod_create_func = UtilityCode.load_as_string("ModuleCreationPEP489", "ModuleSetupCode.c")[1] @@ -3089,7 +3087,7 @@ def generate_module_init_func(self, imported_modules, env, code): code.putln("#if CYTHON_PEP489_MULTI_PHASE_INIT") # Most extension modules simply can't deal with it, and Cython isn't ready either. # See issues listed here: https://docs.python.org/3/c-api/init.html#sub-interpreter-support - code.putln("if (%s) {" % Naming.module_cname) + code.putln("if (API_IS_NOT_NULL(%s)) {" % Naming.module_cname) # Hack: enforce single initialisation. code.putln("if (%s == %s) return 0;" % ( Naming.module_cname, @@ -3103,12 +3101,12 @@ def generate_module_init_func(self, imported_modules, env, code): code.putln("#else") # Hack: enforce single initialisation also on reimports under different names (with PEP 3121/489). code.putln("#if !CYTHON_USING_HPY") - code.putln("if (%s) return __Pyx_NewRef(%s);" % ( + code.putln("if (API_IS_NOT_NULL(%s)) return __Pyx_NewRef(%s);" % ( Naming.module_cname, Naming.module_cname, )) code.putln("#else") - code.putln("if (%s) return __Pyx_hNewRef(%s);" % ( + code.putln("if (API_IS_NOT_NULL(%s)) return __Pyx_hNewRef(%s);" % ( Naming.module_cname, Naming.module_cname, )) diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 2f0f717aed7..6f98c9bb4e6 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -433,7 +433,7 @@ #error "Cython/HPy requires Python 3 or newer" #endif #undef CYTHON_PEP489_MULTI_PHASE_INIT - #define CYTHON_PEP489_MULTI_PHASE_INIT 0 + #define CYTHON_PEP489_MULTI_PHASE_INIT 1 /* Module state is not yet supported in HPy */ #define CYTHON_USE_MODULE_STATE 0 /* Any Python objects are generally opaque in HPy */ @@ -714,19 +714,22 @@ class __Pyx_FakeReference { /////////////// HPyInitCode /////////////// #if CYTHON_USING_HPY + #define HPY_CONTEXT_CNAME ${hpy_context_cname} + #define PYOBJECT_TYPE HPy #define CAPI_IS_POINTER - #define PYOBJECT_ALLOC(h) HPy_Dup(*$hpy_context_cname, h) - #define PYOBJECT_XALLOC(h) HPy_Dup(*$hpy_context_cname, h) - #define PYOBJECT_DEALLOC(h) HPy_Close(*$hpy_context_cname, h) - #define PYOBJECT_XDEALLOC(h) HPy_Close(*$hpy_context_cname, h) + #define PYOBJECT_ALLOC(h) HPy_Dup(HPY_CONTEXT_CNAME, h) + #define PYOBJECT_XALLOC(h) HPy_Dup(HPY_CONTEXT_CNAME, h) + #define PYOBJECT_DEALLOC(h) HPy_Close(HPY_CONTEXT_CNAME, h) + #define PYOBJECT_XDEALLOC(h) HPy_Close(HPY_CONTEXT_CNAME, h) #define HPY_CONTEXT_TYPE HPyContext * #define API_NULL_VALUE HPy_NULL + #define API_IS_NOT_NULL(h) !HPy_IsNull(h) #define PYMODULEDEF_TYPE HPyModuleDef - #define PYOBJECT_GET_ATTR_STR(o, attr_name) HPyObject_GetAttrString(*$hpy_context_name, o, attr_name) + #define PYOBJECT_GET_ATTR_STR(o, attr_name) HPyObject_GetAttrString(HPY_CONTEXT_CNAME, o, attr_name) #else #define PYOBJECT_TYPE PyObject * #define CAPI_IS_POINTER * //Some types are sometimes pointers and sometimes not (i.e. PyModuleDef) where the type is always the same in HPy @@ -736,6 +739,7 @@ class __Pyx_FakeReference { #define PYOBJECT_XDEALLOC(h) Py_XDECREF(h) #define API_NULL_VALUE NULL + #define API_IS_NOT_NULL(h) h #define PYMODULEDEF_TYPE struct PyModuleDef diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index 0708cdf9044..e10077ddf1e 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -108,7 +108,7 @@ static CYTHON_INLINE size_t __Pyx_Py_UNICODE_strlen(const Py_UNICODE *u) #define __Pyx_PyUnicode_FromUnicodeAndLength PyUnicode_FromUnicode #define __Pyx_PyUnicode_AsUnicode PyUnicode_AsUnicode -#define __Pyx_hNewRef(obj) HPy_Dup(*$hpy_context_cname, obj) //This will be merged into Pyx_NewRef eventually, but #if CYTHON_USING_HPY will make all calls to this macro use HPy, even if it hasn't been ported yet +#define __Pyx_hNewRef(obj) HPy_Dup(HPY_CONTEXT_CNAME, obj) //This will be merged into Pyx_NewRef eventually, but #if CYTHON_USING_HPY will make all calls to this macro use HPy, even if it hasn't been ported yet #define __Pyx_NewRef(obj) (Py_INCREF(obj), obj) #define __Pyx_Owned_Py_None(b) __Pyx_NewRef(Py_None) static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b); From b92c1019da3c0176c4e1248503c2c4446b49851e Mon Sep 17 00:00:00 2001 From: dutoit Date: Thu, 6 Jul 2023 14:50:32 +0200 Subject: [PATCH 126/286] Added notes about macros not working; stopped HPy from compiling hpy_mod_create --- Cython/Compiler/ModuleNode.py | 6 +++++- Cython/Utility/ModuleSetupCode.c | 8 ++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index c7c132c0efc..c18b0157ecc 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -3089,7 +3089,7 @@ def generate_module_init_func(self, imported_modules, env, code): # See issues listed here: https://docs.python.org/3/c-api/init.html#sub-interpreter-support code.putln("if (API_IS_NOT_NULL(%s)) {" % Naming.module_cname) # Hack: enforce single initialisation. - code.putln("if (%s == %s) return 0;" % ( + code.putln("if (API_IS_EQUAL(%s, %s)) return 0;" % ( Naming.module_cname, Naming.pymodinit_module_arg, )) @@ -3541,12 +3541,16 @@ def generate_pymoduledef_struct(self, env, code): code.putln("#if PY_MAJOR_VERSION >= 3") code.putln("#if CYTHON_PEP489_MULTI_PHASE_INIT") exec_func_cname = self.module_init_func_cname() + code.putln("#if !CYTHON_USING_HPY") code.putln("static PyObject* %s(PyObject *spec, PyModuleDef *def); /*proto*/" % Naming.pymodule_create_func_cname) + code.putln("#endif") code.putln("static int %s(PYOBJECT_TYPE module); /*proto*/" % exec_func_cname) code.putln("static PyModuleDef_Slot %s[] = {" % Naming.pymoduledef_slots_cname) + code.putln("#if !CYTHON_USING_HPY") code.putln("{Py_mod_create, (void*)%s}," % Naming.pymodule_create_func_cname) + code.putln("#endif") code.putln("{Py_mod_exec, (void*)%s}," % exec_func_cname) code.putln("{0, NULL}") code.putln("};") diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 6f98c9bb4e6..1c77a9e301e 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -714,11 +714,11 @@ class __Pyx_FakeReference { /////////////// HPyInitCode /////////////// #if CYTHON_USING_HPY - #define HPY_CONTEXT_CNAME ${hpy_context_cname} + #define HPY_CONTEXT_CNAME ${hpy_context_cname} //This gives an error, maybe ${} doesn't work in macros? #define PYOBJECT_TYPE HPy #define CAPI_IS_POINTER - #define PYOBJECT_ALLOC(h) HPy_Dup(HPY_CONTEXT_CNAME, h) + #define PYOBJECT_ALLOC(h) HPy_Dup(HPY_CONTEXT_CNAME, h) //Apparently there are too few args here, this might be related to the issue with HPyContext above #define PYOBJECT_XALLOC(h) HPy_Dup(HPY_CONTEXT_CNAME, h) #define PYOBJECT_DEALLOC(h) HPy_Close(HPY_CONTEXT_CNAME, h) #define PYOBJECT_XDEALLOC(h) HPy_Close(HPY_CONTEXT_CNAME, h) @@ -726,6 +726,7 @@ class __Pyx_FakeReference { #define API_NULL_VALUE HPy_NULL #define API_IS_NOT_NULL(h) !HPy_IsNull(h) + #define API_IS_EQUAL(a, b) HPy_Is(HPY_CONTEXT_CNAME, a, b) //Also too few args issue #define PYMODULEDEF_TYPE HPyModuleDef @@ -740,6 +741,7 @@ class __Pyx_FakeReference { #define API_NULL_VALUE NULL #define API_IS_NOT_NULL(h) h + #define API_IS_EQUAL(a, b) a==b #define PYMODULEDEF_TYPE struct PyModuleDef @@ -1594,6 +1596,7 @@ static CYTHON_SMALL_CODE int __Pyx_copy_spec_to_module(PyObject *spec, PyObject return result; } +#if !CYTHON_USING_HPY static CYTHON_SMALL_CODE PyObject* ${pymodule_create_func_cname}(PyObject *spec, PyModuleDef *def) { PyObject *module = NULL, *moddict, *modname; CYTHON_UNUSED_VAR(def); @@ -1629,6 +1632,7 @@ static CYTHON_SMALL_CODE PyObject* ${pymodule_create_func_cname}(PyObject *spec, Py_XDECREF(module); return NULL; } +#endif //#endif From 7335e341c0c4f38e8751354a0079ec00be7394f5 Mon Sep 17 00:00:00 2001 From: dutoit Date: Wed, 26 Jul 2023 14:40:29 +0200 Subject: [PATCH 127/286] Made $ names work, ported more of -y_mod_exec --- Cython/Compiler/ModuleNode.py | 29 +++++++++++++++++++++++------ Cython/Utility/ModuleSetupCode.c | 9 +++++++-- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index c18b0157ecc..033e822afc6 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -2860,7 +2860,7 @@ def generate_import_star(self, env, code): def generate_module_state_start(self, env, code): # TODO: Refactor to move module state struct decl closer to the static decl code.putln('typedef struct {') - code.putln('PyObject *%s;' % env.module_dict_cname) + code.putln('PYOBJECT_TYPE %s;' % env.module_dict_cname) code.putln('PyObject *%s;' % Naming.builtins_cname) code.putln('PyObject *%s;' % Naming.cython_runtime_cname) code.putln('PyObject *%s;' % Naming.empty_tuple) @@ -3062,9 +3062,16 @@ def generate_module_init_func(self, imported_modules, env, code): code.putln("") # main module init code lives in Py_mod_exec function, not in PyInit function + code.putln("#if !CYTHON_USING_HPY") code.putln("static CYTHON_SMALL_CODE int %s(PYOBJECT_TYPE %s)" % ( self.module_init_func_cname(), Naming.pymodinit_module_arg)) + code.putln("#else") + code.putln("int %s_impl(HPyContext *%s, HPy %s) {" % ( + self.module_init_func_cname(), + Naming.hpy_context_cname, + Naming.pymodinit_module_arg)) + code.putln("#endif") code.putln("#endif") # PEP489 # start of module init/exec function (pre/post PEP 489) @@ -3544,16 +3551,23 @@ def generate_pymoduledef_struct(self, env, code): code.putln("#if !CYTHON_USING_HPY") code.putln("static PyObject* %s(PyObject *spec, PyModuleDef *def); /*proto*/" % Naming.pymodule_create_func_cname) - code.putln("#endif") code.putln("static int %s(PYOBJECT_TYPE module); /*proto*/" % exec_func_cname) + code.putln("#else") + code.putln("HPyDef_SLOT(%s, HPy_mod_exec)" % self.module_init_func_cname()) + code.putln("#endif") - code.putln("static PyModuleDef_Slot %s[] = {" % Naming.pymoduledef_slots_cname) code.putln("#if !CYTHON_USING_HPY") + code.putln("static PyModuleDef_Slot %s[] = {" % Naming.pymoduledef_slots_cname) code.putln("{Py_mod_create, (void*)%s}," % Naming.pymodule_create_func_cname) - code.putln("#endif") code.putln("{Py_mod_exec, (void*)%s}," % exec_func_cname) code.putln("{0, NULL}") code.putln("};") + code.putln("#else") + code.putln("static HPyDef *methods[] = {") + code.putln("&%s," % exec_func_cname) + code.putln("NULL,") + code.putln("};") + code.putln("#endif") if not env.module_name.isascii(): code.putln("#else /* CYTHON_PEP489_MULTI_PHASE_INIT */") code.putln('#error "Unicode module names are only supported with multi-phase init' @@ -3599,7 +3613,7 @@ def generate_pymoduledef_struct(self, env, code): code.putln(" .size = -1,") code.putln(" .legacy_methods = 0,") if env.is_c_class_scope and not env.hpyfunc_entries: - code.putln(" .defines = 0,") + code.putln(" .defines = methods,") else: code.putln(" .defines = %s," % env.hpy_defines_cname) code.putln("#endif") @@ -3608,6 +3622,9 @@ def generate_pymoduledef_struct(self, env, code): code.putln('} /* anonymous namespace */') code.putln('#endif') code.putln("#endif") + code.putln("#if CYTHON_USING_HPY") + code.putln("HPy_MODINIT(hpyinit, %s)" % Naming.pymoduledef_cname) + code.putln("#endif") def generate_module_creation_code(self, env, code): # Generate code to create the module object and @@ -3665,7 +3682,7 @@ def generate_module_creation_code(self, env, code): code.putln("CYTHON_UNUSED_VAR(%s);" % module_temp) # only used in limited API code.putln( - "%s = PyModule_GetDict(%s); %s" % ( + "%s = PYMODULE_GETDICT_ATTR(%s); %s" % ( env.module_dict_cname, env.module_cname, code.error_goto_if_null(env.module_dict_cname, self.pos))) code.put_incref(env.module_dict_cname, py_object_type, nanny=False) diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 1c77a9e301e..2d5f5517837 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -712,9 +712,10 @@ class __Pyx_FakeReference { /////////////// HPyInitCode /////////////// +//@substitute: naming #if CYTHON_USING_HPY - #define HPY_CONTEXT_CNAME ${hpy_context_cname} //This gives an error, maybe ${} doesn't work in macros? + #define HPY_CONTEXT_CNAME $hpy_context_cname #define PYOBJECT_TYPE HPy #define CAPI_IS_POINTER @@ -730,7 +731,9 @@ class __Pyx_FakeReference { #define PYMODULEDEF_TYPE HPyModuleDef - #define PYOBJECT_GET_ATTR_STR(o, attr_name) HPyObject_GetAttrString(HPY_CONTEXT_CNAME, o, attr_name) + #define PYOBJECT_GET_ATTR_STR(o, attr_name) HPyObject_GetAttrString(HPY_CONTEXT_CNAME, o, attr_name) + + #define PYMODULE_GETDICT_ATTR(mod) HPy_GetAttr_s(HPY_CONTEXT_CNAME, mod, "__dict__") #else #define PYOBJECT_TYPE PyObject * #define CAPI_IS_POINTER * //Some types are sometimes pointers and sometimes not (i.e. PyModuleDef) where the type is always the same in HPy @@ -746,6 +749,8 @@ class __Pyx_FakeReference { #define PYMODULEDEF_TYPE struct PyModuleDef #define PYOBJECT_GET_ATTR_STR(o, attr_name) PyObject_GetAttrString(o, attr_name) + + #define PYMODULE_GETDICT_ATTR(mod) PyModule_GetDict(mod) #endif /////////////// PythonCompatibility /////////////// From 499c9099a507ead288803f6e9ac263c3a503df29 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 3 Aug 2023 15:55:21 +0200 Subject: [PATCH 128/286] Converted some module state for exec, changed some more of exec, and added new generic APIs --- Cython/Compiler/Code.py | 10 +++++--- Cython/Compiler/ExprNodes.py | 3 ++- Cython/Compiler/ModuleNode.py | 42 ++++++++++++++++---------------- Cython/Utility/ModuleSetupCode.c | 32 +++++++++++++++++++++--- 4 files changed, 59 insertions(+), 28 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 7f5f39cea8b..7471a80913b 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1204,7 +1204,7 @@ def initialize_main_c_code(self): w = self.parts['cached_constants'] w.enter_cfunc_scope() w.putln("") - w.putln("static CYTHON_SMALL_CODE int __Pyx_InitCachedConstants(void) {") + w.putln("static CYTHON_SMALL_CODE int __Pyx_InitCachedConstants(HPY_CONTEXT_ONLY_ARG_DEF) {") w.put_declare_refcount_context() w.put_setup_refcount_context(StringEncoding.EncodedString("__Pyx_InitCachedConstants")) @@ -1602,7 +1602,7 @@ def generate_string_constants(self): else: encoding = '"%s"' % py_string.encoding.lower() - self.parts['module_state'].putln("PyObject *%s;" % py_string.cname) + self.parts['module_state'].putln("PYOBJECT_TYPE %s;" % py_string.cname) self.parts['module_state_defines'].putln("#define %s %s->%s" % ( py_string.cname, Naming.modulestateglobal_cname, @@ -1648,7 +1648,7 @@ def generate_num_constants(self): init_constants = self.parts['init_constants'] for py_type, _, _, value, value_code, c in consts: cname = c.cname - self.parts['module_state'].putln("PyObject *%s;" % cname) + self.parts['module_state'].putln("PYOBJECT_TYPE %s;" % cname) self.parts['module_state_defines'].putln("#define %s %s->%s" % ( cname, Naming.modulestateglobal_cname, cname)) self.parts['module_state_clear'].putln( @@ -2493,6 +2493,10 @@ def error_goto_if(self, cond, pos): def error_goto_if_null(self, cname, pos): return self.error_goto_if("!%s" % cname, pos) + + def error_goto_if_null_object(self, cname, pos): + # In HPy, objects are check if null with HPy_IsNull, but other variables with !, hence the split is necessary + return self.error_goto_if("API_IS_NULL(%s)" % cname, pos) def error_goto_if_neg(self, cname, pos): # Add extra parentheses to silence clang warnings about constant conditions. diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index e6f37e74556..ba2c6bef223 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -10180,7 +10180,8 @@ def generate_result_code(self, code): elif self.def_node.is_generator: flags.append('CO_GENERATOR') - code.putln("%s = (PyObject*)__Pyx_PyCode_New(%d, %d, %d, %d, 0, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d, %s); %s" % ( + code.putln("%s = (PyObject*)__Pyx_PyCode_New(%d, %d, %d, %d, 0, %s, HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), \ + HPY_LEGACY_OBJECT_AS(%s), %s, HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), %s, %s, %d, HPY_LEGACY_OBJECT_AS(%s)); %s" % ( self.result_code, len(func.args) - func.num_kwonly_args, # argcount func.num_posonly_args, # posonlyargcount (Py3.8+ only) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 033e822afc6..dc1b391b428 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -2861,11 +2861,11 @@ def generate_module_state_start(self, env, code): # TODO: Refactor to move module state struct decl closer to the static decl code.putln('typedef struct {') code.putln('PYOBJECT_TYPE %s;' % env.module_dict_cname) - code.putln('PyObject *%s;' % Naming.builtins_cname) - code.putln('PyObject *%s;' % Naming.cython_runtime_cname) - code.putln('PyObject *%s;' % Naming.empty_tuple) - code.putln('PyObject *%s;' % Naming.empty_bytes) - code.putln('PyObject *%s;' % Naming.empty_unicode) + code.putln('PYOBJECT_TYPE %s;' % Naming.builtins_cname) + code.putln('PYOBJECT_TYPE %s;' % Naming.cython_runtime_cname) + code.putln('PYOBJECT_TYPE %s;' % Naming.empty_tuple) + code.putln('PYOBJECT_TYPE %s;' % Naming.empty_bytes) + code.putln('PYOBJECT_TYPE %s;' % Naming.empty_unicode) if Options.pre_import is not None: code.putln('PyObject *%s;' % Naming.preimport_cname) for type_cname, used_name in Naming.used_types_and_macros: @@ -3141,12 +3141,12 @@ def generate_module_init_func(self, imported_modules, env, code): code.putln("#ifdef __Pxy_PyFrame_Initialize_Offsets") code.putln("__Pxy_PyFrame_Initialize_Offsets();") code.putln("#endif") - code.putln("%s = PyTuple_New(0); %s" % ( - Naming.empty_tuple, code.error_goto_if_null(Naming.empty_tuple, self.pos))) - code.putln("%s = PyBytes_FromStringAndSize(\"\", 0); %s" % ( - Naming.empty_bytes, code.error_goto_if_null(Naming.empty_bytes, self.pos))) - code.putln("%s = PyUnicode_FromStringAndSize(\"\", 0); %s" % ( - Naming.empty_unicode, code.error_goto_if_null(Naming.empty_unicode, self.pos))) + code.putln("%s = TUPLE_CREATE_EMPTY(); %s" % ( + Naming.empty_tuple, code.error_goto_if_null_object(Naming.empty_tuple, self.pos))) + code.putln("%s = BYTES_FROM_STR_AND_SIZE(\"\", 0); %s" % ( + Naming.empty_bytes, code.error_goto_if_null_object(Naming.empty_bytes, self.pos))) + code.putln("%s = HPY_LEGACY_OBJECT_FROM(PyUnicode_FromStringAndSize(\"\", 0)); %s" % ( + Naming.empty_unicode, code.error_goto_if_null_object(Naming.empty_unicode, self.pos))) for ext_type in ('CyFunction', 'FusedFunction', 'Coroutine', 'Generator', 'AsyncGen', 'StopAsyncIteration'): code.putln("#ifdef __Pyx_%s_USED" % ext_type) @@ -3169,7 +3169,7 @@ def generate_module_init_func(self, imported_modules, env, code): code.putln("#endif") code.putln("if (%s) {" % self.is_main_module_flag_cname()) - code.put_error_if_neg(self.pos, 'PyObject_SetAttr(%s, %s, %s)' % ( + code.put_error_if_neg(self.pos, 'PYOBJECT_SET_ATTR(%s, %s, %s)' % ( env.module_cname, code.intern_identifier(EncodedString("__name__")), code.intern_identifier(EncodedString("__main__")))) @@ -3183,7 +3183,7 @@ def generate_module_init_func(self, imported_modules, env, code): code.put_error_if_neg(self.pos, "__Pyx_InitCachedBuiltins()") code.putln("/*--- Constants init code ---*/") - code.put_error_if_neg(self.pos, "__Pyx_InitCachedConstants()") + code.put_error_if_neg(self.pos, "__Pyx_InitCachedConstants(HPY_CONTEXT_ONLY_ARG_CALL)") code.putln("/*--- Global type/function init code ---*/") @@ -3684,28 +3684,28 @@ def generate_module_creation_code(self, env, code): code.putln( "%s = PYMODULE_GETDICT_ATTR(%s); %s" % ( env.module_dict_cname, env.module_cname, - code.error_goto_if_null(env.module_dict_cname, self.pos))) + code.error_goto_if_null_object(env.module_dict_cname, self.pos))) code.put_incref(env.module_dict_cname, py_object_type, nanny=False) code.putln( - '%s = __Pyx_PyImport_AddModuleRef(__Pyx_BUILTIN_MODULE_NAME); %s' % ( + '%s = HPY_LEGACY_OBJECT_FROM(__Pyx_PyImport_AddModuleRef(__Pyx_BUILTIN_MODULE_NAME)); %s' % ( Naming.builtins_cname, - code.error_goto_if_null(Naming.builtins_cname, self.pos))) + code.error_goto_if_null_object(Naming.builtins_cname, self.pos))) code.putln( - '%s = __Pyx_PyImport_AddModuleRef("cython_runtime"); %s' % ( + '%s = HPY_LEGACY_OBJECT_FROM(__Pyx_PyImport_AddModuleRef("cython_runtime")); %s' % ( Naming.cython_runtime_cname, - code.error_goto_if_null(Naming.cython_runtime_cname, self.pos))) + code.error_goto_if_null_object(Naming.cython_runtime_cname, self.pos))) code.putln( - 'if (PyObject_SetAttrString(%s, "__builtins__", %s) < 0) %s' % ( + 'if (PYOBJECT_SET_ATTR_STR(%s, "__builtins__", %s) < 0) %s' % ( env.module_cname, Naming.builtins_cname, code.error_goto(self.pos))) if Options.pre_import is not None: code.putln( - '%s = __Pyx_PyImport_AddModuleRef("%s"); %s' % ( + '%s = HPY_LEGACY_OBJECT_FROM(__Pyx_PyImport_AddModuleRef("%s")); %s' % ( Naming.preimport_cname, Options.pre_import, - code.error_goto_if_null(Naming.preimport_cname, self.pos))) + code.error_goto_if_null_object(Naming.preimport_cname, self.pos))) def generate_global_init_code(self, env, code): # Generate code to initialise global PyObject * diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 2d5f5517837..fa949c0648c 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -716,41 +716,67 @@ class __Pyx_FakeReference { #if CYTHON_USING_HPY #define HPY_CONTEXT_CNAME $hpy_context_cname + #define HPY_CONTEXT_TYPE HPyContext * + #define HPY_CONTEXT_ONLY_ARG_DEF HPY_CONTEXT_TYPE HPY_CONTEXT_CNAME + #define HPY_CONTEXT_ONLY_ARG_CALL HPY_CONTEXT_CNAME #define PYOBJECT_TYPE HPy #define CAPI_IS_POINTER - #define PYOBJECT_ALLOC(h) HPy_Dup(HPY_CONTEXT_CNAME, h) //Apparently there are too few args here, this might be related to the issue with HPyContext above + + #define PYOBJECT_ALLOC(h) HPy_Dup(HPY_CONTEXT_CNAME, h) #define PYOBJECT_XALLOC(h) HPy_Dup(HPY_CONTEXT_CNAME, h) #define PYOBJECT_DEALLOC(h) HPy_Close(HPY_CONTEXT_CNAME, h) #define PYOBJECT_XDEALLOC(h) HPy_Close(HPY_CONTEXT_CNAME, h) - #define HPY_CONTEXT_TYPE HPyContext * + + #define HPY_LEGACY_OBJECT_FROM(o) HPy_FromPyObject(HPY_CONTEXT_CNAME, o) + #define HPY_LEGACY_OBJECT_AS(o) HPy_AsPyObject(HPY_CONTEXT_CNAME, o) #define API_NULL_VALUE HPy_NULL + #define API_IS_NULL(h) HPy_IsNull(h) #define API_IS_NOT_NULL(h) !HPy_IsNull(h) - #define API_IS_EQUAL(a, b) HPy_Is(HPY_CONTEXT_CNAME, a, b) //Also too few args issue + #define API_IS_EQUAL(a, b) HPy_Is(HPY_CONTEXT_CNAME, a, b) #define PYMODULEDEF_TYPE HPyModuleDef + #define PYOBJECT_SET_ATTR(o, attr_name, attr_val) HPy_SetAttr(HPY_CONTEXT_CNAME, o, attr_name, attr_val) #define PYOBJECT_GET_ATTR_STR(o, attr_name) HPyObject_GetAttrString(HPY_CONTEXT_CNAME, o, attr_name) + #define PYOBJECT_SET_ATTR_STR(o1, attr_name, o2) HPy_SetAttr_s(HPY_CONTEXT_CNAME, o1, attr_name, o2) #define PYMODULE_GETDICT_ATTR(mod) HPy_GetAttr_s(HPY_CONTEXT_CNAME, mod, "__dict__") + + #define BYTES_FROM_STR_AND_SIZE(str, size) HPyBytes_FromStringAndSize(HPY_CONTEXT_CNAME, str, size) + + #define TUPLE_CREATE_EMPTY() HPyTuple_FromArray(HPY_CONTEXT_CNAME, NULL, 0) #else + #define HPY_CONTEXT_ONLY_ARG_DEF void + #define HPY_CONTEXT_ONLY_ARG_CALL + #define PYOBJECT_TYPE PyObject * #define CAPI_IS_POINTER * //Some types are sometimes pointers and sometimes not (i.e. PyModuleDef) where the type is always the same in HPy + #define PYOBJECT_ALLOC(h) Py_INCREF(h) #define PYOBJECT_XALLOC(h) Py_XINCREF(h) #define PYOBJECT_DEALLOC(h) Py_DECREF(h) #define PYOBJECT_XDEALLOC(h) Py_XDECREF(h) + #define HPY_LEGACY_OBJECT_FROM(o) o + #define HPY_LEGACY_OBJECT_AS(o) o + #define API_NULL_VALUE NULL + #define API_IS_NULL(h) !h //Both are here as otherwise we would get !!h for API_IS_NOT_NULL, which is hard to read - but it can be made so if necessary #define API_IS_NOT_NULL(h) h #define API_IS_EQUAL(a, b) a==b #define PYMODULEDEF_TYPE struct PyModuleDef + #define PYOBJECT_SET_ATTR(o, attr_name, attr_val) PyObject_SetAttr(o, attr_name, attr_val) #define PYOBJECT_GET_ATTR_STR(o, attr_name) PyObject_GetAttrString(o, attr_name) + #define PYOBJECT_SET_ATTR_STR(o1, attr_name, o2) PyObject_SetAttrString(o1, attr_name, o2) #define PYMODULE_GETDICT_ATTR(mod) PyModule_GetDict(mod) + + #define BYTES_FROM_STR_AND_SIZE(str, size) PyBytes_FromStringAndSize(str, size) + #define TUPLE_CREATE_EMPTY() PyTuple_New(0) #endif /////////////// PythonCompatibility /////////////// From b80373f60cad11317a501005679ad71beb8079bb Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Fri, 4 Aug 2023 11:45:37 +0200 Subject: [PATCH 129/286] Ported most of exec method to use generic API --- Cython/Compiler/Code.py | 2 +- Cython/Compiler/ExprNodes.py | 3 ++- Cython/Compiler/ModuleNode.py | 12 ++++++------ Cython/Compiler/Nodes.py | 2 +- Cython/Utility/Exceptions.c | 28 ++++++++++++++-------------- Cython/Utility/ModuleSetupCode.c | 10 +++++++++- 6 files changed, 33 insertions(+), 24 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 7471a80913b..cf9f6afec2c 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -2536,7 +2536,7 @@ def put_add_traceback(self, qualified_name, include_cline=True): ) self.funcstate.uses_error_indicator = True - self.putln('__Pyx_AddTraceback(%s, %s, %s, %s);' % format_tuple) + self.putln('__Pyx_AddTraceback(HPY_CONTEXT_FIRST_ARG_CALL %s, %s, %s, %s);' % format_tuple) def put_unraisable(self, qualified_name, nogil=False): """ diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index ba2c6bef223..08ac395f333 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -2516,7 +2516,8 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, setter = '__Pyx_SetItemOnTypeDict' elif entry.scope.is_module_scope: setter = 'PyDict_SetItem' - namespace = Naming.moddict_cname + namespace = "HPY_LEGACY_OBJECT_AS(" + Naming.moddict_cname + ")" + interned_cname = "HPY_LEGACY_OBJECT_AS(" + interned_cname + ")" elif entry.is_pyclass_attr: # Special-case setting __new__ n = "SetNewInClass" if self.name == "__new__" else "SetNameInClass" diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index dc1b391b428..3907b0ec5c7 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -2971,7 +2971,7 @@ def generate_module_state_clear(self, env, code): Naming.modulestate_cname, Naming.modulestate_cname)) code.putln("if (!clear_module_state) return 0;") - code.putln('Py_CLEAR(clear_module_state->%s);' % + code.putln('Py_CLEAR(HPY_LEGACY_OBJECT_AS(clear_module_state->%s));' % env.module_dict_cname) code.putln('Py_CLEAR(clear_module_state->%s);' % Naming.builtins_cname) @@ -3067,7 +3067,7 @@ def generate_module_init_func(self, imported_modules, env, code): self.module_init_func_cname(), Naming.pymodinit_module_arg)) code.putln("#else") - code.putln("int %s_impl(HPyContext *%s, HPy %s) {" % ( + code.putln("int %s_impl(HPyContext *%s, HPy %s)" % ( self.module_init_func_cname(), Naming.hpy_context_cname, Naming.pymodinit_module_arg)) @@ -3241,8 +3241,8 @@ def generate_module_init_func(self, imported_modules, env, code): code.put_label(code.error_label) for cname, type in code.funcstate.all_managed_temps(): code.put_xdecref(cname, type) - code.putln('if (%s) {' % env.module_cname) - code.putln('if (%s && stringtab_initialized) {' % env.module_dict_cname) + code.putln('if (API_IS_NOT_NULL(%s)) {' % env.module_cname) + code.putln('if (API_IS_NOT_NULL(%s) && stringtab_initialized) {' % env.module_dict_cname) # We can run into errors before the module or stringtab are initialized. # In this case it is not safe to add a traceback (because it uses the stringtab) code.put_add_traceback(EncodedString("init %s" % env.qualified_name)) @@ -3277,7 +3277,7 @@ def generate_module_init_func(self, imported_modules, env, code): code.put_finish_refcount_context() code.putln("#if CYTHON_PEP489_MULTI_PHASE_INIT") - code.putln("return (%s != NULL) ? 0 : -1;" % env.module_cname) + code.putln("return (API_IS_NOT_NULL(%s)) ? 0 : -1;" % env.module_cname) code.putln("#elif PY_MAJOR_VERSION >= 3") code.putln("return %s;" % env.module_cname) code.putln("#else") @@ -3408,7 +3408,7 @@ def generate_module_import_setup(self, env, code): code.putln("PyObject *modules = PyImport_GetModuleDict(); %s" % code.error_goto_if_null("modules", self.pos)) code.putln('if (!PyDict_GetItemString(modules, %s)) {' % fq_module_name_cstring) - code.putln(code.error_goto_if_neg('PyDict_SetItemString(modules, %s, %s)' % ( + code.putln(code.error_goto_if_neg('PyDict_SetItemString(modules, %s, HPY_LEGACY_OBJECT_AS(%s))' % ( fq_module_name_cstring, env.module_cname), self.pos)) code.putln("}") code.putln("}") diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 8d56c0c087d..52564382509 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -1735,7 +1735,7 @@ def generate_execution_code(self, code): item.cname, code.error_goto_if_null(temp, item.pos))) code.put_gotref(temp, PyrexTypes.py_object_type) - code.putln('if (PyDict_SetItemString(%s, "%s", %s) < 0) %s' % ( + code.putln('if (PyDict_SetItemString(HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS("%s"), %s) < 0) %s' % ( Naming.moddict_cname, item.name, temp, diff --git a/Cython/Utility/Exceptions.c b/Cython/Utility/Exceptions.c index 71c6114c008..a0d4a712dde 100644 --- a/Cython/Utility/Exceptions.c +++ b/Cython/Utility/Exceptions.c @@ -762,9 +762,9 @@ static void __Pyx_WriteUnraisable(const char *name, int clineno, /////////////// CLineInTraceback.proto /////////////// #ifdef CYTHON_CLINE_IN_TRACEBACK /* 0 or 1 to disable/enable C line display in tracebacks at C compile time */ -#define __Pyx_CLineForTraceback(tstate, c_line) (((CYTHON_CLINE_IN_TRACEBACK)) ? c_line : 0) +#define __Pyx_CLineForTraceback(HPY_CONTEXT_FIRST_ARG_DEF tstate, c_line) (((CYTHON_CLINE_IN_TRACEBACK)) ? c_line : 0) #else -static int __Pyx_CLineForTraceback(PyThreadState *tstate, int c_line);/*proto*/ +static int __Pyx_CLineForTraceback(HPY_CONTEXT_FIRST_ARG_DEF PyThreadState *tstate, int c_line);/*proto*/ #endif /////////////// CLineInTraceback /////////////// @@ -774,7 +774,7 @@ static int __Pyx_CLineForTraceback(PyThreadState *tstate, int c_line);/*proto*/ //@substitute: naming #ifndef CYTHON_CLINE_IN_TRACEBACK -static int __Pyx_CLineForTraceback(PyThreadState *tstate, int c_line) { +static int __Pyx_CLineForTraceback(HPY_CONTEXT_FIRST_ARG_DEF PyThreadState *tstate, int c_line) { PyObject *use_cline; PyObject *ptype, *pvalue, *ptraceback; #if CYTHON_COMPILING_IN_CPYTHON @@ -783,7 +783,7 @@ static int __Pyx_CLineForTraceback(PyThreadState *tstate, int c_line) { CYTHON_MAYBE_UNUSED_VAR(tstate); - if (unlikely(!${cython_runtime_cname})) { + if (unlikely(API_IS_NULL(${cython_runtime_cname}))) { // Very early error where the runtime module is not set up yet. return c_line; } @@ -791,15 +791,15 @@ static int __Pyx_CLineForTraceback(PyThreadState *tstate, int c_line) { __Pyx_ErrFetchInState(tstate, &ptype, &pvalue, &ptraceback); #if CYTHON_COMPILING_IN_CPYTHON - cython_runtime_dict = _PyObject_GetDictPtr(${cython_runtime_cname}); + cython_runtime_dict = _PyObject_GetDictPtr(HPY_LEGACY_OBJECT_AS(${cython_runtime_cname})); if (likely(cython_runtime_dict)) { __PYX_PY_DICT_LOOKUP_IF_MODIFIED( use_cline, *cython_runtime_dict, - __Pyx_PyDict_GetItemStr(*cython_runtime_dict, PYIDENT("cline_in_traceback"))) + __Pyx_PyDict_GetItemStr(*cython_runtime_dict, HPY_LEGACY_OBJECT_AS(PYIDENT("cline_in_traceback")))) } else #endif { - PyObject *use_cline_obj = __Pyx_PyObject_GetAttrStrNoError(${cython_runtime_cname}, PYIDENT("cline_in_traceback")); + PyObject *use_cline_obj = __Pyx_PyObject_GetAttrStrNoError(HPY_LEGACY_OBJECT_AS(${cython_runtime_cname}), HPY_LEGACY_OBJECT_AS(PYIDENT("cline_in_traceback"))); if (use_cline_obj) { use_cline = PyObject_Not(use_cline_obj) ? Py_False : Py_True; Py_DECREF(use_cline_obj); @@ -811,7 +811,7 @@ static int __Pyx_CLineForTraceback(PyThreadState *tstate, int c_line) { if (!use_cline) { c_line = 0; // No need to handle errors here when we reset the exception state just afterwards. - (void) PyObject_SetAttr(${cython_runtime_cname}, PYIDENT("cline_in_traceback"), Py_False); + (void) PYOBJECT_SET_ATTR(${cython_runtime_cname}, PYIDENT("cline_in_traceback"), API_FALSE); } else if (use_cline == Py_False || (use_cline != Py_True && PyObject_Not(use_cline) != 0)) { c_line = 0; @@ -823,7 +823,7 @@ static int __Pyx_CLineForTraceback(PyThreadState *tstate, int c_line) { /////////////// AddTraceback.proto /////////////// -static void __Pyx_AddTraceback(const char *funcname, int c_line, +static void __Pyx_AddTraceback(HPY_CONTEXT_FIRST_ARG_DEF const char *funcname, int c_line, int py_line, const char *filename); /*proto*/ /////////////// AddTraceback /////////////// @@ -885,7 +885,7 @@ static PyObject *__Pyx_PyCode_Replace_For_AddTraceback(PyObject *code, PyObject #endif } -static void __Pyx_AddTraceback(const char *funcname, int c_line, +static void __Pyx_AddTraceback(HPY_CONTEXT_FIRST_ARGUMENT_CALL const char *funcname, int c_line, int py_line, const char *filename) { PyObject *code_object = NULL, *py_py_line = NULL, *py_funcname = NULL, *dict = NULL; PyObject *replace = NULL, *getframe = NULL, *frame = NULL; @@ -894,7 +894,7 @@ static void __Pyx_AddTraceback(const char *funcname, int c_line, if (c_line) { // Avoid "unused" warning as long as we don't use this. (void) $cfilenm_cname; - (void) __Pyx_CLineForTraceback(__Pyx_PyThreadState_Current, c_line); + (void) __Pyx_CLineForTraceback(HPY_CONTEXT_FIRST_ARGUMENT_CALL __Pyx_PyThreadState_Current, c_line); } // DW - this is a horrendous hack, but I'm quite proud of it. Essentially @@ -971,7 +971,7 @@ static PyCodeObject* __Pyx_CreateCodeObjectForTraceback( return NULL; } -static void __Pyx_AddTraceback(const char *funcname, int c_line, +static void __Pyx_AddTraceback(HPY_CONTEXT_FIRST_ARG_DEF const char *funcname, int c_line, int py_line, const char *filename) { PyCodeObject *py_code = 0; PyFrameObject *py_frame = 0; @@ -979,7 +979,7 @@ static void __Pyx_AddTraceback(const char *funcname, int c_line, PyObject *ptype, *pvalue, *ptraceback; if (c_line) { - c_line = __Pyx_CLineForTraceback(tstate, c_line); + c_line = __Pyx_CLineForTraceback(HPY_CONTEXT_FIRST_ARG_CALL tstate, c_line); } // Negate to avoid collisions between py and c lines. @@ -1002,7 +1002,7 @@ static void __Pyx_AddTraceback(const char *funcname, int c_line, py_frame = PyFrame_New( tstate, /*PyThreadState *tstate,*/ py_code, /*PyCodeObject *code,*/ - $moddict_cname, /*PyObject *globals,*/ + HPY_LEGACY_OBJECT_AS($moddict_cname), /*PyObject *globals,*/ 0 /*PyObject *locals*/ ); if (!py_frame) goto bad; diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index fa949c0648c..a0ebabc65f8 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -719,6 +719,8 @@ class __Pyx_FakeReference { #define HPY_CONTEXT_TYPE HPyContext * #define HPY_CONTEXT_ONLY_ARG_DEF HPY_CONTEXT_TYPE HPY_CONTEXT_CNAME #define HPY_CONTEXT_ONLY_ARG_CALL HPY_CONTEXT_CNAME + #define HPY_CONTEXT_FIRST_ARG_DEF HPY_CONTEXT_TYPE HPY_CONTEXT_CNAME, + #define HPY_CONTEXT_FIRST_ARG_CALL HPY_CONTEXT_CNAME, #define PYOBJECT_TYPE HPy #define CAPI_IS_POINTER @@ -735,6 +737,8 @@ class __Pyx_FakeReference { #define API_IS_NULL(h) HPy_IsNull(h) #define API_IS_NOT_NULL(h) !HPy_IsNull(h) #define API_IS_EQUAL(a, b) HPy_Is(HPY_CONTEXT_CNAME, a, b) + #define API_TRUE HPY_CONTEXT_CNAME->h_True + #define API_FALSE HPY_CONTEXT_CNAME->h_False #define PYMODULEDEF_TYPE HPyModuleDef @@ -749,7 +753,9 @@ class __Pyx_FakeReference { #define TUPLE_CREATE_EMPTY() HPyTuple_FromArray(HPY_CONTEXT_CNAME, NULL, 0) #else #define HPY_CONTEXT_ONLY_ARG_DEF void - #define HPY_CONTEXT_ONLY_ARG_CALL + #define HPY_CONTEXT_ONLY_ARG_CALL + #define HPY_CONTEXT_FIRST_ARG_DEF + #define HPY_CONTEXT_FIRST_ARG_CALL #define PYOBJECT_TYPE PyObject * #define CAPI_IS_POINTER * //Some types are sometimes pointers and sometimes not (i.e. PyModuleDef) where the type is always the same in HPy @@ -766,6 +772,8 @@ class __Pyx_FakeReference { #define API_IS_NULL(h) !h //Both are here as otherwise we would get !!h for API_IS_NOT_NULL, which is hard to read - but it can be made so if necessary #define API_IS_NOT_NULL(h) h #define API_IS_EQUAL(a, b) a==b + #define API_TRUE Py_True + #define API_FALSE Py_False #define PYMODULEDEF_TYPE struct PyModuleDef From f2929b351f6fca229831d0c97e93c96b1c04d745 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Fri, 4 Aug 2023 15:12:03 +0200 Subject: [PATCH 130/286] Made exec function run without errors; can compile blank file now --- Cython/Compiler/ModuleNode.py | 13 +++++++++---- Cython/Utility/ModuleSetupCode.c | 4 ++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 3907b0ec5c7..0c4d511208f 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -1327,6 +1327,7 @@ def generate_c_class_declarations(self, env, code, definition, globalstate): module_state = globalstate['module_state'] module_state_defines = globalstate['module_state_defines'] module_state_clear = globalstate['module_state_clear'] + module_state_clear.putln("#if CYTHON_USING_HPY") module_state_traverse = globalstate['module_state_traverse'] module_state_typeobj = module_state.insertion_point() module_state_defines_typeobj = module_state_defines.insertion_point() @@ -1357,6 +1358,7 @@ def generate_c_class_declarations(self, env, code, definition, globalstate): module_state_traverse.putln( "Py_VISIT(traverse_module_state->%s);" % ( entry.type.typeobj_cname)) + module_state_clear.putln("#endif") for writer in [module_state_typeobj, module_state_defines_typeobj]: writer.putln("#endif") @@ -2971,7 +2973,7 @@ def generate_module_state_clear(self, env, code): Naming.modulestate_cname, Naming.modulestate_cname)) code.putln("if (!clear_module_state) return 0;") - code.putln('Py_CLEAR(HPY_LEGACY_OBJECT_AS(clear_module_state->%s));' % + code.putln('Py_CLEAR(clear_module_state->%s);' % env.module_dict_cname) code.putln('Py_CLEAR(clear_module_state->%s);' % Naming.builtins_cname) @@ -3048,14 +3050,15 @@ def generate_module_init_func(self, imported_modules, env, code): code.putln("void %s(void) {} /* workaround for https://bugs.python.org/issue39432 */" % wrong_punycode_module_name) code.putln("#endif") code.putln(header3) + code.putln("#endif") # CPython 3.5+ supports multi-phase module initialisation (gives access to __spec__, __file__, etc.) code.putln("#if CYTHON_PEP489_MULTI_PHASE_INIT") - code.putln("{") code.putln("#if !CYTHON_USING_HPY") + code.putln("{") code.putln("return PyModuleDef_Init(&%s);" % Naming.pymoduledef_cname) - code.putln("#endif") code.putln("}") + code.putln("#endif") mod_create_func = UtilityCode.load_as_string("ModuleCreationPEP489", "ModuleSetupCode.c")[1] code.put(mod_create_func) @@ -3253,7 +3256,9 @@ def generate_module_init_func(self, imported_modules, env, code): # user code in atexit or other global registries. ##code.put_decref_clear(env.module_dict_cname, py_object_type, nanny=False) code.putln('}') - code.putln("#if !CYTHON_USE_MODULE_STATE") + code.putln("#if CYTHON_USING_HPY") + code.putln("PYOBJECT_DEALLOC(%s);" % env.module_cname) + code.putln("#elif !CYTHON_USE_MODULE_STATE") code.put_decref_clear(env.module_cname, py_object_type, nanny=False, clear_before_decref=True) code.putln("#else") # This section is mainly for the limited API. env.module_cname still owns a reference so diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index a0ebabc65f8..1aec6e5c374 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -723,6 +723,8 @@ class __Pyx_FakeReference { #define HPY_CONTEXT_FIRST_ARG_CALL HPY_CONTEXT_CNAME, #define PYOBJECT_TYPE HPy + #define PYOBJECT_FIELD_TYPE HPyField + #define PYOBJECT_GLOBAL_TYPE HPyGlobal #define CAPI_IS_POINTER #define PYOBJECT_ALLOC(h) HPy_Dup(HPY_CONTEXT_CNAME, h) @@ -758,6 +760,8 @@ class __Pyx_FakeReference { #define HPY_CONTEXT_FIRST_ARG_CALL #define PYOBJECT_TYPE PyObject * + #define PYOBJECT_FIELD_TYPE PyObject * + #define PYOBJECT_GLOBAL_TYPE PyObject * #define CAPI_IS_POINTER * //Some types are sometimes pointers and sometimes not (i.e. PyModuleDef) where the type is always the same in HPy #define PYOBJECT_ALLOC(h) Py_INCREF(h) From 31ecac5c1caf8d9ccce0907acb268966ccfa1ea6 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 7 Aug 2023 15:38:10 +0200 Subject: [PATCH 131/286] Removed some legacy code, now supports int variable assignment too --- Cython/Compiler/Code.py | 10 +++++----- Cython/Compiler/ExprNodes.py | 8 ++++---- Cython/Compiler/ModuleNode.py | 13 +++++++------ Cython/Compiler/Nodes.py | 2 +- Cython/Compiler/PyrexTypes.py | 10 +++++----- Cython/Utility/ModuleSetupCode.c | 20 +++++++++++++++++++- 6 files changed, 41 insertions(+), 22 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index cf9f6afec2c..44c96ffba6e 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1216,7 +1216,7 @@ def initialize_main_c_code(self): w = self.parts['init_constants'] w.enter_cfunc_scope() w.putln("") - w.putln("static CYTHON_SMALL_CODE int __Pyx_InitConstants(void) {") + w.putln("static CYTHON_SMALL_CODE int __Pyx_InitConstants(HPY_CONTEXT_ONLY_ARG_DEF) {") if not Options.generate_cleanup_code: del self.parts['cleanup_globals'] @@ -1662,12 +1662,12 @@ def generate_num_constants(self): elif Utils.long_literal(value): function = 'PyInt_FromString("%s", 0, 0)' elif len(value.lstrip('-')) > 4: - function = "PyInt_FromLong(%sL)" + function = "PYOBJECT_FROM_LONG(%sL)" else: - function = "PyInt_FromLong(%s)" + function = "PYOBJECT_FROM_LONG(%s)" init_constants.putln('%s = %s; %s' % ( cname, function % value_code, - init_constants.error_goto_if_null(cname, self.module_pos))) + init_constants.error_goto_if_null_object(cname, self.module_pos))) # The functions below are there in a transition phase only # and will be deprecated. They are called from Nodes.BlockNode. @@ -2133,7 +2133,7 @@ def put_temp_declarations(self, func_context): else: decl = type.declaration_code(name) if type.is_pyobject: - self.putln("%s = NULL;" % decl) + self.putln("%s = API_NULL_VALUE;" % decl) elif type.is_memoryviewslice: self.putln("%s = %s;" % (decl, type.literal_code(type.default_value))) else: diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 08ac395f333..1631aaa2c5c 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -2515,9 +2515,9 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, # on types, so we create a descriptor which is then added to tp_dict. setter = '__Pyx_SetItemOnTypeDict' elif entry.scope.is_module_scope: - setter = 'PyDict_SetItem' - namespace = "HPY_LEGACY_OBJECT_AS(" + Naming.moddict_cname + ")" - interned_cname = "HPY_LEGACY_OBJECT_AS(" + interned_cname + ")" + setter = 'DICT_SET_ITEM' + namespace = Naming.moddict_cname + interned_cname = interned_cname elif entry.is_pyclass_attr: # Special-case setting __new__ n = "SetNewInClass" if self.name == "__new__" else "SetNameInClass" @@ -9388,7 +9388,7 @@ def generate_evaluation_code(self, code): "%s = __Pyx_PyDict_NewPresized(%d); %s" % ( self.result(), len(self.key_value_pairs), - code.error_goto_if_null(self.result(), self.pos))) + code.error_goto_if_null_object(self.result(), self.pos))) self.generate_gotref(code) keys_seen = set() diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 0c4d511208f..8682caeabfc 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -3161,7 +3161,7 @@ def generate_module_init_func(self, imported_modules, env, code): code.put_error_if_neg(self.pos, "_import_array()") code.putln("/*--- Initialize various global constants etc. ---*/") - code.put_error_if_neg(self.pos, "__Pyx_InitConstants()") + code.put_error_if_neg(self.pos, "__Pyx_InitConstants(HPY_CONTEXT_ONLY_ARG_CALL)") code.putln("stringtab_initialized = 1;") code.put_error_if_neg(self.pos, "__Pyx_InitGlobals()") # calls any utility code @@ -3290,6 +3290,10 @@ def generate_module_init_func(self, imported_modules, env, code): code.putln("#endif") code.putln('}') + code.putln("#if CYTHON_USING_HPY") + code.putln("HPy_MODINIT(%s, %s)" % (env.module_name, Naming.pymoduledef_cname)) + code.putln("#endif") + tempdecl_code.put_temp_declarations(code.funcstate) code.exit_cfunc_scope() @@ -3410,7 +3414,7 @@ def generate_module_import_setup(self, env, code): fq_module_name_cstring = fq_module_name.as_c_string_literal() code.putln("#if PY_MAJOR_VERSION >= 3") code.putln("{") - code.putln("PyObject *modules = PyImport_GetModuleDict(); %s" % + code.putln("PyObject *modules = PyImport_GetModuleDict(); %s" % ##Figure out how to get module dict code.error_goto_if_null("modules", self.pos)) code.putln('if (!PyDict_GetItemString(modules, %s)) {' % fq_module_name_cstring) code.putln(code.error_goto_if_neg('PyDict_SetItemString(modules, %s, HPY_LEGACY_OBJECT_AS(%s))' % ( @@ -3615,7 +3619,7 @@ def generate_pymoduledef_struct(self, env, code): code.putln("#endif") code.putln("#else") code.putln(" .doc = %s," % doc) - code.putln(" .size = -1,") + code.putln(" .size = 0,") code.putln(" .legacy_methods = 0,") if env.is_c_class_scope and not env.hpyfunc_entries: code.putln(" .defines = methods,") @@ -3627,9 +3631,6 @@ def generate_pymoduledef_struct(self, env, code): code.putln('} /* anonymous namespace */') code.putln('#endif') code.putln("#endif") - code.putln("#if CYTHON_USING_HPY") - code.putln("HPy_MODINIT(hpyinit, %s)" % Naming.pymoduledef_cname) - code.putln("#endif") def generate_module_creation_code(self, env, code): # Generate code to create the module object and diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 52564382509..bb760511fef 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -1735,7 +1735,7 @@ def generate_execution_code(self, code): item.cname, code.error_goto_if_null(temp, item.pos))) code.put_gotref(temp, PyrexTypes.py_object_type) - code.putln('if (PyDict_SetItemString(HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS("%s"), %s) < 0) %s' % ( + code.putln('if (DICT_SET_ITEM_STR(%s, "%s", %s) < 0) %s' % ( Naming.moddict_cname, item.name, temp, diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 468c3f924ad..649754661cd 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -1280,13 +1280,13 @@ def declaration_code(self, entity_code, if pyrex or for_display: base_code = "object" else: - base_code = public_decl("PyObject", dll_linkage) - entity_code = "*%s" % entity_code + base_code = public_decl("PYOBJECT_TYPE", dll_linkage) + entity_code = "%s" % entity_code return self.base_declaration_code(base_code, entity_code) def as_pyobject(self, cname): if (not self.is_complete()) or self.is_extension_type: - return "(PyObject *)" + cname + return "(PYOBJECT_TYPE)" + cname else: return cname @@ -1378,10 +1378,10 @@ def _generate_decref(self, code, cname, nanny, null_check=False, X = '' # CPython doesn't have a Py_XCLEAR() code.putln("%s_%sCLEAR(%s);" % (prefix, X, cname)) else: - code.putln("%s_%sDECREF(%s); %s = 0;" % ( + code.putln("REFNANNY_DEALLOC(%s_%sDECREF, %s); %s = API_NULL_VALUE;" % ( prefix, X, self.as_pyobject(cname), cname)) else: - code.putln("%s_%sDECREF(%s);" % ( + code.putln("REFNANNY_DEALLOC(%s_%sDECREF, %s);" % ( prefix, X, self.as_pyobject(cname))) def nullcheck_string(self, cname): diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 1aec6e5c374..16941fde802 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -731,6 +731,9 @@ class __Pyx_FakeReference { #define PYOBJECT_XALLOC(h) HPy_Dup(HPY_CONTEXT_CNAME, h) #define PYOBJECT_DEALLOC(h) HPy_Close(HPY_CONTEXT_CNAME, h) #define PYOBJECT_XDEALLOC(h) HPy_Close(HPY_CONTEXT_CNAME, h) + #define REFNANNY_DEALLOC(func, h) PYOBJECT_DEALLOC(h) + + #define PYOBJECT_FROM_LONG(i) HPyLong_FromLong(HPY_CONTEXT_CNAME, i) #define HPY_LEGACY_OBJECT_FROM(o) HPy_FromPyObject(HPY_CONTEXT_CNAME, o) #define HPY_LEGACY_OBJECT_AS(o) HPy_AsPyObject(HPY_CONTEXT_CNAME, o) @@ -744,6 +747,11 @@ class __Pyx_FakeReference { #define PYMODULEDEF_TYPE HPyModuleDef + #define DICT_NEW() HPyDict_New(HPY_CONTEXT_CNAME) + #define DICT_GET_ITEM(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) + #define DICT_SET_ITEM(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) + #define DICT_SET_ITEM_STR(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) + #define PYOBJECT_SET_ATTR(o, attr_name, attr_val) HPy_SetAttr(HPY_CONTEXT_CNAME, o, attr_name, attr_val) #define PYOBJECT_GET_ATTR_STR(o, attr_name) HPyObject_GetAttrString(HPY_CONTEXT_CNAME, o, attr_name) #define PYOBJECT_SET_ATTR_STR(o1, attr_name, o2) HPy_SetAttr_s(HPY_CONTEXT_CNAME, o1, attr_name, o2) @@ -768,6 +776,9 @@ class __Pyx_FakeReference { #define PYOBJECT_XALLOC(h) Py_XINCREF(h) #define PYOBJECT_DEALLOC(h) Py_DECREF(h) #define PYOBJECT_XDEALLOC(h) Py_XDECREF(h) + #define REFNANNY_DEALLOC(func, h) func(h) + + #define PYOBJECT_FROM_LONG(i) PyInt_FromLong(i) #define HPY_LEGACY_OBJECT_FROM(o) o #define HPY_LEGACY_OBJECT_AS(o) o @@ -781,6 +792,11 @@ class __Pyx_FakeReference { #define PYMODULEDEF_TYPE struct PyModuleDef + #define DICT_NEW() + #define DICT_GET_ITEM(o, attr_name) PyDict_GetItem(o, attr_name) + #define DICT_SET_ITEM(o, attr_name, attr_val) PyDict_SetItem(o, attr_name, attr_val) + #define DICT_SET_ITEM_STR(o, attr_name, attr_val) PyDict_SetItemString(o, attr_name, attr_val) + #define PYOBJECT_SET_ATTR(o, attr_name, attr_val) PyObject_SetAttr(o, attr_name, attr_val) #define PYOBJECT_GET_ATTR_STR(o, attr_name) PyObject_GetAttrString(o, attr_name) #define PYOBJECT_SET_ATTR_STR(o1, attr_name, o2) PyObject_SetAttrString(o1, attr_name, o2) @@ -1124,7 +1140,9 @@ static CYTHON_INLINE void *__Pyx_PyModule_GetState(PyObject *op) #define __Pyx_PyType_GetSlot(type, name, func_ctype) ((type)->name) #endif -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030d0000 || defined(_PyDict_NewPresized) +#if CYTHON_USING_HPY +#define __Pyx_PyDict_NewPresized(n) DICT_NEW() +#elif CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030d0000 || defined(_PyDict_NewPresized) #define __Pyx_PyDict_NewPresized(n) ((n <= 8) ? PyDict_New() : _PyDict_NewPresized(n)) #else #define __Pyx_PyDict_NewPresized(n) PyDict_New() From 4098d75c083d7c34561656c01b221d01406a51c9 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 7 Aug 2023 16:15:31 +0200 Subject: [PATCH 132/286] Got rid of warnings related to PyObject ** array for initstrings --- Cython/Compiler/Code.py | 6 +++--- Cython/Utility/ModuleSetupCode.c | 2 +- Cython/Utility/StringTools.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 44c96ffba6e..8f5c58a681f 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1589,7 +1589,7 @@ def generate_string_constants(self): py_strings.sort() w = self.parts['pystring_table'] w.putln("") - w.putln("static int __Pyx_CreateStringTabAndInitStrings(void) {") + w.putln("static int __Pyx_CreateStringTabAndInitStrings(HPY_CONTEXT_ONLY_ARG_DEF) {") # the stringtab is a function local rather than a global to # ensure that it doesn't conflict with module state w.putln("__Pyx_StringTabEntry %s[] = {" % Naming.stringtab_cname) @@ -1634,11 +1634,11 @@ def generate_string_constants(self): w.putln("#endif") w.putln("{0, 0, 0, 0, 0, 0, 0}") w.putln("};") - w.putln("return __Pyx_InitStrings(%s);" % Naming.stringtab_cname) + w.putln("return __Pyx_InitStrings(HPY_CONTEXT_FIRST_ARG_CALL %s);" % Naming.stringtab_cname) w.putln("}") init_constants.putln( - "if (__Pyx_CreateStringTabAndInitStrings() < 0) %s;" % + "if (__Pyx_CreateStringTabAndInitStrings(HPY_CONTEXT_ONLY_ARG_CALL) < 0) %s;" % init_constants.error_goto(self.module_pos)) def generate_num_constants(self): diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 16941fde802..6f1599ab686 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -1599,7 +1599,7 @@ static CYTHON_INLINE float __PYX_NAN() { /////////////// UtilityFunctionPredeclarations.proto /////////////// -typedef struct {PyObject **p; const char *s; const Py_ssize_t n; const char* encoding; +typedef struct {PYOBJECT_TYPE* p; const char *s; const Py_ssize_t n; const char* encoding; const char is_unicode; const char is_str; const char intern; } __Pyx_StringTabEntry; /*proto*/ diff --git a/Cython/Utility/StringTools.c b/Cython/Utility/StringTools.c index 8187af29b87..d29e55c6ecc 100644 --- a/Cython/Utility/StringTools.c +++ b/Cython/Utility/StringTools.c @@ -26,11 +26,11 @@ static CYTHON_INLINE Py_ssize_t __Pyx_Py_UNICODE_ssize_strlen(const Py_UNICODE * //////////////////// InitStrings.proto //////////////////// -static int __Pyx_InitStrings(__Pyx_StringTabEntry *t); /*proto*/ +static int __Pyx_InitStrings(HPY_CONTEXT_FIRST_ARG_DEF __Pyx_StringTabEntry *t); /*proto*/ //////////////////// InitStrings //////////////////// -static int __Pyx_InitStrings(__Pyx_StringTabEntry *t) { +static int __Pyx_InitStrings(HPY_CONTEXT_FIRST_ARG_DEF __Pyx_StringTabEntry *t) { while (t->p) { PyObject *str; if (t->is_unicode | t->is_str) { From 6b1db2b9f3f13b08630eabde0618fb6d32d58166 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Tue, 8 Aug 2023 10:26:12 +0200 Subject: [PATCH 133/286] Float declaration instantiation now has HPy versions --- Cython/Compiler/Code.py | 2 +- Cython/Compiler/PyrexTypes.py | 2 +- Cython/Utility/ModuleSetupCode.c | 12 ++++++++++-- Cython/Utility/TypeConversion.c | 2 +- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 8f5c58a681f..63a28823ff0 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1656,7 +1656,7 @@ def generate_num_constants(self): self.parts['module_state_traverse'].putln( "Py_VISIT(traverse_module_state->%s);" % cname) if py_type == 'float': - function = 'PyFloat_FromDouble(%s)' + function = 'PYOBJECT_FROM_DOUBLE(%s)' elif py_type == 'long': function = 'PyLong_FromString("%s", 0, 0)' elif Utils.long_literal(value): diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 649754661cd..25bfdcce2cc 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -2375,7 +2375,7 @@ def sign_and_name(self): class CFloatType(CNumericType): is_float = 1 - to_py_function = "PyFloat_FromDouble" + to_py_function = "PYOBJECT_FROM_DOUBLE" from_py_function = "__Pyx_PyFloat_AsDouble" exception_value = -1 diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 6f1599ab686..e75f92cb219 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -734,24 +734,28 @@ class __Pyx_FakeReference { #define REFNANNY_DEALLOC(func, h) PYOBJECT_DEALLOC(h) #define PYOBJECT_FROM_LONG(i) HPyLong_FromLong(HPY_CONTEXT_CNAME, i) + #define PYOBJECT_FROM_DOUBLE(f) HPyFloat_FromDouble(HPY_CONTEXT_CNAME, f) #define HPY_LEGACY_OBJECT_FROM(o) HPy_FromPyObject(HPY_CONTEXT_CNAME, o) #define HPY_LEGACY_OBJECT_AS(o) HPy_AsPyObject(HPY_CONTEXT_CNAME, o) + #define PYMODULEDEF_TYPE HPyModuleDef + #define API_NULL_VALUE HPy_NULL #define API_IS_NULL(h) HPy_IsNull(h) #define API_IS_NOT_NULL(h) !HPy_IsNull(h) #define API_IS_EQUAL(a, b) HPy_Is(HPY_CONTEXT_CNAME, a, b) #define API_TRUE HPY_CONTEXT_CNAME->h_True #define API_FALSE HPY_CONTEXT_CNAME->h_False + #define API_IS_TRUE(h) HPy_IsTrue(HPY_CONTEXT_CNAME, h) + #define API_IS_FALSE(h) !HPy_IsTrue(HPY_CONTEXT_CNAME, h) - #define PYMODULEDEF_TYPE HPyModuleDef - #define DICT_NEW() HPyDict_New(HPY_CONTEXT_CNAME) #define DICT_GET_ITEM(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) #define DICT_SET_ITEM(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) #define DICT_SET_ITEM_STR(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) + #define PYOBJECT_GET_ATTR(o, attr_name) HPy_GetAttr(HPY_CONTEXT_CNAME, o, attr_name) #define PYOBJECT_SET_ATTR(o, attr_name, attr_val) HPy_SetAttr(HPY_CONTEXT_CNAME, o, attr_name, attr_val) #define PYOBJECT_GET_ATTR_STR(o, attr_name) HPyObject_GetAttrString(HPY_CONTEXT_CNAME, o, attr_name) #define PYOBJECT_SET_ATTR_STR(o1, attr_name, o2) HPy_SetAttr_s(HPY_CONTEXT_CNAME, o1, attr_name, o2) @@ -779,6 +783,7 @@ class __Pyx_FakeReference { #define REFNANNY_DEALLOC(func, h) func(h) #define PYOBJECT_FROM_LONG(i) PyInt_FromLong(i) + #define PYOBJECT_FROM_DOUBLE(f) PyFloat_FromDouble(f) #define HPY_LEGACY_OBJECT_FROM(o) o #define HPY_LEGACY_OBJECT_AS(o) o @@ -789,6 +794,8 @@ class __Pyx_FakeReference { #define API_IS_EQUAL(a, b) a==b #define API_TRUE Py_True #define API_FALSE Py_False + #define API_IS_TRUE(h) PyObject_IsTrue(h) + #define API_IS_FALSE(h) !PyObject_Not(h) #define PYMODULEDEF_TYPE struct PyModuleDef @@ -797,6 +804,7 @@ class __Pyx_FakeReference { #define DICT_SET_ITEM(o, attr_name, attr_val) PyDict_SetItem(o, attr_name, attr_val) #define DICT_SET_ITEM_STR(o, attr_name, attr_val) PyDict_SetItemString(o, attr_name, attr_val) + #define PYOBJECT_GET_ATTR(o, attr_name) PyObject_GetAttr(o, attr_name) #define PYOBJECT_SET_ATTR(o, attr_name, attr_val) PyObject_SetAttr(o, attr_name, attr_val) #define PYOBJECT_GET_ATTR_STR(o, attr_name) PyObject_GetAttrString(o, attr_name) #define PYOBJECT_SET_ATTR_STR(o1, attr_name, o2) PyObject_SetAttrString(o1, attr_name, o2) diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index e10077ddf1e..1dd563d7ab3 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -469,7 +469,7 @@ static CYTHON_INLINE PyObject* __Pyx__PyNumber_Float(PyObject* obj) { #if CYTHON_USE_PYLONG_INTERNALS no_error: #endif - return PyFloat_FromDouble(val); + return PYOBJECT_FROM_DOUBLE(val); } /////////////// GCCDiagnostics.proto /////////////// From b75ce8b87496760538a6e135246634c2915eefa2 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Tue, 8 Aug 2023 11:25:26 +0200 Subject: [PATCH 134/286] Boolean variables can now be declared --- Cython/Utility/Exceptions.c | 18 +++++++++--------- Cython/Utility/ModuleSetupCode.c | 6 ++++-- Cython/Utility/ObjectHandling.c | 14 +++++++------- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/Cython/Utility/Exceptions.c b/Cython/Utility/Exceptions.c index a0d4a712dde..65a5e43408f 100644 --- a/Cython/Utility/Exceptions.c +++ b/Cython/Utility/Exceptions.c @@ -775,7 +775,7 @@ static int __Pyx_CLineForTraceback(HPY_CONTEXT_FIRST_ARG_DEF PyThreadState *tsta #ifndef CYTHON_CLINE_IN_TRACEBACK static int __Pyx_CLineForTraceback(HPY_CONTEXT_FIRST_ARG_DEF PyThreadState *tstate, int c_line) { - PyObject *use_cline; + PYOBJECT_TYPE use_cline; PyObject *ptype, *pvalue, *ptraceback; #if CYTHON_COMPILING_IN_CPYTHON PyObject **cython_runtime_dict; @@ -795,25 +795,25 @@ static int __Pyx_CLineForTraceback(HPY_CONTEXT_FIRST_ARG_DEF PyThreadState *tsta if (likely(cython_runtime_dict)) { __PYX_PY_DICT_LOOKUP_IF_MODIFIED( use_cline, *cython_runtime_dict, - __Pyx_PyDict_GetItemStr(*cython_runtime_dict, HPY_LEGACY_OBJECT_AS(PYIDENT("cline_in_traceback")))) + __Pyx_PyDict_GetItemStr(HPY_LEGACY_OBJECT_FROM(*cython_runtime_dict), PYIDENT("cline_in_traceback"))) } else #endif { - PyObject *use_cline_obj = __Pyx_PyObject_GetAttrStrNoError(HPY_LEGACY_OBJECT_AS(${cython_runtime_cname}), HPY_LEGACY_OBJECT_AS(PYIDENT("cline_in_traceback"))); - if (use_cline_obj) { - use_cline = PyObject_Not(use_cline_obj) ? Py_False : Py_True; - Py_DECREF(use_cline_obj); + PYOBJECT_TYPE use_cline_obj = __Pyx_PyObject_GetAttrStrNoError(HPY_CONTEXT_FIRST_ARG_CALL ${cython_runtime_cname}, PYIDENT("cline_in_traceback")); + if (API_IS_NOT_NULL(use_cline_obj)) { + use_cline = API_IS_FALSE(use_cline_obj) ? API_FALSE : API_TRUE; + PYOBJECT_DEALLOC(use_cline_obj); } else { PyErr_Clear(); - use_cline = NULL; + use_cline = API_NULL_VALUE; } } - if (!use_cline) { + if (API_IS_NULL(use_cline)) { c_line = 0; // No need to handle errors here when we reset the exception state just afterwards. (void) PYOBJECT_SET_ATTR(${cython_runtime_cname}, PYIDENT("cline_in_traceback"), API_FALSE); } - else if (use_cline == Py_False || (use_cline != Py_True && PyObject_Not(use_cline) != 0)) { + else if (API_IS_EQUAL(use_cline, API_FALSE) || ((!API_IS_EQUAL(use_cline, API_TRUE)) && API_IS_FALSE(use_cline) != 0)) { c_line = 0; } __Pyx_ErrRestoreInState(tstate, ptype, pvalue, ptraceback); diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index e75f92cb219..77ac5ca166f 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -439,6 +439,8 @@ /* Any Python objects are generally opaque in HPy */ #undef CYTHON_USE_PYLONG_INTERNALS #define CYTHON_USE_PYLONG_INTERNALS 0 + #undef CYTHON_USE_UNICODE_INTERNALS + #define CYTHON_USE_UNICODE_INTERNALS 0 #undef CYTHON_USE_DICT_VERSIONS #define CYTHON_USE_DICT_VERSIONS 0 #undef CYTHON_FAST_THREAD_STATE @@ -1170,7 +1172,7 @@ static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStr(PyObject *dict, PyObject } #elif !CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07020000 #define __Pyx_PyDict_GetItemStrWithError PyDict_GetItemWithError -#define __Pyx_PyDict_GetItemStr PyDict_GetItem +#define __Pyx_PyDict_GetItemStr(dict, name) DICT_GET_ITEM(dict, name) #else static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStrWithError(PyObject *dict, PyObject *name) { // This is tricky - we should return a borrowed reference but not swallow non-KeyError exceptions. 8-| @@ -1196,7 +1198,7 @@ static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStrWithError(PyObject *dict, return ep->me_value; #endif } -#define __Pyx_PyDict_GetItemStr PyDict_GetItem +#define __Pyx_PyDict_GetItemStr(dict, name) DICT_GET_ITEM(dict, name) #endif /* Type slots */ diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 89d461d1b71..20f815d5770 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1505,7 +1505,7 @@ static CYTHON_INLINE PyObject* __Pyx__PyObject_LookupSpecial(PyObject* obj, PyOb /////////////// PyObjectGetAttrStrNoError.proto /////////////// -static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStrNoError(PyObject* obj, PyObject* attr_name);/*proto*/ +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStrNoError(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, PYOBJECT_TYPE attr_name);/*proto*/ /////////////// PyObjectGetAttrStrNoError /////////////// //@requires: PyObjectGetAttrStr @@ -1522,13 +1522,13 @@ static void __Pyx_PyObject_GetAttrStr_ClearAttributeError(void) { } #endif -static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStrNoError(PyObject* obj, PyObject* attr_name) { - PyObject *result; -#if __PYX_LIMITED_VERSION_HEX >= 0x030d00A1 +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStrNoError(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, PYOBJECT_TYPE attr_name) { + PYOBJECT_TYPE result; +#if __PYX_LIMITED_VERSION_HEX >= 0x030d00A1 && !CYTHON_USING_HPY (void) PyObject_GetOptionalAttr(obj, attr_name, &result); return result; #else -#if CYTHON_COMPILING_IN_CPYTHON && CYTHON_USE_TYPE_SLOTS +#if CYTHON_COMPILING_IN_CPYTHON && CYTHON_USE_TYPE_SLOTS && !CYTHON_USING_HPY // _PyObject_GenericGetAttrWithDict() in CPython 3.7+ can avoid raising the AttributeError. // See https://bugs.python.org/issue32544 PyTypeObject* tp = Py_TYPE(obj); @@ -1537,7 +1537,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStrNoError(PyObject* obj, P } #endif result = __Pyx_PyObject_GetAttrStr(obj, attr_name); - if (unlikely(!result)) { + if (unlikely(API_IS_NULL(result))) { __Pyx_PyObject_GetAttrStr_ClearAttributeError(); } return result; @@ -1550,7 +1550,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStrNoError(PyObject* obj, P #if CYTHON_USE_TYPE_SLOTS static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStr(PyObject* obj, PyObject* attr_name);/*proto*/ #else -#define __Pyx_PyObject_GetAttrStr(o,n) PyObject_GetAttr(o,n) +#define __Pyx_PyObject_GetAttrStr(o,n) PYOBJECT_GET_ATTR(o,n) //Needed to turn this into a function macro to be able to pass the context properly #endif /////////////// PyObjectGetAttrStr /////////////// From ada2ad7d823ab4bfec20ff98d225efb26afb156b Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Tue, 8 Aug 2023 14:28:30 +0200 Subject: [PATCH 135/286] Fixed issue with bools --- Cython/Compiler/ExprNodes.py | 2 +- Cython/Compiler/ModuleNode.py | 22 +++++++++++----------- Cython/Utility/ModuleSetupCode.c | 5 ++++- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 1631aaa2c5c..f22743cad4a 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -1306,7 +1306,7 @@ def compile_time_value(self, denv): def calculate_result_code(self): if self.type.is_pyobject: - return 'Py_True' if self.value else 'Py_False' + return 'API_TRUE' if self.value else 'API_FALSE' else: return str(int(self.value)) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 8682caeabfc..85ffa5d4c41 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -2261,11 +2261,11 @@ def generate_richcmp_function(self, scope, code): # Implement the and/or check with an if. if comp_op == '&&': code.putln("if (%s order_res) {" % ('!!' if invert_comp else '!')) - code.putln("ret = __Pyx_NewRef(Py_False);") + code.putln("ret = __Pyx_NewRef(API_FALSE);") code.putln("} else {") elif comp_op == '||': code.putln("if (%s order_res) {" % ('!' if invert_comp else '')) - code.putln("ret = __Pyx_NewRef(Py_True);") + code.putln("ret = __Pyx_NewRef(API_TRUE);") code.putln("} else {") else: raise AssertionError('Unknown op %s' % (comp_op, )) @@ -2282,18 +2282,18 @@ def generate_richcmp_function(self, scope, code): code.putln("Py_DECREF(ret);") code.putln("if (unlikely(eq_res < 0)) return NULL;") if invert_equals: - code.putln("ret = eq_res ? Py_False : Py_True;") + code.putln("ret = eq_res ? API_FALSE : API_TRUE;") else: - code.putln("ret = eq_res ? Py_True : Py_False;") + code.putln("ret = eq_res ? API_TRUE : API_FALSE;") code.putln("Py_INCREF(ret);") code.putln("}") # equals success code.putln("}") # Needs to try equals else: # Convert direct to a boolean. if invert_comp: - code.putln("ret = order_res ? Py_False : Py_True;") + code.putln("ret = order_res ? API_FALSE : API_TRUE;") else: - code.putln("ret = order_res ? Py_True : Py_False;") + code.putln("ret = order_res ? API_TRUE : API_FALSE;") code.putln("Py_INCREF(ret);") code.putln("}") # comp_op code.putln("return ret;") @@ -2311,7 +2311,7 @@ def generate_richcmp_function(self, scope, code): code.putln("int b = __Pyx_PyObject_IsTrue(ret);") code.putln("Py_DECREF(ret);") code.putln("if (unlikely(b < 0)) return NULL;") - code.putln("ret = (b) ? Py_False : Py_True;") + code.putln("ret = (b) ? API_FALSE : API_TRUE;") code.putln("Py_INCREF(ret);") code.putln("}") code.putln("return ret;") @@ -3414,10 +3414,10 @@ def generate_module_import_setup(self, env, code): fq_module_name_cstring = fq_module_name.as_c_string_literal() code.putln("#if PY_MAJOR_VERSION >= 3") code.putln("{") - code.putln("PyObject *modules = PyImport_GetModuleDict(); %s" % ##Figure out how to get module dict - code.error_goto_if_null("modules", self.pos)) - code.putln('if (!PyDict_GetItemString(modules, %s)) {' % fq_module_name_cstring) - code.putln(code.error_goto_if_neg('PyDict_SetItemString(modules, %s, HPY_LEGACY_OBJECT_AS(%s))' % ( + code.putln("PYOBJECT_TYPE modules = HPY_LEGACY_OBJECT_FROM(PyImport_GetModuleDict()); %s" % ##TODO: Figure out how to get module dict + code.error_goto_if_null_object("modules", self.pos)) + code.putln('if (API_IS_NULL(DICT_GET_ITEM_STR(modules, %s))) {' % fq_module_name_cstring) + code.putln(code.error_goto_if_neg('DICT_SET_ITEM_STR(modules, %s, %s)' % ( fq_module_name_cstring, env.module_cname), self.pos)) code.putln("}") code.putln("}") diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 77ac5ca166f..4fa51dda7b9 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -737,6 +737,7 @@ class __Pyx_FakeReference { #define PYOBJECT_FROM_LONG(i) HPyLong_FromLong(HPY_CONTEXT_CNAME, i) #define PYOBJECT_FROM_DOUBLE(f) HPyFloat_FromDouble(HPY_CONTEXT_CNAME, f) + #define PYOBJECT_FROM_STRING(s) HPyUnicode_FromString(HPY_CONTEXT_CNAME, s) //not yet needed in C API version #define HPY_LEGACY_OBJECT_FROM(o) HPy_FromPyObject(HPY_CONTEXT_CNAME, o) #define HPY_LEGACY_OBJECT_AS(o) HPy_AsPyObject(HPY_CONTEXT_CNAME, o) @@ -755,7 +756,8 @@ class __Pyx_FakeReference { #define DICT_NEW() HPyDict_New(HPY_CONTEXT_CNAME) #define DICT_GET_ITEM(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) #define DICT_SET_ITEM(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) - #define DICT_SET_ITEM_STR(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) + #define DICT_GET_ITEM_STR(o, attr_name) HPy_GetItem_s(HPY_CONTEXT_CNAME, o, attr_name) + #define DICT_SET_ITEM_STR(o, attr_name, attr_val) HPy_SetItem_s(HPY_CONTEXT_CNAME, o, attr_name, attr_val) #define PYOBJECT_GET_ATTR(o, attr_name) HPy_GetAttr(HPY_CONTEXT_CNAME, o, attr_name) #define PYOBJECT_SET_ATTR(o, attr_name, attr_val) HPy_SetAttr(HPY_CONTEXT_CNAME, o, attr_name, attr_val) @@ -804,6 +806,7 @@ class __Pyx_FakeReference { #define DICT_NEW() #define DICT_GET_ITEM(o, attr_name) PyDict_GetItem(o, attr_name) #define DICT_SET_ITEM(o, attr_name, attr_val) PyDict_SetItem(o, attr_name, attr_val) + #define DICT_GET_ITEM_STR(o, attr_name) PyDict_GetItemString(o, attr_name) #define DICT_SET_ITEM_STR(o, attr_name, attr_val) PyDict_SetItemString(o, attr_name, attr_val) #define PYOBJECT_GET_ATTR(o, attr_name) PyObject_GetAttr(o, attr_name) From 14971c770b02df28cc45f8a5a6762bdcd4f16b09 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 9 Aug 2023 11:03:26 +0200 Subject: [PATCH 136/286] Made tests work on C API --- Cython/Utility/Exceptions.c | 4 ++-- Cython/Utility/ModuleSetupCode.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cython/Utility/Exceptions.c b/Cython/Utility/Exceptions.c index 65a5e43408f..4354dc11098 100644 --- a/Cython/Utility/Exceptions.c +++ b/Cython/Utility/Exceptions.c @@ -762,7 +762,7 @@ static void __Pyx_WriteUnraisable(const char *name, int clineno, /////////////// CLineInTraceback.proto /////////////// #ifdef CYTHON_CLINE_IN_TRACEBACK /* 0 or 1 to disable/enable C line display in tracebacks at C compile time */ -#define __Pyx_CLineForTraceback(HPY_CONTEXT_FIRST_ARG_DEF tstate, c_line) (((CYTHON_CLINE_IN_TRACEBACK)) ? c_line : 0) +#define __Pyx_CLineForTraceback(tstate, c_line) (((CYTHON_CLINE_IN_TRACEBACK)) ? c_line : 0) #else static int __Pyx_CLineForTraceback(HPY_CONTEXT_FIRST_ARG_DEF PyThreadState *tstate, int c_line);/*proto*/ #endif @@ -813,7 +813,7 @@ static int __Pyx_CLineForTraceback(HPY_CONTEXT_FIRST_ARG_DEF PyThreadState *tsta // No need to handle errors here when we reset the exception state just afterwards. (void) PYOBJECT_SET_ATTR(${cython_runtime_cname}, PYIDENT("cline_in_traceback"), API_FALSE); } - else if (API_IS_EQUAL(use_cline, API_FALSE) || ((!API_IS_EQUAL(use_cline, API_TRUE)) && API_IS_FALSE(use_cline) != 0)) { + else if (API_IS_EQUAL(use_cline, API_FALSE) || ((!(API_IS_EQUAL(use_cline, API_TRUE))) && API_IS_FALSE(use_cline) != 0)) { c_line = 0; } __Pyx_ErrRestoreInState(tstate, ptype, pvalue, ptraceback); diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 4fa51dda7b9..f0c1dedfb61 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -452,6 +452,8 @@ #define CYTHON_REFNANNY 0 #undef CYTHON_METH_FASTCALL #define CYTHON_METH_FASTCALL 1 + #undef CYTHON_CLINE_IN_TRACEBACK + #define CYTHON_CLINE_IN_TRACEBACK 0 //Is disabled for the Limited API - probably safest to disable it for HPy then #else #define CYTHON_USING_HPY 0 #endif From aba7de76c00a4616ef8deb0122bafe06fcc72cd4 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 9 Aug 2023 11:36:23 +0200 Subject: [PATCH 137/286] Tuple initialisation ported --- Cython/Compiler/ExprNodes.py | 4 ++-- Cython/Utility/ModuleSetupCode.c | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index f22743cad4a..4283fe752a5 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -8176,11 +8176,11 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): if self.type is tuple_type and (self.is_literal or self.slow) and not c_mult: # use PyTuple_Pack() to avoid generating huge amounts of one-time code - code.putln('%s = PyTuple_Pack(%d, %s); %s' % ( + code.putln('%s = TUPLE_PACK(%d, %s); %s' % ( target, len(self.args), ', '.join(arg.py_result() for arg in self.args), - code.error_goto_if_null(target, self.pos))) + code.error_goto_if_null_object(target, self.pos))) code.put_gotref(target, py_object_type) elif self.type.is_ctuple: for i, arg in enumerate(self.args): diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index f0c1dedfb61..e49751e695e 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -771,6 +771,7 @@ class __Pyx_FakeReference { #define BYTES_FROM_STR_AND_SIZE(str, size) HPyBytes_FromStringAndSize(HPY_CONTEXT_CNAME, str, size) #define TUPLE_CREATE_EMPTY() HPyTuple_FromArray(HPY_CONTEXT_CNAME, NULL, 0) + #define TUPLE_PACK(num_args, ...) HPyTuple_Pack(HPY_CONTEXT_CNAME, num_args, __VA_ARGS__) #else #define HPY_CONTEXT_ONLY_ARG_DEF void #define HPY_CONTEXT_ONLY_ARG_CALL @@ -819,7 +820,10 @@ class __Pyx_FakeReference { #define PYMODULE_GETDICT_ATTR(mod) PyModule_GetDict(mod) #define BYTES_FROM_STR_AND_SIZE(str, size) PyBytes_FromStringAndSize(str, size) + #define TUPLE_CREATE_EMPTY() PyTuple_New(0) + #define TUPLE_PACK(num_args, ...) PyTuple_Pack(num_args, __VA_ARGS__) + #endif /////////////// PythonCompatibility /////////////// From 3ce52bb1a642fa9da2f875fc5e69a5e269144f23 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 9 Aug 2023 17:10:15 +0200 Subject: [PATCH 138/286] Can currently create one tuple from non-literals - need to get temp builder variable to get more --- Cython/Compiler/Code.py | 6 +++--- Cython/Compiler/ExprNodes.py | 24 ++++++++++++++++-------- Cython/Compiler/Nodes.py | 3 +++ Cython/Utility/ModuleSetupCode.c | 12 ++++++++++++ Cython/Utility/ObjectHandling.c | 32 ++++++++++++++++---------------- Cython/Utility/TypeConversion.c | 2 +- 6 files changed, 51 insertions(+), 28 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 63a28823ff0..d846d7b40aa 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1498,7 +1498,7 @@ def put_cached_builtin_init(self, pos, name, cname): interned_cname = self.get_interned_identifier(name).cname self.use_utility_code( UtilityCode.load_cached("GetBuiltinName", "ObjectHandling.c")) - w.putln('%s = __Pyx_GetBuiltinName(%s); if (!%s) %s' % ( + w.putln('%s = __Pyx_GetBuiltinName(HPY_CONTEXT_FIRST_ARG_CALL %s); if (!%s) %s' % ( cname, interned_cname, cname, @@ -2595,8 +2595,8 @@ def redef_builtin_expect(self, cond): self.putln("#if %s" % cond) self.putln(" #undef likely") self.putln(" #undef unlikely") - self.putln(" #define likely(x) __builtin_expect(!!(x), 1)") - self.putln(" #define unlikely(x) __builtin_expect(!!(x), 0)") + self.putln(" #define likely(x) __builtin_expect(!!(HPY_LEGACY_OBJECT_AS(x)), 1)") + self.putln(" #define unlikely(x) __builtin_expect(!!(HPY_LEGACY_OBJECT_AS(x)), 0)") self.putln("#endif") diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 4283fe752a5..79a11ea1a92 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -859,7 +859,7 @@ def generate_post_assignment_code(self, code): self.generate_subexpr_disposal_code(code) self.free_subexpr_temps(code) elif self.type.is_pyobject: - code.putln("%s = 0;" % self.result()) + code.putln("%s = API_NULL_VALUE;" % self.result()) elif self.type.is_memoryviewslice: code.putln("%s.memview = NULL;" % self.result()) code.putln("%s.data = NULL;" % self.result()) @@ -2443,10 +2443,10 @@ def generate_result_code(self, code): code.globalstate.use_utility_code( UtilityCode.load_cached("GetBuiltinName", "ObjectHandling.c")) code.putln( - '%s = __Pyx_GetBuiltinName(%s); %s' % ( + '%s = __Pyx_GetBuiltinName(HPY_CONTEXT_FIRST_ARG_CALL %s); %s' % ( self.result(), interned_cname, - code.error_goto_if_null(self.result(), self.pos))) + code.error_goto_if_null_object(self.result(), self.pos))) self.generate_gotref(code) elif entry.is_pyglobal or (entry.is_builtin and entry.scope.is_module_scope): @@ -2460,7 +2460,7 @@ def generate_result_code(self, code): '__Pyx_GetModuleGlobalName(%s, %s); %s' % ( self.result(), interned_cname, - code.error_goto_if_null(self.result(), self.pos))) + code.error_goto_if_null_object(self.result(), self.pos))) else: # FIXME: is_pyglobal is also used for class namespace code.globalstate.use_utility_code( @@ -8191,13 +8191,18 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): if self.type is list_type: create_func, set_item_func = 'PyList_New', '__Pyx_PyList_SET_ITEM' elif self.type is tuple_type: - create_func, set_item_func = 'PyTuple_New', '__Pyx_PyTuple_SET_ITEM' + create_func, set_item_func = 'TUPLE_CREATE_START', 'TUPLE_CREATE_ASSIGN' else: raise InternalError("sequence packing for unexpected type %s" % self.type) arg_count = len(self.args) - code.putln("%s = %s(%s%s); %s" % ( - target, create_func, arg_count, size_factor, - code.error_goto_if_null(target, self.pos))) + if self.type is tuple_type: + code.putln("%s(%s,%s%s); %s" % ( + create_func, target, arg_count, size_factor, + code.error_goto_if_null_object(target, self.pos))) + else: + code.putln("%s = %s(%s%s); %s" % ( + target, create_func, arg_count, size_factor, + code.error_goto_if_null(target, self.pos))) code.put_gotref(target, py_object_type) if c_mult: @@ -8229,6 +8234,9 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): (offset and i) and ('%s + %s' % (offset, i)) or (offset or i), arg.py_result(), code.error_goto(self.pos))) + + if self.type is tuple_type: + code.putln("TUPLE_CREATE_FINALISE(%s)" % target) if c_mult: code.putln('}') diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index bb760511fef..c5f4ba11791 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -10348,6 +10348,9 @@ def generate_execution_code(self, code): && (__GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95))) #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) + + #define likely_object(x) __builtin_expect(!API_IS_NULL(x), 1) + #define unlikely_object(x) __builtin_expect(!API_IS_NULL(x), 0) #else /* !__GNUC__ or GCC < 2.95 */ #define likely(x) (x) #define unlikely(x) (x) diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index e49751e695e..63460779923 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -447,6 +447,8 @@ #define CYTHON_FAST_THREAD_STATE 0 #undef CYTHON_USE_TYPE_SLOTS #define CYTHON_USE_TYPE_SLOTS 0 + #undef CYTHON_AVOID_BORROWED_REFS + #define CYTHON_AVOID_BORROWED_REFS 1 /* We don't use refnanny in HPy since it has the debug mode */ #undef CYTHON_REFNANNY #define CYTHON_REFNANNY 0 @@ -761,6 +763,8 @@ class __Pyx_FakeReference { #define DICT_GET_ITEM_STR(o, attr_name) HPy_GetItem_s(HPY_CONTEXT_CNAME, o, attr_name) #define DICT_SET_ITEM_STR(o, attr_name, attr_val) HPy_SetItem_s(HPY_CONTEXT_CNAME, o, attr_name, attr_val) + #define PYOBJECT_GET_ITEM(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) + #define PYOBJECT_SET_ITEM(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) #define PYOBJECT_GET_ATTR(o, attr_name) HPy_GetAttr(HPY_CONTEXT_CNAME, o, attr_name) #define PYOBJECT_SET_ATTR(o, attr_name, attr_val) HPy_SetAttr(HPY_CONTEXT_CNAME, o, attr_name, attr_val) #define PYOBJECT_GET_ATTR_STR(o, attr_name) HPyObject_GetAttrString(HPY_CONTEXT_CNAME, o, attr_name) @@ -771,6 +775,9 @@ class __Pyx_FakeReference { #define BYTES_FROM_STR_AND_SIZE(str, size) HPyBytes_FromStringAndSize(HPY_CONTEXT_CNAME, str, size) #define TUPLE_CREATE_EMPTY() HPyTuple_FromArray(HPY_CONTEXT_CNAME, NULL, 0) + #define TUPLE_CREATE_START(builder, size) HPyTupleBuilder temp_builder = HPyTupleBuilder_New(HPY_CONTEXT_CNAME, size) + #define TUPLE_CREATE_ASSIGN(builder, index, item) HPyTupleBuilder_Set(HPY_CONTEXT_CNAME, temp_builder, index, item) + #define TUPLE_CREATE_FINALISE(target) target = HPyTupleBuilder_Build(HPY_CONTEXT_CNAME, temp_builder); #define TUPLE_PACK(num_args, ...) HPyTuple_Pack(HPY_CONTEXT_CNAME, num_args, __VA_ARGS__) #else #define HPY_CONTEXT_ONLY_ARG_DEF void @@ -812,6 +819,8 @@ class __Pyx_FakeReference { #define DICT_GET_ITEM_STR(o, attr_name) PyDict_GetItemString(o, attr_name) #define DICT_SET_ITEM_STR(o, attr_name, attr_val) PyDict_SetItemString(o, attr_name, attr_val) + #define PYOBJECT_GET_ITEM(o, attr_name) PyObject_GetItem(HPY_CONTEXT_CNAME, o, attr_name) + #define PYOBJECT_SET_ITEM(o, attr_name, attr_val) PyObject_SetItem(o, attr_name, attr_val) #define PYOBJECT_GET_ATTR(o, attr_name) PyObject_GetAttr(o, attr_name) #define PYOBJECT_SET_ATTR(o, attr_name, attr_val) PyObject_SetAttr(o, attr_name, attr_val) #define PYOBJECT_GET_ATTR_STR(o, attr_name) PyObject_GetAttrString(o, attr_name) @@ -822,6 +831,9 @@ class __Pyx_FakeReference { #define BYTES_FROM_STR_AND_SIZE(str, size) PyBytes_FromStringAndSize(str, size) #define TUPLE_CREATE_EMPTY() PyTuple_New(0) + #define TUPLE_CREATE_START(target, size) target=PyTuple_New(size) + #define TUPLE_CREATE_ASSIGN(tuple, index, item) __Pyx_PyTuple_SET_ITEM(tuple, index, item) + #define TUPLE_CREATE_FINALISE(target) #define TUPLE_PACK(num_args, ...) PyTuple_Pack(num_args, __VA_ARGS__) #endif diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 20f815d5770..4d8695f5b7e 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1270,15 +1270,15 @@ static CYTHON_INLINE PyObject* __Pyx_PyBoolOrNull_FromLong(long b) { /////////////// GetBuiltinName.proto /////////////// -static PyObject *__Pyx_GetBuiltinName(PyObject *name); /*proto*/ +static PYOBJECT_TYPE __Pyx_GetBuiltinName(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE name); /*proto*/ /////////////// GetBuiltinName /////////////// //@requires: PyObjectGetAttrStrNoError //@substitute: naming -static PyObject *__Pyx_GetBuiltinName(PyObject *name) { - PyObject* result = __Pyx_PyObject_GetAttrStrNoError($builtins_cname, name); - if (unlikely(!result) && !PyErr_Occurred()) { +static PYOBJECT_TYPE __Pyx_GetBuiltinName(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE name) { + PYOBJECT_TYPE result = __Pyx_PyObject_GetAttrStrNoError(HPY_CONTEXT_FIRST_ARG_CALL $builtins_cname, name); + if (unlikely(API_IS_NULL(result)) && !PyErr_Occurred()) { PyErr_Format(PyExc_NameError, "name '%U' is not defined", name); } @@ -1374,7 +1374,7 @@ static int __Pyx_SetNewInClass(PyObject *ns, PyObject *name, PyObject *value) { static PY_UINT64_T __pyx_dict_version = 0; \ static PyObject *__pyx_dict_cached_value = NULL; \ (var) = (likely(__pyx_dict_version == __PYX_GET_DICT_VERSION($moddict_cname))) ? \ - (likely(__pyx_dict_cached_value) ? __Pyx_NewRef(__pyx_dict_cached_value) : __Pyx_GetBuiltinName(name)) : \ + (likely(__pyx_dict_cached_value) ? __Pyx_NewRef(__pyx_dict_cached_value) : __Pyx_GetBuiltinName(HPY_CONTEXT_FIRST_ARG_CALL name)) : \ __Pyx__GetModuleGlobalName(name, &__pyx_dict_version, &__pyx_dict_cached_value); \ } while(0) #define __Pyx_GetModuleGlobalNameUncached(var, name) do { \ @@ -1384,9 +1384,9 @@ static int __Pyx_SetNewInClass(PyObject *ns, PyObject *name, PyObject *value) { } while(0) static PyObject *__Pyx__GetModuleGlobalName(PyObject *name, PY_UINT64_T *dict_version, PyObject **dict_cached_value); /*proto*/ #else -#define __Pyx_GetModuleGlobalName(var, name) (var) = __Pyx__GetModuleGlobalName(name) -#define __Pyx_GetModuleGlobalNameUncached(var, name) (var) = __Pyx__GetModuleGlobalName(name) -static CYTHON_INLINE PyObject *__Pyx__GetModuleGlobalName(PyObject *name); /*proto*/ +#define __Pyx_GetModuleGlobalName(var, name) (var) = __Pyx__GetModuleGlobalName(HPY_CONTEXT_FIRST_ARG_CALL name) +#define __Pyx_GetModuleGlobalNameUncached(var, name) (var) = __Pyx__GetModuleGlobalName(HPY_CONTEXT_FIRST_ARG_CALL name) +static CYTHON_INLINE PYOBJECT_TYPE __Pyx__GetModuleGlobalName(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE name); /*proto*/ #endif @@ -1395,17 +1395,17 @@ static CYTHON_INLINE PyObject *__Pyx__GetModuleGlobalName(PyObject *name); /*pro //@substitute: naming #if CYTHON_USE_DICT_VERSIONS -static PyObject *__Pyx__GetModuleGlobalName(PyObject *name, PY_UINT64_T *dict_version, PyObject **dict_cached_value) +static PYOBJECT_TYPE __Pyx__GetModuleGlobalName(PyObject *name, PY_UINT64_T *dict_version, PyObject **dict_cached_value) #else -static CYTHON_INLINE PyObject *__Pyx__GetModuleGlobalName(PyObject *name) +static CYTHON_INLINE PYOBJECT_TYPE __Pyx__GetModuleGlobalName(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE name) #endif { - PyObject *result; + PYOBJECT_TYPE result; // FIXME: clean up the macro guard order here: limited API first, then borrowed refs, then cpython #if !CYTHON_AVOID_BORROWED_REFS #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030d0000 // Identifier names are always interned and have a pre-calculated hash value. - result = _PyDict_GetItem_KnownHash($moddict_cname, name, ((PyASCIIObject *) name)->hash); + result = _PyDict_GetItem_KnownHash(HPY_LEGACY_OBJECT_AS($moddict_cname), name, ((PyASCIIObject *) name)->hash); __PYX_UPDATE_DICT_CACHE($moddict_cname, result, *dict_cached_value, *dict_version) if (likely(result)) { return __Pyx_NewRef(result); @@ -1428,14 +1428,14 @@ static CYTHON_INLINE PyObject *__Pyx__GetModuleGlobalName(PyObject *name) } #endif #else - result = PyObject_GetItem($moddict_cname, name); + result = PYOBJECT_GET_ITEM($moddict_cname, name); __PYX_UPDATE_DICT_CACHE($moddict_cname, result, *dict_cached_value, *dict_version) - if (likely(result)) { - return __Pyx_NewRef(result); + if (likely_object(result)) { + return __Pyx_hNewRef(result); } PyErr_Clear(); #endif - return __Pyx_GetBuiltinName(name); + return __Pyx_GetBuiltinName(HPY_CONTEXT_FIRST_ARG_CALL name); } //////////////////// GetAttr.proto //////////////////// diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index 1dd563d7ab3..f42b674ed25 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -108,7 +108,7 @@ static CYTHON_INLINE size_t __Pyx_Py_UNICODE_strlen(const Py_UNICODE *u) #define __Pyx_PyUnicode_FromUnicodeAndLength PyUnicode_FromUnicode #define __Pyx_PyUnicode_AsUnicode PyUnicode_AsUnicode -#define __Pyx_hNewRef(obj) HPy_Dup(HPY_CONTEXT_CNAME, obj) //This will be merged into Pyx_NewRef eventually, but #if CYTHON_USING_HPY will make all calls to this macro use HPy, even if it hasn't been ported yet +#define __Pyx_hNewRef(obj) PYOBJECT_ALLOC(obj) //This will be merged into Pyx_NewRef eventually, but #if CYTHON_USING_HPY will make all calls to this macro use HPy, even if it hasn't been ported yet #define __Pyx_NewRef(obj) (Py_INCREF(obj), obj) #define __Pyx_Owned_Py_None(b) __Pyx_NewRef(Py_None) static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b); From f1caf9b48f171e343804f89d996334af69233530 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 10 Aug 2023 15:20:55 +0200 Subject: [PATCH 139/286] Properly deal with HPyGlobals during module initialisation --- Cython/Compiler/ExprNodes.py | 2 +- Cython/Compiler/ModuleNode.py | 91 ++++++++++++++++++++++---------- Cython/Compiler/Nodes.py | 2 +- Cython/Utility/Exceptions.c | 2 +- Cython/Utility/ModuleSetupCode.c | 6 +++ 5 files changed, 71 insertions(+), 32 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 79a11ea1a92..f0f63d13e7d 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -2527,7 +2527,7 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, assert False, repr(entry) code.put_error_if_neg( self.pos, - '%s(%s, %s, %s)' % ( + '%s(PYOBJECT_GLOBAL_LOAD(%s), %s, %s)' % ( setter, namespace, interned_cname, diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 85ffa5d4c41..cbcd9702155 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -510,6 +510,7 @@ def generate_c_code(self, env, options, result): self.generate_typeobj_definitions(env, code) self.generate_method_table(env, code) self.generate_hpy_define_array(env, code) + self.define_globals_array(env, code) if env.has_import_star: self.generate_import_star(env, code) @@ -881,7 +882,7 @@ def generate_module_preamble(self, env, options, cimported_modules, metadata, co code.putln('static CYTHON_INLINE void __Pyx_pretend_to_initialize(void* ptr) { (void)ptr; }') code.putln('') code.putln('#if !CYTHON_USE_MODULE_STATE') - code.putln('static PYOBJECT_TYPE %s = API_NULL_VALUE;' % env.module_cname) + code.putln('static PYOBJECT_GLOBAL_TYPE %s;' % env.module_cname) if Options.pre_import is not None: code.putln('static PyObject *%s;' % Naming.preimport_cname) code.putln('#endif') @@ -2742,6 +2743,25 @@ def generate_hpy_define_array(self, env, code): if wrapper_code_writer.getvalue(): wrapper_code_writer.putln("") + + + def define_globals_array(self, env, code): + code.putln("#if CYTHON_USING_HPY") + code.putln("static HPyGlobal *globals_array[10];") + code.putln("#endif") + + def generate_globals_array(self, env, code): + code.putln("#if CYTHON_USING_HPY") + code.putln("globals_array[0] = &%s;" % env.module_cname) + code.putln("globals_array[1] = &%s;" % env.module_dict_cname) + code.putln("globals_array[2] = &%s;" % Naming.cython_runtime_cname) + code.putln("globals_array[3] = &%s;" % Naming.empty_tuple) + code.putln("globals_array[4] = &%s;" % Naming.empty_bytes) + code.putln("globals_array[5] = &%s;" % Naming.empty_unicode) + if Options.pre_import is not None: + code.putln("globals_array[6] = &%s;" % Naming.preimport_cname) + code.putln("#endif") + def generate_dict_getter_function(self, scope, code): dict_attr = scope.lookup_here("__dict__") if not dict_attr or not dict_attr.is_variable: @@ -2862,12 +2882,12 @@ def generate_import_star(self, env, code): def generate_module_state_start(self, env, code): # TODO: Refactor to move module state struct decl closer to the static decl code.putln('typedef struct {') - code.putln('PYOBJECT_TYPE %s;' % env.module_dict_cname) + code.putln('PYOBJECT_GLOBAL_TYPE %s;' % env.module_dict_cname) code.putln('PYOBJECT_TYPE %s;' % Naming.builtins_cname) - code.putln('PYOBJECT_TYPE %s;' % Naming.cython_runtime_cname) - code.putln('PYOBJECT_TYPE %s;' % Naming.empty_tuple) - code.putln('PYOBJECT_TYPE %s;' % Naming.empty_bytes) - code.putln('PYOBJECT_TYPE %s;' % Naming.empty_unicode) + code.putln('PYOBJECT_GLOBAL_TYPE %s;' % Naming.cython_runtime_cname) + code.putln('PYOBJECT_GLOBAL_TYPE %s;' % Naming.empty_tuple) + code.putln('PYOBJECT_GLOBAL_TYPE %s;' % Naming.empty_bytes) + code.putln('PYOBJECT_GLOBAL_TYPE %s;' % Naming.empty_unicode) if Options.pre_import is not None: code.putln('PyObject *%s;' % Naming.preimport_cname) for type_cname, used_name in Naming.used_types_and_macros: @@ -2926,7 +2946,7 @@ def generate_module_state_end(self, env, modules, globalstate): module_state_clear.putln("#endif") module_state_traverse.putln("return 0;") module_state_traverse.putln("}") - module_state_traverse.putln("#endif") + module_state_traverse.putln("#endif") def generate_module_state_defines(self, env, code): code.putln('#define %s %s->%s' % ( @@ -3084,6 +3104,8 @@ def generate_module_init_func(self, imported_modules, env, code): code.putln('int pystate_addmodule_run = 0;') code.putln("#endif") + self.generate_globals_array(env, code) + tempdecl_code = code.insertion_point() profile = code.globalstate.directives['profile'] @@ -3099,7 +3121,7 @@ def generate_module_init_func(self, imported_modules, env, code): # See issues listed here: https://docs.python.org/3/c-api/init.html#sub-interpreter-support code.putln("if (API_IS_NOT_NULL(%s)) {" % Naming.module_cname) # Hack: enforce single initialisation. - code.putln("if (API_IS_EQUAL(%s, %s)) return 0;" % ( + code.putln("if (API_IS_EQUAL(PYOBJECT_GLOBAL_LOAD(%s), %s)) return 0;" % ( Naming.module_cname, Naming.pymodinit_module_arg, )) @@ -3144,12 +3166,12 @@ def generate_module_init_func(self, imported_modules, env, code): code.putln("#ifdef __Pxy_PyFrame_Initialize_Offsets") code.putln("__Pxy_PyFrame_Initialize_Offsets();") code.putln("#endif") - code.putln("%s = TUPLE_CREATE_EMPTY(); %s" % ( - Naming.empty_tuple, code.error_goto_if_null_object(Naming.empty_tuple, self.pos))) - code.putln("%s = BYTES_FROM_STR_AND_SIZE(\"\", 0); %s" % ( - Naming.empty_bytes, code.error_goto_if_null_object(Naming.empty_bytes, self.pos))) - code.putln("%s = HPY_LEGACY_OBJECT_FROM(PyUnicode_FromStringAndSize(\"\", 0)); %s" % ( - Naming.empty_unicode, code.error_goto_if_null_object(Naming.empty_unicode, self.pos))) + code.putln("PYOBJECT_GLOBAL_STORE(%s, TUPLE_CREATE_EMPTY()); %s" % ( + Naming.empty_tuple, code.error_goto_if_null_object("TUPLE_CREATE_EMPTY()", self.pos))) + code.putln("PYOBJECT_GLOBAL_STORE(%s, BYTES_FROM_STR_AND_SIZE(\"\", 0)); %s" % ( + Naming.empty_bytes, code.error_goto_if_null_object("BYTES_FROM_STR_AND_SIZE(\"\", 0)", self.pos))) + code.putln("PYOBJECT_GLOBAL_STORE(%s, HPY_LEGACY_OBJECT_FROM(PyUnicode_FromStringAndSize(\"\", 0))); %s" % ( + Naming.empty_unicode, code.error_goto_if_null_object("HPY_LEGACY_OBJECT_FROM(PyUnicode_FromStringAndSize(\"\", 0))", self.pos))) for ext_type in ('CyFunction', 'FusedFunction', 'Coroutine', 'Generator', 'AsyncGen', 'StopAsyncIteration'): code.putln("#ifdef __Pyx_%s_USED" % ext_type) @@ -3173,7 +3195,7 @@ def generate_module_init_func(self, imported_modules, env, code): code.putln("if (%s) {" % self.is_main_module_flag_cname()) code.put_error_if_neg(self.pos, 'PYOBJECT_SET_ATTR(%s, %s, %s)' % ( - env.module_cname, + Naming.pymodinit_module_arg, code.intern_identifier(EncodedString("__name__")), code.intern_identifier(EncodedString("__main__")))) code.putln("}") @@ -3244,7 +3266,7 @@ def generate_module_init_func(self, imported_modules, env, code): code.put_label(code.error_label) for cname, type in code.funcstate.all_managed_temps(): code.put_xdecref(cname, type) - code.putln('if (API_IS_NOT_NULL(%s)) {' % env.module_cname) + code.putln('if (API_IS_NOT_NULL(%s)) {' % Naming.pymodinit_module_arg) code.putln('if (API_IS_NOT_NULL(%s) && stringtab_initialized) {' % env.module_dict_cname) # We can run into errors before the module or stringtab are initialized. # In this case it is not safe to add a traceback (because it uses the stringtab) @@ -3257,13 +3279,13 @@ def generate_module_init_func(self, imported_modules, env, code): ##code.put_decref_clear(env.module_dict_cname, py_object_type, nanny=False) code.putln('}') code.putln("#if CYTHON_USING_HPY") - code.putln("PYOBJECT_DEALLOC(%s);" % env.module_cname) + code.putln("PYOBJECT_DEALLOC(%s);" % Naming.pymodinit_module_arg) code.putln("#elif !CYTHON_USE_MODULE_STATE") - code.put_decref_clear(env.module_cname, py_object_type, nanny=False, clear_before_decref=True) + code.put_decref_clear(Naming.pymodinit_module_arg, py_object_type, nanny=False, clear_before_decref=True) code.putln("#else") # This section is mainly for the limited API. env.module_cname still owns a reference so # decrement that - code.put_decref(env.module_cname, py_object_type, nanny=False) + code.put_decref(Naming.pymodinit_module_arg, py_object_type, nanny=False) # Also remove the failed module from the module state lookup # fetch/restore the error indicator because PyState_RemvoeModule might fail itself code.putln("if (pystate_addmodule_run) {") @@ -3282,9 +3304,9 @@ def generate_module_init_func(self, imported_modules, env, code): code.put_finish_refcount_context() code.putln("#if CYTHON_PEP489_MULTI_PHASE_INIT") - code.putln("return (API_IS_NOT_NULL(%s)) ? 0 : -1;" % env.module_cname) + code.putln("return (API_IS_NOT_NULL(%s)) ? 0 : -1;" % Naming.pymodinit_module_arg) code.putln("#elif PY_MAJOR_VERSION >= 3") - code.putln("return %s;" % env.module_cname) + code.putln("return %s;" % Naming.pymodinit_module_arg) code.putln("#else") code.putln("return;") code.putln("#endif") @@ -3418,7 +3440,7 @@ def generate_module_import_setup(self, env, code): code.error_goto_if_null_object("modules", self.pos)) code.putln('if (API_IS_NULL(DICT_GET_ITEM_STR(modules, %s))) {' % fq_module_name_cstring) code.putln(code.error_goto_if_neg('DICT_SET_ITEM_STR(modules, %s, %s)' % ( - fq_module_name_cstring, env.module_cname), self.pos)) + fq_module_name_cstring, Naming.pymodinit_module_arg), self.pos)) code.putln("}") code.putln("}") code.putln("#endif") @@ -3620,11 +3642,12 @@ def generate_pymoduledef_struct(self, env, code): code.putln("#else") code.putln(" .doc = %s," % doc) code.putln(" .size = 0,") - code.putln(" .legacy_methods = 0,") + code.putln(" .legacy_methods = %s," % env.method_table_cname) if env.is_c_class_scope and not env.hpyfunc_entries: code.putln(" .defines = methods,") else: code.putln(" .defines = %s," % env.hpy_defines_cname) + code.putln(" .globals = globals_array,") code.putln("#endif") code.putln("};") code.putln('#ifdef __cplusplus') @@ -3641,10 +3664,12 @@ def generate_module_creation_code(self, env, code): doc = "0" code.putln("#if CYTHON_PEP489_MULTI_PHASE_INIT") - code.putln("%s = %s;" % ( + code.putln("PYOBJECT_GLOBAL_STORE(%s, %s);" % ( env.module_cname, Naming.pymodinit_module_arg)) + code.putln("#if !CYTHON_USING_HPY") code.put_incref(env.module_cname, py_object_type, nanny=False) + code.putln("#endif") code.putln("#else") code.putln("#if PY_MAJOR_VERSION < 3") code.putln( @@ -3688,22 +3713,30 @@ def generate_module_creation_code(self, env, code): code.putln("CYTHON_UNUSED_VAR(%s);" % module_temp) # only used in limited API code.putln( - "%s = PYMODULE_GETDICT_ATTR(%s); %s" % ( - env.module_dict_cname, env.module_cname, - code.error_goto_if_null_object(env.module_dict_cname, self.pos))) + "PYOBJECT_GLOBAL_STORE(%s, PYMODULE_GETDICT_ATTR(%s)); %s" % ( + env.module_dict_cname, Naming.pymodinit_module_arg, + code.error_goto_if_null_object("PYMODULE_GETDICT_ATTR(%s)" % Naming.pymodinit_module_arg, self.pos))) + code.putln("#if !CYTHON_USING_HPY") code.put_incref(env.module_dict_cname, py_object_type, nanny=False) code.putln( '%s = HPY_LEGACY_OBJECT_FROM(__Pyx_PyImport_AddModuleRef(__Pyx_BUILTIN_MODULE_NAME)); %s' % ( Naming.builtins_cname, code.error_goto_if_null_object(Naming.builtins_cname, self.pos))) + code.put_incref(Naming.builtins_cname, py_object_type, nanny=False) + code.putln('#else') + code.putln('%s = %s->h_Builtins;' % ( + Naming.builtins_cname, + Naming.hpy_context_cname + )) + code.putln('#endif') code.putln( - '%s = HPY_LEGACY_OBJECT_FROM(__Pyx_PyImport_AddModuleRef("cython_runtime")); %s' % ( + 'PYOBJECT_GLOBAL_STORE(%s, HPY_LEGACY_OBJECT_FROM(__Pyx_PyImport_AddModuleRef("cython_runtime"))); %s' % ( Naming.cython_runtime_cname, code.error_goto_if_null_object(Naming.cython_runtime_cname, self.pos))) code.putln( 'if (PYOBJECT_SET_ATTR_STR(%s, "__builtins__", %s) < 0) %s' % ( - env.module_cname, + Naming.pymodinit_module_arg, Naming.builtins_cname, code.error_goto(self.pos))) if Options.pre_import is not None: diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index c5f4ba11791..b2f8078ce24 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -1735,7 +1735,7 @@ def generate_execution_code(self, code): item.cname, code.error_goto_if_null(temp, item.pos))) code.put_gotref(temp, PyrexTypes.py_object_type) - code.putln('if (DICT_SET_ITEM_STR(%s, "%s", %s) < 0) %s' % ( + code.putln('if (DICT_SET_ITEM_STR(PYOBJECT_GLOBAL_LOAD(%s), "%s", %s) < 0) %s' % ( Naming.moddict_cname, item.name, temp, diff --git a/Cython/Utility/Exceptions.c b/Cython/Utility/Exceptions.c index 4354dc11098..e2aa3853706 100644 --- a/Cython/Utility/Exceptions.c +++ b/Cython/Utility/Exceptions.c @@ -1002,7 +1002,7 @@ static void __Pyx_AddTraceback(HPY_CONTEXT_FIRST_ARG_DEF const char *funcname, i py_frame = PyFrame_New( tstate, /*PyThreadState *tstate,*/ py_code, /*PyCodeObject *code,*/ - HPY_LEGACY_OBJECT_AS($moddict_cname), /*PyObject *globals,*/ + HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD($moddict_cname)), /*PyObject *globals,*/ 0 /*PyObject *locals*/ ); if (!py_frame) goto bad; diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 63460779923..6c5949ac6a2 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -731,6 +731,8 @@ class __Pyx_FakeReference { #define PYOBJECT_TYPE HPy #define PYOBJECT_FIELD_TYPE HPyField #define PYOBJECT_GLOBAL_TYPE HPyGlobal + #define PYOBJECT_GLOBAL_STORE(global, h) HPyGlobal_Store(HPY_CONTEXT_CNAME, &global, h) + #define PYOBJECT_GLOBAL_LOAD(global) HPyGlobal_Load(HPY_CONTEXT_CNAME, global) #define CAPI_IS_POINTER #define PYOBJECT_ALLOC(h) HPy_Dup(HPY_CONTEXT_CNAME, h) @@ -749,6 +751,7 @@ class __Pyx_FakeReference { #define PYMODULEDEF_TYPE HPyModuleDef #define API_NULL_VALUE HPy_NULL + #define API_DEFAULT_VALUE HPy_NULL #define API_IS_NULL(h) HPy_IsNull(h) #define API_IS_NOT_NULL(h) !HPy_IsNull(h) #define API_IS_EQUAL(a, b) HPy_Is(HPY_CONTEXT_CNAME, a, b) @@ -788,6 +791,8 @@ class __Pyx_FakeReference { #define PYOBJECT_TYPE PyObject * #define PYOBJECT_FIELD_TYPE PyObject * #define PYOBJECT_GLOBAL_TYPE PyObject * + #define PYOBJECT_GLOBAL_STORE(global, h) global = h + #define PYOBJECT_GLOBAL_LOAD(global) global #define CAPI_IS_POINTER * //Some types are sometimes pointers and sometimes not (i.e. PyModuleDef) where the type is always the same in HPy #define PYOBJECT_ALLOC(h) Py_INCREF(h) @@ -803,6 +808,7 @@ class __Pyx_FakeReference { #define HPY_LEGACY_OBJECT_AS(o) o #define API_NULL_VALUE NULL + #define API_DEFAULT_VALUE 0 #define API_IS_NULL(h) !h //Both are here as otherwise we would get !!h for API_IS_NOT_NULL, which is hard to read - but it can be made so if necessary #define API_IS_NOT_NULL(h) h #define API_IS_EQUAL(a, b) a==b From e363da105ac53273c0998acca883079f5415b61a Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Fri, 11 Aug 2023 14:39:40 +0200 Subject: [PATCH 140/286] Started work towards migrating function headers --- Cython/Compiler/Code.py | 12 +++++++++-- Cython/Compiler/ExprNodes.py | 19 +++++++++------- Cython/Compiler/Nodes.py | 36 +++++++++++++++++-------------- Cython/Compiler/PyrexTypes.py | 4 ++-- Cython/Compiler/TypeSlots.py | 24 +++++++++++++++++++++ Cython/Utility/CommonStructures.c | 33 ++++++++++++++-------------- Cython/Utility/CythonFunction.c | 28 ++++++++++++++---------- Cython/Utility/ModuleSetupCode.c | 24 +++++++++++++++------ 8 files changed, 119 insertions(+), 61 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index d846d7b40aa..2a8f88c4ffa 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -2122,7 +2122,7 @@ def put_var_declaration(self, entry, storage_class="", if entry.init is not None: self.put_safe(" = %s" % entry.type.literal_code(entry.init)) elif entry.type.is_pyobject: - self.put(" = NULL") + self.put(" = API_NULL_VALUE") self.putln(";") self.funcstate.scope.use_entry_utility_code(entry) @@ -2349,7 +2349,7 @@ def put_pymethoddef_wrapper(self, entry): if method_noargs in method_flags: # Special NOARGS methods really take no arguments besides 'self', but PyCFunction expects one. func_cname = Naming.method_wrapper_prefix + func_cname - self.putln("static PyObject *%s(PyObject *self, CYTHON_UNUSED PyObject *arg) {" % func_cname) + self.putln("static PYOBJECT_TYPE %s(PYOBJECT_TYPE self, CYTHON_UNUSED PYOBJECT_TYPE arg) {" % func_cname) func_call = "%s(self)" % entry.func_cname if entry.name == "__next__": self.putln("PyObject *res = %s;" % func_call) @@ -2360,6 +2360,14 @@ def put_pymethoddef_wrapper(self, entry): self.putln("return %s;" % func_call) self.putln("}") return func_cname + + def put_hpymethoddef(self, entry, term, allow_skip=True, wrapper_code_writer=None): + method_flags = entry.signature.hpy_method_flags() + if not method_flags: + return None + entry_name = entry.name.as_c_string_literal() + self.putln("HPyDef_METH(%s, %s, %s, .doc=%s);" % ( + entry.pymethdef_cname, entry_name, method_flags, entry.doc_cname if entry.doc else '0')) # GIL methods diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index f0f63d13e7d..9c7fb019470 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -1330,7 +1330,7 @@ def coerce_to(self, dst_type, env): class NullNode(ConstNode): type = PyrexTypes.c_null_ptr_type - value = "NULL" + value = "API_NULL_VALUE" constant_result = 0 def get_constant_c_result_code(self): @@ -10007,7 +10007,7 @@ def may_be_none(self): gil_message = "Constructing Python function" def closure_result_code(self): - return "NULL" + return "API_NULL_VALUE" def generate_result_code(self, code): if self.binding: @@ -10045,7 +10045,7 @@ def generate_cyfunction_code(self, code): if self.code_object: code_object_result = self.code_object.py_result() else: - code_object_result = 'NULL' + code_object_result = 'API_NULL_VALUE' flags = [] if def_node.is_staticmethod: @@ -10065,7 +10065,7 @@ def generate_cyfunction_code(self, code): flags = '0' code.putln( - '%s = %s(&%s, %s, %s, %s, %s, %s, %s); %s' % ( + '%s = %s(HPY_CONTEXT_FIRST_ARG_CALL &%s, %s, %s, %s, %s, PYOBJECT_GLOBAL_LOAD(%s), %s); %s' % ( self.result(), constructor, self.pymethdef_cname, @@ -10075,7 +10075,7 @@ def generate_cyfunction_code(self, code): self.get_py_mod_name(code), Naming.moddict_cname, code_object_result, - code.error_goto_if_null(self.result(), self.pos))) + code.error_goto_if_null_object(self.result(), self.pos))) self.generate_gotref(code) @@ -10189,8 +10189,11 @@ def generate_result_code(self, code): elif self.def_node.is_generator: flags.append('CO_GENERATOR') - code.putln("%s = (PyObject*)__Pyx_PyCode_New(%d, %d, %d, %d, 0, %s, HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), \ - HPY_LEGACY_OBJECT_AS(%s), %s, HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), %s, %s, %d, HPY_LEGACY_OBJECT_AS(%s)); %s" % ( + code.putln("%s = HPY_LEGACY_OBJECT_FROM((PyObject*)__Pyx_PyCode_New(%d, %d, %d, %d, 0, %s, HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), \ + HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), \ + HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), \ + HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), %d, \ + HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)))); %s" % ( self.result_code, len(func.args) - func.num_kwonly_args, # argcount func.num_posonly_args, # posonlyargcount (Py3.8+ only) @@ -10207,7 +10210,7 @@ def generate_result_code(self, code): func_name, # name self.pos[1], # firstlineno Naming.empty_bytes, # lnotab - code.error_goto_if_null(self.result_code, self.pos), + code.error_goto_if_null_object(self.result_code, self.pos), )) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index b2f8078ce24..5b6a21540e4 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -2026,7 +2026,7 @@ def generate_function_definitions(self, env, code): return_type = return_type.cv_base_type if not return_type.is_void: if return_type.is_pyobject: - init = " = NULL" + init = " = API_NULL_VALUE" elif return_type.is_memoryviewslice: init = ' = ' + return_type.literal_code(return_type.default_value) @@ -2945,7 +2945,7 @@ def generate_execution_code(self, code): def error_value(self): if self.return_type.is_pyobject: - return "0" + return "API_DEFAULT_VALUE" else: return self.entry.type.exception_value @@ -3427,7 +3427,7 @@ def declare_arguments(self, env): if arg.needs_conversion: arg.entry = env.declare_var(arg.name, arg.type, arg.pos) if arg.type.is_pyobject: - arg.entry.init = "0" + arg.entry.init = "API_DEFAULT_VALUE" else: arg.entry = self.declare_argument(env, arg) arg.entry.is_arg = 1 @@ -3445,7 +3445,7 @@ def declare_python_arg(self, env, arg): entry = env.declare_var(arg.name, type, arg.pos) entry.is_arg = 1 entry.used = 1 - entry.init = "0" + entry.init = "API_DEFAULT_VALUE" entry.xdecref_cleanup = 1 arg.entry = entry @@ -3505,7 +3505,7 @@ def generate_function_header(self, code, with_pymethdef, proto_only=0): return arg_code_list = [] if self.entry.signature.has_dummy_arg: - self_arg = 'PyObject *%s' % Naming.self_cname + self_arg = 'PYOBJECT_TYPE %s' % Naming.self_cname if not self.needs_outer_scope: self_arg = 'CYTHON_UNUSED ' + self_arg arg_code_list.append(self_arg) @@ -3538,10 +3538,10 @@ def arg_decl_code(arg): if preprocessor_guard: decls_code.putln(preprocessor_guard) decls_code.putln( - "static %s(%s); /* proto */" % (dc, arg_code)) + "static %s(HPY_CONTEXT_FIRST_ARG_DEF %s); /* proto */" % (dc, arg_code)) if preprocessor_guard: decls_code.putln("#endif") - code.putln("static %s(%s) {" % (dc, arg_code)) + code.putln("static %s(HPY_CONTEXT_FIRST_ARG_DEF %s) {" % (dc, arg_code)) def generate_argument_declarations(self, env, code): pass @@ -3668,7 +3668,7 @@ def generate_function_body(self, code): args = ', '.join(args) if not self.return_type.is_void: code.put('%s = ' % Naming.retval_cname) - code.putln('%s(%s);' % ( + code.putln('%s(HPY_CONTEXT_FIRST_ARG_CALL %s);' % ( self.target.entry.pyfunc_cname, args)) def generate_function_definitions(self, env, code): @@ -3691,7 +3691,7 @@ def generate_function_definitions(self, env, code): tempvardecl_code = code.insertion_point() if self.return_type.is_pyobject: - retval_init = ' = 0' + retval_init = ' = API_DEFAULT_VALUE' else: retval_init = '' if not self.return_type.is_void: @@ -3754,7 +3754,7 @@ def generate_function_header(self, code, with_pymethdef, proto_only=0): sig = self.signature if sig.has_dummy_arg or self.self_in_stararg: - arg_code = "PyObject *%s" % Naming.self_cname + arg_code = "PYOBJECT_TYPE %s" % Naming.self_cname if not sig.has_dummy_arg: arg_code = 'CYTHON_UNUSED ' + arg_code arg_code_list.append(arg_code) @@ -3762,18 +3762,18 @@ def generate_function_header(self, code, with_pymethdef, proto_only=0): for arg in self.args: if not arg.is_generic: if arg.is_self_arg or arg.is_type_arg: - arg_code_list.append("PyObject *%s" % arg.hdr_cname) + arg_code_list.append("PYOBJECT_TYPE %s" % arg.hdr_cname) else: arg_code_list.append( arg.hdr_type.declaration_code(arg.hdr_cname)) entry = self.target.entry if not entry.is_special and sig.method_flags() == [TypeSlots.method_noargs]: - arg_code_list.append("CYTHON_UNUSED PyObject *unused") + arg_code_list.append("CYTHON_UNUSED PYOBJECT_TYPE unused") if sig.has_generic_args: - varargs_args = "PyObject *%s, PyObject *%s" % ( + varargs_args = "PYOBJECT_TYPE %s, PYOBJECT_TYPE %s" % ( Naming.args_cname, Naming.kwds_cname) if sig.use_fastcall: - fastcall_args = "PyObject *const *%s, Py_ssize_t %s, PyObject *%s" % ( + fastcall_args = "PYOBJECT_TYPE const *%s, API_SSIZE_T %s, PYOBJECT_TYPE %s" % ( Naming.args_cname, Naming.nargs_cname, Naming.kwds_cname) arg_code_list.append( "\n#if CYTHON_METH_FASTCALL\n%s\n#else\n%s\n#endif\n" % ( @@ -3782,7 +3782,7 @@ def generate_function_header(self, code, with_pymethdef, proto_only=0): arg_code_list.append(varargs_args) if entry.is_special: for n in range(len(self.args), sig.max_num_fixed_args()): - arg_code_list.append("CYTHON_UNUSED PyObject *unused_arg_%s" % n) + arg_code_list.append("CYTHON_UNUSED PYOBJECT_TYPE unused_arg_%s" % n) arg_code = ", ".join(arg_code_list) # Prevent warning: unused function '__pyx_pw_5numpy_7ndarray_1__getbuffer__' @@ -3793,7 +3793,7 @@ def generate_function_header(self, code, with_pymethdef, proto_only=0): with_pymethdef = False dc = self.return_type.declaration_code(entry.func_cname) - header = "%sstatic %s(%s)" % (mf, dc, arg_code) + header = "%sstatic %s(HPY_CONTEXT_FIRST_ARG_DEF %s)" % (mf, dc, arg_code) code.putln("%s; /*proto*/" % header) if proto_only: @@ -3827,9 +3827,13 @@ def generate_function_header(self, code, with_pymethdef, proto_only=0): code.putln('#endif') if with_pymethdef or self.target.fused_py_func: + code.putln("#if !CYTHON_USING_HPY") code.put( "static PyMethodDef %s = " % entry.pymethdef_cname) code.put_pymethoddef(self.target.entry, ";", allow_skip=False) + code.putln("#else") + code.put_hpymethoddef(self.target.entry, ";", allow_skip=False) + code.putln("#endif") code.putln("%s {" % header) def generate_argument_declarations(self, env, code): diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 25bfdcce2cc..d951ff36aa3 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -1239,8 +1239,8 @@ class PyObjectType(PyrexType): name = "object" is_pyobject = 1 - default_value = "0" - declaration_value = "0" + default_value = "API_DEFAULT_VALUE" + declaration_value = "API_DEFAULT_VALUE" buffer_defaults = None is_extern = False is_subclassed = False diff --git a/Cython/Compiler/TypeSlots.py b/Cython/Compiler/TypeSlots.py index 54f70bc9c18..bd38ef7dcb2 100644 --- a/Cython/Compiler/TypeSlots.py +++ b/Cython/Compiler/TypeSlots.py @@ -186,6 +186,23 @@ def method_flags(self): else: return [method_varargs, method_keywords] return None + + def hpy_method_flags(self): + if self.ret_format == "O": + full_args = self.fixed_arg_format + if self.has_dummy_arg: + full_args = "O" + full_args + if full_args in ["O", "T"]: + if not self.has_generic_args: + return hpy_method_noargs + else: + return hpy_method_keywords + elif full_args in ["OO", "TO"] and not self.has_generic_args: + return hpy_method_onearg + + if self.is_staticmethod: + return hpy_method_keywords + return None def method_function_type(self): # Return the C function type @@ -1158,3 +1175,10 @@ def get_slot_table(compiler_directives): method_fastcall = "__Pyx_METH_FASTCALL" # Actually VARARGS on versions < 3.7 method_keywords = "METH_KEYWORDS" method_coexist = "METH_COEXIST" + +# HPy Method flags for python-exposed methods. + +hpy_method_noargs = "HPyFunc_NOARGS" +hpy_method_onearg = "HPyFunc_O" +hpy_method_varargs = "HPyFunc_VARARGS" +hpy_method_keywords = "HPyFunc_KEYWORDS" \ No newline at end of file diff --git a/Cython/Utility/CommonStructures.c b/Cython/Utility/CommonStructures.c index 8287b041b83..8dd8d6eb570 100644 --- a/Cython/Utility/CommonStructures.c +++ b/Cython/Utility/CommonStructures.c @@ -1,11 +1,11 @@ /////////////// FetchSharedCythonModule.proto /////// -static PyObject *__Pyx_FetchSharedCythonABIModule(void); +static PYOBJECT_TYPE __Pyx_FetchSharedCythonABIModule(void); /////////////// FetchSharedCythonModule //////////// -static PyObject *__Pyx_FetchSharedCythonABIModule(void) { - return __Pyx_PyImport_AddModuleRef(__PYX_ABI_MODULE_NAME); +static PYOBJECT_TYPE __Pyx_FetchSharedCythonABIModule(void) { + return HPY_LEGACY_OBJECT_FROM(__Pyx_PyImport_AddModuleRef(__PYX_ABI_MODULE_NAME)); } /////////////// FetchCommonType.proto /////////////// @@ -13,7 +13,7 @@ static PyObject *__Pyx_FetchSharedCythonABIModule(void) { #if !CYTHON_USE_TYPE_SPECS static PyTypeObject* __Pyx_FetchCommonType(PyTypeObject* type); #else -static PyTypeObject* __Pyx_FetchCommonTypeFromSpec(PyObject *module, PyType_Spec *spec, PyObject *bases); +static PyTypeObject* __Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_GLOBAL_TYPE module, TYPESPEC_TYPE spec, PYOBJECT_TYPE bases); #endif /////////////// FetchCommonType /////////////// @@ -82,24 +82,25 @@ static PyTypeObject* __Pyx_FetchCommonType(PyTypeObject* type) { } #else -static PyTypeObject *__Pyx_FetchCommonTypeFromSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) { - PyObject *abi_module, *cached_type = NULL; +static PyTypeObject *__Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_GLOBAL_TYPE module, TYPESPEC_TYPE spec, PYOBJECT_TYPE bases) { + PYOBJECT_TYPE abi_module; + PYOBJECT_TYPE cached_type = API_NULL_VALUE; // get the final part of the object name (after the last dot) const char* object_name = strrchr(spec->name, '.'); object_name = object_name ? object_name+1 : spec->name; abi_module = __Pyx_FetchSharedCythonABIModule(); - if (!abi_module) return NULL; + if (API_IS_NULL(abi_module)) return NULL; - cached_type = PyObject_GetAttrString(abi_module, object_name); + cached_type = PYOBJECT_GET_ATTR_S(abi_module, object_name); if (cached_type) { - Py_ssize_t basicsize; + API_SSIZE_T basicsize; #if CYTHON_COMPILING_IN_LIMITED_API - PyObject *py_basicsize; - py_basicsize = PyObject_GetAttrString(cached_type, "__basicsize__"); - if (unlikely(!py_basicsize)) goto bad; - basicsize = PyLong_AsSsize_t(py_basicsize); - Py_DECREF(py_basicsize); + PYOBJECT_TYPE py_basicsize; + py_basicsize = PYOBJECT_GET_ATTR_STR(cached_type, "__basicsize__"); + if (unlikely(API_IS_NULL(py_basicsize))) goto bad; + basicsize = PYOBJECT_LONG_AS_SSIZE(py_basicsize); + PYOBJECT_DEALLOC(py_basicsize); py_basicsize = 0; if (unlikely(basicsize == (Py_ssize_t)-1) && PyErr_Occurred()) goto bad; #else @@ -125,13 +126,13 @@ static PyTypeObject *__Pyx_FetchCommonTypeFromSpec(PyObject *module, PyType_Spec if (PyObject_SetAttrString(abi_module, object_name, cached_type) < 0) goto bad; done: - Py_DECREF(abi_module); + PYOBJECT_DEALLOC(abi_module); // NOTE: always returns owned reference, or NULL on error assert(cached_type == NULL || PyType_Check(cached_type)); return (PyTypeObject *) cached_type; bad: - Py_XDECREF(cached_type); + PYOBJECT_DEALLOC(cached_type); cached_type = NULL; goto done; } diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 2a3b67335d2..74c4e78be70 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -97,7 +97,7 @@ static CYTHON_INLINE void __Pyx_CyFunction_SetAnnotationsDict(PyObject *m, PyObject *dict); -static int __pyx_CyFunction_init(PyObject *module); +static int __pyx_CyFunction_init(PYOBJECT_GLOBAL_TYPE module); #if CYTHON_METH_FASTCALL static PyObject * __Pyx_CyFunction_Vectorcall_NOARGS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); @@ -1156,14 +1156,14 @@ static PyTypeObject __pyx_CyFunctionType_type = { #endif /* CYTHON_USE_TYPE_SPECS */ -static int __pyx_CyFunction_init(PyObject *module) { +static int __pyx_CyFunction_init(PYOBJECT_GLOBAL_TYPE module) { #if CYTHON_USE_TYPE_SPECS __pyx_CyFunctionType = __Pyx_FetchCommonTypeFromSpec(module, &__pyx_CyFunctionType_spec, NULL); #else CYTHON_UNUSED_VAR(module); __pyx_CyFunctionType = __Pyx_FetchCommonType(&__pyx_CyFunctionType_type); #endif - if (unlikely(__pyx_CyFunctionType == NULL)) { + if (unlikely(API_IS_NULL(__pyx_CyFunctionType))) { return -1; } return 0; @@ -1202,18 +1202,20 @@ static CYTHON_INLINE void __Pyx_CyFunction_SetAnnotationsDict(PyObject *func, Py //////////////////// CythonFunction.proto //////////////////// -static PyObject *__Pyx_CyFunction_New(PyMethodDef *ml, - int flags, PyObject* qualname, - PyObject *closure, - PyObject *module, PyObject *globals, - PyObject* code); +static PYOBJECT_TYPE __Pyx_CyFunction_New(HPY_CONTEXT_FIRST_ARG_DEF + PYMETHODDEF_TYPE *ml, + int flags, PYOBJECT_TYPE qualname, + PYOBJECT_TYPE closure, + PYOBJECT_TYPE module, PYOBJECT_TYPE globals, + PYOBJECT_TYPE code); //////////////////// CythonFunction //////////////////// //@requires: CythonFunctionShared -static PyObject *__Pyx_CyFunction_New(PyMethodDef *ml, int flags, PyObject* qualname, - PyObject *closure, PyObject *module, PyObject* globals, PyObject* code) { - PyObject *op = __Pyx_CyFunction_Init( +static PYOBJECT_TYPE __Pyx_CyFunction_New(HPY_CONTEXT_FIRST_ARG_DEF PYMETHODDEF_TYPE *ml, int flags, PYOBJECT_TYPE qualname, + PYOBJECT_TYPE closure, PYOBJECT_TYPE module, PYOBJECT_TYPE globals, PYOBJECT_TYPE code) { +#if !CYTHON_USING_HPY + PYOBJECT_TYPE op = __Pyx_CyFunction_Init( PyObject_GC_New(__pyx_CyFunctionObject, __pyx_CyFunctionType), ml, flags, qualname, closure, module, globals, code ); @@ -1221,6 +1223,10 @@ static PyObject *__Pyx_CyFunction_New(PyMethodDef *ml, int flags, PyObject* qual PyObject_GC_Track(op); } return op; +#else + PYOBJECT_TYPE op = HPy_NULL; + return op; +#endif } //////////////////// CyFunctionClassCell.proto //////////////////// diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 6c5949ac6a2..52a591f76b4 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -728,6 +728,8 @@ class __Pyx_FakeReference { #define HPY_CONTEXT_FIRST_ARG_DEF HPY_CONTEXT_TYPE HPY_CONTEXT_CNAME, #define HPY_CONTEXT_FIRST_ARG_CALL HPY_CONTEXT_CNAME, + #define API_SSIZE_T HPy_ssize_t + #define PYOBJECT_TYPE HPy #define PYOBJECT_FIELD_TYPE HPyField #define PYOBJECT_GLOBAL_TYPE HPyGlobal @@ -745,10 +747,14 @@ class __Pyx_FakeReference { #define PYOBJECT_FROM_DOUBLE(f) HPyFloat_FromDouble(HPY_CONTEXT_CNAME, f) #define PYOBJECT_FROM_STRING(s) HPyUnicode_FromString(HPY_CONTEXT_CNAME, s) //not yet needed in C API version + #define PYOBJECT_LONG_AS_SSIZE(l) HPyLong_AsSsize_t(HPY_CONTEXT_CNAME, l) + #define HPY_LEGACY_OBJECT_FROM(o) HPy_FromPyObject(HPY_CONTEXT_CNAME, o) #define HPY_LEGACY_OBJECT_AS(o) HPy_AsPyObject(HPY_CONTEXT_CNAME, o) #define PYMODULEDEF_TYPE HPyModuleDef + #define PYMETHODDEF_TYPE HPyDef + #define TYPESPEC_TYPE HPyType_Spec #define API_NULL_VALUE HPy_NULL #define API_DEFAULT_VALUE HPy_NULL @@ -788,6 +794,8 @@ class __Pyx_FakeReference { #define HPY_CONTEXT_FIRST_ARG_DEF #define HPY_CONTEXT_FIRST_ARG_CALL + #define API_SSIZE_T Py_ssize_t + #define PYOBJECT_TYPE PyObject * #define PYOBJECT_FIELD_TYPE PyObject * #define PYOBJECT_GLOBAL_TYPE PyObject * @@ -804,9 +812,15 @@ class __Pyx_FakeReference { #define PYOBJECT_FROM_LONG(i) PyInt_FromLong(i) #define PYOBJECT_FROM_DOUBLE(f) PyFloat_FromDouble(f) + #define PYOBJECT_LONG_AS_SSIZE(l) PyLong_AsSsize_t(l) + #define HPY_LEGACY_OBJECT_FROM(o) o #define HPY_LEGACY_OBJECT_AS(o) o + #define PYMODULEDEF_TYPE struct PyModuleDef + #define PYMETHODDEF_TYPE PyMethodDef + #define TYPESPEC_TYPE PyType_Spec + #define API_NULL_VALUE NULL #define API_DEFAULT_VALUE 0 #define API_IS_NULL(h) !h //Both are here as otherwise we would get !!h for API_IS_NOT_NULL, which is hard to read - but it can be made so if necessary @@ -817,8 +831,6 @@ class __Pyx_FakeReference { #define API_IS_TRUE(h) PyObject_IsTrue(h) #define API_IS_FALSE(h) !PyObject_Not(h) - #define PYMODULEDEF_TYPE struct PyModuleDef - #define DICT_NEW() #define DICT_GET_ITEM(o, attr_name) PyDict_GetItem(o, attr_name) #define DICT_SET_ITEM(o, attr_name, attr_val) PyDict_SetItem(o, attr_name, attr_val) @@ -1983,12 +1995,12 @@ static CYTHON_INLINE int __Pyx_Is_Little_Endian(void) #define __Pyx_RefNannySetupContext(name, acquire_gil) #define __Pyx_RefNannyFinishContextNogil() #define __Pyx_RefNannyFinishContext() - #define __Pyx_INCREF(r) Py_INCREF(r) - #define __Pyx_DECREF(r) Py_DECREF(r) + #define __Pyx_INCREF(r) PYOBJECT_ALLOC(r) + #define __Pyx_DECREF(r) PYOBJECT_DEALLOC(r) #define __Pyx_GOTREF(r) #define __Pyx_GIVEREF(r) - #define __Pyx_XINCREF(r) Py_XINCREF(r) - #define __Pyx_XDECREF(r) Py_XDECREF(r) + #define __Pyx_XINCREF(r) PYOBJECT_XALLOC(r) + #define __Pyx_XDECREF(r) PYOBJECT_XDEALLOC(r) #define __Pyx_XGOTREF(r) #define __Pyx_XGIVEREF(r) #endif /* CYTHON_REFNANNY */ From f27ba6f475b48e5340443fbaf445647d0c82175f Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 14 Aug 2023 13:19:22 +0200 Subject: [PATCH 141/286] More changes towards function initialisation; added HPyUtils.c helper file --- Cython/Compiler/ModuleNode.py | 6 ++++- Cython/Utility/CommonStructures.c | 40 ++++++++++++++++-------------- Cython/Utility/Coroutine.c | 6 ++--- Cython/Utility/CythonFunction.c | 26 +++++++++---------- Cython/Utility/Exceptions.c | 4 +-- Cython/Utility/FunctionArguments.c | 4 +-- Cython/Utility/HPyUtils.c | 22 ++++++++++++++++ Cython/Utility/ModuleSetupCode.c | 16 ++++++++++-- Cython/Utility/ObjectHandling.c | 18 ++++++++------ Cython/Utility/TypeConversion.c | 2 +- 10 files changed, 94 insertions(+), 50 deletions(-) create mode 100644 Cython/Utility/HPyUtils.c diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index cbcd9702155..ff65f6e90d8 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -812,6 +812,7 @@ def generate_module_preamble(self, env, options, cimported_modules, metadata, co else: self._put_setup_code(code, "CInitCode") self._put_setup_code(code, "HPyInitCode") + code.put(UtilityCode.load_as_string("HPyHelperFuncs", "HPyUtils.c")[1]) self._put_setup_code(code, "PythonCompatibility") self._put_setup_code(code, "MathInitCode") @@ -3175,7 +3176,10 @@ def generate_module_init_func(self, imported_modules, env, code): for ext_type in ('CyFunction', 'FusedFunction', 'Coroutine', 'Generator', 'AsyncGen', 'StopAsyncIteration'): code.putln("#ifdef __Pyx_%s_USED" % ext_type) - code.put_error_if_neg(self.pos, "__pyx_%s_init(%s)" % (ext_type, env.module_cname)) + if ext_type == 'CyFunction': + code.put_error_if_neg(self.pos, "__pyx_%s_init(HPY_CONTEXT_FIRST_ARG_CALL %s)" % (ext_type, env.module_cname)) + else: + code.put_error_if_neg(self.pos, "__pyx_%s_init(%s)" % (ext_type, env.module_cname)) code.putln("#endif") code.putln("/*--- Library function declarations ---*/") diff --git a/Cython/Utility/CommonStructures.c b/Cython/Utility/CommonStructures.c index 8dd8d6eb570..159638aa9a7 100644 --- a/Cython/Utility/CommonStructures.c +++ b/Cython/Utility/CommonStructures.c @@ -1,10 +1,10 @@ /////////////// FetchSharedCythonModule.proto /////// -static PYOBJECT_TYPE __Pyx_FetchSharedCythonABIModule(void); +static PYOBJECT_TYPE __Pyx_FetchSharedCythonABIModule(HPY_CONTEXT_ONLY_ARG_DEF); /////////////// FetchSharedCythonModule //////////// -static PYOBJECT_TYPE __Pyx_FetchSharedCythonABIModule(void) { +static PYOBJECT_TYPE __Pyx_FetchSharedCythonABIModule(HPY_CONTEXT_ONLY_ARG_DEF) { return HPY_LEGACY_OBJECT_FROM(__Pyx_PyImport_AddModuleRef(__PYX_ABI_MODULE_NAME)); } @@ -41,12 +41,12 @@ static int __Pyx_VerifyCachedType(PyObject *cached_type, #if !CYTHON_USE_TYPE_SPECS static PyTypeObject* __Pyx_FetchCommonType(PyTypeObject* type) { - PyObject* abi_module; + PYOBJECT_TYPE abi_module; const char* object_name; PyTypeObject *cached_type = NULL; - abi_module = __Pyx_FetchSharedCythonABIModule(); - if (!abi_module) return NULL; + abi_module = __Pyx_FetchSharedCythonABIModule(HPY_CONTEXT_ONLY_ARG_CALL); + if (API_IS_NULL(abi_module)) return API_NULL_VALUE; // get the final part of the object name (after the last dot) object_name = strrchr(type->tp_name, '.'); object_name = object_name ? object_name+1 : type->tp_name; @@ -86,14 +86,14 @@ static PyTypeObject *__Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_DEF PYO PYOBJECT_TYPE abi_module; PYOBJECT_TYPE cached_type = API_NULL_VALUE; // get the final part of the object name (after the last dot) - const char* object_name = strrchr(spec->name, '.'); - object_name = object_name ? object_name+1 : spec->name; + const char* object_name = strrchr(TYPESPEC_GET(spec,name), '.'); + object_name = object_name ? object_name+1 : TYPESPEC_GET(spec,name); - abi_module = __Pyx_FetchSharedCythonABIModule(); + abi_module = __Pyx_FetchSharedCythonABIModule(HPY_CONTEXT_ONLY_ARG_CALL); if (API_IS_NULL(abi_module)) return NULL; - cached_type = PYOBJECT_GET_ATTR_S(abi_module, object_name); - if (cached_type) { + cached_type = PYOBJECT_GET_ATTR_STR(abi_module, object_name); + if (API_IS_NOT_NULL(cached_type)) { API_SSIZE_T basicsize; #if CYTHON_COMPILING_IN_LIMITED_API PYOBJECT_TYPE py_basicsize; @@ -101,16 +101,16 @@ static PyTypeObject *__Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_DEF PYO if (unlikely(API_IS_NULL(py_basicsize))) goto bad; basicsize = PYOBJECT_LONG_AS_SSIZE(py_basicsize); PYOBJECT_DEALLOC(py_basicsize); - py_basicsize = 0; + py_basicsize = API_DEFAULT_VALUE; if (unlikely(basicsize == (Py_ssize_t)-1) && PyErr_Occurred()) goto bad; #else basicsize = likely(PyType_Check(cached_type)) ? ((PyTypeObject*) cached_type)->tp_basicsize : -1; #endif if (__Pyx_VerifyCachedType( - cached_type, + HPY_LEGACY_OBJECT_AS(cached_type), object_name, basicsize, - spec->basicsize) < 0) { + TYPESPEC_GET(spec, basicsize)) < 0) { goto bad; } goto done; @@ -121,19 +121,21 @@ static PyTypeObject *__Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_DEF PYO // We pass the ABI module reference to avoid keeping the user module alive by foreign type usages. CYTHON_UNUSED_VAR(module); cached_type = __Pyx_PyType_FromModuleAndSpec(abi_module, spec, bases); - if (unlikely(!cached_type)) goto bad; - if (unlikely(__Pyx_fix_up_extension_type_from_spec(spec, (PyTypeObject *) cached_type) < 0)) goto bad; - if (PyObject_SetAttrString(abi_module, object_name, cached_type) < 0) goto bad; + if (unlikely(API_IS_NULL(cached_type))) goto bad; +#if !CYTHON_USING_HPY + if (unlikely(__Pyx_fix_up_extension_type_from_spec(spec, (PyTypeObject *) cached_type) < 0)) goto bad; //TODO: port this function +#endif + if (PYOBJECT_SET_ATTR_STR(abi_module, object_name, cached_type) < 0) goto bad; done: PYOBJECT_DEALLOC(abi_module); // NOTE: always returns owned reference, or NULL on error - assert(cached_type == NULL || PyType_Check(cached_type)); - return (PyTypeObject *) cached_type; + assert(API_IS_NULL(cached_type) || PyType_Check(cached_type)); + return (PyTypeObject *) HPY_LEGACY_OBJECT_AS(cached_type); bad: PYOBJECT_DEALLOC(cached_type); - cached_type = NULL; + cached_type = API_NULL_VALUE; goto done; } #endif diff --git a/Cython/Utility/Coroutine.c b/Cython/Utility/Coroutine.c index 74fbefeac09..9dd14786f38 100644 --- a/Cython/Utility/Coroutine.c +++ b/Cython/Utility/Coroutine.c @@ -421,7 +421,7 @@ static CYTHON_INLINE void __Pyx_Coroutine_ResetFrameBackpointer(__Pyx_ExcInfoStr #define __Pyx_Coroutine_New(body, code, closure, name, qualname, module_name) \ __Pyx__Coroutine_New(__pyx_CoroutineType, body, code, closure, name, qualname, module_name) -static int __pyx_Coroutine_init(PyObject *module); /*proto*/ +static int __pyx_Coroutine_init(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_GLOBAL_TYPE module); /*proto*/ static PyObject *__Pyx__Coroutine_await(PyObject *coroutine); /*proto*/ typedef struct { @@ -1765,11 +1765,11 @@ static PyTypeObject __pyx_CoroutineType_type = { }; #endif /* CYTHON_USE_TYPE_SPECS */ -static int __pyx_Coroutine_init(PyObject *module) { +static int __pyx_Coroutine_init(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_GLOBAL_TYPE module) { CYTHON_MAYBE_UNUSED_VAR(module); // on Windows, C-API functions can't be used in slots statically #if CYTHON_USE_TYPE_SPECS - __pyx_CoroutineType = __Pyx_FetchCommonTypeFromSpec(module, &__pyx_CoroutineType_spec, NULL); + __pyx_CoroutineType = __Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_CALL module, &__pyx_CoroutineType_spec, NULL); #else __pyx_CoroutineType_type.tp_getattro = __Pyx_PyObject_GenericGetAttrNoDict; __pyx_CoroutineType = __Pyx_FetchCommonType(&__pyx_CoroutineType_type); diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 74c4e78be70..5dedd7fdbad 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -97,7 +97,7 @@ static CYTHON_INLINE void __Pyx_CyFunction_SetAnnotationsDict(PyObject *m, PyObject *dict); -static int __pyx_CyFunction_init(PYOBJECT_GLOBAL_TYPE module); +static int __pyx_CyFunction_init(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_GLOBAL_TYPE module); #if CYTHON_METH_FASTCALL static PyObject * __Pyx_CyFunction_Vectorcall_NOARGS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); @@ -415,19 +415,19 @@ __Pyx_CyFunction_get_annotations(__pyx_CyFunctionObject *op, void *context) { return result; } -static PyObject * -__Pyx_CyFunction_get_is_coroutine(__pyx_CyFunctionObject *op, void *context) { +static PYOBJECT_TYPE +__Pyx_CyFunction_get_is_coroutine(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject *op, void *context) { int is_coroutine; CYTHON_UNUSED_VAR(context); if (op->func_is_coroutine) { - return __Pyx_NewRef(op->func_is_coroutine); + return __Pyx_hNewRef(HPY_LEGACY_OBJECT_FROM(op->func_is_coroutine)); } is_coroutine = op->flags & __Pyx_CYFUNCTION_COROUTINE; if (is_coroutine) { - PyObject *module, *fromlist, *marker = PYIDENT("_is_coroutine"); + PyObject *module, *fromlist, *marker = HPY_LEGACY_OBJECT_AS(PYIDENT("_is_coroutine")); fromlist = PyList_New(1); - if (unlikely(!fromlist)) return NULL; + if (unlikely(!fromlist)) return API_NULL_VALUE; Py_INCREF(marker); #if CYTHON_ASSUME_SAFE_MACROS PyList_SET_ITEM(fromlist, 0, marker); @@ -438,20 +438,20 @@ __Pyx_CyFunction_get_is_coroutine(__pyx_CyFunctionObject *op, void *context) { return NULL; } #endif - module = PyImport_ImportModuleLevelObject(PYIDENT("asyncio.coroutines"), NULL, NULL, fromlist, 0); + module = PyImport_ImportModuleLevelObject(HPY_LEGACY_OBJECT_AS(PYIDENT("asyncio.coroutines")), NULL, NULL, fromlist, 0); Py_DECREF(fromlist); if (unlikely(!module)) goto ignore; - op->func_is_coroutine = __Pyx_PyObject_GetAttrStr(module, marker); + op->func_is_coroutine = HPY_LEGACY_OBJECT_AS(__Pyx_PyObject_GetAttrStr(HPY_LEGACY_OBJECT_FROM(module), HPY_LEGACY_OBJECT_FROM(marker))); Py_DECREF(module); if (likely(op->func_is_coroutine)) { - return __Pyx_NewRef(op->func_is_coroutine); + return __Pyx_hNewRef(HPY_LEGACY_OBJECT_FROM(op->func_is_coroutine)); } ignore: PyErr_Clear(); } op->func_is_coroutine = __Pyx_PyBool_FromLong(is_coroutine); - return __Pyx_NewRef(op->func_is_coroutine); + return __Pyx_hNewRef(HPY_LEGACY_OBJECT_FROM(op->func_is_coroutine)); } //static PyObject * @@ -1156,14 +1156,14 @@ static PyTypeObject __pyx_CyFunctionType_type = { #endif /* CYTHON_USE_TYPE_SPECS */ -static int __pyx_CyFunction_init(PYOBJECT_GLOBAL_TYPE module) { +static int __pyx_CyFunction_init(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_GLOBAL_TYPE module) { #if CYTHON_USE_TYPE_SPECS - __pyx_CyFunctionType = __Pyx_FetchCommonTypeFromSpec(module, &__pyx_CyFunctionType_spec, NULL); + __pyx_CyFunctionType = __Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_CALL module, &__pyx_CyFunctionType_spec, API_NULL_VALUE); #else CYTHON_UNUSED_VAR(module); __pyx_CyFunctionType = __Pyx_FetchCommonType(&__pyx_CyFunctionType_type); #endif - if (unlikely(API_IS_NULL(__pyx_CyFunctionType))) { + if (unlikely(!__pyx_CyFunctionType)) { return -1; } return 0; diff --git a/Cython/Utility/Exceptions.c b/Cython/Utility/Exceptions.c index e2aa3853706..9a2255fc333 100644 --- a/Cython/Utility/Exceptions.c +++ b/Cython/Utility/Exceptions.c @@ -885,7 +885,7 @@ static PyObject *__Pyx_PyCode_Replace_For_AddTraceback(PyObject *code, PyObject #endif } -static void __Pyx_AddTraceback(HPY_CONTEXT_FIRST_ARGUMENT_CALL const char *funcname, int c_line, +static void __Pyx_AddTraceback(HPY_CONTEXT_FIRST_ARG_CALL const char *funcname, int c_line, int py_line, const char *filename) { PyObject *code_object = NULL, *py_py_line = NULL, *py_funcname = NULL, *dict = NULL; PyObject *replace = NULL, *getframe = NULL, *frame = NULL; @@ -894,7 +894,7 @@ static void __Pyx_AddTraceback(HPY_CONTEXT_FIRST_ARGUMENT_CALL const char *funcn if (c_line) { // Avoid "unused" warning as long as we don't use this. (void) $cfilenm_cname; - (void) __Pyx_CLineForTraceback(HPY_CONTEXT_FIRST_ARGUMENT_CALL __Pyx_PyThreadState_Current, c_line); + (void) __Pyx_CLineForTraceback(HPY_CONTEXT_FIRST_ARG_CALL __Pyx_PyThreadState_Current, c_line); } // DW - this is a horrendous hack, but I'm quite proud of it. Essentially diff --git a/Cython/Utility/FunctionArguments.c b/Cython/Utility/FunctionArguments.c index 90c2d4a4582..c998922a234 100644 --- a/Cython/Utility/FunctionArguments.c +++ b/Cython/Utility/FunctionArguments.c @@ -497,8 +497,8 @@ static int __Pyx_MergeKeywords(PyObject *kwdict, PyObject *source_mapping) { #endif #if CYTHON_COMPILING_IN_CPYTHON && CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS -#define __Pyx_ArgsSlice_VARARGS(args, start, stop) __Pyx_PyTuple_FromArray(&__Pyx_Arg_VARARGS(args, start), stop - start) -#define __Pyx_ArgsSlice_FASTCALL(args, start, stop) __Pyx_PyTuple_FromArray(&__Pyx_Arg_FASTCALL(args, start), stop - start) +#define __Pyx_ArgsSlice_VARARGS(args, start, stop) __Pyx_PyTuple_FromArray(HPY_CONTEXT_FIRST_ARG_CALL &__Pyx_Arg_VARARGS(args, start), stop - start) +#define __Pyx_ArgsSlice_FASTCALL(args, start, stop) __Pyx_PyTuple_FromArray(HPY_CONTEXT_FIRST_ARG_CALL &__Pyx_Arg_FASTCALL(args, start), stop - start) #else /* Not CPython, so certainly no METH_FASTCALL support */ #define __Pyx_ArgsSlice_VARARGS(args, start, stop) PyTuple_GetSlice(args, start, stop) diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c new file mode 100644 index 00000000000..9c7c8992c42 --- /dev/null +++ b/Cython/Utility/HPyUtils.c @@ -0,0 +1,22 @@ +//////////////////// HPyHelperFuncs.proto //////////////////// + +#if CYTHON_USING_HPY +static CYTHON_INLINE PyObject ** HPy_AsPyObjectArray(HPyContext *ctx, HPy *h_arr, HPy_ssize_t n); +#endif + +//////////////////// HPyHelperFuncs //////////////////// + +#if CYTHON_USING_HPY +static CYTHON_INLINE PyObject ** +HPy_AsPyObjectArray(HPyContext *ctx, HPy *h_arr, HPy_ssize_t n) +{ + if (!h_arr) + return NULL; + PyObject **arr = PyMem_RawCalloc(n, sizeof(PyObject *)); + HPy_ssize_t i; + for (i = 0; i < n; i++) { + arr[i] = HPy_AsPyObject(ctx, h_arr[i]); + } + return arr; +} +#endif \ No newline at end of file diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 52a591f76b4..fba20be023d 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -449,6 +449,10 @@ #define CYTHON_USE_TYPE_SLOTS 0 #undef CYTHON_AVOID_BORROWED_REFS #define CYTHON_AVOID_BORROWED_REFS 1 + #undef CYTHON_USE_TYPE_SPECS + #define CYTHON_USE_TYPE_SPECS 1 + #undef CYTHON_COMPILING_IN_LIMITED_API + #define CYTHON_COMPILING_IN_LIMITED_API 1 /* We don't use refnanny in HPy since it has the debug mode */ #undef CYTHON_REFNANNY #define CYTHON_REFNANNY 0 @@ -741,6 +745,7 @@ class __Pyx_FakeReference { #define PYOBJECT_XALLOC(h) HPy_Dup(HPY_CONTEXT_CNAME, h) #define PYOBJECT_DEALLOC(h) HPy_Close(HPY_CONTEXT_CNAME, h) #define PYOBJECT_XDEALLOC(h) HPy_Close(HPY_CONTEXT_CNAME, h) + #define PYOBJECT_ALLOC_NEWREF(h) HPy_Dup(HPY_CONTEXT_CNAME, h) #define REFNANNY_DEALLOC(func, h) PYOBJECT_DEALLOC(h) #define PYOBJECT_FROM_LONG(i) HPyLong_FromLong(HPY_CONTEXT_CNAME, i) @@ -751,10 +756,13 @@ class __Pyx_FakeReference { #define HPY_LEGACY_OBJECT_FROM(o) HPy_FromPyObject(HPY_CONTEXT_CNAME, o) #define HPY_LEGACY_OBJECT_AS(o) HPy_AsPyObject(HPY_CONTEXT_CNAME, o) + #define HPY_LEGACY_OBJECT_ARRAY_AS(o, ssize) HPy_AsPyObjectArray(HPY_CONTEXT_CNAME, o, ssize) #define PYMODULEDEF_TYPE HPyModuleDef #define PYMETHODDEF_TYPE HPyDef #define TYPESPEC_TYPE HPyType_Spec + #define TYPE_FROM_MOD_AND_SPEC(m, s, b) HPyType_FromSpec(HPY_CONTEXT_CNAME, &s, HPy_NULL) + #define TYPESPEC_GET(s, field) s.field #define API_NULL_VALUE HPy_NULL #define API_DEFAULT_VALUE HPy_NULL @@ -776,7 +784,7 @@ class __Pyx_FakeReference { #define PYOBJECT_SET_ITEM(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) #define PYOBJECT_GET_ATTR(o, attr_name) HPy_GetAttr(HPY_CONTEXT_CNAME, o, attr_name) #define PYOBJECT_SET_ATTR(o, attr_name, attr_val) HPy_SetAttr(HPY_CONTEXT_CNAME, o, attr_name, attr_val) - #define PYOBJECT_GET_ATTR_STR(o, attr_name) HPyObject_GetAttrString(HPY_CONTEXT_CNAME, o, attr_name) + #define PYOBJECT_GET_ATTR_STR(o, attr_name) HPy_GetAttr_s(HPY_CONTEXT_CNAME, o, attr_name) #define PYOBJECT_SET_ATTR_STR(o1, attr_name, o2) HPy_SetAttr_s(HPY_CONTEXT_CNAME, o1, attr_name, o2) #define PYMODULE_GETDICT_ATTR(mod) HPy_GetAttr_s(HPY_CONTEXT_CNAME, mod, "__dict__") @@ -807,6 +815,7 @@ class __Pyx_FakeReference { #define PYOBJECT_XALLOC(h) Py_XINCREF(h) #define PYOBJECT_DEALLOC(h) Py_DECREF(h) #define PYOBJECT_XDEALLOC(h) Py_XDECREF(h) + #define PYOBJECT_ALLOC_NEWREF(h) (Py_INCREF(h), h) #define REFNANNY_DEALLOC(func, h) func(h) #define PYOBJECT_FROM_LONG(i) PyInt_FromLong(i) @@ -816,10 +825,13 @@ class __Pyx_FakeReference { #define HPY_LEGACY_OBJECT_FROM(o) o #define HPY_LEGACY_OBJECT_AS(o) o + #define HPY_LEGACY_OBJECT_ARRAY_AS(o, ssize) o #define PYMODULEDEF_TYPE struct PyModuleDef #define PYMETHODDEF_TYPE PyMethodDef #define TYPESPEC_TYPE PyType_Spec + #define TYPE_FROM_MOD_AND_SPEC(m, s, b) PyType_FromModuleAndSpec(m, s, b) + #define TYPESPEC_GET(s, field) s->field #define API_NULL_VALUE NULL #define API_DEFAULT_VALUE 0 @@ -1139,7 +1151,7 @@ static CYTHON_INLINE int __Pyx__IsSameCFunction(PyObject *func, void *cfunc) { #define __Pyx_PyType_FromModuleAndSpec(m, s, b) ((void)m, PyType_FromSpecWithBases(s, b)) typedef PyObject *(*__Pyx_PyCMethod)(PyObject *, PyTypeObject *, PyObject *const *, size_t, PyObject *); #else - #define __Pyx_PyType_FromModuleAndSpec(m, s, b) PyType_FromModuleAndSpec(m, s, b) + #define __Pyx_PyType_FromModuleAndSpec(m, s, b) TYPE_FROM_MOD_AND_SPEC(m, s, b) #define __Pyx_PyCMethod PyCMethod #endif #ifndef METH_METHOD diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 4d8695f5b7e..3c8474f34a9 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -738,7 +738,7 @@ static CYTHON_INLINE int __Pyx_PyObject_SetSlice(PyObject* obj, PyObject* value, #if CYTHON_COMPILING_IN_CPYTHON static CYTHON_INLINE PyObject* __Pyx_PyList_FromArray(PyObject *const *src, Py_ssize_t n); -static CYTHON_INLINE PyObject* __Pyx_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n); +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyTuple_FromArray(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE const *src, API_SSIZE_T n); #endif /////////////// TupleAndListFromArray /////////////// @@ -754,18 +754,22 @@ static CYTHON_INLINE void __Pyx_copy_object_array(PyObject *const *CYTHON_RESTRI } } -static CYTHON_INLINE PyObject * -__Pyx_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n) +static CYTHON_INLINE PYOBJECT_TYPE +__Pyx_PyTuple_FromArray(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE const *src, API_SSIZE_T n) { PyObject *res; if (n <= 0) { +#if !CYTHON_USING_HPY Py_INCREF($empty_tuple); return $empty_tuple; +#else + return PYOBJECT_GLOBAL_LOAD($empty_tuple); +#endif } res = PyTuple_New(n); - if (unlikely(res == NULL)) return NULL; - __Pyx_copy_object_array(src, ((PyTupleObject*)res)->ob_item, n); - return res; + if (unlikely(res == NULL)) return API_NULL_VALUE; + __Pyx_copy_object_array(HPY_LEGACY_OBJECT_ARRAY_AS(src, n), ((PyTupleObject*)res)->ob_item, (Py_ssize_t) n); + return HPY_LEGACY_OBJECT_FROM(res); } static CYTHON_INLINE PyObject * @@ -2825,7 +2829,7 @@ typedef const char *__Pyx_TypeName; #if CYTHON_COMPILING_IN_LIMITED_API static __Pyx_TypeName -__Pyx_PyType_GetName(PyTypeObject* tp) +__Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_DEF PyTypeObject* tp) { PyObject *name = __Pyx_PyObject_GetAttrStr((PyObject *)tp, PYIDENT("__name__")); diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index f42b674ed25..e0dc18cda45 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -108,7 +108,7 @@ static CYTHON_INLINE size_t __Pyx_Py_UNICODE_strlen(const Py_UNICODE *u) #define __Pyx_PyUnicode_FromUnicodeAndLength PyUnicode_FromUnicode #define __Pyx_PyUnicode_AsUnicode PyUnicode_AsUnicode -#define __Pyx_hNewRef(obj) PYOBJECT_ALLOC(obj) //This will be merged into Pyx_NewRef eventually, but #if CYTHON_USING_HPY will make all calls to this macro use HPy, even if it hasn't been ported yet +#define __Pyx_hNewRef(obj) PYOBJECT_ALLOC_NEWREF(obj) //This will be merged into Pyx_NewRef eventually, but #if CYTHON_USING_HPY will make all calls to this macro use HPy, even if it hasn't been ported yet #define __Pyx_NewRef(obj) (Py_INCREF(obj), obj) #define __Pyx_Owned_Py_None(b) __Pyx_NewRef(Py_None) static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b); From 613b61407c9573281eb67b895799fbfabe6cbc16 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 16 Aug 2023 09:54:45 +0200 Subject: [PATCH 142/286] Function headers for NOARGS now mostly work - just a small issue with args remaining --- Cython/Compiler/Code.py | 4 ++-- Cython/Utility/CythonFunction.c | 11 ++++++++++- Cython/Utility/Exceptions.c | 2 +- Cython/Utility/HPyUtils.c | 8 ++++++++ Cython/Utility/ModuleSetupCode.c | 2 +- Cython/Utility/ObjectHandling.c | 19 ++++++++++++------- Cython/Utility/TypeConversion.c | 20 ++++++++++---------- 7 files changed, 44 insertions(+), 22 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 2a8f88c4ffa..09fb8d3044a 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -2366,8 +2366,8 @@ def put_hpymethoddef(self, entry, term, allow_skip=True, wrapper_code_writer=Non if not method_flags: return None entry_name = entry.name.as_c_string_literal() - self.putln("HPyDef_METH(%s, %s, %s, .doc=%s);" % ( - entry.pymethdef_cname, entry_name, method_flags, entry.doc_cname if entry.doc else '0')) + self.putln("HPyDef_METH_IMPL(%s, %s, %s, %s, .doc=%s);" % ( + entry.pymethdef_cname, entry_name, entry.func_cname, method_flags, entry.doc_cname if entry.doc else '0')) # GIL methods diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 5dedd7fdbad..718a5c0af97 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -1064,6 +1064,7 @@ static PyType_Slot __pyx_CyFunctionType_slots[] = { {0, 0}, }; +#if !CYTHON_USING_HPY static PyType_Spec __pyx_CyFunctionType_spec = { __PYX_TYPE_MODULE_PREFIX "cython_function_or_method", sizeof(__pyx_CyFunctionObject), @@ -1077,6 +1078,14 @@ static PyType_Spec __pyx_CyFunctionType_spec = { Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /*tp_flags*/ __pyx_CyFunctionType_slots }; +#else +static HPyType_Spec __pyx_CyFunctionType_spec = { + .basicsize = sizeof(__pyx_CyFunctionObject), + .itemsize = 0, + .flags = HPy_TPFLAGS_DEFAULT | HPy_TPFLAGS_HAVE_GC | HPy_TPFLAGS_BASETYPE, /*tp_flags*/ + .legacy_slots = __pyx_CyFunctionType_slots +}; +#endif #else /* CYTHON_USE_TYPE_SPECS */ static PyTypeObject __pyx_CyFunctionType_type = { @@ -1158,7 +1167,7 @@ static PyTypeObject __pyx_CyFunctionType_type = { static int __pyx_CyFunction_init(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_GLOBAL_TYPE module) { #if CYTHON_USE_TYPE_SPECS - __pyx_CyFunctionType = __Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_CALL module, &__pyx_CyFunctionType_spec, API_NULL_VALUE); + __pyx_CyFunctionType = __Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_CALL module, __pyx_CyFunctionType_spec, API_NULL_VALUE); #else CYTHON_UNUSED_VAR(module); __pyx_CyFunctionType = __Pyx_FetchCommonType(&__pyx_CyFunctionType_type); diff --git a/Cython/Utility/Exceptions.c b/Cython/Utility/Exceptions.c index 9a2255fc333..6937ebe9727 100644 --- a/Cython/Utility/Exceptions.c +++ b/Cython/Utility/Exceptions.c @@ -885,7 +885,7 @@ static PyObject *__Pyx_PyCode_Replace_For_AddTraceback(PyObject *code, PyObject #endif } -static void __Pyx_AddTraceback(HPY_CONTEXT_FIRST_ARG_CALL const char *funcname, int c_line, +static void __Pyx_AddTraceback(HPY_CONTEXT_FIRST_ARG_DEF const char *funcname, int c_line, int py_line, const char *filename) { PyObject *code_object = NULL, *py_py_line = NULL, *py_funcname = NULL, *dict = NULL; PyObject *replace = NULL, *getframe = NULL, *frame = NULL; diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 9c7c8992c42..e24b1b5bcfc 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -19,4 +19,12 @@ HPy_AsPyObjectArray(HPyContext *ctx, HPy *h_arr, HPy_ssize_t n) } return arr; } + +static HPyType_SpecParam* get_temp_specparams(HPyContext *ctx) { + HPyType_SpecParam temp_params[] = { + { HPyType_SpecParam_Base, ctx->h_LongType }, + { (HPyType_SpecParam_Kind)0 } + }; + return temp_params; +} #endif \ No newline at end of file diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index fba20be023d..8ef9b1165b0 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -761,7 +761,7 @@ class __Pyx_FakeReference { #define PYMODULEDEF_TYPE HPyModuleDef #define PYMETHODDEF_TYPE HPyDef #define TYPESPEC_TYPE HPyType_Spec - #define TYPE_FROM_MOD_AND_SPEC(m, s, b) HPyType_FromSpec(HPY_CONTEXT_CNAME, &s, HPy_NULL) + #define TYPE_FROM_MOD_AND_SPEC(m, s, b) HPyType_FromSpec(HPY_CONTEXT_CNAME, &s, get_temp_specparams(HPY_CONTEXT_CNAME)) #define TYPESPEC_GET(s, field) s.field #define API_NULL_VALUE HPy_NULL diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 3c8474f34a9..1b29728f90d 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1555,6 +1555,7 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStrNoError(HPY_CONTEXT_ static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStr(PyObject* obj, PyObject* attr_name);/*proto*/ #else #define __Pyx_PyObject_GetAttrStr(o,n) PYOBJECT_GET_ATTR(o,n) //Needed to turn this into a function macro to be able to pass the context properly +#define __Pyx_PyObject_GetAttrStr_legacy(o,n) PyObject_GetAttr(o,n) //Used for functions where the context isn't reachable yet #endif /////////////// PyObjectGetAttrStr /////////////// @@ -2814,10 +2815,10 @@ static CYTHON_INLINE PyObject* __Pyx_PySequence_Multiply(PyObject *seq, Py_ssize /////////////// FormatTypeName.proto /////////////// #if CYTHON_COMPILING_IN_LIMITED_API -typedef PyObject *__Pyx_TypeName; +typedef PYOBJECT_TYPE __Pyx_TypeName; #define __Pyx_FMT_TYPENAME "%U" -static __Pyx_TypeName __Pyx_PyType_GetName(PyTypeObject* tp); /*proto*/ -#define __Pyx_DECREF_TypeName(obj) Py_XDECREF(obj) +static __Pyx_TypeName __Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_DEF PyTypeObject* tp); /*proto*/ +#define __Pyx_DECREF_TypeName(obj) PYOBJECT_XDEALLOC(obj) #else typedef const char *__Pyx_TypeName; #define __Pyx_FMT_TYPENAME "%.200s" @@ -2827,16 +2828,20 @@ typedef const char *__Pyx_TypeName; /////////////// FormatTypeName /////////////// +//Duplication will be removed once port is completed + #if CYTHON_COMPILING_IN_LIMITED_API static __Pyx_TypeName __Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_DEF PyTypeObject* tp) { - PyObject *name = __Pyx_PyObject_GetAttrStr((PyObject *)tp, + PYOBJECT_TYPE name = __Pyx_PyObject_GetAttrStr(HPY_LEGACY_OBJECT_FROM((PyObject *)tp), PYIDENT("__name__")); - if (unlikely(name == NULL) || unlikely(!PyUnicode_Check(name))) { + if (unlikely(API_IS_NULL(name)) || unlikely(!PyUnicode_Check(HPY_LEGACY_OBJECT_AS(name)))) { PyErr_Clear(); - Py_XDECREF(name); - name = __Pyx_NewRef(PYIDENT("?")); +#if !CYTHON_USING_HPY + Py_XDECREF(HPY_LEGACY_OBJECT_AS(name)); + name = __Pyx_NewRef(PYIDENT("?")); //Not sure how this should look in HPy +#endif } return name; } diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index e0dc18cda45..b055622c4f3 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -114,7 +114,7 @@ static CYTHON_INLINE size_t __Pyx_Py_UNICODE_strlen(const Py_UNICODE *u) static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b); static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*); static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject*); -static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(PyObject* x); +static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(HPY_CONTEXT_FIRST_ARG_DEF PyObject* x); #define __Pyx_PySequence_Tuple(obj) \ (likely(PyTuple_CheckExact(obj)) ? __Pyx_NewRef(obj) : PySequence_Tuple(obj)) @@ -318,8 +318,8 @@ static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject* x) { return retval; } -static PyObject* __Pyx_PyNumber_IntOrLongWrongResultType(PyObject* result, const char* type_name) { - __Pyx_TypeName result_type_name = __Pyx_PyType_GetName(Py_TYPE(result)); +static PyObject* __Pyx_PyNumber_IntOrLongWrongResultType(HPY_CONTEXT_FIRST_ARG_DEF PyObject* result, const char* type_name) { + __Pyx_TypeName result_type_name = __Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_CALL Py_TYPE(result)); if (PyLong_Check(result)) { // CPython issue #17576: warn if 'result' not of exact type int. if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, @@ -342,7 +342,7 @@ static PyObject* __Pyx_PyNumber_IntOrLongWrongResultType(PyObject* result, const return NULL; } -static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(PyObject* x) { +static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(HPY_CONTEXT_FIRST_ARG_DEF PyObject* x) { #if CYTHON_USE_TYPE_SLOTS PyNumberMethods *m; #endif @@ -363,7 +363,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(PyObject* x) { #endif if (likely(res)) { if (unlikely(!PyLong_CheckExact(res))) { - return __Pyx_PyNumber_IntOrLongWrongResultType(res, name); + return __Pyx_PyNumber_IntOrLongWrongResultType(HPY_CONTEXT_FIRST_ARG_CALL res, name); } } else if (!PyErr_Occurred()) { @@ -918,7 +918,7 @@ static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value, Py_ssize_t wid /////////////// CIntFromPy.proto /////////////// -static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *); +static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(HPY_CONTEXT_FIRST_ARG_DEF PyObject *); /////////////// CIntFromPy /////////////// //@requires: CIntFromPyVerify @@ -926,7 +926,7 @@ static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *); {{py: from Cython.Utility import pylong_join }} -static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *x) { +static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(HPY_CONTEXT_FIRST_ARG_DEF PyObject *x) { #ifdef __Pyx_HAS_GCC_DIAGNOSTIC #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" @@ -1026,7 +1026,7 @@ static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *x) { // large integer type and no access to PyLong internals => allow for a more expensive conversion { {{TYPE}} val; - PyObject *v = __Pyx_PyNumber_IntOrLong(x); + PyObject *v = __Pyx_PyNumber_IntOrLong(HPY_CONTEXT_FIRST_ARG_CALL x); if (likely(v)) { int ret = -1; #if PY_VERSION_HEX < 0x030d0000 && !(CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API) || defined(_PyLong_AsByteArray) @@ -1137,9 +1137,9 @@ static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *x) { {{endif}} } else { {{TYPE}} val; - PyObject *tmp = __Pyx_PyNumber_IntOrLong(x); + PyObject *tmp = __Pyx_PyNumber_IntOrLong(HPY_CONTEXT_FIRST_ARG_CALL x); if (!tmp) return ({{TYPE}}) -1; - val = {{FROM_PY_FUNCTION}}(tmp); + val = {{FROM_PY_FUNCTION}}(HPY_CONTEXT_FIRST_ARG_CALL tmp); Py_DECREF(tmp); return val; } From fb47c28c5e1021ec04f9b8900002c01db27293ca Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 17 Aug 2023 10:03:03 +0200 Subject: [PATCH 143/286] Made proper HPyGlobals table --- Cython/Compiler/Code.py | 26 ++++++++++++++++++++++---- Cython/Compiler/ExprNodes.py | 8 ++++---- Cython/Compiler/ModuleNode.py | 10 +--------- Cython/Compiler/Nodes.py | 4 ++-- Cython/Utility/CythonFunction.c | 4 ++-- Cython/Utility/ModuleSetupCode.c | 4 +++- Cython/Utility/ObjectHandling.c | 2 +- Cython/Utility/StringTools.c | 8 ++++---- 8 files changed, 39 insertions(+), 27 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 09fb8d3044a..03e3d7bc9d7 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1143,6 +1143,7 @@ class GlobalState: 'cached_constants', 'init_constants', 'init_globals', # (utility code called at init-time) + 'globals_table', 'init_module', 'cleanup_globals', 'cleanup_module', @@ -1153,6 +1154,8 @@ class GlobalState: 'end' ] + globals_counter = 5 + # h files can only have a much smaller list of sections h_code_layout = [ 'h_code', @@ -1218,6 +1221,13 @@ def initialize_main_c_code(self): w.putln("") w.putln("static CYTHON_SMALL_CODE int __Pyx_InitConstants(HPY_CONTEXT_ONLY_ARG_DEF) {") + w = self.parts['globals_table'] + w.enter_cfunc_scope() + self.globals_counter = 0 + w.putln("") + w.putln("#if CYTHON_USING_HPY") + w.putln("static CYTHON_SMALL_CODE int __Pyx_InitGlobalsTable() {") + if not Options.generate_cleanup_code: del self.parts['cleanup_globals'] else: @@ -1293,13 +1303,19 @@ def close_global_decls(self): w.putln("}") w.exit_cfunc_scope() - for part in ['init_globals', 'init_constants']: + for part in ['init_globals', 'init_constants', 'globals_table']: w = self.parts[part] w.putln("return 0;") if w.label_used(w.error_label): w.put_label(w.error_label) w.putln("return -1;") w.putln("}") + if (part == 'init_constants'): + w.putln("#if CYTHON_USING_HPY") + w.putln("HPyGlobal *globals_array[%s];" % self.globals_counter) + w.putln("#endif") + if (part == 'globals_table'): + w.putln("#endif") w.exit_cfunc_scope() if Options.generate_cleanup_code: @@ -1602,7 +1618,7 @@ def generate_string_constants(self): else: encoding = '"%s"' % py_string.encoding.lower() - self.parts['module_state'].putln("PYOBJECT_TYPE %s;" % py_string.cname) + self.parts['module_state'].putln("PYOBJECT_GLOBAL_TYPE %s;" % py_string.cname) self.parts['module_state_defines'].putln("#define %s %s->%s" % ( py_string.cname, Naming.modulestateglobal_cname, @@ -1611,9 +1627,11 @@ def generate_string_constants(self): py_string.cname) self.parts['module_state_traverse'].putln("Py_VISIT(traverse_module_state->%s);" % py_string.cname) + self.parts['globals_table'].putln("globals_array[%d] = &%s;" % (self.globals_counter, py_string.cname)) + self.globals_counter += 1 if py_string.py3str_cstring: w.putln("#if PY_MAJOR_VERSION >= 3") - w.putln("{&%s, %s, sizeof(%s), %s, %d, %d, %d}," % ( + w.putln("{CAPI_NEEDS_DEREFERENCE %s, %s, sizeof(%s), %s, %d, %d, %d}," % ( py_string.cname, py_string.py3str_cstring.cname, py_string.py3str_cstring.cname, @@ -1621,7 +1639,7 @@ def generate_string_constants(self): py_string.intern )) w.putln("#else") - w.putln("{&%s, %s, sizeof(%s), %s, %d, %d, %d}," % ( + w.putln("{CAPI_NEEDS_DEREFERENCE %s, %s, sizeof(%s), %s, %d, %d, %d}," % ( py_string.cname, c_cname, c_cname, diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 9c7fb019470..ca62eb64a32 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -2527,7 +2527,7 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, assert False, repr(entry) code.put_error_if_neg( self.pos, - '%s(PYOBJECT_GLOBAL_LOAD(%s), %s, %s)' % ( + '%s(PYOBJECT_GLOBAL_LOAD(%s), PYOBJECT_GLOBAL_LOAD(%s), %s)' % ( setter, namespace, interned_cname, @@ -10065,7 +10065,7 @@ def generate_cyfunction_code(self, code): flags = '0' code.putln( - '%s = %s(HPY_CONTEXT_FIRST_ARG_CALL &%s, %s, %s, %s, %s, PYOBJECT_GLOBAL_LOAD(%s), %s); %s' % ( + '%s = %s(HPY_CONTEXT_FIRST_ARG_CALL &%s, %s, PYOBJECT_GLOBAL_LOAD(%s), %s, PYOBJECT_GLOBAL_LOAD(%s), PYOBJECT_GLOBAL_LOAD(%s), %s); %s' % ( self.result(), constructor, self.pymethdef_cname, @@ -10192,8 +10192,8 @@ def generate_result_code(self, code): code.putln("%s = HPY_LEGACY_OBJECT_FROM((PyObject*)__Pyx_PyCode_New(%d, %d, %d, %d, 0, %s, HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), \ HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), \ HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), \ - HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), %d, \ - HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)))); %s" % ( + HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), \ + HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), %d, HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)))); %s" % ( self.result_code, len(func.args) - func.num_kwonly_args, # argcount func.num_posonly_args, # posonlyargcount (Py3.8+ only) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index ff65f6e90d8..13aab25c123 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -510,7 +510,6 @@ def generate_c_code(self, env, options, result): self.generate_typeobj_definitions(env, code) self.generate_method_table(env, code) self.generate_hpy_define_array(env, code) - self.define_globals_array(env, code) if env.has_import_star: self.generate_import_star(env, code) @@ -2744,13 +2743,6 @@ def generate_hpy_define_array(self, env, code): if wrapper_code_writer.getvalue(): wrapper_code_writer.putln("") - - - def define_globals_array(self, env, code): - code.putln("#if CYTHON_USING_HPY") - code.putln("static HPyGlobal *globals_array[10];") - code.putln("#endif") - def generate_globals_array(self, env, code): code.putln("#if CYTHON_USING_HPY") code.putln("globals_array[0] = &%s;" % env.module_cname) @@ -3198,7 +3190,7 @@ def generate_module_init_func(self, imported_modules, env, code): code.putln("#endif") code.putln("if (%s) {" % self.is_main_module_flag_cname()) - code.put_error_if_neg(self.pos, 'PYOBJECT_SET_ATTR(%s, %s, %s)' % ( + code.put_error_if_neg(self.pos, 'PYOBJECT_SET_ATTR(%s, PYOBJECT_GLOBAL_LOAD(%s), PYOBJECT_GLOBAL_LOAD(%s))' % ( Naming.pymodinit_module_arg, code.intern_identifier(EncodedString("__name__")), code.intern_identifier(EncodedString("__main__")))) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 5b6a21540e4..9da837a66fd 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -3767,8 +3767,8 @@ def generate_function_header(self, code, with_pymethdef, proto_only=0): arg_code_list.append( arg.hdr_type.declaration_code(arg.hdr_cname)) entry = self.target.entry - if not entry.is_special and sig.method_flags() == [TypeSlots.method_noargs]: - arg_code_list.append("CYTHON_UNUSED PYOBJECT_TYPE unused") + #if not entry.is_special and sig.method_flags() == [TypeSlots.method_noargs]: + # arg_code_list.append("CYTHON_UNUSED PYOBJECT_TYPE unused") if sig.has_generic_args: varargs_args = "PYOBJECT_TYPE %s, PYOBJECT_TYPE %s" % ( Naming.args_cname, Naming.kwds_cname) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 718a5c0af97..428617ea7f8 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -425,7 +425,7 @@ __Pyx_CyFunction_get_is_coroutine(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObje is_coroutine = op->flags & __Pyx_CYFUNCTION_COROUTINE; if (is_coroutine) { - PyObject *module, *fromlist, *marker = HPY_LEGACY_OBJECT_AS(PYIDENT("_is_coroutine")); + PyObject *module, *fromlist, *marker = HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(PYIDENT("_is_coroutine"))); fromlist = PyList_New(1); if (unlikely(!fromlist)) return API_NULL_VALUE; Py_INCREF(marker); @@ -438,7 +438,7 @@ __Pyx_CyFunction_get_is_coroutine(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObje return NULL; } #endif - module = PyImport_ImportModuleLevelObject(HPY_LEGACY_OBJECT_AS(PYIDENT("asyncio.coroutines")), NULL, NULL, fromlist, 0); + module = PyImport_ImportModuleLevelObject(HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(PYIDENT("asyncio.coroutines"))), NULL, NULL, fromlist, 0); Py_DECREF(fromlist); if (unlikely(!module)) goto ignore; op->func_is_coroutine = HPY_LEGACY_OBJECT_AS(__Pyx_PyObject_GetAttrStr(HPY_LEGACY_OBJECT_FROM(module), HPY_LEGACY_OBJECT_FROM(marker))); diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 8ef9b1165b0..f64d0fbd31f 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -740,6 +740,7 @@ class __Pyx_FakeReference { #define PYOBJECT_GLOBAL_STORE(global, h) HPyGlobal_Store(HPY_CONTEXT_CNAME, &global, h) #define PYOBJECT_GLOBAL_LOAD(global) HPyGlobal_Load(HPY_CONTEXT_CNAME, global) #define CAPI_IS_POINTER + #define CAPI_NEEDS_DEREFERENCE #define PYOBJECT_ALLOC(h) HPy_Dup(HPY_CONTEXT_CNAME, h) #define PYOBJECT_XALLOC(h) HPy_Dup(HPY_CONTEXT_CNAME, h) @@ -810,6 +811,7 @@ class __Pyx_FakeReference { #define PYOBJECT_GLOBAL_STORE(global, h) global = h #define PYOBJECT_GLOBAL_LOAD(global) global #define CAPI_IS_POINTER * //Some types are sometimes pointers and sometimes not (i.e. PyModuleDef) where the type is always the same in HPy + #define CAPI_NEEDS_DEREFERENCE & #define PYOBJECT_ALLOC(h) Py_INCREF(h) #define PYOBJECT_XALLOC(h) Py_XINCREF(h) @@ -1660,7 +1662,7 @@ static CYTHON_INLINE float __PYX_NAN() { /////////////// UtilityFunctionPredeclarations.proto /////////////// -typedef struct {PYOBJECT_TYPE* p; const char *s; const Py_ssize_t n; const char* encoding; +typedef struct {PYOBJECT_GLOBAL_TYPE CAPI_IS_POINTER p; const char *s; const Py_ssize_t n; const char* encoding; const char is_unicode; const char is_str; const char intern; } __Pyx_StringTabEntry; /*proto*/ diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 1b29728f90d..adc1e2b07fd 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -2835,7 +2835,7 @@ static __Pyx_TypeName __Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_DEF PyTypeObject* tp) { PYOBJECT_TYPE name = __Pyx_PyObject_GetAttrStr(HPY_LEGACY_OBJECT_FROM((PyObject *)tp), - PYIDENT("__name__")); + PYOBJECT_GLOBAL_LOAD(PYIDENT("__name__"))); if (unlikely(API_IS_NULL(name)) || unlikely(!PyUnicode_Check(HPY_LEGACY_OBJECT_AS(name)))) { PyErr_Clear(); #if !CYTHON_USING_HPY diff --git a/Cython/Utility/StringTools.c b/Cython/Utility/StringTools.c index d29e55c6ecc..8df65b6c3ec 100644 --- a/Cython/Utility/StringTools.c +++ b/Cython/Utility/StringTools.c @@ -35,14 +35,14 @@ static int __Pyx_InitStrings(HPY_CONTEXT_FIRST_ARG_DEF __Pyx_StringTabEntry *t) PyObject *str; if (t->is_unicode | t->is_str) { if (t->intern) { - str = PyUnicode_InternFromString(t->s); + PYOBJECT_GLOBAL_STORE(CAPI_IS_POINTER str, HPY_LEGACY_OBJECT_FROM(PyUnicode_InternFromString(t->s))); } else if (t->encoding) { - str = PyUnicode_Decode(t->s, t->n - 1, t->encoding, NULL); + PYOBJECT_GLOBAL_STORE(CAPI_IS_POINTER str, HPY_LEGACY_OBJECT_FROM(PyUnicode_Decode(t->s, t->n - 1, t.encoding, NULL))); } else { - str = PyUnicode_FromStringAndSize(t->s, t->n - 1); + PYOBJECT_GLOBAL_STORE(CAPI_IS_POINTER str, HPY_LEGACY_OBJECT_FROM(PyUnicode_FromStringAndSize(t->s, t->n - 1))); } } else { - str = PyBytes_FromStringAndSize(t->s, t->n - 1); + PYOBJECT_GLOBAL_STORE(CAPI_IS_POINTER str, BYTES_FROM_STR_AND_SIZE(t->s, t->n - 1)); } if (!str) return -1; From 66008872ef70b84f50cb3b31d557a1977b4edaaa Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 17 Aug 2023 11:40:52 +0200 Subject: [PATCH 144/286] Removed unnecessary added code --- Cython/Compiler/ModuleNode.py | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 13aab25c123..72cf8b5d251 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -2741,19 +2741,7 @@ def generate_hpy_define_array(self, env, code): code.putln("") if wrapper_code_writer.getvalue(): - wrapper_code_writer.putln("") - - def generate_globals_array(self, env, code): - code.putln("#if CYTHON_USING_HPY") - code.putln("globals_array[0] = &%s;" % env.module_cname) - code.putln("globals_array[1] = &%s;" % env.module_dict_cname) - code.putln("globals_array[2] = &%s;" % Naming.cython_runtime_cname) - code.putln("globals_array[3] = &%s;" % Naming.empty_tuple) - code.putln("globals_array[4] = &%s;" % Naming.empty_bytes) - code.putln("globals_array[5] = &%s;" % Naming.empty_unicode) - if Options.pre_import is not None: - code.putln("globals_array[6] = &%s;" % Naming.preimport_cname) - code.putln("#endif") + wrapper_code_writer.putln("") def generate_dict_getter_function(self, scope, code): dict_attr = scope.lookup_here("__dict__") @@ -3097,8 +3085,6 @@ def generate_module_init_func(self, imported_modules, env, code): code.putln('int pystate_addmodule_run = 0;') code.putln("#endif") - self.generate_globals_array(env, code) - tempdecl_code = code.insertion_point() profile = code.globalstate.directives['profile'] From 528ee8f96bb2de9244ee69675426d24dc37eba6b Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 21 Aug 2023 15:43:09 +0200 Subject: [PATCH 145/286] Can now instantiate multiple tuples in HPy Mode; more globals now added to array --- Cython/Compiler/Code.py | 15 +++++++--- Cython/Compiler/ExprNodes.py | 47 ++++++++++++++++++++++---------- Cython/Compiler/PyrexTypes.py | 44 ++++++++++++++++++++++++++++++ Cython/Utility/ModuleSetupCode.c | 14 ++++++---- Cython/Utility/ObjectHandling.c | 4 +-- 5 files changed, 97 insertions(+), 27 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 03e3d7bc9d7..b2b8940e220 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1531,9 +1531,12 @@ def generate_object_constant_decls(self): for c in self.py_constants] consts.sort() for _, cname, c in consts: + c.type.is_global = True self.parts['module_state'].putln("%s;" % c.type.declaration_code(cname)) self.parts['module_state_defines'].putln( "#define %s %s->%s" % (cname, Naming.modulestateglobal_cname, cname)) + self.parts['globals_table'].putln("globals_array[%d] = &%s;" % (self.globals_counter, cname)) + self.globals_counter += 1 if not c.type.needs_refcounting: # Note that py_constants is used for all argument defaults # which aren't necessarily PyObjects, so aren't appropriate @@ -1666,24 +1669,26 @@ def generate_num_constants(self): init_constants = self.parts['init_constants'] for py_type, _, _, value, value_code, c in consts: cname = c.cname - self.parts['module_state'].putln("PYOBJECT_TYPE %s;" % cname) + self.parts['module_state'].putln("PYOBJECT_GLOBAL_TYPE %s;" % cname) self.parts['module_state_defines'].putln("#define %s %s->%s" % ( cname, Naming.modulestateglobal_cname, cname)) self.parts['module_state_clear'].putln( "Py_CLEAR(clear_module_state->%s);" % cname) self.parts['module_state_traverse'].putln( "Py_VISIT(traverse_module_state->%s);" % cname) + self.parts['globals_table'].putln("globals_array[%d] = &%s;" % (self.globals_counter, cname)) + self.globals_counter += 1 if py_type == 'float': function = 'PYOBJECT_FROM_DOUBLE(%s)' elif py_type == 'long': - function = 'PyLong_FromString("%s", 0, 0)' + function = 'HPY_LEGACY_OBJECT_FROM(PyLong_FromString((char *)"%s", 0, 0))' elif Utils.long_literal(value): - function = 'PyInt_FromString("%s", 0, 0)' + function = 'HPY_LEGACY_OBJECT_FROM(PyInt_FromString((char *)"%s", 0, 0))' elif len(value.lstrip('-')) > 4: function = "PYOBJECT_FROM_LONG(%sL)" else: function = "PYOBJECT_FROM_LONG(%s)" - init_constants.putln('%s = %s; %s' % ( + init_constants.putln('PYOBJECT_GLOBAL_STORE(%s, %s); %s' % ( cname, function % value_code, init_constants.error_goto_if_null_object(cname, self.module_pos))) @@ -2135,6 +2140,7 @@ def put_var_declaration(self, entry, storage_class="", self.put(entry.type.cpp_optional_declaration_code( entry.cname, dll_linkage=dll_linkage)) else: + entry.type.is_global = True self.put(entry.type.declaration_code( entry.cname, dll_linkage=dll_linkage)) if entry.init is not None: @@ -2151,6 +2157,7 @@ def put_temp_declarations(self, func_context): else: decl = type.declaration_code(name) if type.is_pyobject: + type.is_global=False self.putln("%s = API_NULL_VALUE;" % decl) elif type.is_memoryviewslice: self.putln("%s = %s;" % (decl, type.literal_code(type.default_value))) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index ca62eb64a32..3d84842530e 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -31,7 +31,7 @@ from .Nodes import Node, SingleAssignmentNode from . import PyrexTypes from .PyrexTypes import py_object_type, typecast, error_type, \ - unspecified_type + unspecified_type, tuple_builder_type from . import TypeSlots from .Builtin import ( list_type, tuple_type, set_type, dict_type, type_type, @@ -2428,7 +2428,7 @@ def generate_result_code(self, code): code.globalstate.use_utility_code( UtilityCode.load_cached("GetModuleGlobalName", "ObjectHandling.c")) code.putln( - '__Pyx_GetModuleGlobalName(%s, %s);' % ( + '__Pyx_GetModuleGlobalName(%s, PYOBJECT_GLOBAL_LOAD(%s));' % ( self.result(), interned_cname)) if not self.cf_is_null: @@ -2457,7 +2457,7 @@ def generate_result_code(self, code): code.globalstate.use_utility_code( UtilityCode.load_cached("GetModuleGlobalName", "ObjectHandling.c")) code.putln( - '__Pyx_GetModuleGlobalName(%s, %s); %s' % ( + '__Pyx_GetModuleGlobalName(%s, PYOBJECT_GLOBAL_LOAD(%s)); %s' % ( self.result(), interned_cname, code.error_goto_if_null_object(self.result(), self.pos))) @@ -2510,6 +2510,7 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, assert entry.type.is_pyobject, "Python global or builtin not a Python object" interned_cname = code.intern_identifier(self.entry.name) namespace = self.entry.scope.namespace_cname + rhs_result = rhs.py_result() if entry.is_member: # if the entry is a member we have to cheat: SetAttr does not work # on types, so we create a descriptor which is then added to tp_dict. @@ -2518,6 +2519,8 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, setter = 'DICT_SET_ITEM' namespace = Naming.moddict_cname interned_cname = interned_cname + if not rhs_result.startswith("__pyx_t_"): + rhs_result = "PYOBJECT_GLOBAL_LOAD(" + rhs_result + ")" #temp fix for globals and non-globals being handled by the same code elif entry.is_pyclass_attr: # Special-case setting __new__ n = "SetNewInClass" if self.name == "__new__" else "SetNameInClass" @@ -2531,7 +2534,7 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, setter, namespace, interned_cname, - rhs.py_result())) + rhs_result)) if debug_disposal_code: print("NameNode.generate_assignment_code:") print("...generating disposal code for %s" % rhs) @@ -8176,10 +8179,10 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): if self.type is tuple_type and (self.is_literal or self.slow) and not c_mult: # use PyTuple_Pack() to avoid generating huge amounts of one-time code - code.putln('%s = TUPLE_PACK(%d, %s); %s' % ( + code.putln('PYOBJECT_GLOBAL_STORE(%s, TUPLE_PACK(%d, PYOBJECT_GLOBAL_LOAD(%s))); %s' % ( target, len(self.args), - ', '.join(arg.py_result() for arg in self.args), + '), PYOBJECT_GLOBAL_LOAD('.join(arg.py_result() for arg in self.args), code.error_goto_if_null_object(target, self.pos))) code.put_gotref(target, py_object_type) elif self.type.is_ctuple: @@ -8191,13 +8194,17 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): if self.type is list_type: create_func, set_item_func = 'PyList_New', '__Pyx_PyList_SET_ITEM' elif self.type is tuple_type: + builder_type = tuple_builder_type create_func, set_item_func = 'TUPLE_CREATE_START', 'TUPLE_CREATE_ASSIGN' + build_func = 'TUPLE_CREATE_FINALISE' else: raise InternalError("sequence packing for unexpected type %s" % self.type) + if self.type is tuple_type: + tmp_builder = code.funcstate.allocate_temp(tuple_builder_type, manage_ref=False) arg_count = len(self.args) if self.type is tuple_type: - code.putln("%s(%s,%s%s); %s" % ( - create_func, target, arg_count, size_factor, + code.putln("%s(%s,%s,%s%s); %s" % ( + create_func, target, tmp_builder, arg_count, size_factor, code.error_goto_if_null_object(target, self.pos))) else: code.putln("%s = %s(%s%s); %s" % ( @@ -8228,15 +8235,25 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): if c_mult or not arg.result_in_temp(): code.put_incref(arg.result(), arg.ctype()) arg.generate_giveref(code) - code.putln("if (%s(%s, %s, %s)) %s;" % ( - set_item_func, - target, - (offset and i) and ('%s + %s' % (offset, i)) or (offset or i), - arg.py_result(), - code.error_goto(self.pos))) + if self.type is tuple_type: + code.putln("if (%s(%s, %s, %s, %s)) %s;" % ( + set_item_func, + target, + tmp_builder, + (offset and i) and ('%s + %s' % (offset, i)) or (offset or i), + arg.py_result(), + code.error_goto(self.pos))) + else: + code.putln("if (%s(%s, %s, %s)) %s;" % ( + set_item_func, + target, + (offset and i) and ('%s + %s' % (offset, i)) or (offset or i), + arg.py_result(), + code.error_goto(self.pos))) if self.type is tuple_type: - code.putln("TUPLE_CREATE_FINALISE(%s)" % target) + code.putln("%s(%s, %s);" % (build_func, target, tmp_builder)) + code.funcstate.release_temp(tmp_builder) if c_mult: code.putln('}') diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index d951ff36aa3..a5cde0048e6 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -1239,6 +1239,7 @@ class PyObjectType(PyrexType): name = "object" is_pyobject = 1 + is_global = False default_value = "API_DEFAULT_VALUE" declaration_value = "API_DEFAULT_VALUE" buffer_defaults = None @@ -1279,6 +1280,9 @@ def declaration_code(self, entity_code, for_display = 0, dll_linkage = None, pyrex = 0): if pyrex or for_display: base_code = "object" + elif self.is_global: + base_code = public_decl("PYOBJECT_GLOBAL_TYPE", dll_linkage) + entity_code = "%s" % entity_code else: base_code = public_decl("PYOBJECT_TYPE", dll_linkage) entity_code = "%s" % entity_code @@ -1397,6 +1401,45 @@ def nullcheck_string(self, cname): 'dict', 'list', 'set', 'frozenset', 'tuple', 'type', }) +class TupleBuilderType(PyrexType): + # + # Class for a C tuple builder + # + + name = "tuple_builder" + buffer_defaults = None + is_extern = False + is_subclassed = False + is_gc_simple = False + builtin_trashcan = False # builtin type using trashcan + + def __str__(self): + return "tuple builder" + + def __repr__(self): + return "" + + def default_coerced_ctype(self): + """The default C type that this Python type coerces to, or None.""" + return None + + def assignable_from(self, src_type): + # except for pointers, conversion will be attempted + return False + + def declaration_code(self, entity_code, + for_display = 0, dll_linkage = None, pyrex = 0): + return self.base_declaration_code("TUPLE_BUILDER_TYPE", entity_code) + + def py_type_name(self): + return "object" + + def __lt__(self, other): + """ + Make sure we sort highest, as instance checking on py_type_name + ('object') is always true + """ + return False class BuiltinObjectType(PyObjectType): # objstruct_cname string Name of PyObject struct @@ -4764,6 +4807,7 @@ def specialize_here(self, pos, env, template_values=None): unspecified_type = UnspecifiedType() py_object_type = PyObjectType() +tuple_builder_type = TupleBuilderType() c_void_type = CVoidType() diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index f64d0fbd31f..90eb388b291 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -793,9 +793,10 @@ class __Pyx_FakeReference { #define BYTES_FROM_STR_AND_SIZE(str, size) HPyBytes_FromStringAndSize(HPY_CONTEXT_CNAME, str, size) #define TUPLE_CREATE_EMPTY() HPyTuple_FromArray(HPY_CONTEXT_CNAME, NULL, 0) - #define TUPLE_CREATE_START(builder, size) HPyTupleBuilder temp_builder = HPyTupleBuilder_New(HPY_CONTEXT_CNAME, size) - #define TUPLE_CREATE_ASSIGN(builder, index, item) HPyTupleBuilder_Set(HPY_CONTEXT_CNAME, temp_builder, index, item) - #define TUPLE_CREATE_FINALISE(target) target = HPyTupleBuilder_Build(HPY_CONTEXT_CNAME, temp_builder); + #define TUPLE_BUILDER_TYPE HPyTupleBuilder + #define TUPLE_CREATE_START(target, builder, size) builder = HPyTupleBuilder_New(HPY_CONTEXT_CNAME, size) + #define TUPLE_CREATE_ASSIGN(tuple, builder, index, item) HPyTupleBuilder_Set(HPY_CONTEXT_CNAME, builder, index, item) + #define TUPLE_CREATE_FINALISE(target, builder) target = HPyTupleBuilder_Build(HPY_CONTEXT_CNAME, builder); #define TUPLE_PACK(num_args, ...) HPyTuple_Pack(HPY_CONTEXT_CNAME, num_args, __VA_ARGS__) #else #define HPY_CONTEXT_ONLY_ARG_DEF void @@ -863,9 +864,10 @@ class __Pyx_FakeReference { #define BYTES_FROM_STR_AND_SIZE(str, size) PyBytes_FromStringAndSize(str, size) #define TUPLE_CREATE_EMPTY() PyTuple_New(0) - #define TUPLE_CREATE_START(target, size) target=PyTuple_New(size) - #define TUPLE_CREATE_ASSIGN(tuple, index, item) __Pyx_PyTuple_SET_ITEM(tuple, index, item) - #define TUPLE_CREATE_FINALISE(target) + #define TUPLE_BUILDER_TYPE PyObject * //Not used, just needed to prevent errors + #define TUPLE_CREATE_START(target, builder, size) target=PyTuple_New(size) + #define TUPLE_CREATE_ASSIGN(tuple, builder, index, item) __Pyx_PyTuple_SET_ITEM(tuple, index, item) + #define TUPLE_CREATE_FINALISE(target, null) #define TUPLE_PACK(num_args, ...) PyTuple_Pack(num_args, __VA_ARGS__) #endif diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index adc1e2b07fd..277b6ef0ad3 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1432,8 +1432,8 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx__GetModuleGlobalName(HPY_CONTEXT_FIRST_ } #endif #else - result = PYOBJECT_GET_ITEM($moddict_cname, name); - __PYX_UPDATE_DICT_CACHE($moddict_cname, result, *dict_cached_value, *dict_version) + result = PYOBJECT_GET_ITEM(PYOBJECT_GLOBAL_LOAD($moddict_cname), name); + __PYX_UPDATE_DICT_CACHE(PYOBJECT_GLOBAL_LOAD($moddict_cname), result, *dict_cached_value, *dict_version) if (likely_object(result)) { return __Pyx_hNewRef(result); } From bc3dd23072e4d83023d6771580c16eb82b1b9cca Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Tue, 22 Aug 2023 09:14:46 +0200 Subject: [PATCH 146/286] Fixed issue with incorrect global assignments --- Cython/Compiler/Code.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index b2b8940e220..e8152093f5a 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -2140,7 +2140,7 @@ def put_var_declaration(self, entry, storage_class="", self.put(entry.type.cpp_optional_declaration_code( entry.cname, dll_linkage=dll_linkage)) else: - entry.type.is_global = True + entry.type.is_global = False self.put(entry.type.declaration_code( entry.cname, dll_linkage=dll_linkage)) if entry.init is not None: From 1e70f931ebb5bba0c926c7f559d6aa21485e4115 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 24 Aug 2023 16:54:56 +0200 Subject: [PATCH 147/286] Converted many INCREFs accesible by HPy to Pyx_NewRefs --- Cython/Compiler/Code.py | 4 ++-- Cython/Compiler/ExprNodes.py | 38 +++++++++++++++++++++++--------- Cython/Compiler/ModuleNode.py | 2 +- Cython/Compiler/Nodes.py | 10 ++++----- Cython/Utility/ModuleSetupCode.c | 11 ++++----- Cython/Utility/TypeConversion.c | 2 +- 6 files changed, 41 insertions(+), 26 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index e8152093f5a..cf52cadce4d 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -2311,9 +2311,9 @@ def put_init_to_py_none(self, cname, type, nanny=True): from .PyrexTypes import py_object_type, typecast py_none = typecast(type, py_object_type, "Py_None") if nanny: - self.putln("%s = %s; __Pyx_INCREF(Py_None);" % (cname, py_none)) + self.putln("%s = __Pyx_NewRef(Py_None);" % (cname)) else: - self.putln("%s = %s; Py_INCREF(Py_None);" % (cname, py_none)) + self.putln("%s = Py_NewRef(Py_None);" % (cname)) def put_init_var_to_py_none(self, entry, template = "%s", nanny=True): code = template % entry.cname diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 3d84842530e..f2833c2e866 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -795,7 +795,9 @@ def make_owned_reference(self, code): If the result is in a temp, it is already a new reference. """ if not self.result_in_temp(): + code.putln("#if !CYTHON_USING_HPY") code.put_incref(self.result(), self.ctype()) + code.putln("#endif") def make_owned_memoryviewslice(self, code): """ @@ -3029,7 +3031,11 @@ def generate_result_code(self, code): code.putln("#endif") code.putln("--%s;" % self.counter_cname) # len -> last item else: - code.putln("%s = 0;" % self.counter_cname) + init_value = '0' + code.putln("%s = __Pyx_NewRef(%s);" % ( + self.result(), + self.sequence.py_result())) + code.putln("%s = 0;" % self.counter_cname) if not is_builtin_sequence: self.iter_func_ptr = code.funcstate.allocate_temp(self._func_iternext_type, manage_ref=False) @@ -3085,12 +3091,11 @@ def generate_next_sequence_item(self, test_name, result_name, code): inc_dec = '++' code.putln("#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS") code.putln( - "%s = Py%s_GET_ITEM(%s, %s); __Pyx_INCREF(%s); %s%s; %s" % ( + "%s = __Pyx_NewRef(Py%s_GET_ITEM(%s, %s)); %s%s; %s" % ( result_name, test_name, self.py_result(), self.counter_cname, - result_name, self.counter_cname, inc_dec, # use the error label to avoid C compiler warnings if we only use it below @@ -6573,7 +6578,7 @@ def generate_evaluation_code(self, code): self.function.free_temps(code) self_arg = code.funcstate.allocate_temp(py_object_type, manage_ref=True) - code.putln("%s = NULL;" % self_arg) + code.putln("%s = API_NULL_VALUE;" % self_arg) arg_offset_cname = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False) code.putln("%s = 0;" % arg_offset_cname) @@ -8232,9 +8237,11 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): for i in range(arg_count): arg = self.args[i] + code.putln("#if !CYTHON_USING_HPY") #This icref is only needed in the C API as the SET_ITEM functions steal their references if c_mult or not arg.result_in_temp(): code.put_incref(arg.result(), arg.ctype()) arg.generate_giveref(code) + code.putln("#endif") if self.type is tuple_type: code.putln("if (%s(%s, %s, %s, %s)) %s;" % ( set_item_func, @@ -8378,8 +8385,10 @@ def generate_special_parallel_unpacking_code(self, code, rhs, use_loop): code.putln("%s = Py%s_GET_ITEM(sequence, %d); " % ( item.result(), sequence_types[1], i)) code.putln("}") + code.putln("#if !CYTHON_USING_HPY") for item in self.unpacked_items: code.put_incref(item.result(), item.ctype()) + code.putln("#endif") code.putln("#else") # in non-CPython, use the PySequence protocol (which can fail) @@ -10508,8 +10517,7 @@ def generate_yield_code(self, code): self.generate_sent_value_handling_code(code, Naming.sent_value_cname) if self.result_is_used: self.allocate_temp_result(code) - code.put('%s = %s; ' % (self.result(), Naming.sent_value_cname)) - code.put_incref(self.result(), py_object_type) + code.put('%s = __Pyx_NewRef(%s); ' % (self.result(), Naming.sent_value_cname)) def generate_sent_value_handling_code(self, code, value_cname): code.putln(code.error_goto_if_null(value_cname, self.pos)) @@ -14472,14 +14480,22 @@ def coerce_to_boolean(self, env): def generate_result_code(self, code): #self.arg.generate_evaluation_code(code) # Already done # by generic generate_subexpr_evaluation_code! - code.putln("%s = %s;" % ( - self.result(), self.arg.result_as(self.ctype()))) + if self.use_managed_ref: - if not self.type.is_memoryviewslice: - code.put_incref(self.result(), self.ctype()) - else: + if self.type.is_pyobject: + code.putln("%s = __Pyx_NewRef(%s);" % ( + self.result(), self.arg.result_as(self.ctype()))) + elif not self.type.is_memoryviewslice: + code.putln("%s = %s;" % ( + self.result(), self.arg.result_as(self.ctype()))) + else: + code.putln("%s = %s;" % ( + self.result(), self.arg.result_as(self.ctype()))) code.put_incref_memoryviewslice(self.result(), self.type, have_gil=not self.in_nogil_context) + else: + code.putln("%s = %s;" % ( + self.result(), self.arg.result_as(self.ctype()))) class ProxyNode(CoercionNode): diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 72cf8b5d251..8c9c97aacc9 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -3723,7 +3723,7 @@ def generate_module_creation_code(self, env, code): code.error_goto(self.pos))) if Options.pre_import is not None: code.putln( - '%s = HPY_LEGACY_OBJECT_FROM(__Pyx_PyImport_AddModuleRef("%s")); %s' % ( + '%s = __Pyx_NewRef(HPY_LEGACY_OBJECT_FROM(__Pyx_PyImport_AddModuleRef("%s"))); %s' % ( Naming.preimport_cname, Options.pre_import, code.error_goto_if_null_object(Naming.preimport_cname, self.pos))) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 9da837a66fd..38b936722d0 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -2089,10 +2089,9 @@ def generate_function_definitions(self, env, code): Naming.empty_tuple)) code.putln("if (unlikely(!%s)) {" % Naming.cur_scope_cname) # Scope unconditionally DECREFed on return. - code.putln("%s = %s;" % ( + code.putln("%s = __Pyx_NewRef(%s);" % ( Naming.cur_scope_cname, lenv.scope_class.type.cast_code("Py_None"))) - code.put_incref("Py_None", py_object_type) code.putln(code.error_goto(self.pos)) code.putln("} else {") code.put_gotref(Naming.cur_scope_cname, lenv.scope_class.type) @@ -4003,7 +4002,7 @@ def generate_stararg_copy_code(self, code): code.error_goto_if_null(self.star_arg.entry.cname, self.pos) )) code.put_var_gotref(self.star_arg.entry) - code.put_incref(Naming.self_cname, py_object_type) + code.put_incref(Naming.self_cname, py_object_type) #TODO: convert this to use TupleBuilder - only hide INCREF behind #if then code.put_giveref(Naming.self_cname, py_object_type) code.putln("PyTuple_SET_ITEM(%s, 0, %s);" % ( self.star_arg.entry.cname, Naming.self_cname)) @@ -4258,8 +4257,7 @@ def generate_stararg_init_code(self, max_positional_args, code): # If there are no positional arguments, use the args tuple # directly assert not self.signature.use_fastcall - code.put_incref(Naming.args_cname, py_object_type) - code.putln("%s = %s;" % (self.star_arg.entry.cname, Naming.args_cname)) + code.putln("%s = __Pyx_NewRef(%s);" % (self.star_arg.entry.cname, Naming.args_cname)) else: # It is possible that this is a slice of "negative" length, # as in args[5:3]. That's not a problem, the function below @@ -6873,7 +6871,7 @@ def generate_execution_code(self, code): value.make_owned_reference(code) code.putln("%s = %s;" % ( Naming.retval_cname, - value.result_as(self.return_type))) + value.result_as(self.return_type))) #Need to change this to also use is_global to load globals when needed value.generate_post_assignment_code(code) value.free_temps(code) else: diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 90eb388b291..61235abf783 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -453,6 +453,8 @@ #define CYTHON_USE_TYPE_SPECS 1 #undef CYTHON_COMPILING_IN_LIMITED_API #define CYTHON_COMPILING_IN_LIMITED_API 1 + #undef CYTHON_UNPACK_METHODS + #define CYTHON_UNPACK_METHODS 0 /* We don't use refnanny in HPy since it has the debug mode */ #undef CYTHON_REFNANNY #define CYTHON_REFNANNY 0 @@ -793,6 +795,7 @@ class __Pyx_FakeReference { #define BYTES_FROM_STR_AND_SIZE(str, size) HPyBytes_FromStringAndSize(HPY_CONTEXT_CNAME, str, size) #define TUPLE_CREATE_EMPTY() HPyTuple_FromArray(HPY_CONTEXT_CNAME, NULL, 0) + #define TUPLE_GET_ITEM(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, pos) #define TUPLE_BUILDER_TYPE HPyTupleBuilder #define TUPLE_CREATE_START(target, builder, size) builder = HPyTupleBuilder_New(HPY_CONTEXT_CNAME, size) #define TUPLE_CREATE_ASSIGN(tuple, builder, index, item) HPyTupleBuilder_Set(HPY_CONTEXT_CNAME, builder, index, item) @@ -818,7 +821,7 @@ class __Pyx_FakeReference { #define PYOBJECT_XALLOC(h) Py_XINCREF(h) #define PYOBJECT_DEALLOC(h) Py_DECREF(h) #define PYOBJECT_XDEALLOC(h) Py_XDECREF(h) - #define PYOBJECT_ALLOC_NEWREF(h) (Py_INCREF(h), h) + #define PYOBJECT_ALLOC_NEWREF(h) Py_NewRef(h) #define REFNANNY_DEALLOC(func, h) func(h) #define PYOBJECT_FROM_LONG(i) PyInt_FromLong(i) @@ -864,6 +867,7 @@ class __Pyx_FakeReference { #define BYTES_FROM_STR_AND_SIZE(str, size) PyBytes_FromStringAndSize(str, size) #define TUPLE_CREATE_EMPTY() PyTuple_New(0) + #define TUPLE_GET_ITEM(h, pos) PyTuple_GET_ITEM(HPY_CONTEXT_CNAME, h, pos) #define TUPLE_BUILDER_TYPE PyObject * //Not used, just needed to prevent errors #define TUPLE_CREATE_START(target, builder, size) target=PyTuple_New(size) #define TUPLE_CREATE_ASSIGN(tuple, builder, index, item) __Pyx_PyTuple_SET_ITEM(tuple, index, item) @@ -1811,7 +1815,6 @@ static int __pyx_bisect_code_objects(__Pyx_CodeObjectCacheEntry* entries, int co } static PyCodeObject *__pyx_find_code_object(int code_line) { - PyCodeObject* code_object; int pos; if (unlikely(!code_line) || unlikely(!__pyx_code_cache.entries)) { return NULL; @@ -1820,9 +1823,7 @@ static PyCodeObject *__pyx_find_code_object(int code_line) { if (unlikely(pos >= __pyx_code_cache.count) || unlikely(__pyx_code_cache.entries[pos].code_line != code_line)) { return NULL; } - code_object = __pyx_code_cache.entries[pos].code_object; - Py_INCREF(code_object); - return code_object; + return __Pyx_NewRef(__pyx_code_cache.entries[pos].code_object); } static void __pyx_insert_code_object(int code_line, PyCodeObject* code_object) { diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index b055622c4f3..90a1e13cae2 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -109,7 +109,7 @@ static CYTHON_INLINE size_t __Pyx_Py_UNICODE_strlen(const Py_UNICODE *u) #define __Pyx_PyUnicode_AsUnicode PyUnicode_AsUnicode #define __Pyx_hNewRef(obj) PYOBJECT_ALLOC_NEWREF(obj) //This will be merged into Pyx_NewRef eventually, but #if CYTHON_USING_HPY will make all calls to this macro use HPy, even if it hasn't been ported yet -#define __Pyx_NewRef(obj) (Py_INCREF(obj), obj) +#define __Pyx_NewRef(obj) Py_NewRef(obj) #define __Pyx_Owned_Py_None(b) __Pyx_NewRef(Py_None) static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b); static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*); From 96005a1250016cc14147bb7f73cd99c6be0d16ce Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Tue, 29 Aug 2023 15:10:26 +0200 Subject: [PATCH 148/286] First steps to implement py_object_global_type --- Cython/Compiler/ExprNodes.py | 8 +- Cython/Compiler/Nodes.py | 8 +- Cython/Compiler/PyrexTypes.py | 151 ++++++++++++++++++++++++++++++- Cython/Compiler/Symtab.py | 6 +- Cython/Utility/ModuleSetupCode.c | 12 +++ 5 files changed, 175 insertions(+), 10 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index f2833c2e866..eed208f70b8 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -7,7 +7,7 @@ cython.declare(error=object, warning=object, warn_once=object, InternalError=object, CompileError=object, UtilityCode=object, TempitaUtilityCode=object, StringEncoding=object, operator=object, local_errors=object, report_error=object, - Naming=object, Nodes=object, PyrexTypes=object, py_object_type=object, + Naming=object, Nodes=object, PyrexTypes=object, py_object_type=object, py_object_global_type=object, list_type=object, tuple_type=object, set_type=object, dict_type=object, unicode_type=object, str_type=object, bytes_type=object, type_type=object, Builtin=object, Symtab=object, Utils=object, find_coercion_error=object, @@ -30,7 +30,7 @@ from . import Nodes from .Nodes import Node, SingleAssignmentNode from . import PyrexTypes -from .PyrexTypes import py_object_type, typecast, error_type, \ +from .PyrexTypes import py_object_type, py_object_global_type, typecast, error_type, \ unspecified_type, tuple_builder_type from . import TypeSlots from .Builtin import ( @@ -2038,6 +2038,8 @@ def coerce_to(self, dst_type, env): # C function with a Python equivalent, manufacture a NameNode # referring to the Python builtin. #print "NameNode.coerce_to:", self.name, dst_type ### + if self.type is py_object_global_type and dst_type is py_object_type: + dst_type = py_object_global_type #temp workaround until globals work probably if dst_type is py_object_type: entry = self.entry if entry and entry.is_cfunction: @@ -2150,6 +2152,8 @@ def analyse_as_type(self, env): if type.is_pyobject and type.equivalent_type: type = type.equivalent_type return type + if entry and entry.type is py_object_global_type: + return py_object_global_type if self.name == 'object': # This is normally parsed as "simple C type", but not if we don't parse C types. return py_object_type diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 38b936722d0..2b40498f6dc 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -7,8 +7,8 @@ cython.declare(os=object, copy=object, chain=object, Builtin=object, error=object, warning=object, Naming=object, PyrexTypes=object, - py_object_type=object, ModuleScope=object, LocalScope=object, ClosureScope=object, - StructOrUnionScope=object, PyClassScope=object, + py_object_type=object, py_object_global_type=object, ModuleScope=object, + LocalScope=object, ClosureScope=object, StructOrUnionScope=object, PyClassScope=object, CppClassScope=object, UtilityCode=object, EncodedString=object, error_type=object) @@ -20,7 +20,7 @@ from . import Naming from . import PyrexTypes from . import TypeSlots -from .PyrexTypes import py_object_type, error_type +from .PyrexTypes import py_object_type, py_object_global_type, error_type from .Symtab import (ModuleScope, LocalScope, ClosureScope, PropertyScope, StructOrUnionScope, PyClassScope, CppClassScope, TemplateScope, GeneratorExpressionScope, CppScopedEnumScope, punycodify_name) @@ -6871,7 +6871,7 @@ def generate_execution_code(self, code): value.make_owned_reference(code) code.putln("%s = %s;" % ( Naming.retval_cname, - value.result_as(self.return_type))) #Need to change this to also use is_global to load globals when needed + value.type.load_value(value.result()))) #Need to change this to also use is_global to load globals when needed value.generate_post_assignment_code(code) value.free_temps(code) else: diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index a5cde0048e6..f96f4fc0199 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -67,6 +67,9 @@ def base_declaration_code(self, base_code, entity_code): return "%s %s" % (base_code, entity_code) else: return base_code + + def load_value(self, cname): #Needed to load HPyGlobals + return cname def __deepcopy__(self, memo): """ @@ -234,6 +237,7 @@ class PyrexType(BaseType): # is_pyobject = 0 + is_global = 0 is_unspecified = 0 is_extension_type = 0 is_final_type = 0 @@ -1239,7 +1243,6 @@ class PyObjectType(PyrexType): name = "object" is_pyobject = 1 - is_global = False default_value = "API_DEFAULT_VALUE" declaration_value = "API_DEFAULT_VALUE" buffer_defaults = None @@ -1401,6 +1404,151 @@ def nullcheck_string(self, cname): 'dict', 'list', 'set', 'frozenset', 'tuple', 'type', }) +class PyObjectGlobalType(PyObjectType): + # + # Base class for all Python object types (reference-counted). + # + # buffer_defaults dict or None Default options for bu + + name = "global_object" + is_pyobject = 1 + is_global = 1 + default_value = "API_DEFAULT_VALUE" + declaration_value = "API_DEFAULT_VALUE" + buffer_defaults = None + + is_extern = False + is_subclassed = False + is_gc_simple = False + builtin_trashcan = False # builtin type using trashcan + needs_refcounting = True + rank = 0 + + def __str__(self): + return "Python global object" + + def __repr__(self): + return "" + + def can_coerce_to_pyobject(self, env): + return True + + def can_coerce_from_pyobject(self, env): + return True + + def load_value(self, cname): + return "PYOBJECT_GLOBAL_LOAD(%s)" % cname + + def default_coerced_ctype(self): + """The default C type that this Python type coerces to, or None.""" + return None + + def assignable_from(self, src_type): + # except for pointers, conversion will be attempted + return not src_type.is_ptr or src_type.is_string or src_type.is_pyunicode_ptr + + def declaration_code(self, entity_code, + for_display = 0, dll_linkage = None, pyrex = 0): + if pyrex or for_display: + base_code = "object" + elif self.is_global: + base_code = public_decl("PYOBJECT_GLOBAL_TYPE", dll_linkage) + entity_code = "%s" % entity_code + else: + base_code = public_decl("PYOBJECT_TYPE", dll_linkage) + entity_code = "%s" % entity_code + return self.base_declaration_code(base_code, entity_code) + + def as_pyobject(self, cname): + return "PYOBJECT_GLOBAL_LOAD(%s)" % cname + + def py_type_name(self): + return "global_object" + + def __lt__(self, other): + """ + Make sure we sort highest, as instance checking on py_type_name + ('object') is always true + """ + return False + + def global_init_code(self, entry, code): + pass + + def check_for_null_code(self, cname): + return "PYOBJECT_GLOBAL_LOAD(%s)" % cname + + def generate_incref(self, code, cname, nanny): + if nanny: + code.putln("__Pyx__GLOBAL_INCREF(%s);" % self.as_pyobject(cname)) + else: + code.putln("PYOBJECT_GLOBAL_ALLOC(%s);" % self.as_pyobject(cname)) + + def generate_xincref(self, code, cname, nanny): + if nanny: + code.putln("__Pyx_GLOBAL_XINCREF(%s);" % self.as_pyobject(cname)) + else: + code.putln("PYOJBECT_GLOBAL_XALLOC(%s);" % self.as_pyobject(cname)) + + def generate_decref(self, code, cname, nanny, have_gil): + # have_gil is for the benefit of memoryviewslice - it's ignored here + assert have_gil + self._generate_decref(code, cname, nanny, null_check=False, clear=False) + + def generate_xdecref(self, code, cname, nanny, have_gil): + # in this (and other) PyObjectType functions, have_gil is being + # passed to provide a common interface with MemoryviewSlice. + # It's ignored here + self._generate_decref(code, cname, nanny, null_check=True, + clear=False) + + def generate_decref_clear(self, code, cname, clear_before_decref, nanny, have_gil): + self._generate_decref(code, cname, nanny, null_check=False, + clear=True, clear_before_decref=clear_before_decref) + + def generate_xdecref_clear(self, code, cname, clear_before_decref=False, nanny=True, have_gil=None): + self._generate_decref(code, cname, nanny, null_check=True, + clear=True, clear_before_decref=clear_before_decref) + + def generate_gotref(self, code, cname): + code.putln("__Pyx_GOTREF(%s);" % self.as_pyobject(cname)) + + def generate_xgotref(self, code, cname): + code.putln("__Pyx_XGOTREF(%s);" % self.as_pyobject(cname)) + + def generate_giveref(self, code, cname): + code.putln("__Pyx_GIVEREF(%s);" % self.as_pyobject(cname)) + + def generate_xgiveref(self, code, cname): + code.putln("__Pyx_XGIVEREF(%s);" % self.as_pyobject(cname)) + + def generate_decref_set(self, code, cname, rhs_cname): + code.putln("__Pyx_DECREF_SET(%s, %s);" % (cname, rhs_cname)) + + def generate_xdecref_set(self, code, cname, rhs_cname): + code.putln("__Pyx_XDECREF_SET(%s, %s);" % (cname, rhs_cname)) + + def _generate_decref(self, code, cname, nanny, null_check=False, + clear=False, clear_before_decref=False): + prefix = '__Pyx' if nanny else 'Py' + X = 'X' if null_check else '' + + if clear: + if clear_before_decref: + if not nanny: + X = '' # CPython doesn't have a Py_XCLEAR() + code.putln("%s_%sCLEAR(%s);" % (prefix, X, cname)) + else: + code.putln("REFNANNY_DEALLOC(%s_%sDECREF, %s); %s = API_NULL_VALUE;" % ( + prefix, X, self.as_pyobject(cname), cname)) + else: + code.putln("REFNANNY_DEALLOC(%s_%sDECREF, %s);" % ( + prefix, X, self.as_pyobject(cname))) + + def nullcheck_string(self, cname): + return cname + + class TupleBuilderType(PyrexType): # # Class for a C tuple builder @@ -4807,6 +4955,7 @@ def specialize_here(self, pos, env, template_values=None): unspecified_type = UnspecifiedType() py_object_type = PyObjectType() +py_object_global_type = PyObjectGlobalType() tuple_builder_type = TupleBuilderType() c_void_type = CVoidType() diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index f8706f6268b..24b44e55000 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -14,7 +14,7 @@ from .StringEncoding import EncodedString from . import Options, Naming from . import PyrexTypes -from .PyrexTypes import py_object_type, unspecified_type +from .PyrexTypes import py_object_type, py_object_global_type, unspecified_type from .TypeSlots import ( pyfunction_signature, pymethod_signature, richcmp_special_methods, get_slot_table, get_property_accessor_signature) @@ -1548,7 +1548,7 @@ def declare_var(self, name, type, pos, self._reject_pytyping_modifiers(pos, pytyping_modifiers, ('typing.Optional',)) # let's allow at least this one if not is_cdef: if type is unspecified_type: - type = py_object_type + type = py_object_global_type if not (type.is_pyobject and not type.is_extension_type): raise InternalError( "Non-cdef global variable is not a generic Python object") @@ -1625,7 +1625,7 @@ def declare_cfunction(self, name, type, pos, def declare_global(self, name, pos): entry = self.lookup_here(name) if not entry: - self.declare_var(name, py_object_type, pos) + self.declare_var(name, py_object_global_type, pos) def use_utility_code(self, new_code): if new_code is not None: diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 61235abf783..1715d08b664 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -745,9 +745,13 @@ class __Pyx_FakeReference { #define CAPI_NEEDS_DEREFERENCE #define PYOBJECT_ALLOC(h) HPy_Dup(HPY_CONTEXT_CNAME, h) + #define PYOBJECT_GLOBAL_ALLOC(h) #define PYOBJECT_XALLOC(h) HPy_Dup(HPY_CONTEXT_CNAME, h) + #define PYOBJECT_GLOBAL_XALLOC(h) #define PYOBJECT_DEALLOC(h) HPy_Close(HPY_CONTEXT_CNAME, h) + #define PYOBJECT_GLOBAL_DEALLOC(h) //This one might not be empty, still need to test this to be sure #define PYOBJECT_XDEALLOC(h) HPy_Close(HPY_CONTEXT_CNAME, h) + #define PYOBJECT_GLOBAL_XDEALLOC(h) #define PYOBJECT_ALLOC_NEWREF(h) HPy_Dup(HPY_CONTEXT_CNAME, h) #define REFNANNY_DEALLOC(func, h) PYOBJECT_DEALLOC(h) @@ -818,9 +822,13 @@ class __Pyx_FakeReference { #define CAPI_NEEDS_DEREFERENCE & #define PYOBJECT_ALLOC(h) Py_INCREF(h) + #define PYOBJECT_GLOBAL_ALLOC(h) PYOBJECT_ALLOC(h) #define PYOBJECT_XALLOC(h) Py_XINCREF(h) + #define PYOBJECT_GLOBAL_XALLOC(h) PYOBJECT_XALLOC(h) #define PYOBJECT_DEALLOC(h) Py_DECREF(h) + #define PYOBJECT_GLOBAL_DEALLOC(h) PYOBJECT_DEALLOC(h) #define PYOBJECT_XDEALLOC(h) Py_XDECREF(h) + #define PYOBJECT_GLOBAL_XDEALLOC(h) PYOBJECT_XDEALLOC(h) #define PYOBJECT_ALLOC_NEWREF(h) Py_NewRef(h) #define REFNANNY_DEALLOC(func, h) func(h) @@ -2000,7 +2008,9 @@ static CYTHON_INLINE int __Pyx_Is_Little_Endian(void) #define __Pyx_RefNannyFinishContext() \ __Pyx_RefNanny->FinishContext(&__pyx_refnanny) #define __Pyx_INCREF(r) __Pyx_RefNanny->INCREF(__pyx_refnanny, (PyObject *)(r), (__LINE__)) + #define __Pyx_GLOBAL_INCREF(r) __Pyx_INCREF(r) #define __Pyx_DECREF(r) __Pyx_RefNanny->DECREF(__pyx_refnanny, (PyObject *)(r), (__LINE__)) + #define __Pyx_GLOBAL_DECREF(r) __Pyx_DECREF(r) #define __Pyx_GOTREF(r) __Pyx_RefNanny->GOTREF(__pyx_refnanny, (PyObject *)(r), (__LINE__)) #define __Pyx_GIVEREF(r) __Pyx_RefNanny->GIVEREF(__pyx_refnanny, (PyObject *)(r), (__LINE__)) #define __Pyx_XINCREF(r) do { if((r) == NULL); else {__Pyx_INCREF(r); }} while(0) @@ -2013,7 +2023,9 @@ static CYTHON_INLINE int __Pyx_Is_Little_Endian(void) #define __Pyx_RefNannyFinishContextNogil() #define __Pyx_RefNannyFinishContext() #define __Pyx_INCREF(r) PYOBJECT_ALLOC(r) + #define __Pyx_GLOBAL_INCREF PYOBJECT_GLOBAL_ALLOC(r) #define __Pyx_DECREF(r) PYOBJECT_DEALLOC(r) + #define __Pyx_GLOBAL_DECREF PYOBJECT_GLOBAL_DEALLOC(r) #define __Pyx_GOTREF(r) #define __Pyx_GIVEREF(r) #define __Pyx_XINCREF(r) PYOBJECT_XALLOC(r) From e71b671bf769f6d53b82fc949251a944f7960ad3 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 30 Aug 2023 14:12:55 +0200 Subject: [PATCH 149/286] Introduced global PyrexType to type coercion --- Cython/Compiler/ExprNodes.py | 71 +++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index eed208f70b8..a9705af7a47 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -1017,6 +1017,12 @@ def coerce_to(self, dst_type, env): error(self.pos, msg % tup) + elif dst_type is py_object_global_type: + src = CoerceToGlobalPyObjectNode(src, env) + + elif src.type is py_object_global_type: + src = CoerceFromGlobalPyObjectNode(src, env) + elif dst_type.is_pyobject: # We never need a type check when assigning None to a Python object type. if src.is_none: @@ -2038,8 +2044,6 @@ def coerce_to(self, dst_type, env): # C function with a Python equivalent, manufacture a NameNode # referring to the Python builtin. #print "NameNode.coerce_to:", self.name, dst_type ### - if self.type is py_object_global_type and dst_type is py_object_type: - dst_type = py_object_global_type #temp workaround until globals work probably if dst_type is py_object_type: entry = self.entry if entry and entry.is_cfunction: @@ -13977,6 +13981,69 @@ def generate_result_code(self, code): code )) +class CoerceToGlobalPyObjectNode(CoercionNode): + """ + Coerce an object from a standard PyObject to a global PyObject. This + is needed for HPy, as these are seperate handle types in the HPy API + """ + + type = py_object_global_type + + def __init__(self, arg, env): + CoercionNode.__init__(self, arg) + + def generate_result_code(self, code): + code.putln("PYOBJECT_GLOBAL_STORE(%s, %s);" % (self.result(), self.arg.result())) + + def calculate_result_code(self): + return self.arg.result() + + def generate_post_assignment_code(self, code): + self.arg.generate_post_assignment_code(code) + + def allocate_temp_result(self, code): + pass + + def release_temp_result(self, code): + pass + + def free_temps(self, code): + self.arg.free_temps(code) + + def free_subexpr_temps(self, code): + self.arg.free_subexpr_temps(code) + +class CoerceFromGlobalPyObjectNode(CoercionNode): + """ + Coerce an object from a global PyObject to a standard PyObject. This + is needed for HPy, as these are seperate handle types in the HPy API + """ + + type = py_object_global_type + + def __init__(self, arg, env): + CoercionNode.__init__(self, arg) + + def generate_result_code(self, code): + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (self.result(), self.arg.result())) + + def calculate_result_code(self): + return self.arg.result() + + def generate_post_assignment_code(self, code): + self.arg.generate_post_assignment_code(code) + + def allocate_temp_result(self, code): + pass + + def release_temp_result(self, code): + pass + + def free_temps(self, code): + self.arg.free_temps(code) + + def free_subexpr_temps(self, code): + self.arg.free_subexpr_temps(code) class CastNode(CoercionNode): # Wrap a node in a C type cast. From 606aaacfcd8244c0760515c5fb2d07ce9a3a28be Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 30 Aug 2023 17:53:44 +0200 Subject: [PATCH 150/286] Further progress on making functions work --- Cython/Utility/CythonFunction.c | 54 +++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 428617ea7f8..5640563777e 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -79,11 +79,17 @@ static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void *cfunc) #undef __Pyx_IsSameCFunction #define __Pyx_IsSameCFunction(func, cfunc) __Pyx__IsSameCyOrCFunction(func, cfunc) -static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject* op, PyMethodDef *ml, +#if !CYTHON_USING_HPY +static PYOBJECT_TYPE __Pyx_CyFunction_Init(__pyx_CyFunctionObject* op, PyMethodDef *ml, int flags, PyObject* qualname, PyObject *closure, PyObject *module, PyObject *globals, PyObject* code); +#else +static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF HPy h_op, HPyDef *ml, + int flags, HPy qualname, HPy closure, + HPy module, HPy globals, HPy code); +#endif static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(__pyx_CyFunctionObject* f, PyObject* classobj); static CYTHON_INLINE void *__Pyx_CyFunction_InitDefaults(PyObject *m, @@ -562,6 +568,7 @@ static PyMethodDef __pyx_CyFunction_methods[] = { #define __Pyx_CyFunction_weakreflist(cyfunc) (((PyCFunctionObject*)cyfunc)->m_weakreflist) #endif +#if !CYTHON_USING_HPY //Will combine these later, this is just a temporary measure static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject *op, PyMethodDef *ml, int flags, PyObject* qualname, PyObject *closure, PyObject *module, PyObject* globals, PyObject* code) { #if !CYTHON_COMPILING_IN_LIMITED_API @@ -637,6 +644,43 @@ static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject *op, PyMethodDef * #endif return (PyObject *) op; } +#else +static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF HPy h_op, HPyDef *ml, int flags, HPy qualname, + HPy closure, HPy module, HPy globals, HPy code) { + __pyx_CyFunctionObject* op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_FIRST_ARG_CALL h_op); + PyCFunctionObject *cf = (PyCFunctionObject*) op; + if (unlikely(op == NULL)) + return HPy_NULL; + op->flags = flags; + __Pyx_CyFunction_weakreflist(op) = NULL; + cf->m_ml = HPy_AsPyObject(HPY_CONTEXT_FIRST_ARG_CALL ml); //Find out what to do here + cf->m_self = (PyObject *) op; + op->func_closure = HPy_AsPyObject(HPY_CONTEXT_FIRST_ARG_CALL closure); + cf->m_module = HPy_AsPyObject(HPY_CONTEXT_FIRST_ARG_CALL module); + op->func_dict = NULL; + op->func_name = NULL; + op->func_qualname = HPy_AsPyObject(HPY_CONTEXT_FIRST_ARG_CALL qualname); + op->func_doc = NULL; +#if PY_VERSION_HEX < 0x030900B1 + op->func_classobj = NULL; +#else + ((PyCMethodObject*)op)->mm_class = NULL; +#endif + op->func_globals = HPy_AsPyObject(HPY_CONTEXT_FIRST_ARG_CALL globals); + Py_INCREF(op->func_globals); + op->func_code = HPy_AsPyObject(HPY_CONTEXT_FIRST_ARG_CALL code); + // Dynamic Default args + op->defaults_pyobjects = 0; + op->defaults_size = 0; + op->defaults = NULL; + op->defaults_tuple = NULL; + op->defaults_kwdict = NULL; + op->defaults_getter = NULL; + op->func_annotations = NULL; + op->func_is_coroutine = NULL; + return HPy_FromPyObject(HPY_CONTEXT_FIRST_ARG_CALL op); +} +#endif static int __Pyx_CyFunction_clear(__pyx_CyFunctionObject *m) @@ -1233,8 +1277,14 @@ static PYOBJECT_TYPE __Pyx_CyFunction_New(HPY_CONTEXT_FIRST_ARG_DEF PYMETHODDEF_ } return op; #else - PYOBJECT_TYPE op = HPy_NULL; + __pyx_CyFunctionObject *func_obj; + HPy __pyx_HPyCyFunctionType = HPy_FromPyObject(HPY_CONTEXT_FIRST_ARG_CALL __pyx_CyFunctionType); + HPy func = HPy_New(HPY_CONTEXT_CNAME, __pyx_HPyCyFunctionType, &func_obj); + HPy op = __Pyx_CyFunction_Init( + HPY_CONTEXT_FIRST_ARG_CALL func, ml, flags, qualname, closure, module, globals, code + ); return op; + #endif } From 191da329360c78ce64ff4161c604b4859ae5e3cd Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 31 Aug 2023 08:23:34 +0200 Subject: [PATCH 151/286] Removed not working global PyrexType code --- Cython/Compiler/ExprNodes.py | 91 ++++---------------- Cython/Compiler/Nodes.py | 6 +- Cython/Compiler/PyrexTypes.py | 153 ---------------------------------- Cython/Compiler/Symtab.py | 6 +- 4 files changed, 21 insertions(+), 235 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index a9705af7a47..7e7ab13304c 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -7,7 +7,7 @@ cython.declare(error=object, warning=object, warn_once=object, InternalError=object, CompileError=object, UtilityCode=object, TempitaUtilityCode=object, StringEncoding=object, operator=object, local_errors=object, report_error=object, - Naming=object, Nodes=object, PyrexTypes=object, py_object_type=object, py_object_global_type=object, + Naming=object, Nodes=object, PyrexTypes=object, py_object_type=object, list_type=object, tuple_type=object, set_type=object, dict_type=object, unicode_type=object, str_type=object, bytes_type=object, type_type=object, Builtin=object, Symtab=object, Utils=object, find_coercion_error=object, @@ -30,7 +30,7 @@ from . import Nodes from .Nodes import Node, SingleAssignmentNode from . import PyrexTypes -from .PyrexTypes import py_object_type, py_object_global_type, typecast, error_type, \ +from .PyrexTypes import py_object_type, typecast, error_type, \ unspecified_type, tuple_builder_type from . import TypeSlots from .Builtin import ( @@ -1017,12 +1017,6 @@ def coerce_to(self, dst_type, env): error(self.pos, msg % tup) - elif dst_type is py_object_global_type: - src = CoerceToGlobalPyObjectNode(src, env) - - elif src.type is py_object_global_type: - src = CoerceFromGlobalPyObjectNode(src, env) - elif dst_type.is_pyobject: # We never need a type check when assigning None to a Python object type. if src.is_none: @@ -1367,6 +1361,17 @@ class IntNode(ConstNode): unsigned = "" longness = "" is_c_literal = None # unknown + is_global = True + + # hex_value and base_10_value are designed only to simplify + # writing tests to get a consistent representation of value + @property + def hex_value(self): + return Utils.strip_py2_long_suffix(hex(Utils.str_to_number(self.value))) + + @property + def base_10_value(self): + return str(Utils.str_to_number(self.value)) # hex_value and base_10_value are designed only to simplify # writing tests to get a consistent representation of value @@ -2156,8 +2161,6 @@ def analyse_as_type(self, env): if type.is_pyobject and type.equivalent_type: type = type.equivalent_type return type - if entry and entry.type is py_object_global_type: - return py_object_global_type if self.name == 'object': # This is normally parsed as "simple C type", but not if we don't parse C types. return py_object_type @@ -10223,11 +10226,11 @@ def generate_result_code(self, code): elif self.def_node.is_generator: flags.append('CO_GENERATOR') - code.putln("%s = HPY_LEGACY_OBJECT_FROM((PyObject*)__Pyx_PyCode_New(%d, %d, %d, %d, 0, %s, HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), \ + code.putln("PYOBJECT_GLOBAL_STORE(%s, HPY_LEGACY_OBJECT_FROM((PyObject*)__Pyx_PyCode_New(%d, %d, %d, %d, 0, %s, HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), \ HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), \ HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), \ HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), \ - HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), %d, HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)))); %s" % ( + HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), %d, HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s))))); %s" % ( self.result_code, len(func.args) - func.num_kwonly_args, # argcount func.num_posonly_args, # posonlyargcount (Py3.8+ only) @@ -13981,70 +13984,6 @@ def generate_result_code(self, code): code )) -class CoerceToGlobalPyObjectNode(CoercionNode): - """ - Coerce an object from a standard PyObject to a global PyObject. This - is needed for HPy, as these are seperate handle types in the HPy API - """ - - type = py_object_global_type - - def __init__(self, arg, env): - CoercionNode.__init__(self, arg) - - def generate_result_code(self, code): - code.putln("PYOBJECT_GLOBAL_STORE(%s, %s);" % (self.result(), self.arg.result())) - - def calculate_result_code(self): - return self.arg.result() - - def generate_post_assignment_code(self, code): - self.arg.generate_post_assignment_code(code) - - def allocate_temp_result(self, code): - pass - - def release_temp_result(self, code): - pass - - def free_temps(self, code): - self.arg.free_temps(code) - - def free_subexpr_temps(self, code): - self.arg.free_subexpr_temps(code) - -class CoerceFromGlobalPyObjectNode(CoercionNode): - """ - Coerce an object from a global PyObject to a standard PyObject. This - is needed for HPy, as these are seperate handle types in the HPy API - """ - - type = py_object_global_type - - def __init__(self, arg, env): - CoercionNode.__init__(self, arg) - - def generate_result_code(self, code): - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (self.result(), self.arg.result())) - - def calculate_result_code(self): - return self.arg.result() - - def generate_post_assignment_code(self, code): - self.arg.generate_post_assignment_code(code) - - def allocate_temp_result(self, code): - pass - - def release_temp_result(self, code): - pass - - def free_temps(self, code): - self.arg.free_temps(code) - - def free_subexpr_temps(self, code): - self.arg.free_subexpr_temps(code) - class CastNode(CoercionNode): # Wrap a node in a C type cast. diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 2b40498f6dc..420204ff74f 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -7,7 +7,7 @@ cython.declare(os=object, copy=object, chain=object, Builtin=object, error=object, warning=object, Naming=object, PyrexTypes=object, - py_object_type=object, py_object_global_type=object, ModuleScope=object, + py_object_type=object, ModuleScope=object, LocalScope=object, ClosureScope=object, StructOrUnionScope=object, PyClassScope=object, CppClassScope=object, UtilityCode=object, EncodedString=object, error_type=object) @@ -20,7 +20,7 @@ from . import Naming from . import PyrexTypes from . import TypeSlots -from .PyrexTypes import py_object_type, py_object_global_type, error_type +from .PyrexTypes import py_object_type, error_type from .Symtab import (ModuleScope, LocalScope, ClosureScope, PropertyScope, StructOrUnionScope, PyClassScope, CppClassScope, TemplateScope, GeneratorExpressionScope, CppScopedEnumScope, punycodify_name) @@ -6871,7 +6871,7 @@ def generate_execution_code(self, code): value.make_owned_reference(code) code.putln("%s = %s;" % ( Naming.retval_cname, - value.type.load_value(value.result()))) #Need to change this to also use is_global to load globals when needed + value.result_as(self.return_type))) value.generate_post_assignment_code(code) value.free_temps(code) else: diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index f96f4fc0199..33cfc5b0aba 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -67,9 +67,6 @@ def base_declaration_code(self, base_code, entity_code): return "%s %s" % (base_code, entity_code) else: return base_code - - def load_value(self, cname): #Needed to load HPyGlobals - return cname def __deepcopy__(self, memo): """ @@ -237,7 +234,6 @@ class PyrexType(BaseType): # is_pyobject = 0 - is_global = 0 is_unspecified = 0 is_extension_type = 0 is_final_type = 0 @@ -1283,9 +1279,6 @@ def declaration_code(self, entity_code, for_display = 0, dll_linkage = None, pyrex = 0): if pyrex or for_display: base_code = "object" - elif self.is_global: - base_code = public_decl("PYOBJECT_GLOBAL_TYPE", dll_linkage) - entity_code = "%s" % entity_code else: base_code = public_decl("PYOBJECT_TYPE", dll_linkage) entity_code = "%s" % entity_code @@ -1404,151 +1397,6 @@ def nullcheck_string(self, cname): 'dict', 'list', 'set', 'frozenset', 'tuple', 'type', }) -class PyObjectGlobalType(PyObjectType): - # - # Base class for all Python object types (reference-counted). - # - # buffer_defaults dict or None Default options for bu - - name = "global_object" - is_pyobject = 1 - is_global = 1 - default_value = "API_DEFAULT_VALUE" - declaration_value = "API_DEFAULT_VALUE" - buffer_defaults = None - - is_extern = False - is_subclassed = False - is_gc_simple = False - builtin_trashcan = False # builtin type using trashcan - needs_refcounting = True - rank = 0 - - def __str__(self): - return "Python global object" - - def __repr__(self): - return "" - - def can_coerce_to_pyobject(self, env): - return True - - def can_coerce_from_pyobject(self, env): - return True - - def load_value(self, cname): - return "PYOBJECT_GLOBAL_LOAD(%s)" % cname - - def default_coerced_ctype(self): - """The default C type that this Python type coerces to, or None.""" - return None - - def assignable_from(self, src_type): - # except for pointers, conversion will be attempted - return not src_type.is_ptr or src_type.is_string or src_type.is_pyunicode_ptr - - def declaration_code(self, entity_code, - for_display = 0, dll_linkage = None, pyrex = 0): - if pyrex or for_display: - base_code = "object" - elif self.is_global: - base_code = public_decl("PYOBJECT_GLOBAL_TYPE", dll_linkage) - entity_code = "%s" % entity_code - else: - base_code = public_decl("PYOBJECT_TYPE", dll_linkage) - entity_code = "%s" % entity_code - return self.base_declaration_code(base_code, entity_code) - - def as_pyobject(self, cname): - return "PYOBJECT_GLOBAL_LOAD(%s)" % cname - - def py_type_name(self): - return "global_object" - - def __lt__(self, other): - """ - Make sure we sort highest, as instance checking on py_type_name - ('object') is always true - """ - return False - - def global_init_code(self, entry, code): - pass - - def check_for_null_code(self, cname): - return "PYOBJECT_GLOBAL_LOAD(%s)" % cname - - def generate_incref(self, code, cname, nanny): - if nanny: - code.putln("__Pyx__GLOBAL_INCREF(%s);" % self.as_pyobject(cname)) - else: - code.putln("PYOBJECT_GLOBAL_ALLOC(%s);" % self.as_pyobject(cname)) - - def generate_xincref(self, code, cname, nanny): - if nanny: - code.putln("__Pyx_GLOBAL_XINCREF(%s);" % self.as_pyobject(cname)) - else: - code.putln("PYOJBECT_GLOBAL_XALLOC(%s);" % self.as_pyobject(cname)) - - def generate_decref(self, code, cname, nanny, have_gil): - # have_gil is for the benefit of memoryviewslice - it's ignored here - assert have_gil - self._generate_decref(code, cname, nanny, null_check=False, clear=False) - - def generate_xdecref(self, code, cname, nanny, have_gil): - # in this (and other) PyObjectType functions, have_gil is being - # passed to provide a common interface with MemoryviewSlice. - # It's ignored here - self._generate_decref(code, cname, nanny, null_check=True, - clear=False) - - def generate_decref_clear(self, code, cname, clear_before_decref, nanny, have_gil): - self._generate_decref(code, cname, nanny, null_check=False, - clear=True, clear_before_decref=clear_before_decref) - - def generate_xdecref_clear(self, code, cname, clear_before_decref=False, nanny=True, have_gil=None): - self._generate_decref(code, cname, nanny, null_check=True, - clear=True, clear_before_decref=clear_before_decref) - - def generate_gotref(self, code, cname): - code.putln("__Pyx_GOTREF(%s);" % self.as_pyobject(cname)) - - def generate_xgotref(self, code, cname): - code.putln("__Pyx_XGOTREF(%s);" % self.as_pyobject(cname)) - - def generate_giveref(self, code, cname): - code.putln("__Pyx_GIVEREF(%s);" % self.as_pyobject(cname)) - - def generate_xgiveref(self, code, cname): - code.putln("__Pyx_XGIVEREF(%s);" % self.as_pyobject(cname)) - - def generate_decref_set(self, code, cname, rhs_cname): - code.putln("__Pyx_DECREF_SET(%s, %s);" % (cname, rhs_cname)) - - def generate_xdecref_set(self, code, cname, rhs_cname): - code.putln("__Pyx_XDECREF_SET(%s, %s);" % (cname, rhs_cname)) - - def _generate_decref(self, code, cname, nanny, null_check=False, - clear=False, clear_before_decref=False): - prefix = '__Pyx' if nanny else 'Py' - X = 'X' if null_check else '' - - if clear: - if clear_before_decref: - if not nanny: - X = '' # CPython doesn't have a Py_XCLEAR() - code.putln("%s_%sCLEAR(%s);" % (prefix, X, cname)) - else: - code.putln("REFNANNY_DEALLOC(%s_%sDECREF, %s); %s = API_NULL_VALUE;" % ( - prefix, X, self.as_pyobject(cname), cname)) - else: - code.putln("REFNANNY_DEALLOC(%s_%sDECREF, %s);" % ( - prefix, X, self.as_pyobject(cname))) - - def nullcheck_string(self, cname): - return cname - - class TupleBuilderType(PyrexType): # # Class for a C tuple builder @@ -4955,7 +4803,6 @@ def specialize_here(self, pos, env, template_values=None): unspecified_type = UnspecifiedType() py_object_type = PyObjectType() -py_object_global_type = PyObjectGlobalType() tuple_builder_type = TupleBuilderType() c_void_type = CVoidType() diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index 24b44e55000..f8706f6268b 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -14,7 +14,7 @@ from .StringEncoding import EncodedString from . import Options, Naming from . import PyrexTypes -from .PyrexTypes import py_object_type, py_object_global_type, unspecified_type +from .PyrexTypes import py_object_type, unspecified_type from .TypeSlots import ( pyfunction_signature, pymethod_signature, richcmp_special_methods, get_slot_table, get_property_accessor_signature) @@ -1548,7 +1548,7 @@ def declare_var(self, name, type, pos, self._reject_pytyping_modifiers(pos, pytyping_modifiers, ('typing.Optional',)) # let's allow at least this one if not is_cdef: if type is unspecified_type: - type = py_object_global_type + type = py_object_type if not (type.is_pyobject and not type.is_extension_type): raise InternalError( "Non-cdef global variable is not a generic Python object") @@ -1625,7 +1625,7 @@ def declare_cfunction(self, name, type, pos, def declare_global(self, name, pos): entry = self.lookup_here(name) if not entry: - self.declare_var(name, py_object_global_type, pos) + self.declare_var(name, py_object_type, pos) def use_utility_code(self, new_code): if new_code is not None: From c4e35b842d4044fde4d1d056b28e0c27db429d48 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 31 Aug 2023 09:25:25 +0200 Subject: [PATCH 152/286] Add back code deleted in last commit --- Cython/Compiler/Nodes.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 420204ff74f..1fefb9395a2 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -6869,9 +6869,14 @@ def generate_execution_code(self, code): value.generate_disposal_code(code) else: value.make_owned_reference(code) - code.putln("%s = %s;" % ( - Naming.retval_cname, - value.result_as(self.return_type))) + if not hasattr(value, "is_global") or not value.is_global: + code.putln("%s = %s;" % ( + Naming.retval_cname, + value.result_as(self.return_type))) + else: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % ( + Naming.retval_cname, + value.result_as(self.return_type))) value.generate_post_assignment_code(code) value.free_temps(code) else: From ffe509644bd983572e51439abf3906fb5d09c8c9 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 31 Aug 2023 16:20:59 +0200 Subject: [PATCH 153/286] Make some globals work properly in function bodies, added field macros --- Cython/Compiler/Code.py | 2 +- Cython/Compiler/ExprNodes.py | 4 ++-- Cython/Utility/ModuleSetupCode.c | 17 +++++------------ 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index cf52cadce4d..a3102d810c5 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1532,7 +1532,7 @@ def generate_object_constant_decls(self): consts.sort() for _, cname, c in consts: c.type.is_global = True - self.parts['module_state'].putln("%s;" % c.type.declaration_code(cname)) + self.parts['module_state'].putln("PYOBJECT_GLOBAL_TYPE %s;" % cname) self.parts['module_state_defines'].putln( "#define %s %s->%s" % (cname, Naming.modulestateglobal_cname, cname)) self.parts['globals_table'].putln("globals_array[%d] = &%s;" % (self.globals_counter, cname)) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 7e7ab13304c..24718565f7e 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -10080,7 +10080,7 @@ def generate_cyfunction_code(self, code): constructor = "__Pyx_CyFunction_New" if self.code_object: - code_object_result = self.code_object.py_result() + code_object_result = self.code_object.result_code else: code_object_result = 'API_NULL_VALUE' @@ -10102,7 +10102,7 @@ def generate_cyfunction_code(self, code): flags = '0' code.putln( - '%s = %s(HPY_CONTEXT_FIRST_ARG_CALL &%s, %s, PYOBJECT_GLOBAL_LOAD(%s), %s, PYOBJECT_GLOBAL_LOAD(%s), PYOBJECT_GLOBAL_LOAD(%s), %s); %s' % ( + '%s = %s(HPY_CONTEXT_FIRST_ARG_CALL &%s, %s, PYOBJECT_GLOBAL_LOAD(%s), %s, PYOBJECT_GLOBAL_LOAD(%s), PYOBJECT_GLOBAL_LOAD(%s), PYOBJECT_GLOBAL_LOAD(%s)); %s' % ( self.result(), constructor, self.pymethdef_cname, diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 1715d08b664..4016f3b0fa8 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -738,6 +738,8 @@ class __Pyx_FakeReference { #define PYOBJECT_TYPE HPy #define PYOBJECT_FIELD_TYPE HPyField + #define PYOBJECT_FIELD_STORE(owner, field, h) HPyGlobal_Store(HPY_CONTEXT_CNAME, owner, &field, h) + #define PYOBJECT_FIELD_LOAD(owner, field) HPyGlobal_Load(HPY_CONTEXT_CNAME, owner, field) #define PYOBJECT_GLOBAL_TYPE HPyGlobal #define PYOBJECT_GLOBAL_STORE(global, h) HPyGlobal_Store(HPY_CONTEXT_CNAME, &global, h) #define PYOBJECT_GLOBAL_LOAD(global) HPyGlobal_Load(HPY_CONTEXT_CNAME, global) @@ -745,13 +747,9 @@ class __Pyx_FakeReference { #define CAPI_NEEDS_DEREFERENCE #define PYOBJECT_ALLOC(h) HPy_Dup(HPY_CONTEXT_CNAME, h) - #define PYOBJECT_GLOBAL_ALLOC(h) #define PYOBJECT_XALLOC(h) HPy_Dup(HPY_CONTEXT_CNAME, h) - #define PYOBJECT_GLOBAL_XALLOC(h) #define PYOBJECT_DEALLOC(h) HPy_Close(HPY_CONTEXT_CNAME, h) - #define PYOBJECT_GLOBAL_DEALLOC(h) //This one might not be empty, still need to test this to be sure #define PYOBJECT_XDEALLOC(h) HPy_Close(HPY_CONTEXT_CNAME, h) - #define PYOBJECT_GLOBAL_XDEALLOC(h) #define PYOBJECT_ALLOC_NEWREF(h) HPy_Dup(HPY_CONTEXT_CNAME, h) #define REFNANNY_DEALLOC(func, h) PYOBJECT_DEALLOC(h) @@ -806,6 +804,7 @@ class __Pyx_FakeReference { #define TUPLE_CREATE_FINALISE(target, builder) target = HPyTupleBuilder_Build(HPY_CONTEXT_CNAME, builder); #define TUPLE_PACK(num_args, ...) HPyTuple_Pack(HPY_CONTEXT_CNAME, num_args, __VA_ARGS__) #else + #define HPY_CONTEXT_CNAME #define HPY_CONTEXT_ONLY_ARG_DEF void #define HPY_CONTEXT_ONLY_ARG_CALL #define HPY_CONTEXT_FIRST_ARG_DEF @@ -815,6 +814,8 @@ class __Pyx_FakeReference { #define PYOBJECT_TYPE PyObject * #define PYOBJECT_FIELD_TYPE PyObject * + #define PYOBJECT_FIELD_STORE(owner, field, h) owner->field = h + #define PYOBJECT_FIELD_LOAD(owner, field) owner->field #define PYOBJECT_GLOBAL_TYPE PyObject * #define PYOBJECT_GLOBAL_STORE(global, h) global = h #define PYOBJECT_GLOBAL_LOAD(global) global @@ -822,13 +823,9 @@ class __Pyx_FakeReference { #define CAPI_NEEDS_DEREFERENCE & #define PYOBJECT_ALLOC(h) Py_INCREF(h) - #define PYOBJECT_GLOBAL_ALLOC(h) PYOBJECT_ALLOC(h) #define PYOBJECT_XALLOC(h) Py_XINCREF(h) - #define PYOBJECT_GLOBAL_XALLOC(h) PYOBJECT_XALLOC(h) #define PYOBJECT_DEALLOC(h) Py_DECREF(h) - #define PYOBJECT_GLOBAL_DEALLOC(h) PYOBJECT_DEALLOC(h) #define PYOBJECT_XDEALLOC(h) Py_XDECREF(h) - #define PYOBJECT_GLOBAL_XDEALLOC(h) PYOBJECT_XDEALLOC(h) #define PYOBJECT_ALLOC_NEWREF(h) Py_NewRef(h) #define REFNANNY_DEALLOC(func, h) func(h) @@ -2008,9 +2005,7 @@ static CYTHON_INLINE int __Pyx_Is_Little_Endian(void) #define __Pyx_RefNannyFinishContext() \ __Pyx_RefNanny->FinishContext(&__pyx_refnanny) #define __Pyx_INCREF(r) __Pyx_RefNanny->INCREF(__pyx_refnanny, (PyObject *)(r), (__LINE__)) - #define __Pyx_GLOBAL_INCREF(r) __Pyx_INCREF(r) #define __Pyx_DECREF(r) __Pyx_RefNanny->DECREF(__pyx_refnanny, (PyObject *)(r), (__LINE__)) - #define __Pyx_GLOBAL_DECREF(r) __Pyx_DECREF(r) #define __Pyx_GOTREF(r) __Pyx_RefNanny->GOTREF(__pyx_refnanny, (PyObject *)(r), (__LINE__)) #define __Pyx_GIVEREF(r) __Pyx_RefNanny->GIVEREF(__pyx_refnanny, (PyObject *)(r), (__LINE__)) #define __Pyx_XINCREF(r) do { if((r) == NULL); else {__Pyx_INCREF(r); }} while(0) @@ -2023,9 +2018,7 @@ static CYTHON_INLINE int __Pyx_Is_Little_Endian(void) #define __Pyx_RefNannyFinishContextNogil() #define __Pyx_RefNannyFinishContext() #define __Pyx_INCREF(r) PYOBJECT_ALLOC(r) - #define __Pyx_GLOBAL_INCREF PYOBJECT_GLOBAL_ALLOC(r) #define __Pyx_DECREF(r) PYOBJECT_DEALLOC(r) - #define __Pyx_GLOBAL_DECREF PYOBJECT_GLOBAL_DEALLOC(r) #define __Pyx_GOTREF(r) #define __Pyx_GIVEREF(r) #define __Pyx_XINCREF(r) PYOBJECT_XALLOC(r) From d3c2963a726ff1e0b3ba692a1f3b36b0316c4608 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 31 Aug 2023 16:58:57 +0200 Subject: [PATCH 154/286] Ported some function structs to be HPy-compatible --- Cython/Utility/CythonFunction.c | 159 ++++++++++++++++---------------- 1 file changed, 79 insertions(+), 80 deletions(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 5640563777e..865cf6b51f4 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -26,8 +26,14 @@ #define __Pyx_CyFunction_SetDefaultsGetter(f, g) \ ((__pyx_CyFunctionObject *) (f))->defaults_getter = (g) +#if !CYTHON_USING_HPY + #define __pyx_CyFunctionObject_FuncDef __pyx_CyFunctionObject * +#else + #define __pyx_CyFunctionObject_FuncDef HPy +#endif typedef struct { +#if !CYTHON_USING_HPY #if CYTHON_COMPILING_IN_LIMITED_API PyObject_HEAD // We can't "inherit" from func, but we can use it as a data store @@ -44,17 +50,25 @@ typedef struct { #if CYTHON_COMPILING_IN_LIMITED_API PyObject *func_weakreflist; #endif - PyObject *func_dict; - PyObject *func_name; - PyObject *func_qualname; - PyObject *func_doc; - PyObject *func_globals; - PyObject *func_code; - PyObject *func_closure; #if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API // No-args super() class cell PyObject *func_classobj; #endif +#if CYTHON_BACKPORT_VECTORCALL + __pyx_vectorcallfunc func_vectorcall; +#endif +#else +#if CYTHON_BACKPORT_VECTORCALL + __pyx_vectorcallfunc HPyCallFunction; +#endif +#endif + PYOBJECT_FIELD_TYPE func_dict; + PYOBJECT_FIELD_TYPE func_name; + PYOBJECT_FIELD_TYPE func_qualname; + PYOBJECT_FIELD_TYPE func_doc; + PYOBJECT_FIELD_TYPE func_globals; + PYOBJECT_FIELD_TYPE func_code; + PYOBJECT_FIELD_TYPE func_closure; // Dynamic default args and annotations void *defaults; int defaults_pyobjects; @@ -62,15 +76,23 @@ typedef struct { int flags; // Defaults info - PyObject *defaults_tuple; /* Const defaults tuple */ - PyObject *defaults_kwdict; /* Const kwonly defaults dict */ + PYOBJECT_FIELD_TYPE defaults_tuple; /* Const defaults tuple */ + PYOBJECT_FIELD_TYPE defaults_kwdict; /* Const kwonly defaults dict */ + #if !CYTHON_USING_HPY PyObject *(*defaults_getter)(PyObject *); - PyObject *func_annotations; /* function annotations dict */ + #else + PYOBJECT_FIELD_TYPE *defaults_getter; + #endif + PYOBJECT_FIELD_TYPE func_annotations; /* function annotations dict */ // Coroutine marker - PyObject *func_is_coroutine; + PYOBJECT_FIELD_TYPE func_is_coroutine; } __pyx_CyFunctionObject; +#if CYTHON_USING_HPY +HPyType_HELPERS(__pyx_CyFunctionObject) +#endif + #undef __Pyx_CyOrPyCFunction_Check #define __Pyx_CyFunction_Check(obj) __Pyx_TypeCheck(obj, __pyx_CyFunctionType) #define __Pyx_CyOrPyCFunction_Check(obj) __Pyx_TypeCheck2(obj, __pyx_CyFunctionType, &PyCFunction_Type) @@ -79,17 +101,11 @@ static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void *cfunc) #undef __Pyx_IsSameCFunction #define __Pyx_IsSameCFunction(func, cfunc) __Pyx__IsSameCyOrCFunction(func, cfunc) -#if !CYTHON_USING_HPY -static PYOBJECT_TYPE __Pyx_CyFunction_Init(__pyx_CyFunctionObject* op, PyMethodDef *ml, - int flags, PyObject* qualname, - PyObject *closure, - PyObject *module, PyObject *globals, - PyObject* code); -#else -static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF HPy h_op, HPyDef *ml, - int flags, HPy qualname, HPy closure, - HPy module, HPy globals, HPy code); -#endif +static PYOBJECT_TYPE __Pyx_CyFunction_Init(__pyx_CyFunctionObject_FuncDef op, PYMETHODDEF_TYPE *ml, + int flags, PYOBJECT_TYPE qualname, + PYOBJECT_TYPE closure, + PYOBJECT_TYPE module, PYOBJECT_TYPE globals, + PYOBJECT_TYPE code); static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(__pyx_CyFunctionObject* f, PyObject* classobj); static CYTHON_INLINE void *__Pyx_CyFunction_InitDefaults(PyObject *m, @@ -153,10 +169,11 @@ static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(__pyx_CyFunctionObject* #endif } -static PyObject * -__Pyx_CyFunction_get_doc(__pyx_CyFunctionObject *op, void *closure) +static PYOBJECT_TYPE +__Pyx_CyFunction_get_doc(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject *op, void *closure) { CYTHON_UNUSED_VAR(closure); +#if !CYTHON_USING_HPY //currently no m_ml in the HPy version of op if (unlikely(op->func_doc == NULL)) { #if CYTHON_COMPILING_IN_LIMITED_API op->func_doc = PyObject_GetAttrString(op->func, "__doc__"); @@ -174,6 +191,10 @@ __Pyx_CyFunction_get_doc(__pyx_CyFunctionObject *op, void *closure) } Py_INCREF(op->func_doc); return op->func_doc; +#else + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject(HPY_CONTEXT_CNAME, op); + return PYOBJECT_FIELD_LOAD(op, struct_op->func_doc); +#endif } static int @@ -568,9 +589,9 @@ static PyMethodDef __pyx_CyFunction_methods[] = { #define __Pyx_CyFunction_weakreflist(cyfunc) (((PyCFunctionObject*)cyfunc)->m_weakreflist) #endif -#if !CYTHON_USING_HPY //Will combine these later, this is just a temporary measure -static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject *op, PyMethodDef *ml, int flags, PyObject* qualname, - PyObject *closure, PyObject *module, PyObject* globals, PyObject* code) { +static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, PYMETHODDEF_TYPE *ml, int flags, PYOBJECT_TYPE qualname, + PYOBJECT_TYPE closure, PYOBJECT_TYPE module, PYOBJECT_TYPE globals, PYOBJECT_TYPE code) { +#if !CYTHON_USING_HPY #if !CYTHON_COMPILING_IN_LIMITED_API PyCFunctionObject *cf = (PyCFunctionObject*) op; #endif @@ -584,18 +605,17 @@ static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject *op, PyMethodDef * #endif op->flags = flags; __Pyx_CyFunction_weakreflist(op) = NULL; -#if !CYTHON_COMPILING_IN_LIMITED_API - cf->m_ml = ml; - cf->m_self = (PyObject *) op; -#endif + __pyx_CyFunctionObject *struct_op = op; Py_XINCREF(closure); op->func_closure = closure; #if !CYTHON_COMPILING_IN_LIMITED_API + cf->m_ml = ml; + cf->m_self = (PyObject *) op; Py_XINCREF(module); cf->m_module = module; #endif - op->func_dict = NULL; - op->func_name = NULL; + __Pyx_CyFunction_weakreflist(op) = NULL; + Py_XINCREF(closure); Py_INCREF(qualname); op->func_qualname = qualname; op->func_doc = NULL; @@ -604,19 +624,31 @@ static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject *op, PyMethodDef * #else ((PyCMethodObject*)op)->mm_class = NULL; #endif - op->func_globals = globals; - Py_INCREF(op->func_globals); Py_XINCREF(code); - op->func_code = code; +#else + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); +#endif + if (unlikely(API_IS_NULL(op))) + return API_NULL_VALUE; + op->flags = flags; + PYOBJECT_FIELD_STORE(op, func_closure, closure); + PYOBJECT_FIELD_STORE(op, func_dict, API_NULL_VALUE); + PYOBJECT_FIELD_STORE(op, func_name, API_NULL_VALUE); + PYOBJECT_FIELD_STORE(op, func_qualname, qualname); + PYOBJECT_FIELD_STORE(op, func_doc, API_NULL_VALUE); + PYOBJECT_FIELD_STORE(op, func_globals, globals); + PYOBJECT_FIELD_STORE(op, func_code,code); // Dynamic Default args op->defaults_pyobjects = 0; op->defaults_size = 0; op->defaults = NULL; - op->defaults_tuple = NULL; - op->defaults_kwdict = NULL; - op->defaults_getter = NULL; - op->func_annotations = NULL; - op->func_is_coroutine = NULL; + PYOBJECT_FIELD_STORE(op, defaults_tuple, API_NULL_VALUE ); + PYOBJECT_FIELD_STORE(op, defaults_kwdict, API_NULL_VALUE ); + PYOBJECT_FIELD_STORE(op, defaults_getter, API_NULL_VALUE ); + PYOBJECT_FIELD_STORE(op, func_annotations, API_NULL_VALUE ); + PYOBJECT_FIELD_STORE(op, func_is_coroutine, API_NULL_VALUE ); +#if !CYTHON_USING_HPY + Py_INCREF(op->func_globals); #if CYTHON_METH_FASTCALL switch (ml->ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS | METH_O | METH_KEYWORDS | METH_METHOD)) { case METH_NOARGS: @@ -643,44 +675,10 @@ static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject *op, PyMethodDef * } #endif return (PyObject *) op; -} -#else -static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF HPy h_op, HPyDef *ml, int flags, HPy qualname, - HPy closure, HPy module, HPy globals, HPy code) { - __pyx_CyFunctionObject* op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_FIRST_ARG_CALL h_op); - PyCFunctionObject *cf = (PyCFunctionObject*) op; - if (unlikely(op == NULL)) - return HPy_NULL; - op->flags = flags; - __Pyx_CyFunction_weakreflist(op) = NULL; - cf->m_ml = HPy_AsPyObject(HPY_CONTEXT_FIRST_ARG_CALL ml); //Find out what to do here - cf->m_self = (PyObject *) op; - op->func_closure = HPy_AsPyObject(HPY_CONTEXT_FIRST_ARG_CALL closure); - cf->m_module = HPy_AsPyObject(HPY_CONTEXT_FIRST_ARG_CALL module); - op->func_dict = NULL; - op->func_name = NULL; - op->func_qualname = HPy_AsPyObject(HPY_CONTEXT_FIRST_ARG_CALL qualname); - op->func_doc = NULL; -#if PY_VERSION_HEX < 0x030900B1 - op->func_classobj = NULL; #else - ((PyCMethodObject*)op)->mm_class = NULL; + return op; #endif - op->func_globals = HPy_AsPyObject(HPY_CONTEXT_FIRST_ARG_CALL globals); - Py_INCREF(op->func_globals); - op->func_code = HPy_AsPyObject(HPY_CONTEXT_FIRST_ARG_CALL code); - // Dynamic Default args - op->defaults_pyobjects = 0; - op->defaults_size = 0; - op->defaults = NULL; - op->defaults_tuple = NULL; - op->defaults_kwdict = NULL; - op->defaults_getter = NULL; - op->func_annotations = NULL; - op->func_is_coroutine = NULL; - return HPy_FromPyObject(HPY_CONTEXT_FIRST_ARG_CALL op); } -#endif static int __Pyx_CyFunction_clear(__pyx_CyFunctionObject *m) @@ -1268,7 +1266,7 @@ static PYOBJECT_TYPE __Pyx_CyFunction_New(HPY_CONTEXT_FIRST_ARG_DEF static PYOBJECT_TYPE __Pyx_CyFunction_New(HPY_CONTEXT_FIRST_ARG_DEF PYMETHODDEF_TYPE *ml, int flags, PYOBJECT_TYPE qualname, PYOBJECT_TYPE closure, PYOBJECT_TYPE module, PYOBJECT_TYPE globals, PYOBJECT_TYPE code) { #if !CYTHON_USING_HPY - PYOBJECT_TYPE op = __Pyx_CyFunction_Init( + PYOBJECT_TYPE op = __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_CALL PyObject_GC_New(__pyx_CyFunctionObject, __pyx_CyFunctionType), ml, flags, qualname, closure, module, globals, code ); @@ -1279,9 +1277,9 @@ static PYOBJECT_TYPE __Pyx_CyFunction_New(HPY_CONTEXT_FIRST_ARG_DEF PYMETHODDEF_ #else __pyx_CyFunctionObject *func_obj; HPy __pyx_HPyCyFunctionType = HPy_FromPyObject(HPY_CONTEXT_FIRST_ARG_CALL __pyx_CyFunctionType); - HPy func = HPy_New(HPY_CONTEXT_CNAME, __pyx_HPyCyFunctionType, &func_obj); - HPy op = __Pyx_CyFunction_Init( - HPY_CONTEXT_FIRST_ARG_CALL func, ml, flags, qualname, closure, module, globals, code + HPy op = HPy_New(HPY_CONTEXT_CNAME, __pyx_HPyCyFunctionType, &func_obj); + PYOBJECT_TYPE op = __Pyx_CyFunction_Init( + HPY_CONTEXT_FIRST_ARG_CALL op, ml, flags, qualname, closure, module, globals, code ); return op; @@ -1346,6 +1344,7 @@ __pyx_FusedFunction_New(PyMethodDef *ml, int flags, PyObject *code) { PyObject *op = __Pyx_CyFunction_Init( + HPY_CONTEXT_FIRST_ARG_CALL // __pyx_CyFunctionObject is correct below since that's the cast that we want. PyObject_GC_New(__pyx_CyFunctionObject, __pyx_FusedFunctionType), ml, flags, qualname, closure, module, globals, code From 73518b40909b47dfaa1aed8623de5d05ca0f660f Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Fri, 1 Sep 2023 13:33:45 +0200 Subject: [PATCH 155/286] Ported CythonFunction getsets --- Cython/Utility/CythonFunction.c | 288 ++++++++++++++++++++----------- Cython/Utility/ModuleSetupCode.c | 16 +- 2 files changed, 203 insertions(+), 101 deletions(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 865cf6b51f4..c112a006885 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -170,7 +170,7 @@ static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(__pyx_CyFunctionObject* } static PYOBJECT_TYPE -__Pyx_CyFunction_get_doc(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject *op, void *closure) +__Pyx_CyFunction_get_doc(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, void *closure) { CYTHON_UNUSED_VAR(closure); #if !CYTHON_USING_HPY //currently no m_ml in the HPy version of op @@ -192,137 +192,188 @@ __Pyx_CyFunction_get_doc(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject *op, v Py_INCREF(op->func_doc); return op->func_doc; #else - __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject(HPY_CONTEXT_CNAME, op); + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); return PYOBJECT_FIELD_LOAD(op, struct_op->func_doc); #endif } static int -__Pyx_CyFunction_set_doc(__pyx_CyFunctionObject *op, PyObject *value, void *context) +__Pyx_CyFunction_set_doc(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, PYOBJECT_TYPE value, void *context) { CYTHON_UNUSED_VAR(context); - if (value == NULL) { + if (API_IS_NULL(value)) { // Mark as deleted - value = Py_None; + value = API_ASSIGN_NONE; } +#if !CYTHON_USING_HPY Py_INCREF(value); __Pyx_Py_XDECREF_SET(op->func_doc, value); +#else + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); + PYOBJECT_FIELD_STORE(op, struct_op->func_doc, value); +#endif return 0; } -static PyObject * -__Pyx_CyFunction_get_name(__pyx_CyFunctionObject *op, void *context) +static PYOBJECT_TYPE +__Pyx_CyFunction_get_name(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, void *context) { CYTHON_UNUSED_VAR(context); - if (unlikely(op->func_name == NULL)) { +#if CYTHON_USING_HPY + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); +#else + __pyx_CyFunctionObject *struct_op = op; + if (unlikely(API_IS_NULL(struct_op->func_name))) { #if CYTHON_COMPILING_IN_LIMITED_API op->func_name = PyObject_GetAttrString(op->func, "__name__"); #else op->func_name = PyUnicode_InternFromString(((PyCFunctionObject*)op)->m_ml->ml_name); #endif /* CYTHON_COMPILING_IN_LIMITED_API */ - if (unlikely(op->func_name == NULL)) - return NULL; + if (unlikely(API_IS_NULL(struct_op->func_name))) + return API_NULL_VALUE; } - Py_INCREF(op->func_name); - return op->func_name; + Py_INCREF(struct_op->func_name); +#endif + return PYOBJECT_FIELD_LOAD(op, struct_op->func_name); } static int -__Pyx_CyFunction_set_name(__pyx_CyFunctionObject *op, PyObject *value, void *context) +__Pyx_CyFunction_set_name(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, PYOBJECT_TYPE value, void *context) { CYTHON_UNUSED_VAR(context); - if (unlikely(value == NULL || !PyUnicode_Check(value))) { + if (unlikely(API_IS_NULL(value) || !PyUnicode_Check(HPY_LEGACY_OBJECT_AS(value)))) { PyErr_SetString(PyExc_TypeError, "__name__ must be set to a string object"); return -1; } +#if !CYTHON_USING_HPY Py_INCREF(value); __Pyx_Py_XDECREF_SET(op->func_name, value); +#else + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); + PYOBJECT_FIELD_STORE(op, struct_op->func_name, value); +#endif return 0; } -static PyObject * -__Pyx_CyFunction_get_qualname(__pyx_CyFunctionObject *op, void *context) +static PYOBJECT_TYPE +__Pyx_CyFunction_get_qualname(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, void *context) { CYTHON_UNUSED_VAR(context); +#if !CYTHON_USING_HPY Py_INCREF(op->func_qualname); return op->func_qualname; +#else + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); + PYOBJECT_FIELD_LOAD(op, struct_op->func_qualname); +#endif } static int -__Pyx_CyFunction_set_qualname(__pyx_CyFunctionObject *op, PyObject *value, void *context) +__Pyx_CyFunction_set_qualname(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, PYOBJECT_TYPE value, void *context) { CYTHON_UNUSED_VAR(context); - if (unlikely(value == NULL || !PyUnicode_Check(value))) { + if (unlikely(value == NULL || !PyUnicode_Check(HPY_LEGACY_OBJECT_AS((value)))) { PyErr_SetString(PyExc_TypeError, "__qualname__ must be set to a string object"); return -1; } +#if !CYTHON_USING_HPY Py_INCREF(value); - __Pyx_Py_XDECREF_SET(op->func_qualname, value); + __Pyx_Py_XDECREF_SET(op->func_name, value); +#else + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); + PYOBJECT_FIELD_STORE(op, struct_op->func_qualname, value); +#endif return 0; } -static PyObject * -__Pyx_CyFunction_get_dict(__pyx_CyFunctionObject *op, void *context) +static PYOBJECT_TYPE +__Pyx_CyFunction_get_dict(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, void *context) { +#if CYTHON_USING_HPY + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); +#else + __pyx_CyFunctionObject *struct_op = op; +#endif CYTHON_UNUSED_VAR(context); - if (unlikely(op->func_dict == NULL)) { - op->func_dict = PyDict_New(); - if (unlikely(op->func_dict == NULL)) - return NULL; + if (unlikely(API_IS_NULL(struct_op->func_dict))) { + PYOBJECT_FIELD_STORE(op, struct_op->func_dict, DICT_NEW()); + if (unlikely(API_IS_NULL(struct_op->func_dict))) + return API_NULL_VALUE; } - Py_INCREF(op->func_dict); - return op->func_dict; +#if !CYTHON_USING_HPY + Py_INCREF(struct_op->func_dict); +#endif + return PYOBJECT_FIELD_LOAD(op, struct_op->func_dict); } static int -__Pyx_CyFunction_set_dict(__pyx_CyFunctionObject *op, PyObject *value, void *context) +__Pyx_CyFunction_set_dict(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, PYOBJECT_TYPE value, void *context) { CYTHON_UNUSED_VAR(context); - if (unlikely(value == NULL)) { + if (unlikely(API_IS_NULL(value))) { PyErr_SetString(PyExc_TypeError, "function's dictionary may not be deleted"); return -1; } - if (unlikely(!PyDict_Check(value))) { + if (unlikely(!DICT_CHECK(value))) { PyErr_SetString(PyExc_TypeError, "setting function's dictionary to a non-dict"); return -1; } +#if !CYTHON_USING_HPY Py_INCREF(value); __Pyx_Py_XDECREF_SET(op->func_dict, value); +#else + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); + PYOBJECT_FIELD_STORE(op, struct_op->func_dict, value); +#endif return 0; } -static PyObject * -__Pyx_CyFunction_get_globals(__pyx_CyFunctionObject *op, void *context) +static PYOBJECT_TYPE +__Pyx_CyFunction_get_globals(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, void *context) { CYTHON_UNUSED_VAR(context); +#if !CYTHON_USING_HPY Py_INCREF(op->func_globals); return op->func_globals; +#else + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); + PYOBJECT_FIELD_LOAD(op, struct_op->func_globals); +#endif } -static PyObject * -__Pyx_CyFunction_get_closure(__pyx_CyFunctionObject *op, void *context) +static PYOBJECT_TYPE +__Pyx_CyFunction_get_closure(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, void *context) { CYTHON_UNUSED_VAR(op); CYTHON_UNUSED_VAR(context); +#if !CYTHON_USING_HPY Py_INCREF(Py_None); - return Py_None; +#endif + return API_ASSIGN_NONE; } -static PyObject * -__Pyx_CyFunction_get_code(__pyx_CyFunctionObject *op, void *context) +static PYOBJECT_TYPE +__Pyx_CyFunction_get_code(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, void *context) { - PyObject* result = (op->func_code) ? op->func_code : Py_None; +#if CYTHON_USING_HPY + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); +#else + __pyx_CyFunctionObject *struct_op = op; +#endif + PYOBJECT_TYPE result = PYOBJECT_FIELD_LOAD(op, struct_op->func_code) ? PYOBJECT_FIELD_LOAD(op, struct_op->func_code) : API_ASSIGN_NONE; CYTHON_UNUSED_VAR(context); +#if !CYTHON_USING_HPY Py_INCREF(result); +#endif return result; } static int -__Pyx_CyFunction_init_defaults(__pyx_CyFunctionObject *op) { +__Pyx_CyFunction_init_defaults(__pyx_CyFunctionObject *op) { //Find out what to do about init_defaults int result = 0; PyObject *res = op->defaults_getter((PyObject *) op); if (unlikely(!res)) @@ -347,110 +398,155 @@ __Pyx_CyFunction_init_defaults(__pyx_CyFunctionObject *op) { } static int -__Pyx_CyFunction_set_defaults(__pyx_CyFunctionObject *op, PyObject* value, void *context) { +__Pyx_CyFunction_set_defaults(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, PYOBJECT_TYPE value, void *context) { CYTHON_UNUSED_VAR(context); - if (!value) { + if (API_IS_NULL(value)) { // del => explicit None to prevent rebuilding - value = Py_None; - } else if (unlikely(value != Py_None && !PyTuple_Check(value))) { + value = API_ASSIGN_NONE; + } else if (unlikely(!API_IS_EQUAL(value, API_NONE_VALUE) && !DICT_CHECK(value))) { PyErr_SetString(PyExc_TypeError, "__defaults__ must be set to a tuple object"); return -1; } PyErr_WarnEx(PyExc_RuntimeWarning, "changes to cyfunction.__defaults__ will not " "currently affect the values used in function calls", 1); +#if !CYTHON_USING_HPY Py_INCREF(value); __Pyx_Py_XDECREF_SET(op->defaults_tuple, value); +#else + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); + PYOBJECT_FIELD_STORE(op, struct_op->defaults_tuple, value); +#endif return 0; } -static PyObject * -__Pyx_CyFunction_get_defaults(__pyx_CyFunctionObject *op, void *context) { - PyObject* result = op->defaults_tuple; +static PYOBJECT_TYPE +__Pyx_CyFunction_get_defaults(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, void *context) { +#if CYTHON_USING_HPY + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); +#else + __pyx_CyFunctionObject *struct_op = op; +#endif + PYOBJECT_TYPE result = PYOBJECT_FIELD_LOAD(op, struct_op->defaults_tuple); CYTHON_UNUSED_VAR(context); - if (unlikely(!result)) { - if (op->defaults_getter) { - if (unlikely(__Pyx_CyFunction_init_defaults(op) < 0)) return NULL; - result = op->defaults_tuple; + if (unlikely(API_IS_NULL(result))) { + if (struct_op->defaults_getter) { +#if !CYTHON_USING_HPY + if (unlikely(__Pyx_CyFunction_init_defaults(op) < 0)) return NULL; //First need to port init_defaults +#endif + result = PYOBJECT_FIELD_LOAD(op, struct_op->defaults_tuple); } else { - result = Py_None; + result = API_ASSIGN_NONE; } } +#if !CYTHON_USING_HPY Py_INCREF(result); +#endif return result; } static int -__Pyx_CyFunction_set_kwdefaults(__pyx_CyFunctionObject *op, PyObject* value, void *context) { +__Pyx_CyFunction_set_kwdefaults(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, PYOBJECT_TYPE value, void *context) { CYTHON_UNUSED_VAR(context); - if (!value) { + if (API_IS_NULL(value)) { // del => explicit None to prevent rebuilding - value = Py_None; - } else if (unlikely(value != Py_None && !PyDict_Check(value))) { + value = API_ASSIGN_NONE; + } else if (unlikely(API_IS_EQUAL(value, API_NONE_VALUE) && !DICT_CHECK(value))) { PyErr_SetString(PyExc_TypeError, "__kwdefaults__ must be set to a dict object"); return -1; } PyErr_WarnEx(PyExc_RuntimeWarning, "changes to cyfunction.__kwdefaults__ will not " "currently affect the values used in function calls", 1); +#if !CYTHON_USING_HPY Py_INCREF(value); __Pyx_Py_XDECREF_SET(op->defaults_kwdict, value); +#else + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); + PYOBJECT_FIELD_STORE(op, struct_op->defaults_kwdict, value); +#endif return 0; } -static PyObject * -__Pyx_CyFunction_get_kwdefaults(__pyx_CyFunctionObject *op, void *context) { - PyObject* result = op->defaults_kwdict; +static PYOBJECT_TYPE +__Pyx_CyFunction_get_kwdefaults(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, void *context) { +#if CYTHON_USING_HPY + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); +#else + __pyx_CyFunctionObject *struct_op = op; +#endif + PYOBJECT_TYPE result = PYOBJECT_FIELD_LOAD(op, struct_op->defaults_kwdict); CYTHON_UNUSED_VAR(context); - if (unlikely(!result)) { - if (op->defaults_getter) { + if (unlikely(API_IS_NULL(result))) { + if (struct_op->defaults_getter) { +#if !CYTHON_USING_HPY if (unlikely(__Pyx_CyFunction_init_defaults(op) < 0)) return NULL; - result = op->defaults_kwdict; +#endif + result = PYOBJECT_FIELD_LOAD(op, struct_op->defaults_kwdict); } else { - result = Py_None; + result = API_ASSIGN_NONE; } } +#if !CYTHON_USING_HPY Py_INCREF(result); +#endif return result; } static int -__Pyx_CyFunction_set_annotations(__pyx_CyFunctionObject *op, PyObject* value, void *context) { +__Pyx_CyFunction_set_annotations(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, PYOBJECT_TYPE value, void *context) { CYTHON_UNUSED_VAR(context); - if (!value || value == Py_None) { - value = NULL; - } else if (unlikely(!PyDict_Check(value))) { + if (API_IS_NULL(value) || API_IS_EQUAL(value, API_NONE_VALUE)) { + value = API_NULL_VALUE; + } else if (unlikely(!DICT_CHECK(value))) { PyErr_SetString(PyExc_TypeError, "__annotations__ must be set to a dict object"); return -1; } +#if !CYTHON_USING_HPY Py_XINCREF(value); __Pyx_Py_XDECREF_SET(op->func_annotations, value); +#else + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); + PYOBJECT_FIELD_STORE(op, struct_op->func_annotations, value); +#endif return 0; } -static PyObject * -__Pyx_CyFunction_get_annotations(__pyx_CyFunctionObject *op, void *context) { - PyObject* result = op->func_annotations; +static PYOBJECT_TYPE +__Pyx_CyFunction_get_annotations(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, void *context) { +#if CYTHON_USING_HPY + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); +#else + __pyx_CyFunctionObject *struct_op = op; +#endif + PYOBJECT_TYPE result = PYOBJECT_FIELD_LOAD(op, struct_op->func_annotations); CYTHON_UNUSED_VAR(context); - if (unlikely(!result)) { - result = PyDict_New(); - if (unlikely(!result)) return NULL; - op->func_annotations = result; + if (unlikely(API_IS_NULL(result))) { + result = DICT_NEW(); + if (unlikely(API_IS_NULL(result))) return API_NULL_VALUE; + PYOBJECT_FIELD_STORE(op, struct_op->func_annotations, result); } +#if !CYTHON_USING_HPY Py_INCREF(result); +#endif return result; } static PYOBJECT_TYPE -__Pyx_CyFunction_get_is_coroutine(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject *op, void *context) { +__Pyx_CyFunction_get_is_coroutine(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, void *context) { +#if CYTHON_USING_HPY + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); +#else + __pyx_CyFunctionObject *struct_op = op; +#endif int is_coroutine; CYTHON_UNUSED_VAR(context); - if (op->func_is_coroutine) { - return __Pyx_hNewRef(HPY_LEGACY_OBJECT_FROM(op->func_is_coroutine)); + if (API_IS_NULL(PYOBJECT_FIELD_LOAD(op, struct_op->func_is_coroutine))) { + return __Pyx_hNewRef(PYOBJECT_FIELD_LOAD(op, struct_op->func_is_coroutine)); } - is_coroutine = op->flags & __Pyx_CYFUNCTION_COROUTINE; + is_coroutine = PYOBJECT_FIELD_LOAD(op, struct_op->flags) & __Pyx_CYFUNCTION_COROUTINE; if (is_coroutine) { PyObject *module, *fromlist, *marker = HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(PYIDENT("_is_coroutine"))); fromlist = PyList_New(1); @@ -468,17 +564,17 @@ __Pyx_CyFunction_get_is_coroutine(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObje module = PyImport_ImportModuleLevelObject(HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(PYIDENT("asyncio.coroutines"))), NULL, NULL, fromlist, 0); Py_DECREF(fromlist); if (unlikely(!module)) goto ignore; - op->func_is_coroutine = HPY_LEGACY_OBJECT_AS(__Pyx_PyObject_GetAttrStr(HPY_LEGACY_OBJECT_FROM(module), HPY_LEGACY_OBJECT_FROM(marker))); + PYOBJECT_FIELD_STORE(op, struct_op->func_is_coroutine, __Pyx_PyObject_GetAttrStr(HPY_LEGACY_OBJECT_FROM(module), HPY_LEGACY_OBJECT_FROM(marker))); Py_DECREF(module); - if (likely(op->func_is_coroutine)) { - return __Pyx_hNewRef(HPY_LEGACY_OBJECT_FROM(op->func_is_coroutine)); + if (likely(!API_IS_NULL(PYOBJECT_FIELD_LOAD(op, op->func_is_coroutine)))) { + return __Pyx_hNewRef(HPY_LEGACY_OBJECT_FROM(PYOBJECT_FIELD_LOAD(op, struct_op->func_is_coroutine))); } ignore: PyErr_Clear(); } - op->func_is_coroutine = __Pyx_PyBool_FromLong(is_coroutine); - return __Pyx_hNewRef(HPY_LEGACY_OBJECT_FROM(op->func_is_coroutine)); + PYOBJECT_FIELD_STORE(op, struct_op->func_is_coroutine, __Pyx_PyBool_FromLong(is_coroutine)); + return __Pyx_hNewRef(struct_op->func_is_coroutine); } //static PyObject * @@ -631,22 +727,22 @@ static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFun if (unlikely(API_IS_NULL(op))) return API_NULL_VALUE; op->flags = flags; - PYOBJECT_FIELD_STORE(op, func_closure, closure); - PYOBJECT_FIELD_STORE(op, func_dict, API_NULL_VALUE); - PYOBJECT_FIELD_STORE(op, func_name, API_NULL_VALUE); - PYOBJECT_FIELD_STORE(op, func_qualname, qualname); - PYOBJECT_FIELD_STORE(op, func_doc, API_NULL_VALUE); - PYOBJECT_FIELD_STORE(op, func_globals, globals); - PYOBJECT_FIELD_STORE(op, func_code,code); + PYOBJECT_FIELD_STORE(op, struct_op->func_closure, closure); + PYOBJECT_FIELD_STORE(op, struct_op->func_dict, API_NULL_VALUE); + PYOBJECT_FIELD_STORE(op, struct_op->func_name, API_NULL_VALUE); + PYOBJECT_FIELD_STORE(op, struct_op->func_qualname, qualname); + PYOBJECT_FIELD_STORE(op, struct_op->func_doc, API_NULL_VALUE); + PYOBJECT_FIELD_STORE(op, struct_op->func_globals, globals); + PYOBJECT_FIELD_STORE(op, struct_op->func_code,code); // Dynamic Default args op->defaults_pyobjects = 0; op->defaults_size = 0; op->defaults = NULL; - PYOBJECT_FIELD_STORE(op, defaults_tuple, API_NULL_VALUE ); - PYOBJECT_FIELD_STORE(op, defaults_kwdict, API_NULL_VALUE ); - PYOBJECT_FIELD_STORE(op, defaults_getter, API_NULL_VALUE ); - PYOBJECT_FIELD_STORE(op, func_annotations, API_NULL_VALUE ); - PYOBJECT_FIELD_STORE(op, func_is_coroutine, API_NULL_VALUE ); + PYOBJECT_FIELD_STORE(op, struct_op->defaults_tuple, API_NULL_VALUE ); + PYOBJECT_FIELD_STORE(op, struct_op->defaults_kwdict, API_NULL_VALUE ); + PYOBJECT_FIELD_STORE(op, struct_op->defaults_getter, API_NULL_VALUE ); + PYOBJECT_FIELD_STORE(op, struct_op->func_annotations, API_NULL_VALUE ); + PYOBJECT_FIELD_STORE(op, struct_op->func_is_coroutine, API_NULL_VALUE ); #if !CYTHON_USING_HPY Py_INCREF(op->func_globals); #if CYTHON_METH_FASTCALL @@ -1277,9 +1373,9 @@ static PYOBJECT_TYPE __Pyx_CyFunction_New(HPY_CONTEXT_FIRST_ARG_DEF PYMETHODDEF_ #else __pyx_CyFunctionObject *func_obj; HPy __pyx_HPyCyFunctionType = HPy_FromPyObject(HPY_CONTEXT_FIRST_ARG_CALL __pyx_CyFunctionType); - HPy op = HPy_New(HPY_CONTEXT_CNAME, __pyx_HPyCyFunctionType, &func_obj); + HPy func = HPy_New(HPY_CONTEXT_CNAME, __pyx_HPyCyFunctionType, &func_obj); PYOBJECT_TYPE op = __Pyx_CyFunction_Init( - HPY_CONTEXT_FIRST_ARG_CALL op, ml, flags, qualname, closure, module, globals, code + HPY_CONTEXT_FIRST_ARG_CALL func, ml, flags, qualname, closure, module, globals, code ); return op; diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 4016f3b0fa8..4fe0c4a58fa 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -738,8 +738,8 @@ class __Pyx_FakeReference { #define PYOBJECT_TYPE HPy #define PYOBJECT_FIELD_TYPE HPyField - #define PYOBJECT_FIELD_STORE(owner, field, h) HPyGlobal_Store(HPY_CONTEXT_CNAME, owner, &field, h) - #define PYOBJECT_FIELD_LOAD(owner, field) HPyGlobal_Load(HPY_CONTEXT_CNAME, owner, field) + #define PYOBJECT_FIELD_STORE(owner, field, h) HPyField_Store(HPY_CONTEXT_CNAME, owner, &field, h) + #define PYOBJECT_FIELD_LOAD(owner, field) HPyField_Load(HPY_CONTEXT_CNAME, owner, field) #define PYOBJECT_GLOBAL_TYPE HPyGlobal #define PYOBJECT_GLOBAL_STORE(global, h) HPyGlobal_Store(HPY_CONTEXT_CNAME, &global, h) #define PYOBJECT_GLOBAL_LOAD(global) HPyGlobal_Load(HPY_CONTEXT_CNAME, global) @@ -774,6 +774,8 @@ class __Pyx_FakeReference { #define API_IS_NULL(h) HPy_IsNull(h) #define API_IS_NOT_NULL(h) !HPy_IsNull(h) #define API_IS_EQUAL(a, b) HPy_Is(HPY_CONTEXT_CNAME, a, b) + #define API_NONE_VALUE HPY_CONTEXT_CNAME->h_None + #define API_ASSIGN_NONE HPy_Dup(HPY_CONTEXT_CNAME, HPY_CONTEXT_CNAME->h_None) #define API_TRUE HPY_CONTEXT_CNAME->h_True #define API_FALSE HPY_CONTEXT_CNAME->h_False #define API_IS_TRUE(h) HPy_IsTrue(HPY_CONTEXT_CNAME, h) @@ -784,6 +786,7 @@ class __Pyx_FakeReference { #define DICT_SET_ITEM(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) #define DICT_GET_ITEM_STR(o, attr_name) HPy_GetItem_s(HPY_CONTEXT_CNAME, o, attr_name) #define DICT_SET_ITEM_STR(o, attr_name, attr_val) HPy_SetItem_s(HPY_CONTEXT_CNAME, o, attr_name, attr_val) + #define DICT_CHECK(o) HPyDict_Check(HPY_CONTEXT_CNAME, o) #define PYOBJECT_GET_ITEM(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) #define PYOBJECT_SET_ITEM(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) @@ -814,8 +817,8 @@ class __Pyx_FakeReference { #define PYOBJECT_TYPE PyObject * #define PYOBJECT_FIELD_TYPE PyObject * - #define PYOBJECT_FIELD_STORE(owner, field, h) owner->field = h - #define PYOBJECT_FIELD_LOAD(owner, field) owner->field + #define PYOBJECT_FIELD_STORE(owner, field, h) field = h + #define PYOBJECT_FIELD_LOAD(owner, field) field #define PYOBJECT_GLOBAL_TYPE PyObject * #define PYOBJECT_GLOBAL_STORE(global, h) global = h #define PYOBJECT_GLOBAL_LOAD(global) global @@ -849,16 +852,19 @@ class __Pyx_FakeReference { #define API_IS_NULL(h) !h //Both are here as otherwise we would get !!h for API_IS_NOT_NULL, which is hard to read - but it can be made so if necessary #define API_IS_NOT_NULL(h) h #define API_IS_EQUAL(a, b) a==b + #define API_NONE_VALUE Py_None + #define API_ASSIGN_NONE Py_None #define API_TRUE Py_True #define API_FALSE Py_False #define API_IS_TRUE(h) PyObject_IsTrue(h) #define API_IS_FALSE(h) !PyObject_Not(h) - #define DICT_NEW() + #define DICT_NEW() PyDict_New() #define DICT_GET_ITEM(o, attr_name) PyDict_GetItem(o, attr_name) #define DICT_SET_ITEM(o, attr_name, attr_val) PyDict_SetItem(o, attr_name, attr_val) #define DICT_GET_ITEM_STR(o, attr_name) PyDict_GetItemString(o, attr_name) #define DICT_SET_ITEM_STR(o, attr_name, attr_val) PyDict_SetItemString(o, attr_name, attr_val) + #define DICT_CHECK(o) PyDict_Check(o) #define PYOBJECT_GET_ITEM(o, attr_name) PyObject_GetItem(HPY_CONTEXT_CNAME, o, attr_name) #define PYOBJECT_SET_ITEM(o, attr_name, attr_val) PyObject_SetItem(o, attr_name, attr_val) From 0ed03307f51dc8cc63484baefe51ce784cbe6406 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Fri, 1 Sep 2023 15:39:40 +0200 Subject: [PATCH 156/286] Ported more of CythonFunction, can compile with basic HPy function --- Cython/Utility/CythonFunction.c | 120 ++++++++++++++++++++++++-------- Cython/Utility/TypeConversion.c | 6 +- 2 files changed, 93 insertions(+), 33 deletions(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index c112a006885..79c36fee304 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -80,8 +80,6 @@ typedef struct { PYOBJECT_FIELD_TYPE defaults_kwdict; /* Const kwonly defaults dict */ #if !CYTHON_USING_HPY PyObject *(*defaults_getter)(PyObject *); - #else - PYOBJECT_FIELD_TYPE *defaults_getter; #endif PYOBJECT_FIELD_TYPE func_annotations; /* function annotations dict */ @@ -101,7 +99,8 @@ static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void *cfunc) #undef __Pyx_IsSameCFunction #define __Pyx_IsSameCFunction(func, cfunc) __Pyx__IsSameCyOrCFunction(func, cfunc) -static PYOBJECT_TYPE __Pyx_CyFunction_Init(__pyx_CyFunctionObject_FuncDef op, PYMETHODDEF_TYPE *ml, +static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, + PYMETHODDEF_TYPE *ml, int flags, PYOBJECT_TYPE qualname, PYOBJECT_TYPE closure, PYOBJECT_TYPE module, PYOBJECT_TYPE globals, @@ -111,12 +110,15 @@ static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(__pyx_CyFunctionObject* static CYTHON_INLINE void *__Pyx_CyFunction_InitDefaults(PyObject *m, size_t size, int pyobjects); -static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsTuple(PyObject *m, - PyObject *tuple); -static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsKwDict(PyObject *m, - PyObject *dict); -static CYTHON_INLINE void __Pyx_CyFunction_SetAnnotationsDict(PyObject *m, - PyObject *dict); +static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsTuple(HPY_CONTEXT_FIRST_ARG_DEF + PYOBJECT_TYPE m, + PYOBJECT_TYPE tuple); +static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsKwDict(HPY_CONTEXT_FIRST_ARG_DEF + PYOBJECT_TYPE m, + PYOBJECT_TYPE dict); +static CYTHON_INLINE void __Pyx_CyFunction_SetAnnotationsDict(HPY_CONTEXT_FIRST_ARG_DEF + PYOBJECT_TYPE m, + PYOBJECT_TYPE dict); static int __pyx_CyFunction_init(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_GLOBAL_TYPE module); @@ -364,7 +366,7 @@ __Pyx_CyFunction_get_code(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncD #else __pyx_CyFunctionObject *struct_op = op; #endif - PYOBJECT_TYPE result = PYOBJECT_FIELD_LOAD(op, struct_op->func_code) ? PYOBJECT_FIELD_LOAD(op, struct_op->func_code) : API_ASSIGN_NONE; + PYOBJECT_TYPE result = API_IS_NULL(PYOBJECT_FIELD_LOAD(op, struct_op->func_code)) ? PYOBJECT_FIELD_LOAD(op, struct_op->func_code) : API_ASSIGN_NONE; CYTHON_UNUSED_VAR(context); #if !CYTHON_USING_HPY Py_INCREF(result); @@ -372,6 +374,7 @@ __Pyx_CyFunction_get_code(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncD return result; } +#if !CYTHON_USING_HPY static int __Pyx_CyFunction_init_defaults(__pyx_CyFunctionObject *op) { //Find out what to do about init_defaults int result = 0; @@ -396,6 +399,7 @@ __Pyx_CyFunction_init_defaults(__pyx_CyFunctionObject *op) { //Find out what to Py_DECREF(res); return result; } +#endif static int __Pyx_CyFunction_set_defaults(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, PYOBJECT_TYPE value, void *context) { @@ -430,12 +434,13 @@ __Pyx_CyFunction_get_defaults(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_F PYOBJECT_TYPE result = PYOBJECT_FIELD_LOAD(op, struct_op->defaults_tuple); CYTHON_UNUSED_VAR(context); if (unlikely(API_IS_NULL(result))) { - if (struct_op->defaults_getter) { #if !CYTHON_USING_HPY + if (struct_op->defaults_getter) { if (unlikely(__Pyx_CyFunction_init_defaults(op) < 0)) return NULL; //First need to port init_defaults -#endif result = PYOBJECT_FIELD_LOAD(op, struct_op->defaults_tuple); - } else { + } else +#endif + { result = API_ASSIGN_NONE; } } @@ -478,12 +483,13 @@ __Pyx_CyFunction_get_kwdefaults(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject PYOBJECT_TYPE result = PYOBJECT_FIELD_LOAD(op, struct_op->defaults_kwdict); CYTHON_UNUSED_VAR(context); if (unlikely(API_IS_NULL(result))) { - if (struct_op->defaults_getter) { #if !CYTHON_USING_HPY + if (struct_op->defaults_getter) { if (unlikely(__Pyx_CyFunction_init_defaults(op) < 0)) return NULL; -#endif result = PYOBJECT_FIELD_LOAD(op, struct_op->defaults_kwdict); - } else { + } else +#endif + { result = API_ASSIGN_NONE; } } @@ -566,15 +572,15 @@ __Pyx_CyFunction_get_is_coroutine(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObje if (unlikely(!module)) goto ignore; PYOBJECT_FIELD_STORE(op, struct_op->func_is_coroutine, __Pyx_PyObject_GetAttrStr(HPY_LEGACY_OBJECT_FROM(module), HPY_LEGACY_OBJECT_FROM(marker))); Py_DECREF(module); - if (likely(!API_IS_NULL(PYOBJECT_FIELD_LOAD(op, op->func_is_coroutine)))) { - return __Pyx_hNewRef(HPY_LEGACY_OBJECT_FROM(PYOBJECT_FIELD_LOAD(op, struct_op->func_is_coroutine))); + if (likely(!API_IS_NULL(PYOBJECT_FIELD_LOAD(op, struct_op->func_is_coroutine)))) { + return __Pyx_hNewRef(PYOBJECT_FIELD_LOAD(op, struct_op->func_is_coroutine)); } ignore: PyErr_Clear(); } - PYOBJECT_FIELD_STORE(op, struct_op->func_is_coroutine, __Pyx_PyBool_FromLong(is_coroutine)); - return __Pyx_hNewRef(struct_op->func_is_coroutine); + PYOBJECT_FIELD_STORE(op, struct_op->func_is_coroutine, __Pyx_PyBool_FromLong(HPY_CONTEXT_FIRST_ARG_CALL is_coroutine)); + return __Pyx_hNewRef(PYOBJECT_FIELD_LOAD(op, struct_op->func_is_coroutine)); } //static PyObject * @@ -665,12 +671,17 @@ static PyMemberDef __pyx_CyFunction_members[] = { {0, 0, 0, 0, 0} }; -static PyObject * -__Pyx_CyFunction_reduce(__pyx_CyFunctionObject *m, PyObject *args) +static PYOBJECT_TYPE +__Pyx_CyFunction_reduce(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef m, PYOBJECT_TYPE args) { CYTHON_UNUSED_VAR(args); +#if CYTHON_USING_HPY + __pyx_CyFunctionObject *struct_m = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, m); + return PYOBJECT_FIELD_LOAD(m, struct_m->func_qualname); +#else Py_INCREF(m->func_qualname); return m->func_qualname; +#endif } static PyMethodDef __pyx_CyFunction_methods[] = { @@ -726,7 +737,7 @@ static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFun #endif if (unlikely(API_IS_NULL(op))) return API_NULL_VALUE; - op->flags = flags; + struct_op->flags = flags; PYOBJECT_FIELD_STORE(op, struct_op->func_closure, closure); PYOBJECT_FIELD_STORE(op, struct_op->func_dict, API_NULL_VALUE); PYOBJECT_FIELD_STORE(op, struct_op->func_name, API_NULL_VALUE); @@ -735,12 +746,14 @@ static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFun PYOBJECT_FIELD_STORE(op, struct_op->func_globals, globals); PYOBJECT_FIELD_STORE(op, struct_op->func_code,code); // Dynamic Default args - op->defaults_pyobjects = 0; - op->defaults_size = 0; - op->defaults = NULL; + struct_op->defaults_pyobjects = 0; + struct_op->defaults_size = 0; + struct_op->defaults = NULL; PYOBJECT_FIELD_STORE(op, struct_op->defaults_tuple, API_NULL_VALUE ); PYOBJECT_FIELD_STORE(op, struct_op->defaults_kwdict, API_NULL_VALUE ); +#if !CYTHON_USING_HPY PYOBJECT_FIELD_STORE(op, struct_op->defaults_getter, API_NULL_VALUE ); +#endif PYOBJECT_FIELD_STORE(op, struct_op->func_annotations, API_NULL_VALUE ); PYOBJECT_FIELD_STORE(op, struct_op->func_is_coroutine, API_NULL_VALUE ); #if !CYTHON_USING_HPY @@ -776,6 +789,7 @@ static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFun #endif } +#if !CYTHON_USING_HPY static int __Pyx_CyFunction_clear(__pyx_CyFunctionObject *m) { @@ -834,7 +848,9 @@ static void __Pyx_CyFunction_dealloc(__pyx_CyFunctionObject *m) PyObject_GC_UnTrack(m); __Pyx__CyFunction_dealloc(m); } +#endif +#if !CYTHON_USING_HPY static int __Pyx_CyFunction_traverse(__pyx_CyFunctionObject *m, visitproc visit, void *arg) { Py_VISIT(m->func_closure); @@ -866,6 +882,33 @@ static int __Pyx_CyFunction_traverse(__pyx_CyFunctionObject *m, visitproc visit, return 0; } +#else +static int __Pyx_CyFunction_traverse(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef m, HPyFunc_visitproc visit, void *arg) +{ + __pyx_CyFunctionObject *struct_m = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, m); + HPy_VISIT(&struct_m->func_closure); + HPy_VISIT(&struct_m->func_dict); + HPy_VISIT(&struct_m->func_name); + HPy_VISIT(&struct_m->func_qualname); + HPy_VISIT(&struct_m->func_doc); + HPy_VISIT(&struct_m->func_globals); + HPy_VISIT(&struct_m->func_code); + HPy_VISIT(&struct_m->defaults_tuple); + HPy_VISIT(&struct_m->defaults_kwdict); + HPy_VISIT(&struct_m->func_is_coroutine); + +// Need to port init_defaults first +// if (m->defaults) { +// PyObject **pydefaults = __Pyx_CyFunction_Defaults(PyObject *, m); +// int i; +// +// for (i = 0; i < m->defaults_pyobjects; i++) +// Py_VISIT(pydefaults[i]); +// } + + return 0; +} +#endif static PyObject* __Pyx_CyFunction_repr(__pyx_CyFunctionObject *op) @@ -1190,11 +1233,13 @@ static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD(PyObject #if CYTHON_USE_TYPE_SPECS static PyType_Slot __pyx_CyFunctionType_slots[] = { - {Py_tp_dealloc, (void *)__Pyx_CyFunction_dealloc}, {Py_tp_repr, (void *)__Pyx_CyFunction_repr}, {Py_tp_call, (void *)__Pyx_CyFunction_CallAsMethod}, {Py_tp_traverse, (void *)__Pyx_CyFunction_traverse}, +#if !CYTHON_USING_HPY + {Py_tp_dealloc, (void *)__Pyx_CyFunction_dealloc}, {Py_tp_clear, (void *)__Pyx_CyFunction_clear}, +#endif {Py_tp_methods, (void *)__pyx_CyFunction_methods}, {Py_tp_members, (void *)__pyx_CyFunction_members}, {Py_tp_getset, (void *)__pyx_CyFunction_getsets}, @@ -1328,22 +1373,37 @@ static CYTHON_INLINE void *__Pyx_CyFunction_InitDefaults(PyObject *func, size_t return m->defaults; } -static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsTuple(PyObject *func, PyObject *tuple) { +static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsTuple(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE func, PYOBJECT_TYPE tuple) { +#if !CYTHON_USING_HPY __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func; m->defaults_tuple = tuple; Py_INCREF(tuple); +#else + __pyx_CyFunctionObject *m = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, func); + PYOBJECT_FIELD_STORE(func, m->defaults_tuple, tuple); +#endif } -static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsKwDict(PyObject *func, PyObject *dict) { +static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsKwDict(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE func, PYOBJECT_TYPE dict) { +#if !CYTHON_USING_HPY __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func; m->defaults_kwdict = dict; Py_INCREF(dict); +#else + __pyx_CyFunctionObject *m = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, func); + PYOBJECT_FIELD_STORE(func, m->defaults_kwdict, dict); +#endif } -static CYTHON_INLINE void __Pyx_CyFunction_SetAnnotationsDict(PyObject *func, PyObject *dict) { +static CYTHON_INLINE void __Pyx_CyFunction_SetAnnotationsDict(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE func, PYOBJECT_TYPE dict) { +#if !CYTHON_USING_HPY __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func; m->func_annotations = dict; Py_INCREF(dict); +#else + __pyx_CyFunctionObject *m = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, func); + PYOBJECT_FIELD_STORE(func, m->func_annotations, dict); +#endif } diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index 90a1e13cae2..3d07c52d249 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -111,7 +111,7 @@ static CYTHON_INLINE size_t __Pyx_Py_UNICODE_strlen(const Py_UNICODE *u) #define __Pyx_hNewRef(obj) PYOBJECT_ALLOC_NEWREF(obj) //This will be merged into Pyx_NewRef eventually, but #if CYTHON_USING_HPY will make all calls to this macro use HPy, even if it hasn't been ported yet #define __Pyx_NewRef(obj) Py_NewRef(obj) #define __Pyx_Owned_Py_None(b) __Pyx_NewRef(Py_None) -static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b); +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyBool_FromLong(HPY_CONTEXT_FIRST_ARG_DEF long b); static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*); static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject*); static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(HPY_CONTEXT_FIRST_ARG_DEF PyObject* x); @@ -424,8 +424,8 @@ static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject* o) { } -static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b) { - return b ? __Pyx_NewRef(Py_True) : __Pyx_NewRef(Py_False); +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyBool_FromLong(HPY_CONTEXT_FIRST_ARG_DEF long b) { + return b ? __Pyx_hNewRef(API_TRUE) : __Pyx_hNewRef(API_FALSE); } From 97b42e766349da461015f7ee68c70bc1eded9397 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 4 Sep 2023 15:48:11 +0200 Subject: [PATCH 157/286] Converted more of CythonFunction to be HPy-compatible --- Cython/Utility/CythonFunction.c | 143 ++++++++++++++++++++----------- Cython/Utility/ModuleSetupCode.c | 8 +- 2 files changed, 101 insertions(+), 50 deletions(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 79c36fee304..63156c6e3ce 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -61,6 +61,7 @@ typedef struct { #if CYTHON_BACKPORT_VECTORCALL __pyx_vectorcallfunc HPyCallFunction; #endif + HPyDef *func; #endif PYOBJECT_FIELD_TYPE func_dict; PYOBJECT_FIELD_TYPE func_name; @@ -78,9 +79,7 @@ typedef struct { // Defaults info PYOBJECT_FIELD_TYPE defaults_tuple; /* Const defaults tuple */ PYOBJECT_FIELD_TYPE defaults_kwdict; /* Const kwonly defaults dict */ - #if !CYTHON_USING_HPY - PyObject *(*defaults_getter)(PyObject *); - #endif + PYOBJECT_FIELD_TYPE (*defaults_getter)(PYOBJECT_TYPE); PYOBJECT_FIELD_TYPE func_annotations; /* function annotations dict */ // Coroutine marker @@ -374,32 +373,36 @@ __Pyx_CyFunction_get_code(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncD return result; } -#if !CYTHON_USING_HPY static int -__Pyx_CyFunction_init_defaults(__pyx_CyFunctionObject *op) { //Find out what to do about init_defaults +__Pyx_CyFunction_init_defaults(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op) { +#if CYTHON_USING_HPY + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); + PYOBJECT_TYPE res = PYOBJECT_FIELD_LOAD(op, struct_op->defaults_getter(op)); +#else + __pyx_CyFunctionObject *struct_op = op; + PYOBJECT_TYPE res = struct_op->defaults_getter((PyObject *) op); +#endif int result = 0; - PyObject *res = op->defaults_getter((PyObject *) op); - if (unlikely(!res)) + if (unlikely(API_IS_NULL(res))) return -1; // Cache result - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS + #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS && !CYTHON_USING_HPY op->defaults_tuple = PyTuple_GET_ITEM(res, 0); Py_INCREF(op->defaults_tuple); op->defaults_kwdict = PyTuple_GET_ITEM(res, 1); Py_INCREF(op->defaults_kwdict); #else - op->defaults_tuple = __Pyx_PySequence_ITEM(res, 0); - if (unlikely(!op->defaults_tuple)) result = -1; + PYOBJECT_FIELD_STORE(op, struct_op->defaults_tuple, TUPLE_GET_ITEM(res, 0)); + if (unlikely(API_IS_NULL(PYOBJECT_FIELD_LOAD(op, struct_op->defaults_tuple)))) result = -1; else { - op->defaults_kwdict = __Pyx_PySequence_ITEM(res, 1); - if (unlikely(!op->defaults_kwdict)) result = -1; + PYOBJECT_FIELD_STORE(op, struct_op->defaults_kwdict, TUPLE_GET_ITEM(res, 1)); + if (unlikely(API_IS_NULL(PYOBJECT_FIELD_LOAD(op, struct_op->defaults_kwdict)))) result = -1; } #endif - Py_DECREF(res); + PYOBJECT_DEALLOC(res); return result; } -#endif static int __Pyx_CyFunction_set_defaults(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, PYOBJECT_TYPE value, void *context) { @@ -434,12 +437,10 @@ __Pyx_CyFunction_get_defaults(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_F PYOBJECT_TYPE result = PYOBJECT_FIELD_LOAD(op, struct_op->defaults_tuple); CYTHON_UNUSED_VAR(context); if (unlikely(API_IS_NULL(result))) { -#if !CYTHON_USING_HPY if (struct_op->defaults_getter) { - if (unlikely(__Pyx_CyFunction_init_defaults(op) < 0)) return NULL; //First need to port init_defaults + if (unlikely(__Pyx_CyFunction_init_defaults(HPY_CONTEXT_FIRST_ARG_CALL op) < 0)) return API_NULL_VALUE; //First need to port init_defaults result = PYOBJECT_FIELD_LOAD(op, struct_op->defaults_tuple); - } else -#endif + } else { result = API_ASSIGN_NONE; } @@ -483,12 +484,10 @@ __Pyx_CyFunction_get_kwdefaults(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject PYOBJECT_TYPE result = PYOBJECT_FIELD_LOAD(op, struct_op->defaults_kwdict); CYTHON_UNUSED_VAR(context); if (unlikely(API_IS_NULL(result))) { -#if !CYTHON_USING_HPY if (struct_op->defaults_getter) { - if (unlikely(__Pyx_CyFunction_init_defaults(op) < 0)) return NULL; + if (unlikely(__Pyx_CyFunction_init_defaults(HPY_CONTEXT_FIRST_ARG_CALL op) < 0)) return API_NULL_VALUE; result = PYOBJECT_FIELD_LOAD(op, struct_op->defaults_kwdict); } else -#endif { result = API_ASSIGN_NONE; } @@ -734,6 +733,7 @@ static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFun Py_XINCREF(code); #else __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); + struct_op->func = ml; #endif if (unlikely(API_IS_NULL(op))) return API_NULL_VALUE; @@ -883,19 +883,19 @@ static int __Pyx_CyFunction_traverse(__pyx_CyFunctionObject *m, visitproc visit, return 0; } #else -static int __Pyx_CyFunction_traverse(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef m, HPyFunc_visitproc visit, void *arg) +HPyDef_SLOT(__Pyx_CyFunction_traverse, HPy_tp_traverse) +static int __Pyx_CyFunction_traverse_impl(void *m, HPyFunc_visitproc visit, void *arg) { - __pyx_CyFunctionObject *struct_m = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, m); - HPy_VISIT(&struct_m->func_closure); - HPy_VISIT(&struct_m->func_dict); - HPy_VISIT(&struct_m->func_name); - HPy_VISIT(&struct_m->func_qualname); - HPy_VISIT(&struct_m->func_doc); - HPy_VISIT(&struct_m->func_globals); - HPy_VISIT(&struct_m->func_code); - HPy_VISIT(&struct_m->defaults_tuple); - HPy_VISIT(&struct_m->defaults_kwdict); - HPy_VISIT(&struct_m->func_is_coroutine); + HPy_VISIT(&((__pyx_CyFunctionObject*)m)->func_dict); + HPy_VISIT(&((__pyx_CyFunctionObject*)m)->func_closure); + HPy_VISIT(&((__pyx_CyFunctionObject*)m)->func_name); + HPy_VISIT(&((__pyx_CyFunctionObject*)m)->func_qualname); + HPy_VISIT(&((__pyx_CyFunctionObject*)m)->func_doc); + HPy_VISIT(&((__pyx_CyFunctionObject*)m)->func_globals); + HPy_VISIT(&((__pyx_CyFunctionObject*)m)->func_code); + HPy_VISIT(&((__pyx_CyFunctionObject*)m)->defaults_tuple); + HPy_VISIT(&((__pyx_CyFunctionObject*)m)->defaults_kwdict); + HPy_VISIT(&((__pyx_CyFunctionObject*)m)->func_is_coroutine); // Need to port init_defaults first // if (m->defaults) { @@ -910,14 +910,25 @@ static int __Pyx_CyFunction_traverse(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionO } #endif -static PyObject* -__Pyx_CyFunction_repr(__pyx_CyFunctionObject *op) +#if CYTHON_USING_HPY +HPyDef_SLOT(__Pyx_CyFunction_repr, HPy_tp_repr) +#endif +static PYOBJECT_TYPE +__Pyx_CyFunction_repr_impl(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op) { - return PyUnicode_FromFormat("", - op->func_qualname, (void *)op); +#if CYTHON_USING_HPY + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); + HPy qualname_field = PYOBJECT_FIELD_LOAD(op, struct_op->func_qualname); +#else + __pyx_CyFunctionObject *struct_op = op; + PyObject *qualname_field = struct_op->func_qualname; +#endif + return UNICODE_FROM_FORMAT("", + qualname_field, op); } -static PyObject * __Pyx_CyFunction_CallMethod(PyObject *func, PyObject *self, PyObject *arg, PyObject *kw) { +#if !CYTHON_USING_HPY +static PyObject * __Pyx_CyFunction_CallMethod(HPY_CONTEXT_FIRST_ARG_DEF PyObject *func, PyObject *self, PyObject *arg, PyObject *kw) { // originally copied from PyCFunction_Call() in CPython's Objects/methodobject.c #if CYTHON_COMPILING_IN_LIMITED_API PyObject *f = ((__pyx_CyFunctionObject*)func)->func; @@ -1022,11 +1033,11 @@ static PyObject * __Pyx_CyFunction_CallMethod(PyObject *func, PyObject *self, Py return NULL; } -static CYTHON_INLINE PyObject *__Pyx_CyFunction_Call(PyObject *func, PyObject *arg, PyObject *kw) { +static CYTHON_INLINE PyObject *__Pyx_CyFunction_Call(HPY_CONTEXT_FIRST_ARG_DEF PyObject *func, PyObject *arg, PyObject *kw) { PyObject *self, *result; #if CYTHON_COMPILING_IN_LIMITED_API // PyCFunction_GetSelf returns a borrowed reference - self = PyCFunction_GetSelf(((__pyx_CyFunctionObject*)func)->func); + self = PyCFunction_GetSelf(((__pyx_CyFunctionObject*)func)->HPY_CONTEXT_FIRST_ARG_CALL func); if (unlikely(!self) && PyErr_Occurred()) return NULL; #else self = ((PyCFunctionObject*)func)->m_self; @@ -1035,7 +1046,11 @@ static CYTHON_INLINE PyObject *__Pyx_CyFunction_Call(PyObject *func, PyObject *a return result; } -static PyObject *__Pyx_CyFunction_CallAsMethod(PyObject *func, PyObject *args, PyObject *kw) { +static PYOBJECT_TYPE __Pyx_CyFunction_CallAsMethod(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE func, PYOBJECT_TYPE args, PYOBJECT_TYPE kw) { +#if CYTHON_USING_HPY + return __Pyx_CyFunction_HPyCall_impl(HPY_CONTEXT_FIRST_ARG_CALL func, args, HPy_GetAttr_s(HPY_CONTEXT_FIRST_ARG_CALL args, "size"), kw); + return HPy_NULL; +#endif PyObject *result; __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *) func; @@ -1079,7 +1094,7 @@ static PyObject *__Pyx_CyFunction_CallAsMethod(PyObject *func, PyObject *args, P return NULL; } - result = __Pyx_CyFunction_CallMethod(func, self, new_args, kw); + result = __Pyx_CyFunction_CallMethod(HPY_CONTEXT_FIRST_ARG_CALL func, self, new_args, kw); Py_DECREF(new_args); } else { result = __Pyx_CyFunction_Call(func, args, kw); @@ -1230,13 +1245,35 @@ static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD(PyObject return ((__Pyx_PyCMethod)(void(*)(void))def->ml_meth)(self, cls, args, (size_t)nargs, kwnames); } #endif +#endif + +#if CYTHON_USING_HPY +HPyDef_SLOT(__Pyx_CyFunction_HPyCall, HPy_tp_call) +static HPy __Pyx_CyFunction_HPyCall_impl(HPY_CONTEXT_FIRST_ARG_DEF HPy callable, const HPy *args, size_t nargs, HPy kwnames) +{ + HPy result; + __pyx_CyFunctionObject *cyfunc = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_FIRST_ARG_CALL callable); + +#if CYTHON_METH_FASTCALL + // Prefer vectorcall if available. This is not the typical case, as + // CPython would normally use vectorcall directly instead of tp_call. + __pyx_vectorcallfunc vc = __Pyx_CyFunction_func_vectorcall(cyfunc); +//#if CYTHON_ASSUME_SAFE_MACROS + return HPy_Call(HPY_CONTEXT_FIRST_ARG_CALL callable, args, (size_t)(sizeof(args)), kwnames); +//#else +// // avoid unused function warning +// (void) &__Pyx_PyVectorcall_FastCallDict; +// return PyVectorcall_Call(func, args, kw); +#endif +} +#endif #if CYTHON_USE_TYPE_SPECS static PyType_Slot __pyx_CyFunctionType_slots[] = { - {Py_tp_repr, (void *)__Pyx_CyFunction_repr}, - {Py_tp_call, (void *)__Pyx_CyFunction_CallAsMethod}, - {Py_tp_traverse, (void *)__Pyx_CyFunction_traverse}, #if !CYTHON_USING_HPY + {Py_tp_repr, (void *)__Pyx_CyFunction_repr_impl}, + {Py_tp_call, (void *)__Pyx_CyFunction_CallAsMethod}, + {Py_tp_traverse, (void *)__Pyx_CyFunction_traverse_impl}, {Py_tp_dealloc, (void *)__Pyx_CyFunction_dealloc}, {Py_tp_clear, (void *)__Pyx_CyFunction_clear}, #endif @@ -1247,6 +1284,15 @@ static PyType_Slot __pyx_CyFunctionType_slots[] = { {0, 0}, }; +#if CYTHON_USING_HPY +static HPyDef *__pyx_CyFunctionType_HPyDefines[] = { + &__Pyx_CyFunction_repr, + &__Pyx_CyFunction_HPyCall, + &__Pyx_CyFunction_traverse, + NULL +}; +#endif + #if !CYTHON_USING_HPY static PyType_Spec __pyx_CyFunctionType_spec = { __PYX_TYPE_MODULE_PREFIX "cython_function_or_method", @@ -1266,7 +1312,8 @@ static HPyType_Spec __pyx_CyFunctionType_spec = { .basicsize = sizeof(__pyx_CyFunctionObject), .itemsize = 0, .flags = HPy_TPFLAGS_DEFAULT | HPy_TPFLAGS_HAVE_GC | HPy_TPFLAGS_BASETYPE, /*tp_flags*/ - .legacy_slots = __pyx_CyFunctionType_slots + .legacy_slots = __pyx_CyFunctionType_slots, + .defines = __pyx_CyFunctionType_HPyDefines }; #endif #else /* CYTHON_USE_TYPE_SPECS */ @@ -1287,7 +1334,7 @@ static PyTypeObject __pyx_CyFunctionType_type = { 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_as_async*/ - (reprfunc) __Pyx_CyFunction_repr, /*tp_repr*/ + (reprfunc) __Pyx_CyFunction_repr_impl, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ @@ -1765,7 +1812,7 @@ __pyx_FusedFunction_call(PyObject *func, PyObject *args, PyObject *kw) binding_func->func.defaults_tuple); if (unlikely(!tup)) goto bad; new_func = (__pyx_FusedFunctionObject *) __Pyx_CyFunction_CallMethod( - func, binding_func->__signatures__, tup, NULL); + HPY_CONTEXT_FIRST_ARG_CALL func, binding_func->__signatures__, tup, NULL); } else { tup = PyTuple_Pack(4, binding_func->__signatures__, args, kw == NULL ? Py_None : kw, diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 4fe0c4a58fa..d5d9b743393 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -800,12 +800,14 @@ class __Pyx_FakeReference { #define BYTES_FROM_STR_AND_SIZE(str, size) HPyBytes_FromStringAndSize(HPY_CONTEXT_CNAME, str, size) #define TUPLE_CREATE_EMPTY() HPyTuple_FromArray(HPY_CONTEXT_CNAME, NULL, 0) - #define TUPLE_GET_ITEM(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, pos) + #define TUPLE_GET_ITEM(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, PYOBJECT_FROM_LONG(pos)) #define TUPLE_BUILDER_TYPE HPyTupleBuilder #define TUPLE_CREATE_START(target, builder, size) builder = HPyTupleBuilder_New(HPY_CONTEXT_CNAME, size) #define TUPLE_CREATE_ASSIGN(tuple, builder, index, item) HPyTupleBuilder_Set(HPY_CONTEXT_CNAME, builder, index, item) #define TUPLE_CREATE_FINALISE(target, builder) target = HPyTupleBuilder_Build(HPY_CONTEXT_CNAME, builder); #define TUPLE_PACK(num_args, ...) HPyTuple_Pack(HPY_CONTEXT_CNAME, num_args, __VA_ARGS__) + + #define UNICODE_FROM_FORMAT(format_str, ...) HPyUnicode_FromFormat(HPY_CONTEXT_CNAME, format_str, __VA_ARGS__) #else #define HPY_CONTEXT_CNAME #define HPY_CONTEXT_ONLY_ARG_DEF void @@ -878,13 +880,15 @@ class __Pyx_FakeReference { #define BYTES_FROM_STR_AND_SIZE(str, size) PyBytes_FromStringAndSize(str, size) #define TUPLE_CREATE_EMPTY() PyTuple_New(0) - #define TUPLE_GET_ITEM(h, pos) PyTuple_GET_ITEM(HPY_CONTEXT_CNAME, h, pos) + #define TUPLE_GET_ITEM(h, pos) __Pyx_PySequence_ITEM(HPY_CONTEXT_CNAME, h, pos) #define TUPLE_BUILDER_TYPE PyObject * //Not used, just needed to prevent errors #define TUPLE_CREATE_START(target, builder, size) target=PyTuple_New(size) #define TUPLE_CREATE_ASSIGN(tuple, builder, index, item) __Pyx_PyTuple_SET_ITEM(tuple, index, item) #define TUPLE_CREATE_FINALISE(target, null) #define TUPLE_PACK(num_args, ...) PyTuple_Pack(num_args, __VA_ARGS__) + #define UNICODE_FROM_FORMAT(format_str, ...) PyUnicode_FromFormat(format_str, __VA_ARGS__) + #endif /////////////// PythonCompatibility /////////////// From 0c8ae8b8c0e3073ae00345d8f5432357cca47247 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Tue, 5 Sep 2023 13:15:37 +0200 Subject: [PATCH 158/286] Made GlobalsArray be populated in HPy --- Cython/Compiler/Code.py | 7 +------ Cython/Compiler/ModuleNode.py | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index a3102d810c5..943ec9922e4 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1225,7 +1225,6 @@ def initialize_main_c_code(self): w.enter_cfunc_scope() self.globals_counter = 0 w.putln("") - w.putln("#if CYTHON_USING_HPY") w.putln("static CYTHON_SMALL_CODE int __Pyx_InitGlobalsTable() {") if not Options.generate_cleanup_code: @@ -1311,11 +1310,7 @@ def close_global_decls(self): w.putln("return -1;") w.putln("}") if (part == 'init_constants'): - w.putln("#if CYTHON_USING_HPY") - w.putln("HPyGlobal *globals_array[%s];" % self.globals_counter) - w.putln("#endif") - if (part == 'globals_table'): - w.putln("#endif") + w.putln("PYOBJECT_GLOBAL_TYPE *globals_array[%s];" % self.globals_counter) w.exit_cfunc_scope() if Options.generate_cleanup_code: diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 8c9c97aacc9..7f08689b25d 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -3168,7 +3168,7 @@ def generate_module_init_func(self, imported_modules, env, code): code.put_error_if_neg(self.pos, "__Pyx_InitConstants(HPY_CONTEXT_ONLY_ARG_CALL)") code.putln("stringtab_initialized = 1;") code.put_error_if_neg(self.pos, "__Pyx_InitGlobals()") # calls any utility code - + code.put_error_if_neg(self.pos, "__Pyx_InitGlobalsTable()") code.putln("#if PY_MAJOR_VERSION < 3 && (__PYX_DEFAULT_STRING_ENCODING_IS_ASCII || " "__PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT)") From e2adccf632bf2441464a0ba28be8ef66b0bd60f0 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Tue, 5 Sep 2023 14:07:04 +0200 Subject: [PATCH 159/286] Added noargs test --- tests/run/hpy_basic_noargs.pyx | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/run/hpy_basic_noargs.pyx diff --git a/tests/run/hpy_basic_noargs.pyx b/tests/run/hpy_basic_noargs.pyx new file mode 100644 index 00000000000..7864919af17 --- /dev/null +++ b/tests/run/hpy_basic_noargs.pyx @@ -0,0 +1,8 @@ +#mode: run +#tag: hpy + +import cython + +@cython.hpy +def add(): + return 1+2 \ No newline at end of file From d3160698b0ddd7da62cd03085ac739263ea6e728 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Tue, 5 Sep 2023 15:28:13 +0200 Subject: [PATCH 160/286] Made no_args test work, fixed issue with HPy spec_params --- Cython/Compiler/Code.py | 3 +++ Cython/Compiler/ModuleNode.py | 5 ++--- Cython/Utility/HPyUtils.c | 17 +++++++++++++---- Cython/Utility/ModuleSetupCode.c | 4 ++-- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 943ec9922e4..b514fd49fec 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1233,6 +1233,7 @@ def initialize_main_c_code(self): w = self.parts['cleanup_globals'] w.enter_cfunc_scope() w.putln("") + w.putln("#if !CYTHON_USING_HPY") w.putln("static CYTHON_SMALL_CODE void __Pyx_CleanupGlobals(void) {") code = self.parts['utility_code_proto'] @@ -1316,11 +1317,13 @@ def close_global_decls(self): if Options.generate_cleanup_code: w = self.parts['cleanup_globals'] w.putln("}") + w.putln("#endif") w.exit_cfunc_scope() if Options.generate_cleanup_code: w = self.parts['cleanup_module'] w.putln("}") + w.putln("#endif") w.exit_cfunc_scope() def put_pyobject_decl(self, entry): diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 7f08689b25d..cbdf57dd6d3 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -1328,7 +1328,6 @@ def generate_c_class_declarations(self, env, code, definition, globalstate): module_state = globalstate['module_state'] module_state_defines = globalstate['module_state_defines'] module_state_clear = globalstate['module_state_clear'] - module_state_clear.putln("#if CYTHON_USING_HPY") module_state_traverse = globalstate['module_state_traverse'] module_state_typeobj = module_state.insertion_point() module_state_defines_typeobj = module_state_defines.insertion_point() @@ -1359,7 +1358,6 @@ def generate_c_class_declarations(self, env, code, definition, globalstate): module_state_traverse.putln( "Py_VISIT(traverse_module_state->%s);" % ( entry.type.typeobj_cname)) - module_state_clear.putln("#endif") for writer in [module_state_typeobj, module_state_defines_typeobj]: writer.putln("#endif") @@ -2968,7 +2966,7 @@ def generate_module_state_defines(self, env, code): code.putln('#endif') def generate_module_state_clear(self, env, code): - code.putln("#if CYTHON_USE_MODULE_STATE") + code.putln("#if CYTHON_USE_MODULE_STATE && !CYTHON_USING_HPY") code.putln("static int %s_clear(PyObject *m) {" % Naming.module_cname) code.putln("%s *clear_module_state = %s(m);" % ( Naming.modulestate_cname, @@ -3431,6 +3429,7 @@ def generate_module_cleanup_func(self, env, code): if not Options.generate_cleanup_code: return + code.putln("#if !CYTHON_USING_HPY") code.putln('static void %s(CYTHON_UNUSED PyObject *self) {' % Naming.cleanup_cname) code.enter_cfunc_scope(env) diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index e24b1b5bcfc..415eaaf9ebe 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -19,12 +19,21 @@ HPy_AsPyObjectArray(HPyContext *ctx, HPy *h_arr, HPy_ssize_t n) } return arr; } +#endif -static HPyType_SpecParam* get_temp_specparams(HPyContext *ctx) { +#if CYTHON_USING_HPY +HPy __Pyx_Type_FromSpec(HPyContext *ctx, HPy module, HPyType_Spec *spec, HPy base) +{ HPyType_SpecParam temp_params[] = { - { HPyType_SpecParam_Base, ctx->h_LongType }, - { (HPyType_SpecParam_Kind)0 } + { HPyType_SpecParam_Base, ctx->h_LongType }, + { (HPyType_SpecParam_Kind)0 } }; - return temp_params; + HPy type = HPyType_FromSpec(ctx, spec, temp_params); + return type; +} +#else +static inline PyObject *__Pyx_Type_FromSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) +{ + return PyType_FromModuleAndSpec(module, spec, bases); } #endif \ No newline at end of file diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index d5d9b743393..8d0f59e4ace 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -766,7 +766,7 @@ class __Pyx_FakeReference { #define PYMODULEDEF_TYPE HPyModuleDef #define PYMETHODDEF_TYPE HPyDef #define TYPESPEC_TYPE HPyType_Spec - #define TYPE_FROM_MOD_AND_SPEC(m, s, b) HPyType_FromSpec(HPY_CONTEXT_CNAME, &s, get_temp_specparams(HPY_CONTEXT_CNAME)) + #define TYPE_FROM_MOD_AND_SPEC(m, s, b) __Pyx_Type_FromSpec(HPY_CONTEXT_CNAME, m, &s, b) #define TYPESPEC_GET(s, field) s.field #define API_NULL_VALUE HPy_NULL @@ -846,7 +846,7 @@ class __Pyx_FakeReference { #define PYMODULEDEF_TYPE struct PyModuleDef #define PYMETHODDEF_TYPE PyMethodDef #define TYPESPEC_TYPE PyType_Spec - #define TYPE_FROM_MOD_AND_SPEC(m, s, b) PyType_FromModuleAndSpec(m, s, b) + #define TYPE_FROM_MOD_AND_SPEC(m, s, b) __Pyx_Type_FromSpec(m, s, b) #define TYPESPEC_GET(s, field) s->field #define API_NULL_VALUE NULL From c846365d26c35ec59d49a1424a18af6b177eaad5 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 6 Sep 2023 16:56:29 +0200 Subject: [PATCH 161/286] Renamed macros, starting to fix issue with functions not being callable --- Cython/Compiler/Code.py | 6 +++++- Cython/Compiler/ModuleNode.py | 20 +++++++++++--------- Cython/Compiler/PyrexTypes.py | 8 ++++---- Cython/Utility/CommonStructures.c | 6 +++--- Cython/Utility/CythonFunction.c | 2 +- Cython/Utility/Exceptions.c | 2 +- Cython/Utility/ModuleSetupCode.c | 30 ++++++++++++++---------------- Cython/Utility/ObjectHandling.c | 2 +- Cython/Utility/TypeConversion.c | 2 +- 9 files changed, 41 insertions(+), 37 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index b514fd49fec..c6f9f95bb24 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1223,9 +1223,13 @@ def initialize_main_c_code(self): w = self.parts['globals_table'] w.enter_cfunc_scope() - self.globals_counter = 0 w.putln("") w.putln("static CYTHON_SMALL_CODE int __Pyx_InitGlobalsTable() {") + w.putln("globals_array[0] = &__pyx_d;") + w.putln("globals_array[1] = &__pyx_cython_runtime;") + w.putln("globals_array[2] = &__pyx_empty_tuple;") + w.putln("globals_array[3] = &__pyx_empty_bytes;") + w.putln("globals_array[4] = &__pyx_empty_unicode;") if not Options.generate_cleanup_code: del self.parts['cleanup_globals'] diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index cbdf57dd6d3..d76f2129f73 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -3259,7 +3259,7 @@ def generate_module_init_func(self, imported_modules, env, code): ##code.put_decref_clear(env.module_dict_cname, py_object_type, nanny=False) code.putln('}') code.putln("#if CYTHON_USING_HPY") - code.putln("PYOBJECT_DEALLOC(%s);" % Naming.pymodinit_module_arg) + code.putln("PYOBJECT_CLOSEREF(%s);" % Naming.pymodinit_module_arg) code.putln("#elif !CYTHON_USE_MODULE_STATE") code.put_decref_clear(Naming.pymodinit_module_arg, py_object_type, nanny=False, clear_before_decref=True) code.putln("#else") @@ -3624,10 +3624,9 @@ def generate_pymoduledef_struct(self, env, code): code.putln(" .doc = %s," % doc) code.putln(" .size = 0,") code.putln(" .legacy_methods = %s," % env.method_table_cname) - if env.is_c_class_scope and not env.hpyfunc_entries: - code.putln(" .defines = methods,") - else: - code.putln(" .defines = %s," % env.hpy_defines_cname) + code.putln(" .defines = methods,") + #else: + # code.putln(" .defines = %s," % env.hpy_defines_cname) code.putln(" .globals = globals_array,") code.putln("#endif") code.putln("};") @@ -3693,12 +3692,15 @@ def generate_module_creation_code(self, env, code): code.putln("#endif") # CYTHON_PEP489_MULTI_PHASE_INIT code.putln("CYTHON_UNUSED_VAR(%s);" % module_temp) # only used in limited API + code.putln("PYOBJECT_TYPE temp_moddict = PYOBJECT_NEWREF(PYMODULE_GETDICT_ATTR(%s));" % Naming.pymodinit_module_arg) + code.putln( - "PYOBJECT_GLOBAL_STORE(%s, PYMODULE_GETDICT_ATTR(%s)); %s" % ( - env.module_dict_cname, Naming.pymodinit_module_arg, - code.error_goto_if_null_object("PYMODULE_GETDICT_ATTR(%s)" % Naming.pymodinit_module_arg, self.pos))) + "PYOBJECT_GLOBAL_STORE(%s, temp_moddict); %s" % ( + env.module_dict_cname, + code.error_goto_if_null_object("temp_moddict", self.pos))) + code.putln("PYOBJECT_CLOSEREF(temp_moddict);") code.putln("#if !CYTHON_USING_HPY") - code.put_incref(env.module_dict_cname, py_object_type, nanny=False) + #code.put_incref(env.module_dict_cname, py_object_type, nanny=False) code.putln( '%s = HPY_LEGACY_OBJECT_FROM(__Pyx_PyImport_AddModuleRef(__Pyx_BUILTIN_MODULE_NAME)); %s' % ( diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 33cfc5b0aba..06d4b719760 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -1311,14 +1311,14 @@ def generate_incref(self, code, cname, nanny): code.funcstate.needs_refnanny = True code.putln("__Pyx_INCREF(%s);" % self.as_pyobject(cname)) else: - code.putln("PYOBJECT_ALLOC(%s);" % self.as_pyobject(cname)) + code.putln("PYOBJECT_NEWREF(%s);" % self.as_pyobject(cname)) def generate_xincref(self, code, cname, nanny): if nanny: code.funcstate.needs_refnanny = True code.putln("__Pyx_XINCREF(%s);" % self.as_pyobject(cname)) else: - code.putln("PYOJBECT_XALLOC(%s);" % self.as_pyobject(cname)) + code.putln("PYOJBECT_XNEWREF(%s);" % self.as_pyobject(cname)) def generate_decref(self, code, cname, nanny, have_gil): # have_gil is for the benefit of memoryviewslice - it's ignored here @@ -1378,10 +1378,10 @@ def _generate_decref(self, code, cname, nanny, null_check=False, X = '' # CPython doesn't have a Py_XCLEAR() code.putln("%s_%sCLEAR(%s);" % (prefix, X, cname)) else: - code.putln("REFNANNY_DEALLOC(%s_%sDECREF, %s); %s = API_NULL_VALUE;" % ( + code.putln("REFNANNY_CLOSEREF(%s_%sDECREF, %s); %s = API_NULL_VALUE;" % ( prefix, X, self.as_pyobject(cname), cname)) else: - code.putln("REFNANNY_DEALLOC(%s_%sDECREF, %s);" % ( + code.putln("REFNANNY_CLOSEREF(%s_%sDECREF, %s);" % ( prefix, X, self.as_pyobject(cname))) def nullcheck_string(self, cname): diff --git a/Cython/Utility/CommonStructures.c b/Cython/Utility/CommonStructures.c index 159638aa9a7..2dc2e6457a1 100644 --- a/Cython/Utility/CommonStructures.c +++ b/Cython/Utility/CommonStructures.c @@ -100,7 +100,7 @@ static PyTypeObject *__Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_DEF PYO py_basicsize = PYOBJECT_GET_ATTR_STR(cached_type, "__basicsize__"); if (unlikely(API_IS_NULL(py_basicsize))) goto bad; basicsize = PYOBJECT_LONG_AS_SSIZE(py_basicsize); - PYOBJECT_DEALLOC(py_basicsize); + PYOBJECT_CLOSEREF(py_basicsize); py_basicsize = API_DEFAULT_VALUE; if (unlikely(basicsize == (Py_ssize_t)-1) && PyErr_Occurred()) goto bad; #else @@ -128,13 +128,13 @@ static PyTypeObject *__Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_DEF PYO if (PYOBJECT_SET_ATTR_STR(abi_module, object_name, cached_type) < 0) goto bad; done: - PYOBJECT_DEALLOC(abi_module); + PYOBJECT_CLOSEREF(abi_module); // NOTE: always returns owned reference, or NULL on error assert(API_IS_NULL(cached_type) || PyType_Check(cached_type)); return (PyTypeObject *) HPY_LEGACY_OBJECT_AS(cached_type); bad: - PYOBJECT_DEALLOC(cached_type); + PYOBJECT_CLOSEREF(cached_type); cached_type = API_NULL_VALUE; goto done; } diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 63156c6e3ce..f4eb7f2968f 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -400,7 +400,7 @@ __Pyx_CyFunction_init_defaults(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_ if (unlikely(API_IS_NULL(PYOBJECT_FIELD_LOAD(op, struct_op->defaults_kwdict)))) result = -1; } #endif - PYOBJECT_DEALLOC(res); + PYOBJECT_CLOSEREF(res); return result; } diff --git a/Cython/Utility/Exceptions.c b/Cython/Utility/Exceptions.c index 6937ebe9727..9e777126c72 100644 --- a/Cython/Utility/Exceptions.c +++ b/Cython/Utility/Exceptions.c @@ -802,7 +802,7 @@ static int __Pyx_CLineForTraceback(HPY_CONTEXT_FIRST_ARG_DEF PyThreadState *tsta PYOBJECT_TYPE use_cline_obj = __Pyx_PyObject_GetAttrStrNoError(HPY_CONTEXT_FIRST_ARG_CALL ${cython_runtime_cname}, PYIDENT("cline_in_traceback")); if (API_IS_NOT_NULL(use_cline_obj)) { use_cline = API_IS_FALSE(use_cline_obj) ? API_FALSE : API_TRUE; - PYOBJECT_DEALLOC(use_cline_obj); + PYOBJECT_CLOSEREF(use_cline_obj); } else { PyErr_Clear(); use_cline = API_NULL_VALUE; diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 8d0f59e4ace..689d47772f9 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -746,12 +746,11 @@ class __Pyx_FakeReference { #define CAPI_IS_POINTER #define CAPI_NEEDS_DEREFERENCE - #define PYOBJECT_ALLOC(h) HPy_Dup(HPY_CONTEXT_CNAME, h) - #define PYOBJECT_XALLOC(h) HPy_Dup(HPY_CONTEXT_CNAME, h) - #define PYOBJECT_DEALLOC(h) HPy_Close(HPY_CONTEXT_CNAME, h) - #define PYOBJECT_XDEALLOC(h) HPy_Close(HPY_CONTEXT_CNAME, h) - #define PYOBJECT_ALLOC_NEWREF(h) HPy_Dup(HPY_CONTEXT_CNAME, h) - #define REFNANNY_DEALLOC(func, h) PYOBJECT_DEALLOC(h) + #define PYOBJECT_NEWREF(h) HPy_Dup(HPY_CONTEXT_CNAME, h) + #define PYOBJECT_XNEWREF(h) HPy_Dup(HPY_CONTEXT_CNAME, h) + #define PYOBJECT_CLOSEREF(h) HPy_Close(HPY_CONTEXT_CNAME, h) + #define PYOBJECT_XCLOSEREF(h) HPy_Close(HPY_CONTEXT_CNAME, h) + #define REFNANNY_CLOSEREF(func, h) PYOBJECT_CLOSEREF(h) #define PYOBJECT_FROM_LONG(i) HPyLong_FromLong(HPY_CONTEXT_CNAME, i) #define PYOBJECT_FROM_DOUBLE(f) HPyFloat_FromDouble(HPY_CONTEXT_CNAME, f) @@ -827,12 +826,11 @@ class __Pyx_FakeReference { #define CAPI_IS_POINTER * //Some types are sometimes pointers and sometimes not (i.e. PyModuleDef) where the type is always the same in HPy #define CAPI_NEEDS_DEREFERENCE & - #define PYOBJECT_ALLOC(h) Py_INCREF(h) - #define PYOBJECT_XALLOC(h) Py_XINCREF(h) - #define PYOBJECT_DEALLOC(h) Py_DECREF(h) - #define PYOBJECT_XDEALLOC(h) Py_XDECREF(h) - #define PYOBJECT_ALLOC_NEWREF(h) Py_NewRef(h) - #define REFNANNY_DEALLOC(func, h) func(h) + #define PYOBJECT_NEWREF(h) Py_NewRef(h) + #define PYOBJECT_XNEWREF(h) Py_XNewRef(h) + #define PYOBJECT_CLOSEREF(h) Py_DECREF(h) + #define PYOBJECT_XCLOSEREF(h) Py_XDECREF(h) + #define REFNANNY_CLOSEREF(func, h) func(h) #define PYOBJECT_FROM_LONG(i) PyInt_FromLong(i) #define PYOBJECT_FROM_DOUBLE(f) PyFloat_FromDouble(f) @@ -2027,12 +2025,12 @@ static CYTHON_INLINE int __Pyx_Is_Little_Endian(void) #define __Pyx_RefNannySetupContext(name, acquire_gil) #define __Pyx_RefNannyFinishContextNogil() #define __Pyx_RefNannyFinishContext() - #define __Pyx_INCREF(r) PYOBJECT_ALLOC(r) - #define __Pyx_DECREF(r) PYOBJECT_DEALLOC(r) + #define __Pyx_INCREF(r) PYOBJECT_NEWREF(r) + #define __Pyx_DECREF(r) PYOBJECT_CLOSEREF(r) #define __Pyx_GOTREF(r) #define __Pyx_GIVEREF(r) - #define __Pyx_XINCREF(r) PYOBJECT_XALLOC(r) - #define __Pyx_XDECREF(r) PYOBJECT_XDEALLOC(r) + #define __Pyx_XINCREF(r) PYOBJECT_XNEWREF(r) + #define __Pyx_XDECREF(r) PYOBJECT_XCLOSEREF(r) #define __Pyx_XGOTREF(r) #define __Pyx_XGIVEREF(r) #endif /* CYTHON_REFNANNY */ diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 277b6ef0ad3..759fe6262ad 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -2818,7 +2818,7 @@ static CYTHON_INLINE PyObject* __Pyx_PySequence_Multiply(PyObject *seq, Py_ssize typedef PYOBJECT_TYPE __Pyx_TypeName; #define __Pyx_FMT_TYPENAME "%U" static __Pyx_TypeName __Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_DEF PyTypeObject* tp); /*proto*/ -#define __Pyx_DECREF_TypeName(obj) PYOBJECT_XDEALLOC(obj) +#define __Pyx_DECREF_TypeName(obj) PYOBJECT_XCLOSEREF(obj) #else typedef const char *__Pyx_TypeName; #define __Pyx_FMT_TYPENAME "%.200s" diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index 3d07c52d249..5ac360fea5f 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -108,7 +108,7 @@ static CYTHON_INLINE size_t __Pyx_Py_UNICODE_strlen(const Py_UNICODE *u) #define __Pyx_PyUnicode_FromUnicodeAndLength PyUnicode_FromUnicode #define __Pyx_PyUnicode_AsUnicode PyUnicode_AsUnicode -#define __Pyx_hNewRef(obj) PYOBJECT_ALLOC_NEWREF(obj) //This will be merged into Pyx_NewRef eventually, but #if CYTHON_USING_HPY will make all calls to this macro use HPy, even if it hasn't been ported yet +#define __Pyx_hNewRef(obj) PYOBJECT_NEWREF(obj) //This will be merged into Pyx_NewRef eventually, but #if CYTHON_USING_HPY will make all calls to this macro use HPy, even if it hasn't been ported yet #define __Pyx_NewRef(obj) Py_NewRef(obj) #define __Pyx_Owned_Py_None(b) __Pyx_NewRef(Py_None) static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyBool_FromLong(HPY_CONTEXT_FIRST_ARG_DEF long b); From b7e5a080731a7b0221896c856f7c1a816104b829 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Wed, 6 Sep 2023 13:31:35 +0200 Subject: [PATCH 162/286] Add --hpy-abi option to runtests.py --- runtests.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/runtests.py b/runtests.py index 879da0f2257..e179cffb40a 100755 --- a/runtests.py +++ b/runtests.py @@ -135,7 +135,7 @@ def get_distutils_distro(_cache=[]): 'tag:ipython': 'IPython.testing.globalipapp', 'tag:jedi': 'jedi_BROKEN_AND_DISABLED', 'tag:test.support': 'test.support', # support module for CPython unit tests - 'tag:hpy': 'hpy.devel', + 'tag:hpy': 'hpy', } def patch_inspect_isfunction(): @@ -257,7 +257,7 @@ def update_hpy_extension(ext): if not hasattr(dist, 'hpy_abi'): # can be 'cpython' or 'universal' # for now, always use 'cpython' - dist.hpy_abi = 'cpython' + dist.hpy_abi = HPY_ABI dist.hpy_use_static_libs = False hpy_ext.distribution = dist hpy_ext._finalize_hpy_ext(ext) @@ -460,6 +460,7 @@ def get_openmp_compiler_flags(language): COMPILER_HAS_INT128 = False OPENMP_C_COMPILER_FLAGS = get_openmp_compiler_flags('c') OPENMP_CPP_COMPILER_FLAGS = get_openmp_compiler_flags('cpp') +HPY_ABI = None # Return this from the EXT_EXTRAS matcher callback to exclude the extension EXCLUDE_EXT = object() @@ -2450,6 +2451,8 @@ def main(): help="do not capture stdout, stderr in srctree tests. Makes pdb.set_trace interactive") parser.add_option("--limited-api", dest="limited_api", default=False, action="store_true", help="Compiles Cython using CPython's LIMITED_API") + parser.add_option("--hpy-abi", dest="hpy_abi", default="cpython", action="store", + help="Select the HPy ABI to use when running tests.") options, cmd_args = parser.parse_args(args) @@ -2803,6 +2806,10 @@ def runtests(options, cmd_args, coverage=None): if options.compiler: COMPILER = options.compiler + global HPY_ABI + HPY_ABI = options.hpy_abi + sys.stderr.write("HPy ABI: '%s'\n" % HPY_ABI) + selected_backends = [ name.strip() for name in options.backends.split(',') if name.strip() ] backends = [] for backend in selected_backends: From 64df2135e3f49cdcd110143c0cca923a847f069c Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Wed, 6 Sep 2023 18:27:17 +0200 Subject: [PATCH 163/286] Fix: did not specify type name in __pyx_CyFunctionType_spec --- Cython/Utility/CythonFunction.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index f4eb7f2968f..e8809c05ec5 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -1309,6 +1309,7 @@ static PyType_Spec __pyx_CyFunctionType_spec = { }; #else static HPyType_Spec __pyx_CyFunctionType_spec = { + __PYX_TYPE_MODULE_PREFIX "cython_function_or_method", .basicsize = sizeof(__pyx_CyFunctionObject), .itemsize = 0, .flags = HPy_TPFLAGS_DEFAULT | HPy_TPFLAGS_HAVE_GC | HPy_TPFLAGS_BASETYPE, /*tp_flags*/ From b1b8f0abf0b2f3c267975f33494392efce5e4658 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Thu, 7 Sep 2023 13:17:17 +0200 Subject: [PATCH 164/286] Introduce macro TYPE_CHECK (liek PyType_Check) --- Cython/Utility/CommonStructures.c | 2 +- Cython/Utility/ModuleSetupCode.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Cython/Utility/CommonStructures.c b/Cython/Utility/CommonStructures.c index 2dc2e6457a1..a6d112de24c 100644 --- a/Cython/Utility/CommonStructures.c +++ b/Cython/Utility/CommonStructures.c @@ -130,7 +130,7 @@ static PyTypeObject *__Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_DEF PYO done: PYOBJECT_CLOSEREF(abi_module); // NOTE: always returns owned reference, or NULL on error - assert(API_IS_NULL(cached_type) || PyType_Check(cached_type)); + assert(API_IS_NULL(cached_type) || TYPE_CHECK(cached_type)); return (PyTypeObject *) HPY_LEGACY_OBJECT_AS(cached_type); bad: diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 689d47772f9..768b17c859e 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -767,6 +767,7 @@ class __Pyx_FakeReference { #define TYPESPEC_TYPE HPyType_Spec #define TYPE_FROM_MOD_AND_SPEC(m, s, b) __Pyx_Type_FromSpec(HPY_CONTEXT_CNAME, m, &s, b) #define TYPESPEC_GET(s, field) s.field + #define TYPE_CHECK(o) HPy_TypeCheck(HPY_CONTEXT_CNAME, (o), HPY_CONTEXT_CNAME->h_TypeType) #define API_NULL_VALUE HPy_NULL #define API_DEFAULT_VALUE HPy_NULL @@ -846,6 +847,7 @@ class __Pyx_FakeReference { #define TYPESPEC_TYPE PyType_Spec #define TYPE_FROM_MOD_AND_SPEC(m, s, b) __Pyx_Type_FromSpec(m, s, b) #define TYPESPEC_GET(s, field) s->field + #define TYPE_CHECK(o) PyType_Check(o) #define API_NULL_VALUE NULL #define API_DEFAULT_VALUE 0 From 4edd4dbec3643ad3fc7a20fcd53d45c0ad7eda41 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Thu, 7 Sep 2023 13:18:26 +0200 Subject: [PATCH 165/286] Fix problems with TYPE_FROM_MOD_AND_SPEC --- Cython/Utility/HPyUtils.c | 31 +++++++++++++++++++------------ Cython/Utility/ModuleSetupCode.c | 4 ++-- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 415eaaf9ebe..53c9374bea8 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -19,21 +19,28 @@ HPy_AsPyObjectArray(HPyContext *ctx, HPy *h_arr, HPy_ssize_t n) } return arr; } -#endif -#if CYTHON_USING_HPY -HPy __Pyx_Type_FromSpec(HPyContext *ctx, HPy module, HPyType_Spec *spec, HPy base) +HPy HPyType_FromModuleAndSpec(HPyContext *ctx, HPy module, HPyType_Spec *spec, HPy bases) { HPyType_SpecParam temp_params[] = { - { HPyType_SpecParam_Base, ctx->h_LongType }, + { HPyType_SpecParam_BasesTuple, bases }, + /* unfortunately, not yet supported by HPy */ + /* { HPyType_SpecParam_Module, module }, */ { (HPyType_SpecParam_Kind)0 } }; - HPy type = HPyType_FromSpec(ctx, spec, temp_params); - return type; -} -#else -static inline PyObject *__Pyx_Type_FromSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) -{ - return PyType_FromModuleAndSpec(module, spec, bases); + HPy result; + if (!HPy_IsNull(bases)) { + result = HPyType_FromSpec(ctx, spec, temp_params); + } else { + /* we must not pass bases if it is HPy_NULL */ + result = HPyType_FromSpec(ctx, spec, NULL); + } + /* TODO(fa): remove once HPy supports 'HPyType_SpecParam_Module' */ + if (!HPy_IsNull(result) && !HPy_IsNull(module)) { + PyHeapTypeObject *ht = (PyHeapTypeObject *) HPy_AsPyObject(ctx, result); + ht->ht_module = HPy_AsPyObject(ctx, module); + Py_DECREF(ht); + } + return result; } -#endif \ No newline at end of file +#endif diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 768b17c859e..a25cb106dba 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -765,7 +765,7 @@ class __Pyx_FakeReference { #define PYMODULEDEF_TYPE HPyModuleDef #define PYMETHODDEF_TYPE HPyDef #define TYPESPEC_TYPE HPyType_Spec - #define TYPE_FROM_MOD_AND_SPEC(m, s, b) __Pyx_Type_FromSpec(HPY_CONTEXT_CNAME, m, &s, b) + #define TYPE_FROM_MOD_AND_SPEC(m, s, b) HPyType_FromModuleAndSpec(HPY_CONTEXT_CNAME, m, &s, b) #define TYPESPEC_GET(s, field) s.field #define TYPE_CHECK(o) HPy_TypeCheck(HPY_CONTEXT_CNAME, (o), HPY_CONTEXT_CNAME->h_TypeType) @@ -845,7 +845,7 @@ class __Pyx_FakeReference { #define PYMODULEDEF_TYPE struct PyModuleDef #define PYMETHODDEF_TYPE PyMethodDef #define TYPESPEC_TYPE PyType_Spec - #define TYPE_FROM_MOD_AND_SPEC(m, s, b) __Pyx_Type_FromSpec(m, s, b) + #define TYPE_FROM_MOD_AND_SPEC(m, s, b) PyType_FromModuleAndSpec(m, s, b) #define TYPESPEC_GET(s, field) s->field #define TYPE_CHECK(o) PyType_Check(o) From defff3cb7fcda7b7fb48d6d5042043355afce944 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 7 Sep 2023 15:15:43 +0200 Subject: [PATCH 166/286] Temp fix to strings not getting assigned - not permanent --- Cython/Compiler/Code.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index c6f9f95bb24..40b615257e1 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1657,7 +1657,12 @@ def generate_string_constants(self): w.putln("#endif") w.putln("{0, 0, 0, 0, 0, 0, 0}") w.putln("};") + w.putln("#if !CYTHON_USING_HPY") w.putln("return __Pyx_InitStrings(HPY_CONTEXT_FIRST_ARG_CALL %s);" % Naming.stringtab_cname) + w.putln("#else") + for py_string_args in py_strings: + w.putln("HPyGlobal_Store(HPY_CONTEXT_CNAME, &%s, HPyUnicode_FromString(HPY_CONTEXT_CNAME, %s));" % (py_string.cname, c_cname)) + w.putln("#endif") w.putln("}") init_constants.putln( From 3fd835570bb4231ac0213ae8a06eed7882ae4602 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 7 Sep 2023 15:21:37 +0200 Subject: [PATCH 167/286] Temp change now also works for var_assign --- Cython/Compiler/Code.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 40b615257e1..cc23cfc10a8 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1661,6 +1661,7 @@ def generate_string_constants(self): w.putln("return __Pyx_InitStrings(HPY_CONTEXT_FIRST_ARG_CALL %s);" % Naming.stringtab_cname) w.putln("#else") for py_string_args in py_strings: + c_cname, _, py_string = py_string_args w.putln("HPyGlobal_Store(HPY_CONTEXT_CNAME, &%s, HPyUnicode_FromString(HPY_CONTEXT_CNAME, %s));" % (py_string.cname, c_cname)) w.putln("#endif") w.putln("}") From 006b9a1667636695c02d4120089d425a1e7836a0 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Fri, 8 Sep 2023 13:27:03 +0200 Subject: [PATCH 168/286] Make cython_function_or_method a pure HPy type --- Cython/Compiler/ModuleNode.py | 2 +- Cython/Utility/CommonStructures.c | 14 +-- Cython/Utility/CythonFunction.c | 194 +++++++++++++++++++++--------- Cython/Utility/ModuleSetupCode.c | 40 ++++-- 4 files changed, 175 insertions(+), 75 deletions(-) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index d76f2129f73..4db727d0059 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -2871,7 +2871,7 @@ def generate_module_state_start(self, env, code): code.putln('PyObject *%s;' % Naming.preimport_cname) for type_cname, used_name in Naming.used_types_and_macros: code.putln('#ifdef %s' % used_name) - code.putln('PyTypeObject *%s;' % type_cname) + code.putln('PYOBJECT_GLOBAL_TYPE %s;' % type_cname) code.putln('#endif') def generate_module_state_end(self, env, modules, globalstate): diff --git a/Cython/Utility/CommonStructures.c b/Cython/Utility/CommonStructures.c index a6d112de24c..0ce2b8045fc 100644 --- a/Cython/Utility/CommonStructures.c +++ b/Cython/Utility/CommonStructures.c @@ -13,7 +13,7 @@ static PYOBJECT_TYPE __Pyx_FetchSharedCythonABIModule(HPY_CONTEXT_ONLY_ARG_DEF) #if !CYTHON_USE_TYPE_SPECS static PyTypeObject* __Pyx_FetchCommonType(PyTypeObject* type); #else -static PyTypeObject* __Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_GLOBAL_TYPE module, TYPESPEC_TYPE spec, PYOBJECT_TYPE bases); +static PYTYPEOBJECT_TYPE __Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_GLOBAL_TYPE module, TYPESPEC_TYPE spec, PYOBJECT_TYPE bases); #endif /////////////// FetchCommonType /////////////// @@ -82,7 +82,7 @@ static PyTypeObject* __Pyx_FetchCommonType(PyTypeObject* type) { } #else -static PyTypeObject *__Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_GLOBAL_TYPE module, TYPESPEC_TYPE spec, PYOBJECT_TYPE bases) { +static PYTYPEOBJECT_TYPE __Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_GLOBAL_TYPE module, TYPESPEC_TYPE spec, PYOBJECT_TYPE bases) { PYOBJECT_TYPE abi_module; PYOBJECT_TYPE cached_type = API_NULL_VALUE; // get the final part of the object name (after the last dot) @@ -90,7 +90,7 @@ static PyTypeObject *__Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_DEF PYO object_name = object_name ? object_name+1 : TYPESPEC_GET(spec,name); abi_module = __Pyx_FetchSharedCythonABIModule(HPY_CONTEXT_ONLY_ARG_CALL); - if (API_IS_NULL(abi_module)) return NULL; + if (API_IS_NULL(abi_module)) return API_NULL_VALUE; cached_type = PYOBJECT_GET_ATTR_STR(abi_module, object_name); if (API_IS_NOT_NULL(cached_type)) { @@ -102,7 +102,7 @@ static PyTypeObject *__Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_DEF PYO basicsize = PYOBJECT_LONG_AS_SSIZE(py_basicsize); PYOBJECT_CLOSEREF(py_basicsize); py_basicsize = API_DEFAULT_VALUE; - if (unlikely(basicsize == (Py_ssize_t)-1) && PyErr_Occurred()) goto bad; + if (unlikely(basicsize == (API_SSIZE_T)-1) && PYERR_OCCURRED()) goto bad; #else basicsize = likely(PyType_Check(cached_type)) ? ((PyTypeObject*) cached_type)->tp_basicsize : -1; #endif @@ -116,8 +116,8 @@ static PyTypeObject *__Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_DEF PYO goto done; } - if (!PyErr_ExceptionMatches(PyExc_AttributeError)) goto bad; - PyErr_Clear(); + if (!PYERR_EXCEPTIONMATCHES(API_EXC(AttributeError))) goto bad; + PYERR_CLEAR(); // We pass the ABI module reference to avoid keeping the user module alive by foreign type usages. CYTHON_UNUSED_VAR(module); cached_type = __Pyx_PyType_FromModuleAndSpec(abi_module, spec, bases); @@ -131,7 +131,7 @@ static PyTypeObject *__Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_DEF PYO PYOBJECT_CLOSEREF(abi_module); // NOTE: always returns owned reference, or NULL on error assert(API_IS_NULL(cached_type) || TYPE_CHECK(cached_type)); - return (PyTypeObject *) HPY_LEGACY_OBJECT_AS(cached_type); + return cached_type; bad: PYOBJECT_CLOSEREF(cached_type); diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index e8809c05ec5..88f6982ebfc 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -57,12 +57,10 @@ typedef struct { #if CYTHON_BACKPORT_VECTORCALL __pyx_vectorcallfunc func_vectorcall; #endif -#else -#if CYTHON_BACKPORT_VECTORCALL - __pyx_vectorcallfunc HPyCallFunction; -#endif +#else /* !CYTHON_USING_HPY */ HPyDef *func; -#endif +#endif /* !CYTHON_USING_HPY */ + PYOBJECT_FIELD_TYPE func_module; PYOBJECT_FIELD_TYPE func_dict; PYOBJECT_FIELD_TYPE func_name; PYOBJECT_FIELD_TYPE func_qualname; @@ -98,7 +96,7 @@ static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void *cfunc) #undef __Pyx_IsSameCFunction #define __Pyx_IsSameCFunction(func, cfunc) __Pyx__IsSameCyOrCFunction(func, cfunc) -static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, +static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, PYMETHODDEF_TYPE *ml, int flags, PYOBJECT_TYPE qualname, PYOBJECT_TYPE closure, @@ -170,6 +168,33 @@ static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(__pyx_CyFunctionObject* #endif } +#if CYTHON_USING_HPY +HPyDef_GETSET(__Pyx_CyFunction_doc, "__doc__") +static HPy +__Pyx_CyFunction_doc_get(HPyContext *HPY_CONTEXT_CNAME, __pyx_CyFunctionObject_FuncDef op, void *closure) +{ + CYTHON_UNUSED_VAR(closure); + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); + HPy h_doc = HPyField_Load(HPY_CONTEXT_CNAME, op, struct_op->func_doc); + if (HPy_IsNull(h_doc)) { + return HPy_Dup(HPY_CONTEXT_CNAME, HPY_CONTEXT_CNAME->h_None); + } + return h_doc; +} + +static int +__Pyx_CyFunction_doc_set(HPyContext *HPY_CONTEXT_CNAME, __pyx_CyFunctionObject_FuncDef op, HPy value, void *context) +{ + CYTHON_UNUSED_VAR(context); + if (HPy_IsNull(value)) { + // Mark as deleted + value = HPY_CONTEXT_CNAME->h_None; + } + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); + HPyField_Store(HPY_CONTEXT_CNAME, op, &struct_op->func_doc, value); + return 0; +} +#else /* CYTHON_USING_HPY */ static PYOBJECT_TYPE __Pyx_CyFunction_get_doc(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, void *closure) { @@ -215,6 +240,7 @@ __Pyx_CyFunction_set_doc(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDe #endif return 0; } +#endif /* CYTHON_USING_HPY */ static PYOBJECT_TYPE __Pyx_CyFunction_get_name(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, void *context) @@ -266,8 +292,8 @@ __Pyx_CyFunction_get_qualname(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_F return op->func_qualname; #else __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); - PYOBJECT_FIELD_LOAD(op, struct_op->func_qualname); -#endif + return PYOBJECT_FIELD_LOAD(op, struct_op->func_qualname); +#endif } static int @@ -342,8 +368,8 @@ __Pyx_CyFunction_get_globals(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_Fu return op->func_globals; #else __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); - PYOBJECT_FIELD_LOAD(op, struct_op->func_globals); -#endif + return PYOBJECT_FIELD_LOAD(op, struct_op->func_globals); +#endif } static PYOBJECT_TYPE @@ -487,7 +513,7 @@ __Pyx_CyFunction_get_kwdefaults(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject if (struct_op->defaults_getter) { if (unlikely(__Pyx_CyFunction_init_defaults(HPY_CONTEXT_FIRST_ARG_CALL op) < 0)) return API_NULL_VALUE; result = PYOBJECT_FIELD_LOAD(op, struct_op->defaults_kwdict); - } else + } else { result = API_ASSIGN_NONE; } @@ -645,7 +671,12 @@ static PyGetSetDef __pyx_CyFunction_getsets[] = { #endif {0, 0, 0, 0, 0} }; +#endif /* CYTHON_USING_HPY */ +#if CYTHON_USING_HPY +HPyDef_MEMBER(__pyx_CyFunction_member_module, "__module__", HPyMember_OBJECT, offsetof(__pyx_CyFunctionObject, func_module)) +HPyDef_MEMBER(__pyx_CyFunction_member_dictoffset, "__dictoffset__", HPyMember_HPYSSIZET, offsetof(__pyx_CyFunctionObject, func_dict), .readonly=1) +#else /* CYTHON_USING_HPY */ static PyMemberDef __pyx_CyFunction_members[] = { #if !CYTHON_COMPILING_IN_LIMITED_API {"__module__", T_OBJECT, offsetof(PyCFunctionObject, m_module), 0, 0}, @@ -669,9 +700,21 @@ static PyMemberDef __pyx_CyFunction_members[] = { #endif {0, 0, 0, 0, 0} }; +#endif /* CYTHON_USING_HPY */ +#if CYTHON_USING_HPY +HPyDef_METH(__Pyx_CyFunction_reduce, "__reduce__", HPyFunc_VARARGS) +static HPy +__Pyx_CyFunction_reduce_impl(HPyContext *HPY_CONTEXT_CNAME, HPy self, const HPy *args, size_t nargs) +{ + CYTHON_UNUSED_VAR(args); + CYTHON_UNUSED_VAR(nargs); + __pyx_CyFunctionObject *struct_self = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, self); + return PYOBJECT_FIELD_LOAD(self, struct_self->func_qualname); +} +#else /* CYTHON_USING_HPY */ static PYOBJECT_TYPE -__Pyx_CyFunction_reduce(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef m, PYOBJECT_TYPE args) +__Pyx_CyFunction_reduce(__pyx_CyFunctionObject_FuncDef m, PyObject *args) { CYTHON_UNUSED_VAR(args); #if CYTHON_USING_HPY @@ -687,6 +730,7 @@ static PyMethodDef __pyx_CyFunction_methods[] = { {"__reduce__", (PyCFunction)__Pyx_CyFunction_reduce, METH_VARARGS, 0}, {0, 0, 0, 0} }; +#endif /* CYTHON_USING_HPY */ #if CYTHON_COMPILING_IN_LIMITED_API @@ -696,7 +740,7 @@ static PyMethodDef __pyx_CyFunction_methods[] = { #endif static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, PYMETHODDEF_TYPE *ml, int flags, PYOBJECT_TYPE qualname, - PYOBJECT_TYPE closure, PYOBJECT_TYPE module, PYOBJECT_TYPE globals, PYOBJECT_TYPE code) { + PYOBJECT_TYPE closure, PYOBJECT_TYPE module, PYOBJECT_TYPE globals, PYOBJECT_TYPE code) { #if !CYTHON_USING_HPY #if !CYTHON_COMPILING_IN_LIMITED_API PyCFunctionObject *cf = (PyCFunctionObject*) op; @@ -731,13 +775,14 @@ static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFun ((PyCMethodObject*)op)->mm_class = NULL; #endif Py_XINCREF(code); -#else +#else /* !CYTHON_USING_HPY */ __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); struct_op->func = ml; -#endif +#endif /* !CYTHON_USING_HPY */ if (unlikely(API_IS_NULL(op))) return API_NULL_VALUE; struct_op->flags = flags; + PYOBJECT_FIELD_STORE(op, struct_op->func_module, module); PYOBJECT_FIELD_STORE(op, struct_op->func_closure, closure); PYOBJECT_FIELD_STORE(op, struct_op->func_dict, API_NULL_VALUE); PYOBJECT_FIELD_STORE(op, struct_op->func_name, API_NULL_VALUE); @@ -848,7 +893,17 @@ static void __Pyx_CyFunction_dealloc(__pyx_CyFunctionObject *m) PyObject_GC_UnTrack(m); __Pyx__CyFunction_dealloc(m); } -#endif + +#else /* !CYTHON_USING_HPY */ + +HPyDef_SLOT(__Pyx_CyFunction_destroy, HPy_tp_destroy) +static void __Pyx_CyFunction_destroy_impl(void *ptr) +{ + __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) ptr; + // TODO(fa): I think only freeing defaults is left +} + +#endif /* !CYTHON_USING_HPY */ #if !CYTHON_USING_HPY static int __Pyx_CyFunction_traverse(__pyx_CyFunctionObject *m, visitproc visit, void *arg) @@ -886,16 +941,18 @@ static int __Pyx_CyFunction_traverse(__pyx_CyFunctionObject *m, visitproc visit, HPyDef_SLOT(__Pyx_CyFunction_traverse, HPy_tp_traverse) static int __Pyx_CyFunction_traverse_impl(void *m, HPyFunc_visitproc visit, void *arg) { - HPy_VISIT(&((__pyx_CyFunctionObject*)m)->func_dict); - HPy_VISIT(&((__pyx_CyFunctionObject*)m)->func_closure); - HPy_VISIT(&((__pyx_CyFunctionObject*)m)->func_name); - HPy_VISIT(&((__pyx_CyFunctionObject*)m)->func_qualname); - HPy_VISIT(&((__pyx_CyFunctionObject*)m)->func_doc); - HPy_VISIT(&((__pyx_CyFunctionObject*)m)->func_globals); - HPy_VISIT(&((__pyx_CyFunctionObject*)m)->func_code); - HPy_VISIT(&((__pyx_CyFunctionObject*)m)->defaults_tuple); - HPy_VISIT(&((__pyx_CyFunctionObject*)m)->defaults_kwdict); - HPy_VISIT(&((__pyx_CyFunctionObject*)m)->func_is_coroutine); + __pyx_CyFunctionObject *f = (__pyx_CyFunctionObject *) m; + HPy_VISIT(&f->func_module); + HPy_VISIT(&f->func_dict); + HPy_VISIT(&f->func_closure); + HPy_VISIT(&f->func_name); + HPy_VISIT(&f->func_qualname); + HPy_VISIT(&f->func_doc); + HPy_VISIT(&f->func_globals); + HPy_VISIT(&f->func_code); + HPy_VISIT(&f->defaults_tuple); + HPy_VISIT(&f->defaults_kwdict); + HPy_VISIT(&f->func_is_coroutine); // Need to port init_defaults first // if (m->defaults) { @@ -912,20 +969,27 @@ static int __Pyx_CyFunction_traverse_impl(void *m, HPyFunc_visitproc visit, void #if CYTHON_USING_HPY HPyDef_SLOT(__Pyx_CyFunction_repr, HPy_tp_repr) -#endif +static HPy +__Pyx_CyFunction_repr_impl(HPyContext *HPY_CONTEXT_CNAME, HPy op) +{ + __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); + HPy qualname_field = HPyField_Load(HPY_CONTEXT_CNAME, op, struct_op->func_qualname); + HPy result = HPyUnicode_FromFormat(HPY_CONTEXT_CNAME, "", qualname_field, op); + HPy_Close(HPY_CONTEXT_CNAME, qualname_field); + return result; +} + +#else /* CYTHON_USING_HPY */ static PYOBJECT_TYPE __Pyx_CyFunction_repr_impl(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op) { -#if CYTHON_USING_HPY - __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); - HPy qualname_field = PYOBJECT_FIELD_LOAD(op, struct_op->func_qualname); -#else __pyx_CyFunctionObject *struct_op = op; PyObject *qualname_field = struct_op->func_qualname; #endif return UNICODE_FROM_FORMAT("", qualname_field, op); } +#endif /* CYTHON_USING_HPY */ #if !CYTHON_USING_HPY static PyObject * __Pyx_CyFunction_CallMethod(HPY_CONTEXT_FIRST_ARG_DEF PyObject *func, PyObject *self, PyObject *arg, PyObject *kw) { @@ -1248,8 +1312,8 @@ static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD(PyObject #endif #if CYTHON_USING_HPY -HPyDef_SLOT(__Pyx_CyFunction_HPyCall, HPy_tp_call) -static HPy __Pyx_CyFunction_HPyCall_impl(HPY_CONTEXT_FIRST_ARG_DEF HPy callable, const HPy *args, size_t nargs, HPy kwnames) +HPyDef_SLOT(__Pyx_CyFunction_call, HPy_tp_call) +static HPy __Pyx_CyFunction_call_impl(HPY_CONTEXT_FIRST_ARG_DEF HPy callable, const HPy *args, size_t nargs, HPy kwnames) { HPy result; __pyx_CyFunctionObject *cyfunc = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_FIRST_ARG_CALL callable); @@ -1269,14 +1333,38 @@ static HPy __Pyx_CyFunction_HPyCall_impl(HPY_CONTEXT_FIRST_ARG_DEF HPy callable, #endif #if CYTHON_USE_TYPE_SPECS +#if CYTHON_USING_HPY + +static HPyDef *__pyx_CyFunctionType_HPyDefines[] = { + &__Pyx_CyFunction_repr, + &__Pyx_CyFunction_call, + &__Pyx_CyFunction_traverse, + &__Pyx_CyFunction_destroy, + &__Pyx_CyFunction_reduce, + &__pyx_CyFunction_member_module, + &__pyx_CyFunction_member_dictoffset, + // {Py_tp_getset, (void *)__pyx_CyFunction_getsets}, + &__Pyx_CyFunction_doc, + // {Py_tp_descr_get, (void *)__Pyx_PyMethod_New}, + NULL +}; + +static HPyType_Spec __pyx_CyFunctionType_spec = { + __PYX_TYPE_MODULE_PREFIX "cython_function_or_method", + .basicsize = sizeof(__pyx_CyFunctionObject), + .itemsize = 0, + .flags = HPy_TPFLAGS_DEFAULT | HPy_TPFLAGS_HAVE_GC | HPy_TPFLAGS_BASETYPE, /*tp_flags*/ + .builtin_shape = SHAPE(__pyx_CyFunctionObject), + .legacy_slots = 0, + .defines = __pyx_CyFunctionType_HPyDefines +}; +#else /* CYTHON_USING_HPY */ static PyType_Slot __pyx_CyFunctionType_slots[] = { -#if !CYTHON_USING_HPY {Py_tp_repr, (void *)__Pyx_CyFunction_repr_impl}, {Py_tp_call, (void *)__Pyx_CyFunction_CallAsMethod}, {Py_tp_traverse, (void *)__Pyx_CyFunction_traverse_impl}, {Py_tp_dealloc, (void *)__Pyx_CyFunction_dealloc}, {Py_tp_clear, (void *)__Pyx_CyFunction_clear}, -#endif {Py_tp_methods, (void *)__pyx_CyFunction_methods}, {Py_tp_members, (void *)__pyx_CyFunction_members}, {Py_tp_getset, (void *)__pyx_CyFunction_getsets}, @@ -1284,16 +1372,6 @@ static PyType_Slot __pyx_CyFunctionType_slots[] = { {0, 0}, }; -#if CYTHON_USING_HPY -static HPyDef *__pyx_CyFunctionType_HPyDefines[] = { - &__Pyx_CyFunction_repr, - &__Pyx_CyFunction_HPyCall, - &__Pyx_CyFunction_traverse, - NULL -}; -#endif - -#if !CYTHON_USING_HPY static PyType_Spec __pyx_CyFunctionType_spec = { __PYX_TYPE_MODULE_PREFIX "cython_function_or_method", sizeof(__pyx_CyFunctionObject), @@ -1307,17 +1385,11 @@ static PyType_Spec __pyx_CyFunctionType_spec = { Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /*tp_flags*/ __pyx_CyFunctionType_slots }; -#else -static HPyType_Spec __pyx_CyFunctionType_spec = { - __PYX_TYPE_MODULE_PREFIX "cython_function_or_method", - .basicsize = sizeof(__pyx_CyFunctionObject), - .itemsize = 0, - .flags = HPy_TPFLAGS_DEFAULT | HPy_TPFLAGS_HAVE_GC | HPy_TPFLAGS_BASETYPE, /*tp_flags*/ - .legacy_slots = __pyx_CyFunctionType_slots, - .defines = __pyx_CyFunctionType_HPyDefines -}; -#endif +#endif /* CYTHON_USING_HPY */ #else /* CYTHON_USE_TYPE_SPECS */ +#if CYTHON_USING_HPY +#error "HPy needs CYTHON_USE_TYPE_SPECS" +#endif static PyTypeObject __pyx_CyFunctionType_type = { PyVarObject_HEAD_INIT(0, 0) @@ -1398,12 +1470,15 @@ static PyTypeObject __pyx_CyFunctionType_type = { static int __pyx_CyFunction_init(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_GLOBAL_TYPE module) { #if CYTHON_USE_TYPE_SPECS - __pyx_CyFunctionType = __Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_CALL module, __pyx_CyFunctionType_spec, API_NULL_VALUE); + PYOBJECT_TYPE cyFunctionType = __Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_CALL module, __pyx_CyFunctionType_spec, API_NULL_VALUE); + PYOBJECT_GLOBAL_STORE(__pyx_CyFunctionType, cyFunctionType); + PYOBJECT_GLOBAL_CLOSEREF(cyFunctionType); + int cyFunctionType_is_null = API_IS_NULL(cyFunctionType); #else CYTHON_UNUSED_VAR(module); __pyx_CyFunctionType = __Pyx_FetchCommonType(&__pyx_CyFunctionType_type); #endif - if (unlikely(!__pyx_CyFunctionType)) { + if (unlikely(cyFunctionType_is_null)) { return -1; } return 0; @@ -1480,9 +1555,10 @@ static PYOBJECT_TYPE __Pyx_CyFunction_New(HPY_CONTEXT_FIRST_ARG_DEF PYMETHODDEF_ return op; #else __pyx_CyFunctionObject *func_obj; - HPy __pyx_HPyCyFunctionType = HPy_FromPyObject(HPY_CONTEXT_FIRST_ARG_CALL __pyx_CyFunctionType); + HPy __pyx_HPyCyFunctionType = HPyGlobal_Load(HPY_CONTEXT_CNAME, __pyx_CyFunctionType); HPy func = HPy_New(HPY_CONTEXT_CNAME, __pyx_HPyCyFunctionType, &func_obj); - PYOBJECT_TYPE op = __Pyx_CyFunction_Init( + HPy_Close(HPY_CONTEXT_CNAME, __pyx_HPyCyFunctionType); + HPy op = __Pyx_CyFunction_Init( HPY_CONTEXT_FIRST_ARG_CALL func, ml, flags, qualname, closure, module, globals, code ); return op; diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index a25cb106dba..8e679d15f67 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -484,7 +484,7 @@ #endif /* Whether to use METH_FASTCALL with a fake backported implementation of vectorcall */ -#define CYTHON_BACKPORT_VECTORCALL (CYTHON_METH_FASTCALL && PY_VERSION_HEX < 0x030800B1) +#define CYTHON_BACKPORT_VECTORCALL (!CYTHON_USING_HPY && CYTHON_METH_FASTCALL && PY_VERSION_HEX < 0x030800B1) #if CYTHON_USE_PYLONG_INTERNALS /* These short defines from the PyLong header can easily conflict with other code */ @@ -737,12 +737,14 @@ class __Pyx_FakeReference { #define API_SSIZE_T HPy_ssize_t #define PYOBJECT_TYPE HPy + #define PYTYPEOBJECT_TYPE HPy #define PYOBJECT_FIELD_TYPE HPyField #define PYOBJECT_FIELD_STORE(owner, field, h) HPyField_Store(HPY_CONTEXT_CNAME, owner, &field, h) #define PYOBJECT_FIELD_LOAD(owner, field) HPyField_Load(HPY_CONTEXT_CNAME, owner, field) #define PYOBJECT_GLOBAL_TYPE HPyGlobal #define PYOBJECT_GLOBAL_STORE(global, h) HPyGlobal_Store(HPY_CONTEXT_CNAME, &global, h) #define PYOBJECT_GLOBAL_LOAD(global) HPyGlobal_Load(HPY_CONTEXT_CNAME, global) + #define PYOBJECT_GLOBAL_CLOSEREF(ref) HPy_Close(HPY_CONTEXT_CNAME, ref) #define CAPI_IS_POINTER #define CAPI_NEEDS_DEREFERENCE @@ -752,6 +754,10 @@ class __Pyx_FakeReference { #define PYOBJECT_XCLOSEREF(h) HPy_Close(HPY_CONTEXT_CNAME, h) #define REFNANNY_CLOSEREF(func, h) PYOBJECT_CLOSEREF(h) + #define PYERR_OCCURRED() HPyErr_Occurred(HPY_CONTEXT_CNAME) + #define PYERR_CLEAR() HPyErr_Clear(HPY_CONTEXT_CNAME) + #define PYERR_EXCEPTIONMATCHES(exc) HPyErr_ExceptionMatches(HPY_CONTEXT_CNAME, (exc)) + #define PYOBJECT_FROM_LONG(i) HPyLong_FromLong(HPY_CONTEXT_CNAME, i) #define PYOBJECT_FROM_DOUBLE(f) HPyFloat_FromDouble(HPY_CONTEXT_CNAME, f) #define PYOBJECT_FROM_STRING(s) HPyUnicode_FromString(HPY_CONTEXT_CNAME, s) //not yet needed in C API version @@ -774,13 +780,19 @@ class __Pyx_FakeReference { #define API_IS_NULL(h) HPy_IsNull(h) #define API_IS_NOT_NULL(h) !HPy_IsNull(h) #define API_IS_EQUAL(a, b) HPy_Is(HPY_CONTEXT_CNAME, a, b) - #define API_NONE_VALUE HPY_CONTEXT_CNAME->h_None #define API_ASSIGN_NONE HPy_Dup(HPY_CONTEXT_CNAME, HPY_CONTEXT_CNAME->h_None) - #define API_TRUE HPY_CONTEXT_CNAME->h_True - #define API_FALSE HPY_CONTEXT_CNAME->h_False #define API_IS_TRUE(h) HPy_IsTrue(HPY_CONTEXT_CNAME, h) #define API_IS_FALSE(h) !HPy_IsTrue(HPY_CONTEXT_CNAME, h) - + + /* constants */ + #define API_NONE_VALUE HPY_CONTEXT_CNAME->h_None + #define API_TRUE HPY_CONTEXT_CNAME->h_True + #define API_FALSE HPY_CONTEXT_CNAME->h_False + #define API_EXC_ATTRIBUTEERROR HPY_CONTEXT_CNAME->h_AttributeError + #define API_EXC(name) (HPY_CONTEXT_CNAME->h_ ## name) + + #define API_VECTORCALLFUNC HPyCallFunction + #define DICT_NEW() HPyDict_New(HPY_CONTEXT_CNAME) #define DICT_GET_ITEM(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) #define DICT_SET_ITEM(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) @@ -818,12 +830,14 @@ class __Pyx_FakeReference { #define API_SSIZE_T Py_ssize_t #define PYOBJECT_TYPE PyObject * + #define PYTYPEOBJECT_TYPE PyTypeObject * #define PYOBJECT_FIELD_TYPE PyObject * #define PYOBJECT_FIELD_STORE(owner, field, h) field = h #define PYOBJECT_FIELD_LOAD(owner, field) field #define PYOBJECT_GLOBAL_TYPE PyObject * #define PYOBJECT_GLOBAL_STORE(global, h) global = h #define PYOBJECT_GLOBAL_LOAD(global) global + #define PYOBJECT_GLOBAL_CLOSEREF(ref) /* nop */ #define CAPI_IS_POINTER * //Some types are sometimes pointers and sometimes not (i.e. PyModuleDef) where the type is always the same in HPy #define CAPI_NEEDS_DEREFERENCE & @@ -833,6 +847,10 @@ class __Pyx_FakeReference { #define PYOBJECT_XCLOSEREF(h) Py_XDECREF(h) #define REFNANNY_CLOSEREF(func, h) func(h) + #define PYERR_OCCURRED() (!!PyErr_Occurred()) + #define PYERR_CLEAR() PyErr_Clear() + #define PYERR_EXCEPTIONMATCHES(exc) PyErr_ExceptionMatches((exc)) + #define PYOBJECT_FROM_LONG(i) PyInt_FromLong(i) #define PYOBJECT_FROM_DOUBLE(f) PyFloat_FromDouble(f) @@ -854,13 +872,16 @@ class __Pyx_FakeReference { #define API_IS_NULL(h) !h //Both are here as otherwise we would get !!h for API_IS_NOT_NULL, which is hard to read - but it can be made so if necessary #define API_IS_NOT_NULL(h) h #define API_IS_EQUAL(a, b) a==b - #define API_NONE_VALUE Py_None #define API_ASSIGN_NONE Py_None - #define API_TRUE Py_True - #define API_FALSE Py_False #define API_IS_TRUE(h) PyObject_IsTrue(h) #define API_IS_FALSE(h) !PyObject_Not(h) + /* constants */ + #define API_NONE_VALUE Py_None + #define API_TRUE Py_True + #define API_FALSE Py_False + #define API_EXC(name) (PyExc_ ## name) + #define DICT_NEW() PyDict_New() #define DICT_GET_ITEM(o, attr_name) PyDict_GetItem(o, attr_name) #define DICT_SET_ITEM(o, attr_name, attr_val) PyDict_SetItem(o, attr_name, attr_val) @@ -1127,6 +1148,9 @@ class __Pyx_FakeReference { #define __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET PY_VECTORCALL_ARGUMENTS_OFFSET #define __Pyx_PyVectorcall_NARGS(n) PyVectorcall_NARGS((size_t)(n)) #elif CYTHON_BACKPORT_VECTORCALL +#if CYTHON_USING_HPY +#error "cannot use HPy when backporting vectorcall impl" +#endif typedef PyObject *(*__pyx_vectorcallfunc)(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames); #define __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET ((size_t)1 << (8 * sizeof(size_t) - 1)) From ca7c2b380ffed751c65d2f9f74f5ffdad922b8c6 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Fri, 8 Sep 2023 15:54:21 +0200 Subject: [PATCH 169/286] Implemment cyfunction call for NOARGS convention --- Cython/Utility/CythonFunction.c | 96 +++++++++++++++++++++++--------- Cython/Utility/ModuleSetupCode.c | 6 +- 2 files changed, 73 insertions(+), 29 deletions(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 88f6982ebfc..b2c5538aafb 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -60,6 +60,7 @@ typedef struct { #else /* !CYTHON_USING_HPY */ HPyDef *func; #endif /* !CYTHON_USING_HPY */ + PYOBJECT_FIELD_TYPE func_self; PYOBJECT_FIELD_TYPE func_module; PYOBJECT_FIELD_TYPE func_dict; PYOBJECT_FIELD_TYPE func_name; @@ -120,11 +121,15 @@ static CYTHON_INLINE void __Pyx_CyFunction_SetAnnotationsDict(HPY_CONTEXT_FIRST_ static int __pyx_CyFunction_init(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_GLOBAL_TYPE module); +#if CYTHON_USING_HPY +HPyDef_CALL_FUNCTION(__Pyx_CyFunction_hpycall_NOARGS) +#else /* CYTHON_USING_HPY */ #if CYTHON_METH_FASTCALL static PyObject * __Pyx_CyFunction_Vectorcall_NOARGS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); static PyObject * __Pyx_CyFunction_Vectorcall_O(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); +#endif /* CYTHON_USING_HPY */ #if CYTHON_BACKPORT_VECTORCALL #define __Pyx_CyFunction_func_vectorcall(f) (((__pyx_CyFunctionObject*)f)->func_vectorcall) #else @@ -777,11 +782,13 @@ static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFun Py_XINCREF(code); #else /* !CYTHON_USING_HPY */ __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); + assert(ml->kind == HPyDef_Kind_Meth); struct_op->func = ml; #endif /* !CYTHON_USING_HPY */ if (unlikely(API_IS_NULL(op))) return API_NULL_VALUE; struct_op->flags = flags; + PYOBJECT_FIELD_STORE(op, struct_op->func_self, op); PYOBJECT_FIELD_STORE(op, struct_op->func_module, module); PYOBJECT_FIELD_STORE(op, struct_op->func_closure, closure); PYOBJECT_FIELD_STORE(op, struct_op->func_dict, API_NULL_VALUE); @@ -801,7 +808,18 @@ static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFun #endif PYOBJECT_FIELD_STORE(op, struct_op->func_annotations, API_NULL_VALUE ); PYOBJECT_FIELD_STORE(op, struct_op->func_is_coroutine, API_NULL_VALUE ); -#if !CYTHON_USING_HPY +#if CYTHON_USING_HPY + switch (ml->meth.signature) { + case HPyFunc_NOARGS: + HPy_SetCallFunction(HPY_CONTEXT_CNAME, op, &__Pyx_CyFunction_hpycall_NOARGS); + break; + default: + HPyErr_SetString(HPY_CONTEXT_CNAME, API_EXC(TypeError), "unknown signature"); + return HPy_NULL; + } + return op; + +#else /* CYTHON_USING_HPY */ Py_INCREF(op->func_globals); #if CYTHON_METH_FASTCALL switch (ml->ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS | METH_O | METH_KEYWORDS | METH_METHOD)) { @@ -829,8 +847,6 @@ static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFun } #endif return (PyObject *) op; -#else - return op; #endif } @@ -1111,10 +1127,6 @@ static CYTHON_INLINE PyObject *__Pyx_CyFunction_Call(HPY_CONTEXT_FIRST_ARG_DEF P } static PYOBJECT_TYPE __Pyx_CyFunction_CallAsMethod(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE func, PYOBJECT_TYPE args, PYOBJECT_TYPE kw) { -#if CYTHON_USING_HPY - return __Pyx_CyFunction_HPyCall_impl(HPY_CONTEXT_FIRST_ARG_CALL func, args, HPy_GetAttr_s(HPY_CONTEXT_FIRST_ARG_CALL args, "size"), kw); - return HPy_NULL; -#endif PyObject *result; __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *) func; @@ -1165,6 +1177,7 @@ static PYOBJECT_TYPE __Pyx_CyFunction_CallAsMethod(HPY_CONTEXT_FIRST_ARG_DEF PYO } return result; } +#endif /* CYTHON_USING_HPY */ #if CYTHON_METH_FASTCALL && (CYTHON_VECTORCALL || CYTHON_BACKPORT_VECTORCALL) // Check that kwnames is empty (if you want to allow keyword arguments, @@ -1173,25 +1186,64 @@ static PYOBJECT_TYPE __Pyx_CyFunction_CallAsMethod(HPY_CONTEXT_FIRST_ARG_DEF PYO // 1: self = args[0] // 0: self = cyfunc->func.m_self // -1: error -static CYTHON_INLINE int __Pyx_CyFunction_Vectorcall_CheckArgs(__pyx_CyFunctionObject *cyfunc, Py_ssize_t nargs, PyObject *kwnames) +static CYTHON_INLINE int __Pyx_CyFunction_Vectorcall_CheckArgs(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject *cyfunc, ssize_t nargs, PYOBJECT_TYPE kwnames) { int ret = 0; if ((cyfunc->flags & __Pyx_CYFUNCTION_CCLASS) && !(cyfunc->flags & __Pyx_CYFUNCTION_STATICMETHOD)) { if (unlikely(nargs < 1)) { PyErr_Format(PyExc_TypeError, "%.200s() needs an argument", - ((PyCFunctionObject*)cyfunc)->m_ml->ml_name); + ""); + // ((PyCFunctionObject*)cyfunc)->m_ml->ml_name); return -1; } ret = 1; } - if (unlikely(kwnames) && unlikely(__Pyx_PyTuple_GET_SIZE(kwnames))) { + if (unlikely(API_IS_NOT_NULL(kwnames)) && unlikely(__Pyx_TUPLE_GET_SIZE(kwnames))) { PyErr_Format(PyExc_TypeError, - "%.200s() takes no keyword arguments", ((PyCFunctionObject*)cyfunc)->m_ml->ml_name); + "%.200s() takes no keyword arguments", + ""); + // ((PyCFunctionObject*)cyfunc)->m_ml->ml_name); return -1; } return ret; } +#if CYTHON_USING_HPY +static HPy __Pyx_CyFunction_hpycall_NOARGS_impl(HPyContext *HPY_CONTEXT_CNAME, HPy func, const HPy *args, size_t nargs, HPy kwnames) +{ + __pyx_CyFunctionObject *cyfunc = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, func); + HPyDef *def = cyfunc->func; + assert(def->kind == HPyDef_Kind_Meth); + HPy result, self; + int self_needs_close = 0; + switch (__Pyx_CyFunction_Vectorcall_CheckArgs(HPY_CONTEXT_CNAME, cyfunc, nargs, kwnames)) { + case 1: + self = args[0]; + args += 1; + nargs -= 1; + break; + case 0: + self = HPyField_Load(HPY_CONTEXT_CNAME, func, cyfunc->func_self); + self_needs_close = 1; + break; + default: + return HPy_NULL; + } + + if (unlikely(nargs != 0)) { + HPyErr_Format(HPY_CONTEXT_CNAME, API_EXC(TypeError), + "%.200s() takes no arguments (%" CYTHON_FORMAT_SSIZE_T "d given)", + def->meth.name, nargs); + return HPy_NULL; + } + HPyFunc_noargs func_noargs = def->meth.impl; + result = func_noargs(HPY_CONTEXT_CNAME, self); + if (self_needs_close) { + HPy_Close(HPY_CONTEXT_CNAME, self); + } + return result; +} +#else /* CYTHON_USING_HPY */ static PyObject * __Pyx_CyFunction_Vectorcall_NOARGS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func; @@ -1223,7 +1275,9 @@ static PyObject * __Pyx_CyFunction_Vectorcall_NOARGS(PyObject *func, PyObject *c } return def->ml_meth(self, NULL); } +#endif /* CYTHON_USING_HPY */ +#if !CYTHON_USING_HPY static PyObject * __Pyx_CyFunction_Vectorcall_O(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func; @@ -1309,26 +1363,14 @@ static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD(PyObject return ((__Pyx_PyCMethod)(void(*)(void))def->ml_meth)(self, cls, args, (size_t)nargs, kwnames); } #endif -#endif +#endif /* !CYTHON_USING_HPY */ #if CYTHON_USING_HPY HPyDef_SLOT(__Pyx_CyFunction_call, HPy_tp_call) -static HPy __Pyx_CyFunction_call_impl(HPY_CONTEXT_FIRST_ARG_DEF HPy callable, const HPy *args, size_t nargs, HPy kwnames) +static HPy __Pyx_CyFunction_call_impl(HPyContext *HPY_CONTEXT_CNAME, HPy func, const HPy *args, size_t nargs, HPy kwnames) { - HPy result; - __pyx_CyFunctionObject *cyfunc = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_FIRST_ARG_CALL callable); - -#if CYTHON_METH_FASTCALL - // Prefer vectorcall if available. This is not the typical case, as - // CPython would normally use vectorcall directly instead of tp_call. - __pyx_vectorcallfunc vc = __Pyx_CyFunction_func_vectorcall(cyfunc); -//#if CYTHON_ASSUME_SAFE_MACROS - return HPy_Call(HPY_CONTEXT_FIRST_ARG_CALL callable, args, (size_t)(sizeof(args)), kwnames); -//#else -// // avoid unused function warning -// (void) &__Pyx_PyVectorcall_FastCallDict; -// return PyVectorcall_Call(func, args, kw); -#endif + HPyErr_SetString(HPY_CONTEXT_CNAME, API_EXC(TypeError), "unsupported generic call"); + return HPy_NULL; } #endif diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 8e679d15f67..35ec474fedf 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -807,12 +807,13 @@ class __Pyx_FakeReference { #define PYOBJECT_GET_ATTR_STR(o, attr_name) HPy_GetAttr_s(HPY_CONTEXT_CNAME, o, attr_name) #define PYOBJECT_SET_ATTR_STR(o1, attr_name, o2) HPy_SetAttr_s(HPY_CONTEXT_CNAME, o1, attr_name, o2) - #define PYMODULE_GETDICT_ATTR(mod) HPy_GetAttr_s(HPY_CONTEXT_CNAME, mod, "__dict__") + #define PYMODULE_GETDICT_ATTR(mod) HPy_GetAttr_s(HPY_CONTEXT_CNAME, mod, "__dict__") #define BYTES_FROM_STR_AND_SIZE(str, size) HPyBytes_FromStringAndSize(HPY_CONTEXT_CNAME, str, size) #define TUPLE_CREATE_EMPTY() HPyTuple_FromArray(HPY_CONTEXT_CNAME, NULL, 0) #define TUPLE_GET_ITEM(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, PYOBJECT_FROM_LONG(pos)) + #define TUPLE_GET_SIZE(h) HPy_Length(HPY_CONTEXT_CNAME, (h)) #define TUPLE_BUILDER_TYPE HPyTupleBuilder #define TUPLE_CREATE_START(target, builder, size) builder = HPyTupleBuilder_New(HPY_CONTEXT_CNAME, size) #define TUPLE_CREATE_ASSIGN(tuple, builder, index, item) HPyTupleBuilder_Set(HPY_CONTEXT_CNAME, builder, index, item) @@ -896,12 +897,13 @@ class __Pyx_FakeReference { #define PYOBJECT_GET_ATTR_STR(o, attr_name) PyObject_GetAttrString(o, attr_name) #define PYOBJECT_SET_ATTR_STR(o1, attr_name, o2) PyObject_SetAttrString(o1, attr_name, o2) - #define PYMODULE_GETDICT_ATTR(mod) PyModule_GetDict(mod) + #define PYMODULE_GETDICT_ATTR(mod) PyModule_GetDict(mod) #define BYTES_FROM_STR_AND_SIZE(str, size) PyBytes_FromStringAndSize(str, size) #define TUPLE_CREATE_EMPTY() PyTuple_New(0) #define TUPLE_GET_ITEM(h, pos) __Pyx_PySequence_ITEM(HPY_CONTEXT_CNAME, h, pos) + #define TUPLE_GET_SIZE(h) PyTuple_GET_SIZE(h) #define TUPLE_BUILDER_TYPE PyObject * //Not used, just needed to prevent errors #define TUPLE_CREATE_START(target, builder, size) target=PyTuple_New(size) #define TUPLE_CREATE_ASSIGN(tuple, builder, index, item) __Pyx_PyTuple_SET_ITEM(tuple, index, item) From c066e4fd70c06208ed5c08e52f782fe58be3f8c1 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 11 Sep 2023 10:30:43 +0200 Subject: [PATCH 170/286] Fixed undefined variable in CyFunction_init --- Cython/Utility/CythonFunction.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index b2c5538aafb..de8aea7e2ef 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -1519,6 +1519,7 @@ static int __pyx_CyFunction_init(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_GLOBAL_TYPE #else CYTHON_UNUSED_VAR(module); __pyx_CyFunctionType = __Pyx_FetchCommonType(&__pyx_CyFunctionType_type); + int cyFunctionType_is_null = API_IS_NULL(__pyx_CyFunctionType); #endif if (unlikely(cyFunctionType_is_null)) { return -1; From 4b5a961f33311937d4facaa55722e85009cbb6ea Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 13 Sep 2023 09:34:06 +0200 Subject: [PATCH 171/286] Fixed issues arising from rebasing onto upstream --- Cython/Utility/CythonFunction.c | 14 ++-- Cython/Utility/Exceptions.c | 111 +++++++++++++++++-------------- Cython/Utility/ModuleSetupCode.c | 4 ++ 3 files changed, 70 insertions(+), 59 deletions(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index de8aea7e2ef..13fbfb729f4 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -11,7 +11,7 @@ #define __Pyx_CyFunction_GetClosure(f) \ (((__pyx_CyFunctionObject *) (f))->func_closure) -#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API +#if PY_VERSION_HEX < 0x030900B1 || (CYTHON_COMPILING_IN_LIMITED_API && !CYTHON_USING_HPY) #define __Pyx_CyFunction_GetClassObj(f) \ (((__pyx_CyFunctionObject *) (f))->func_classobj) #else @@ -47,10 +47,10 @@ typedef struct { #if CYTHON_BACKPORT_VECTORCALL __pyx_vectorcallfunc func_vectorcall; #endif -#if CYTHON_COMPILING_IN_LIMITED_API +#if (CYTHON_COMPILING_IN_LIMITED_API && !CYTHON_USING_HPY) PyObject *func_weakreflist; #endif -#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API +#if PY_VERSION_HEX < 0x030900B1 || (CYTHON_COMPILING_IN_LIMITED_API && !CYTHON_USING_HPY) // No-args super() class cell PyObject *func_classobj; #endif @@ -161,7 +161,7 @@ static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void *cfunc) #endif static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(__pyx_CyFunctionObject* f, PyObject* classobj) { -#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API +#if PY_VERSION_HEX < 0x030900B1 || (CYTHON_COMPILING_IN_LIMITED_API && !CYTHON_USING_HPY) __Pyx_Py_XDECREF_SET( __Pyx_CyFunction_GetClassObj(f), ((classobj) ? __Pyx_NewRef(classobj) : NULL)); @@ -199,7 +199,7 @@ __Pyx_CyFunction_doc_set(HPyContext *HPY_CONTEXT_CNAME, __pyx_CyFunctionObject_F HPyField_Store(HPY_CONTEXT_CNAME, op, &struct_op->func_doc, value); return 0; } -#else /* CYTHON_USING_HPY */ +#endif /* CYTHON_USING_HPY */ static PYOBJECT_TYPE __Pyx_CyFunction_get_doc(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, void *closure) { @@ -245,7 +245,6 @@ __Pyx_CyFunction_set_doc(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDe #endif return 0; } -#endif /* CYTHON_USING_HPY */ static PYOBJECT_TYPE __Pyx_CyFunction_get_name(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, void *context) @@ -774,7 +773,7 @@ static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFun Py_INCREF(qualname); op->func_qualname = qualname; op->func_doc = NULL; -#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API +#if PY_VERSION_HEX < 0x030900B1 || (CYTHON_COMPILING_IN_LIMITED_API && !CYTHON_USING_HPY) op->func_classobj = NULL; #else ((PyCMethodObject*)op)->mm_class = NULL; @@ -1362,7 +1361,6 @@ static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD(PyObject return ((__Pyx_PyCMethod)(void(*)(void))def->ml_meth)(self, cls, args, (size_t)nargs, kwnames); } -#endif #endif /* !CYTHON_USING_HPY */ #if CYTHON_USING_HPY diff --git a/Cython/Utility/Exceptions.c b/Cython/Utility/Exceptions.c index 9e777126c72..e36657c5295 100644 --- a/Cython/Utility/Exceptions.c +++ b/Cython/Utility/Exceptions.c @@ -842,22 +842,24 @@ static void __Pyx_AddTraceback(HPY_CONTEXT_FIRST_ARG_DEF const char *funcname, i #endif #if CYTHON_COMPILING_IN_LIMITED_API -static PyObject *__Pyx_PyCode_Replace_For_AddTraceback(PyObject *code, PyObject *scratch_dict, - PyObject *firstlineno, PyObject *name) { - PyObject *replace = NULL; - if (unlikely(PyDict_SetItemString(scratch_dict, "co_firstlineno", firstlineno))) return NULL; - if (unlikely(PyDict_SetItemString(scratch_dict, "co_name", name))) return NULL; - - replace = PyObject_GetAttrString(code, "replace"); - if (likely(replace)) { - PyObject *result; - result = PyObject_Call(replace, $empty_tuple, scratch_dict); - Py_DECREF(replace); +static PYOBJECT_TYPE __Pyx_PyCode_Replace_For_AddTraceback(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE code, PYOBJECT_TYPE scratch_dict, + PYOBJECT_TYPE firstlineno, PYOBJECT_TYPE name) { + PYOBJECT_TYPE replace = API_NULL_VALUE; + if (unlikely(DICT_SET_ITEM_STR(scratch_dict, "co_firstlineno", firstlineno))) return API_NULL_VALUE; + if (unlikely(DICT_SET_ITEM_STR(scratch_dict, "co_name", name))) return API_NULL_VALUE; + + replace = PYOBJECT_GET_ATTR_STR(code, "replace"); + if (likely(API_IS_NOT_NULL(replace))) { + PYOBJECT_TYPE result; + PYOBJECT_TYPE temp_empty_tuple = PYOBJECT_GLOBAL_LOAD($empty_tuple); + result = API_CALL_FUNC(replace, &temp_empty_tuple, 0, scratch_dict); + PYOBJECT_CLOSEREF(replace); + PYOBJECT_CLOSEREF(temp_empty_tuple); return result; } PyErr_Clear(); - #if __PYX_LIMITED_VERSION_HEX < 0x030780000 + #if __PYX_LIMITED_VERSION_HEX < 0x030780000 && !CYTHON_USING_HPY // If we're here, we're probably on Python <=3.7 which doesn't have code.replace. // In this we take a lazy interpreted route (without regard to performance // since it's fairly old and this is mostly just to get something working) @@ -887,9 +889,16 @@ static PyObject *__Pyx_PyCode_Replace_For_AddTraceback(PyObject *code, PyObject static void __Pyx_AddTraceback(HPY_CONTEXT_FIRST_ARG_DEF const char *funcname, int c_line, int py_line, const char *filename) { - PyObject *code_object = NULL, *py_py_line = NULL, *py_funcname = NULL, *dict = NULL; - PyObject *replace = NULL, *getframe = NULL, *frame = NULL; - PyObject *exc_type, *exc_value, *exc_traceback; + PYOBJECT_TYPE code_object = API_NULL_VALUE; + PYOBJECT_TYPE py_py_line = API_NULL_VALUE; + PYOBJECT_TYPE py_funcname = API_NULL_VALUE; + PYOBJECT_TYPE dict = API_NULL_VALUE; + PYOBJECT_TYPE replace = API_NULL_VALUE; + PYOBJECT_TYPE getframe = API_NULL_VALUE; + PYOBJECT_TYPE frame = API_NULL_VALUE; + PYOBJECT_TYPE exc_type; + PYOBJECT_TYPE exc_value; + PYOBJECT_TYPE exc_traceback; int success = 0; if (c_line) { // Avoid "unused" warning as long as we don't use this. @@ -903,52 +912,52 @@ static void __Pyx_AddTraceback(HPY_CONTEXT_FIRST_ARG_DEF const char *funcname, i // frame, and then customizing the details of the code to match. // We then run the code object and use the generated frame to set the traceback. - PyErr_Fetch(&exc_type, &exc_value, &exc_traceback); + //PyErr_Fetch(&exc_type, &exc_value, &exc_traceback); - code_object = Py_CompileString("_getframe()", filename, Py_eval_input); - if (unlikely(!code_object)) goto bad; - py_py_line = PyLong_FromLong(py_line); - if (unlikely(!py_py_line)) goto bad; - py_funcname = PyUnicode_FromString(funcname); - if (unlikely(!py_funcname)) goto bad; - dict = PyDict_New(); - if (unlikely(!dict)) goto bad; + code_object = HPY_LEGACY_OBJECT_FROM(Py_CompileString("_getframe()", filename, Py_eval_input)); + if (unlikely(API_IS_NULL(code_object))) goto bad; + py_py_line = PYOBJECT_FROM_LONG(py_line); + if (unlikely(API_IS_NULL(py_py_line))) goto bad; + py_funcname = PYOBJECT_FROM_STRING(funcname); + if (unlikely(API_IS_NULL(py_funcname))) goto bad; + dict = DICT_NEW(); + if (unlikely(API_IS_NULL(dict))) goto bad; { - PyObject *old_code_object = code_object; - code_object = __Pyx_PyCode_Replace_For_AddTraceback(code_object, dict, py_py_line, py_funcname); - Py_DECREF(old_code_object); + PYOBJECT_TYPE old_code_object = code_object; + code_object = __Pyx_PyCode_Replace_For_AddTraceback(HPY_CONTEXT_FIRST_ARG_CALL code_object, dict, py_py_line, py_funcname); + PYOBJECT_CLOSEREF(old_code_object); } - if (unlikely(!code_object)) goto bad; + if (unlikely(API_IS_NULL(code_object))) goto bad; // Note that getframe is borrowed - getframe = PySys_GetObject("_getframe"); - if (unlikely(!getframe)) goto bad; + getframe = HPY_LEGACY_OBJECT_FROM(PySys_GetObject("_getframe")); + if (unlikely(API_IS_NULL(getframe))) goto bad; // reuse dict as globals (nothing conflicts, and it saves an allocation) - if (unlikely(PyDict_SetItemString(dict, "_getframe", getframe))) goto bad; + if (unlikely(DICT_SET_ITEM_STR(dict, "_getframe", getframe))) goto bad; - frame = PyEval_EvalCode(code_object, dict, dict); - if (unlikely(!frame) || frame == Py_None) goto bad; + frame = HPY_LEGACY_OBJECT_FROM(PyEval_EvalCode(HPY_LEGACY_OBJECT_AS(code_object), HPY_LEGACY_OBJECT_AS(dict), HPY_LEGACY_OBJECT_AS(dict))); + if (unlikely(API_IS_NULL(frame)) || API_IS_EQUAL(frame, API_NONE_VALUE)) goto bad; success = 1; bad: - PyErr_Restore(exc_type, exc_value, exc_traceback); - Py_XDECREF(code_object); - Py_XDECREF(py_py_line); - Py_XDECREF(py_funcname); - Py_XDECREF(dict); - Py_XDECREF(replace); - - if (success) { - // Unfortunately an easy way to check the type of frame isn't in the - // limited API. The check against None should cover the most - // likely wrong answer though. - PyTraceBack_Here( - // Python < 0x03090000 didn't expose PyFrameObject - // but they all expose struct _frame as an opaque type - (struct _frame*)frame); - } - - Py_XDECREF(frame); + //PyErr_Restore(exc_type, exc_value, exc_traceback); + PYOBJECT_XCLOSEREF(code_object); + PYOBJECT_XCLOSEREF(py_py_line); + PYOBJECT_XCLOSEREF(py_funcname); + PYOBJECT_XCLOSEREF(dict); + PYOBJECT_XCLOSEREF(replace); + + //if (success) { + // // Unfortunately an easy way to check the type of frame isn't in the + // // limited API. The check against None should cover the most + // // likely wrong answer though. + // PyTraceBack_Here( + // // Python < 0x03090000 didn't expose PyFrameObject + // // but they all expose struct _frame as an opaque type + // (struct _frame*)frame); + //} + + PYOBJECT_XCLOSEREF(frame); } #else static PyCodeObject* __Pyx_CreateCodeObjectForTraceback( diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 35ec474fedf..e32f311d9a0 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -754,6 +754,8 @@ class __Pyx_FakeReference { #define PYOBJECT_XCLOSEREF(h) HPy_Close(HPY_CONTEXT_CNAME, h) #define REFNANNY_CLOSEREF(func, h) PYOBJECT_CLOSEREF(h) + #define API_CALL_FUNC(callable, args, nargs, kwnames) HPy_Call(HPY_CONTEXT_CNAME, callable, args, nargs, kwnames) + #define PYERR_OCCURRED() HPyErr_Occurred(HPY_CONTEXT_CNAME) #define PYERR_CLEAR() HPyErr_Clear(HPY_CONTEXT_CNAME) #define PYERR_EXCEPTIONMATCHES(exc) HPyErr_ExceptionMatches(HPY_CONTEXT_CNAME, (exc)) @@ -852,6 +854,8 @@ class __Pyx_FakeReference { #define PYERR_CLEAR() PyErr_Clear() #define PYERR_EXCEPTIONMATCHES(exc) PyErr_ExceptionMatches((exc)) + #define API_CALL_FUNC(callable, args, nargs, kwnames) PyObject_Call(HPY_CONTEXT_CNAME, callable, args, kwnames) + #define PYOBJECT_FROM_LONG(i) PyInt_FromLong(i) #define PYOBJECT_FROM_DOUBLE(f) PyFloat_FromDouble(f) From ce7022c78d39de8899cac22b50196966a8944d7f Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 14 Sep 2023 11:07:24 +0200 Subject: [PATCH 172/286] Started work on fixing memory leak from HPyGlobal_Load --- Cython/Compiler/ModuleNode.py | 2 +- Cython/Utility/ModuleSetupCode.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 4db727d0059..f0947f4055e 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -2871,7 +2871,7 @@ def generate_module_state_start(self, env, code): code.putln('PyObject *%s;' % Naming.preimport_cname) for type_cname, used_name in Naming.used_types_and_macros: code.putln('#ifdef %s' % used_name) - code.putln('PYOBJECT_GLOBAL_TYPE %s;' % type_cname) + code.putln('PYTYPEOBJECT_GLOBAL_TYPE %s;' % type_cname) code.putln('#endif') def generate_module_state_end(self, env, modules, globalstate): diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index e32f311d9a0..9deaaf2ba00 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -742,6 +742,7 @@ class __Pyx_FakeReference { #define PYOBJECT_FIELD_STORE(owner, field, h) HPyField_Store(HPY_CONTEXT_CNAME, owner, &field, h) #define PYOBJECT_FIELD_LOAD(owner, field) HPyField_Load(HPY_CONTEXT_CNAME, owner, field) #define PYOBJECT_GLOBAL_TYPE HPyGlobal + #define PYTYPEOBJECT_GLOBAL_TYPE HPyGlobal #define PYOBJECT_GLOBAL_STORE(global, h) HPyGlobal_Store(HPY_CONTEXT_CNAME, &global, h) #define PYOBJECT_GLOBAL_LOAD(global) HPyGlobal_Load(HPY_CONTEXT_CNAME, global) #define PYOBJECT_GLOBAL_CLOSEREF(ref) HPy_Close(HPY_CONTEXT_CNAME, ref) @@ -838,6 +839,7 @@ class __Pyx_FakeReference { #define PYOBJECT_FIELD_STORE(owner, field, h) field = h #define PYOBJECT_FIELD_LOAD(owner, field) field #define PYOBJECT_GLOBAL_TYPE PyObject * + #define PYTYPEOBJECT_GLOBAL_TYPE PyTypeObject * #define PYOBJECT_GLOBAL_STORE(global, h) global = h #define PYOBJECT_GLOBAL_LOAD(global) global #define PYOBJECT_GLOBAL_CLOSEREF(ref) /* nop */ From 5704790243a5a0999d67330e08bfde3eabf71ef6 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 14 Sep 2023 11:09:35 +0200 Subject: [PATCH 173/286] Fixing HPyGlobal_Load memory leak - forgot to add file to previous commit --- Cython/Compiler/ExprNodes.py | 156 ++++++++++++++++++++++++++++++++--- 1 file changed, 146 insertions(+), 10 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 24718565f7e..4b47e925041 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -2440,10 +2440,21 @@ def generate_result_code(self, code): code.putln('PyErr_Clear();') code.globalstate.use_utility_code( UtilityCode.load_cached("GetModuleGlobalName", "ObjectHandling.c")) + load_cname_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=False) + code.putln("#if CYTHON_USING_HPY") + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_cname_temp, interned_cname)) code.putln( - '__Pyx_GetModuleGlobalName(%s, PYOBJECT_GLOBAL_LOAD(%s));' % ( + '__Pyx_GetModuleGlobalName(%s, %s);' % ( + self.result(), + load_cname_temp)) + code.putln("PYOBJECT_CLOSEREF(%s);" % load_cname_temp) + code.putln("#else") + code.putln( + '__Pyx_GetModuleGlobalName(%s, %s);' % ( self.result(), interned_cname)) + code.putln("#endif") + code.funcstate.release_temp(load_cname_temp) if not self.cf_is_null: code.putln("}") code.putln(code.error_goto_if_null(self.result(), self.pos)) @@ -2469,11 +2480,23 @@ def generate_result_code(self, code): if entry.scope.is_module_scope: code.globalstate.use_utility_code( UtilityCode.load_cached("GetModuleGlobalName", "ObjectHandling.c")) + load_cname_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=False) + code.putln("#if CYTHON_USING_HPY") + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_cname_temp, interned_cname)) + code.putln( + '__Pyx_GetModuleGlobalName(%s, %s); %s' % ( + self.result(), + load_cname_temp, + code.error_goto_if_null_object(self.result(), self.pos))) + code.putln("PYOBJECT_CLOSEREF(%s);" % load_cname_temp) + code.putln("#else") code.putln( - '__Pyx_GetModuleGlobalName(%s, PYOBJECT_GLOBAL_LOAD(%s)); %s' % ( + '__Pyx_GetModuleGlobalName(%s, %s); %s' % ( self.result(), interned_cname, code.error_goto_if_null_object(self.result(), self.pos))) + code.putln("#endif") + code.funcstate.release_temp(load_cname_temp) else: # FIXME: is_pyglobal is also used for class namespace code.globalstate.use_utility_code( @@ -2524,6 +2547,7 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, interned_cname = code.intern_identifier(self.entry.name) namespace = self.entry.scope.namespace_cname rhs_result = rhs.py_result() + load_result_temp = rhs_result if entry.is_member: # if the entry is a member we have to cheat: SetAttr does not work # on types, so we create a descriptor which is then added to tp_dict. @@ -2533,7 +2557,13 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, namespace = Naming.moddict_cname interned_cname = interned_cname if not rhs_result.startswith("__pyx_t_"): - rhs_result = "PYOBJECT_GLOBAL_LOAD(" + rhs_result + ")" #temp fix for globals and non-globals being handled by the same code + code.putln("#if CYTHON_USING_HPY") + load_result_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=False) + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_result_temp, rhs_result)) + rhs_result = rhs_result #temp fix for globals and non-globals being handled by the same code + code.putln("PYOBJECT_CLOSEREF(%s);" % load_result_temp) + code.funcstate.release_temp(load_result_temp) + code.putln("#endif") elif entry.is_pyclass_attr: # Special-case setting __new__ n = "SetNewInClass" if self.name == "__new__" else "SetNameInClass" @@ -2541,13 +2571,31 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, setter = '__Pyx_' + n else: assert False, repr(entry) + code.putln("#if CYTHON_USING_HPY") + load_namespace_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=False) + load_cname_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=False) + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_namespace_temp, namespace)) + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_cname_temp, interned_cname)) code.put_error_if_neg( self.pos, - '%s(PYOBJECT_GLOBAL_LOAD(%s), PYOBJECT_GLOBAL_LOAD(%s), %s)' % ( + '%s(%s, %s, %s)' % ( + setter, + load_namespace_temp, + load_cname_temp, + load_result_temp)) + code.putln("PYOBJECT_CLOSEREF(%s);" % load_namespace_temp) + code.putln("PYOBJECT_CLOSEREF(%s);" % load_cname_temp) + code.funcstate.release_temp(load_namespace_temp) + code.funcstate.release_temp(load_cname_temp) + code.putln("#else") + code.put_error_if_neg( + self.pos, + '%s(%s, %s, %s)' % ( setter, namespace, interned_cname, rhs_result)) + code.putln("#endif") if debug_disposal_code: print("NameNode.generate_assignment_code:") print("...generating disposal code for %s" % rhs) @@ -10101,8 +10149,40 @@ def generate_cyfunction_code(self, code): else: flags = '0' + load_py_qual_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True) + load_py_mod_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True) + load_moddict_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True) + load_code_obj_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True) + + code.putln("#if CYTHON_USING_HPY") + + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_py_qual_temp, self.get_py_qualified_name(code))) + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_py_mod_temp, self.get_py_mod_name(code))) + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_moddict_temp, Naming.moddict_cname)) + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_code_obj_temp, code_object_result)) + code.putln( - '%s = %s(HPY_CONTEXT_FIRST_ARG_CALL &%s, %s, PYOBJECT_GLOBAL_LOAD(%s), %s, PYOBJECT_GLOBAL_LOAD(%s), PYOBJECT_GLOBAL_LOAD(%s), PYOBJECT_GLOBAL_LOAD(%s)); %s' % ( + '%s = %s(HPY_CONTEXT_FIRST_ARG_CALL &%s, %s, %s, %s, %s, %s, %s); %s' % ( + self.result(), + constructor, + self.pymethdef_cname, + flags, + self.get_py_qualified_name(code), + self.closure_result_code(), + self.get_py_mod_name(code), + Naming.moddict_cname, + code_object_result, + code.error_goto_if_null_object(self.result(), self.pos))) + + code.putln("PYOBJECT_CLOSEREF(%s);" % load_py_qual_temp) + code.putln("PYOBJECT_CLOSEREF(%s);" % load_py_mod_temp) + code.putln("PYOBJECT_CLOSEREF(%s);" % load_moddict_temp) + code.putln("PYOBJECT_CLOSEREF(%s);" % load_code_obj_temp) + + code.putln("#else") + + code.putln( + '%s = %s(HPY_CONTEXT_FIRST_ARG_CALL &%s, %s, %s, %s, %s, %s, %s); %s' % ( self.result(), constructor, self.pymethdef_cname, @@ -10115,6 +10195,12 @@ def generate_cyfunction_code(self, code): code.error_goto_if_null_object(self.result(), self.pos))) self.generate_gotref(code) + code.putln("#endif") + + code.funcstate.release_temp(load_py_qual_temp) + code.funcstate.release_temp(load_py_mod_temp) + code.funcstate.release_temp(load_moddict_temp) + code.funcstate.release_temp(load_code_obj_temp) if def_node.requires_classobj: assert code.pyclass_stack, "pyclass_stack is empty" @@ -10226,11 +10312,53 @@ def generate_result_code(self, code): elif self.def_node.is_generator: flags.append('CO_GENERATOR') - code.putln("PYOBJECT_GLOBAL_STORE(%s, HPY_LEGACY_OBJECT_FROM((PyObject*)__Pyx_PyCode_New(%d, %d, %d, %d, 0, %s, HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), \ - HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), \ - HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), \ - HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), \ - HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s)), %d, HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(%s))))); %s" % ( + load_empty_bytes_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True) + load_empty_tuple_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True) + load_varnames_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True) + load_filepath_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True) + load_funcname_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True) + + code.putln("#if CYTHON_USING_HPY") + + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_empty_bytes_temp, Naming.empty_bytes)) + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_empty_tuple_temp, Naming.empty_tuple)) + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_varnames_temp, self.varnames.result())) + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_filepath_temp, file_path_const)) + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_funcname_temp, func_name)) + + code.putln("PYOBJECT_GLOBAL_STORE(%s, HPY_LEGACY_OBJECT_FROM((PyObject*)__Pyx_PyCode_New(%d, %d, %d, %d, 0, %s, HPY_LEGACY_OBJECT_AS(%s), \ + HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), \ + HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), \ + HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), \ + HPY_LEGACY_OBJECT_AS(%s), %d, HPY_LEGACY_OBJECT_AS(%s)))); %s" % ( + self.result_code, + len(func.args) - func.num_kwonly_args, # argcount + func.num_posonly_args, # posonlyargcount (Py3.8+ only) + func.num_kwonly_args, # kwonlyargcount (Py3 only) + len(self.varnames.args), # nlocals + '|'.join(flags) or '0', # flags + load_empty_bytes_temp, # code + load_empty_tuple_temp, # consts + load_empty_tuple_temp, # names (FIXME) + load_varnames_temp, # varnames + load_empty_tuple_temp, # freevars (FIXME) + load_empty_tuple_temp, # cellvars (FIXME) + load_filepath_temp, # filename + load_funcname_temp, # name + self.pos[1], # firstlineno + load_empty_bytes_temp, # lnotab + code.error_goto_if_null_object(self.result_code, self.pos), + )) + + code.putln("PYOBJECT_CLOSEREF(%s);" % load_empty_bytes_temp) + code.putln("PYOBJECT_CLOSEREF(%s);" % load_empty_tuple_temp) + code.putln("PYOBJECT_CLOSEREF(%s);" % load_varnames_temp) + code.putln("PYOBJECT_CLOSEREF(%s);" % load_filepath_temp) + code.putln("PYOBJECT_CLOSEREF(%s);" % load_funcname_temp) + + code.putln("#else") + + code.putln("%s = (PyObject*)__Pyx_PyCode_New(%d, %d, %d, %d, 0, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d, %s); %s" % ( self.result_code, len(func.args) - func.num_kwonly_args, # argcount func.num_posonly_args, # posonlyargcount (Py3.8+ only) @@ -10250,6 +10378,14 @@ def generate_result_code(self, code): code.error_goto_if_null_object(self.result_code, self.pos), )) + code.putln("#endif") + + code.funcstate.release_temp(load_empty_bytes_temp) + code.funcstate.release_temp(load_empty_tuple_temp) + code.funcstate.release_temp(load_varnames_temp) + code.funcstate.release_temp(load_filepath_temp) + code.funcstate.release_temp(load_funcname_temp) + class DefaultLiteralArgNode(ExprNode): # CyFunction's literal argument default value From bcf5ded685c4a43c6bdfd3fdad56b2ed30026ede Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 14 Sep 2023 14:21:14 +0200 Subject: [PATCH 174/286] Fixed HPyGlobal_Load and HPyField_Load memory leaks --- Cython/Compiler/ExprNodes.py | 20 ++++++++------- Cython/Compiler/ModuleNode.py | 19 ++++++++++----- Cython/Compiler/Nodes.py | 8 +++++- Cython/Utility/CythonFunction.c | 43 +++++++++++++++++++++++++-------- Cython/Utility/Exceptions.c | 6 ++++- Cython/Utility/ObjectHandling.c | 15 +++++++++--- 6 files changed, 80 insertions(+), 31 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 4b47e925041..f44debc55e3 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -10167,11 +10167,11 @@ def generate_cyfunction_code(self, code): constructor, self.pymethdef_cname, flags, - self.get_py_qualified_name(code), + load_py_qual_temp, self.closure_result_code(), - self.get_py_mod_name(code), - Naming.moddict_cname, - code_object_result, + load_py_mod_temp, + load_moddict_temp, + load_code_obj_temp, code.error_goto_if_null_object(self.result(), self.pos))) code.putln("PYOBJECT_CLOSEREF(%s);" % load_py_qual_temp) @@ -10320,11 +10320,13 @@ def generate_result_code(self, code): code.putln("#if CYTHON_USING_HPY") - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_empty_bytes_temp, Naming.empty_bytes)) - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_empty_tuple_temp, Naming.empty_tuple)) - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_varnames_temp, self.varnames.result())) - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_filepath_temp, file_path_const)) - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_funcname_temp, func_name)) + #Type decls are temporary, still need to figure out how to get them to be declared automatically + + code.putln("PYOBJECT_TYPE %s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_empty_bytes_temp, Naming.empty_bytes)) + code.putln("PYOBJECT_TYPE %s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_empty_tuple_temp, Naming.empty_tuple)) + code.putln("PYOBJECT_TYPE %s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_varnames_temp, self.varnames.result())) + code.putln("PYOBJECT_TYPE %s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_filepath_temp, file_path_const)) + code.putln("PYOBJECT_TYPE %s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_funcname_temp, func_name)) code.putln("PYOBJECT_GLOBAL_STORE(%s, HPY_LEGACY_OBJECT_FROM((PyObject*)__Pyx_PyCode_New(%d, %d, %d, %d, 0, %s, HPY_LEGACY_OBJECT_AS(%s), \ HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), \ diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index f0947f4055e..40489270fe8 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -3098,10 +3098,13 @@ def generate_module_init_func(self, imported_modules, env, code): # See issues listed here: https://docs.python.org/3/c-api/init.html#sub-interpreter-support code.putln("if (API_IS_NOT_NULL(%s)) {" % Naming.module_cname) # Hack: enforce single initialisation. - code.putln("if (API_IS_EQUAL(PYOBJECT_GLOBAL_LOAD(%s), %s)) return 0;" % ( - Naming.module_cname, + code.putln("PYOBJECT_TYPE temp_module_cname = PYOBJECT_GLOBAL_LOAD(%s);" % Naming.module_cname) + code.putln("if (API_IS_EQUAL(temp_module_cname, %s)) return 0;" % ( Naming.pymodinit_module_arg, )) + code.putln("#if CYTHON_USING_HPY") + code.putln("PYOBJECT_CLOSEREF(temp_module_cname);") + code.putln("#endif") code.putln('PyErr_SetString(PyExc_RuntimeError,' ' "Module \'%s\' has already been imported. Re-initialisation is not supported.");' % env.module_name.as_c_string_literal()[1:-1]) @@ -3174,10 +3177,14 @@ def generate_module_init_func(self, imported_modules, env, code): code.putln("#endif") code.putln("if (%s) {" % self.is_main_module_flag_cname()) - code.put_error_if_neg(self.pos, 'PYOBJECT_SET_ATTR(%s, PYOBJECT_GLOBAL_LOAD(%s), PYOBJECT_GLOBAL_LOAD(%s))' % ( - Naming.pymodinit_module_arg, - code.intern_identifier(EncodedString("__name__")), - code.intern_identifier(EncodedString("__main__")))) + code.putln("PYOBJECT_TYPE temp_intern_name = PYOBJECT_GLOBAL_LOAD(%s);" % code.intern_identifier(EncodedString("__name__"))) + code.putln("PYOBJECT_TYPE temp_intern_main = PYOBJECT_GLOBAL_LOAD(%s);" % code.intern_identifier(EncodedString("__main__"))) + code.put_error_if_neg(self.pos, 'PYOBJECT_SET_ATTR(%s, temp_intern_name, temp_intern_main)' % + Naming.pymodinit_module_arg) + code.putln("#if CYTHON_USING_HPY") + code.putln("PYOBJECT_CLOSEREF(temp_intern_name);") + code.putln("PYOBJECT_CLOSEREF(temp_intern_main);") + code.putln("#endif") code.putln("}") # set up __file__ and __path__, then add the module to sys.modules diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 1fefb9395a2..7dffbc306db 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -1728,6 +1728,8 @@ def generate_execution_code(self, code): return # nothing to do here for C++ enums if self.visibility == 'public' or self.api: code.mark_pos(self.pos) + load_moddict_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True) + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_moddict_temp, Naming.moddict_cname)) temp = code.funcstate.allocate_temp(PyrexTypes.py_object_type, manage_ref=True) for item in self.entry.enum_values: code.putln("%s = PyInt_FromLong(%s); %s" % ( @@ -1735,12 +1737,16 @@ def generate_execution_code(self, code): item.cname, code.error_goto_if_null(temp, item.pos))) code.put_gotref(temp, PyrexTypes.py_object_type) - code.putln('if (DICT_SET_ITEM_STR(PYOBJECT_GLOBAL_LOAD(%s), "%s", %s) < 0) %s' % ( + code.putln('if (DICT_SET_ITEM_STR(load_moddict_temp, "%s", %s) < 0) %s' % ( Naming.moddict_cname, item.name, temp, code.error_goto(item.pos))) code.put_decref_clear(temp, PyrexTypes.py_object_type) + code.putln("#if CYTHON_USING_HPY") + code.putln("PYOBJECT_CLOSEREF(%s);" % load_moddict_temp) + code.putln("#endif") + code.funcstate.release_temp(load_moddict_temp) code.funcstate.release_temp(temp) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 13fbfb729f4..410b5a2794f 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -395,7 +395,8 @@ __Pyx_CyFunction_get_code(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncD #else __pyx_CyFunctionObject *struct_op = op; #endif - PYOBJECT_TYPE result = API_IS_NULL(PYOBJECT_FIELD_LOAD(op, struct_op->func_code)) ? PYOBJECT_FIELD_LOAD(op, struct_op->func_code) : API_ASSIGN_NONE; + PYOBJECT_TYPE load_func_code_temp = PYOBJECT_FIELD_LOAD(op, struct_op->func_code); + PYOBJECT_TYPE result = API_IS_NULL(load_func_code_temp) ? load_func_code_temp : API_ASSIGN_NONE; CYTHON_UNUSED_VAR(context); #if !CYTHON_USING_HPY Py_INCREF(result); @@ -424,11 +425,16 @@ __Pyx_CyFunction_init_defaults(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_ Py_INCREF(op->defaults_kwdict); #else PYOBJECT_FIELD_STORE(op, struct_op->defaults_tuple, TUPLE_GET_ITEM(res, 0)); - if (unlikely(API_IS_NULL(PYOBJECT_FIELD_LOAD(op, struct_op->defaults_tuple)))) result = -1; + PYOBJECT_TYPE load_default_tuple_temp = PYOBJECT_FIELD_LOAD(op, struct_op->defaults_tuple); + if (unlikely(API_IS_NULL(load_default_tuple_temp))) result = -1; else { PYOBJECT_FIELD_STORE(op, struct_op->defaults_kwdict, TUPLE_GET_ITEM(res, 1)); - if (unlikely(API_IS_NULL(PYOBJECT_FIELD_LOAD(op, struct_op->defaults_kwdict)))) result = -1; + PYOBJECT_TYPE load_default_kwdict_temp = PYOBJECT_FIELD_LOAD(op, struct_op->defaults_kwdict); + if (unlikely(API_IS_NULL(load_default_kwdict_temp))) result = -1; } + #if CYTHON_USING_HPY + PYOBJECT_CLOSE(load_default_tuple_temp); + #endif #endif PYOBJECT_CLOSEREF(res); return result; @@ -577,13 +583,18 @@ __Pyx_CyFunction_get_is_coroutine(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObje #endif int is_coroutine; CYTHON_UNUSED_VAR(context); - if (API_IS_NULL(PYOBJECT_FIELD_LOAD(op, struct_op->func_is_coroutine))) { - return __Pyx_hNewRef(PYOBJECT_FIELD_LOAD(op, struct_op->func_is_coroutine)); + PYOBJECT_TYPE load_is_coroutine_temp = PYOBJECT_FIELD_LOAD(op, struct_op->func_is_coroutine); + if (API_IS_NULL(load_is_coroutine_temp)) { + return __Pyx_hNewRef(load_is_coroutine_temp); } is_coroutine = PYOBJECT_FIELD_LOAD(op, struct_op->flags) & __Pyx_CYFUNCTION_COROUTINE; if (is_coroutine) { - PyObject *module, *fromlist, *marker = HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(PYIDENT("_is_coroutine"))); + PYOBJECT_TYPE load_marker_temp = PYOBJECT_GLOBAL_LOAD(PYIDENT("_is_coroutine")); + PyObject *module, *fromlist, *marker = HPY_LEGACY_OBJECT_AS(load_marker_temp); +#if CYTHON_USING_HPY + PYOBJECT_CLOSEREF(load_marker_temp); +#endif fromlist = PyList_New(1); if (unlikely(!fromlist)) return API_NULL_VALUE; Py_INCREF(marker); @@ -596,20 +607,32 @@ __Pyx_CyFunction_get_is_coroutine(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObje return NULL; } #endif - module = PyImport_ImportModuleLevelObject(HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(PYIDENT("asyncio.coroutines"))), NULL, NULL, fromlist, 0); + PYOBJECT_TYPE load_asyncio_temp = PYOBJECT_GLOBAL_LOAD(PYIDENT("asyncio.coroutines")); + module = PyImport_ImportModuleLevelObject(HPY_LEGACY_OBJECT_AS(load_asyncio_temp), NULL, NULL, fromlist, 0); +#if CYTHON_USING_HPY + PYOBJECT_CLOSEREF(load_asyncio_temp); +#endif Py_DECREF(fromlist); if (unlikely(!module)) goto ignore; PYOBJECT_FIELD_STORE(op, struct_op->func_is_coroutine, __Pyx_PyObject_GetAttrStr(HPY_LEGACY_OBJECT_FROM(module), HPY_LEGACY_OBJECT_FROM(marker))); Py_DECREF(module); - if (likely(!API_IS_NULL(PYOBJECT_FIELD_LOAD(op, struct_op->func_is_coroutine)))) { - return __Pyx_hNewRef(PYOBJECT_FIELD_LOAD(op, struct_op->func_is_coroutine)); + if (likely(!API_IS_NULL(load_is_coroutine_temp))) { +#if CYTHON_USING_HPY + return load_is_coroutine_temp; +#else + __Pyx_NewRef(load_is_coroutine_temp); +#endif } ignore: PyErr_Clear(); } PYOBJECT_FIELD_STORE(op, struct_op->func_is_coroutine, __Pyx_PyBool_FromLong(HPY_CONTEXT_FIRST_ARG_CALL is_coroutine)); - return __Pyx_hNewRef(PYOBJECT_FIELD_LOAD(op, struct_op->func_is_coroutine)); +#if CYTHON_USING_HPY + return PYOBJECT_FIELD_LOAD(op, struct_op->func_is_coroutine); +#else + __Pyx_NewRef(struct_op->func_is_coroutine); +#endif } //static PyObject * diff --git a/Cython/Utility/Exceptions.c b/Cython/Utility/Exceptions.c index e36657c5295..8415de6c8a6 100644 --- a/Cython/Utility/Exceptions.c +++ b/Cython/Utility/Exceptions.c @@ -1008,12 +1008,16 @@ static void __Pyx_AddTraceback(HPY_CONTEXT_FIRST_ARG_DEF const char *funcname, i __Pyx_ErrRestoreInState(tstate, ptype, pvalue, ptraceback); $global_code_object_cache_insert(c_line ? -c_line : py_line, py_code); } + PYOBJECT_TYPE load_moddict_temp = PYOBJECT_GLOBAL_LOAD($moddict_cname); py_frame = PyFrame_New( tstate, /*PyThreadState *tstate,*/ py_code, /*PyCodeObject *code,*/ - HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD($moddict_cname)), /*PyObject *globals,*/ + HPY_LEGACY_OBJECT_AS(load_moddict_temp), /*PyObject *globals,*/ 0 /*PyObject *locals*/ ); +#if CYTHON_USING_HPY + PYOBJECT_CLOSEREF(load_moddict_temp); +#endif if (!py_frame) goto bad; __Pyx_PyFrame_SetLineNumber(py_frame, py_line); PyTraceBack_Here(py_frame); diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 759fe6262ad..e21a4699aa1 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1432,8 +1432,12 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx__GetModuleGlobalName(HPY_CONTEXT_FIRST_ } #endif #else - result = PYOBJECT_GET_ITEM(PYOBJECT_GLOBAL_LOAD($moddict_cname), name); - __PYX_UPDATE_DICT_CACHE(PYOBJECT_GLOBAL_LOAD($moddict_cname), result, *dict_cached_value, *dict_version) + PYOBJECT_TYPE load_moddict_temp = PYOBJECT_GLOBAL_LOAD($moddict_cname); + result = PYOBJECT_GET_ITEM(load_moddict_temp, name); + __PYX_UPDATE_DICT_CACHE(load_moddict_temp, result, *dict_cached_value, *dict_version) +#if CYTHON_USING_HPY + PYOBJECT_CLOSEREF(load_moddict_temp); +#endif if (likely_object(result)) { return __Pyx_hNewRef(result); } @@ -2834,8 +2838,11 @@ typedef const char *__Pyx_TypeName; static __Pyx_TypeName __Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_DEF PyTypeObject* tp) { - PYOBJECT_TYPE name = __Pyx_PyObject_GetAttrStr(HPY_LEGACY_OBJECT_FROM((PyObject *)tp), - PYOBJECT_GLOBAL_LOAD(PYIDENT("__name__"))); + PYOBJECT_TYPE load_name_temp = PYOBJECT_GLOBAL_LOAD(PYIDENT("__name__")); + PYOBJECT_TYPE name = __Pyx_PyObject_GetAttrStr(HPY_LEGACY_OBJECT_FROM((PyObject *)tp), load_name_temp); +#if CYTHON_USING_HPY + PYOBJECT_CLOSEREF(load_name_temp); +#endif if (unlikely(API_IS_NULL(name)) || unlikely(!PyUnicode_Check(HPY_LEGACY_OBJECT_AS(name)))) { PyErr_Clear(); #if !CYTHON_USING_HPY From d4d98dbfacd63d76fb75f5859843ac4a037b97e5 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 14 Sep 2023 14:40:41 +0200 Subject: [PATCH 175/286] Added in forgotten PYOBJECT_CLOSEREF --- Cython/Utility/CythonFunction.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 410b5a2794f..a8115914567 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -398,6 +398,11 @@ __Pyx_CyFunction_get_code(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncD PYOBJECT_TYPE load_func_code_temp = PYOBJECT_FIELD_LOAD(op, struct_op->func_code); PYOBJECT_TYPE result = API_IS_NULL(load_func_code_temp) ? load_func_code_temp : API_ASSIGN_NONE; CYTHON_UNUSED_VAR(context); +#if CYTHON_USING_HPY + if (API_IS_NULL(load_func_code_temp)) { + PYOBJECT_CLOSEREF(load_func_code_temp); + } +#endif #if !CYTHON_USING_HPY Py_INCREF(result); #endif @@ -433,7 +438,7 @@ __Pyx_CyFunction_init_defaults(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_ if (unlikely(API_IS_NULL(load_default_kwdict_temp))) result = -1; } #if CYTHON_USING_HPY - PYOBJECT_CLOSE(load_default_tuple_temp); + PYOBJECT_CLOSEREF(load_default_tuple_temp); #endif #endif PYOBJECT_CLOSEREF(res); From ab168b51c816ce048c67952bcd4a169cc78bb328 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 14 Sep 2023 16:09:33 +0200 Subject: [PATCH 176/286] Used better macro to close loaded handles --- Cython/Compiler/ModuleNode.py | 10 +++------- Cython/Compiler/Nodes.py | 4 +--- Cython/Utility/CythonFunction.c | 16 ++++------------ Cython/Utility/Exceptions.c | 4 +--- Cython/Utility/ObjectHandling.c | 8 ++------ 5 files changed, 11 insertions(+), 31 deletions(-) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 40489270fe8..7f8931b8898 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -3102,9 +3102,7 @@ def generate_module_init_func(self, imported_modules, env, code): code.putln("if (API_IS_EQUAL(temp_module_cname, %s)) return 0;" % ( Naming.pymodinit_module_arg, )) - code.putln("#if CYTHON_USING_HPY") - code.putln("PYOBJECT_CLOSEREF(temp_module_cname);") - code.putln("#endif") + code.putln("PYOBJECT_GLOBAL_CLOSEREF(temp_module_cname);") code.putln('PyErr_SetString(PyExc_RuntimeError,' ' "Module \'%s\' has already been imported. Re-initialisation is not supported.");' % env.module_name.as_c_string_literal()[1:-1]) @@ -3181,10 +3179,8 @@ def generate_module_init_func(self, imported_modules, env, code): code.putln("PYOBJECT_TYPE temp_intern_main = PYOBJECT_GLOBAL_LOAD(%s);" % code.intern_identifier(EncodedString("__main__"))) code.put_error_if_neg(self.pos, 'PYOBJECT_SET_ATTR(%s, temp_intern_name, temp_intern_main)' % Naming.pymodinit_module_arg) - code.putln("#if CYTHON_USING_HPY") - code.putln("PYOBJECT_CLOSEREF(temp_intern_name);") - code.putln("PYOBJECT_CLOSEREF(temp_intern_main);") - code.putln("#endif") + code.putln("PYOBJECT_GLOBAL_CLOSEREF(temp_intern_name);") + code.putln("PYOBJECT_GLOBAL_CLOSEREF(temp_intern_main);") code.putln("}") # set up __file__ and __path__, then add the module to sys.modules diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 7dffbc306db..92251000cbe 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -1743,9 +1743,7 @@ def generate_execution_code(self, code): temp, code.error_goto(item.pos))) code.put_decref_clear(temp, PyrexTypes.py_object_type) - code.putln("#if CYTHON_USING_HPY") - code.putln("PYOBJECT_CLOSEREF(%s);" % load_moddict_temp) - code.putln("#endif") + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % load_moddict_temp) code.funcstate.release_temp(load_moddict_temp) code.funcstate.release_temp(temp) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index a8115914567..0cba2eeec6c 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -398,11 +398,9 @@ __Pyx_CyFunction_get_code(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncD PYOBJECT_TYPE load_func_code_temp = PYOBJECT_FIELD_LOAD(op, struct_op->func_code); PYOBJECT_TYPE result = API_IS_NULL(load_func_code_temp) ? load_func_code_temp : API_ASSIGN_NONE; CYTHON_UNUSED_VAR(context); -#if CYTHON_USING_HPY if (API_IS_NULL(load_func_code_temp)) { - PYOBJECT_CLOSEREF(load_func_code_temp); + PYOBJECT_GLOBAL_CLOSEREF(load_func_code_temp); } -#endif #if !CYTHON_USING_HPY Py_INCREF(result); #endif @@ -437,9 +435,7 @@ __Pyx_CyFunction_init_defaults(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_ PYOBJECT_TYPE load_default_kwdict_temp = PYOBJECT_FIELD_LOAD(op, struct_op->defaults_kwdict); if (unlikely(API_IS_NULL(load_default_kwdict_temp))) result = -1; } - #if CYTHON_USING_HPY - PYOBJECT_CLOSEREF(load_default_tuple_temp); - #endif + PYOBJECT_GLOBAL_CLOSEREF(load_default_tuple_temp); #endif PYOBJECT_CLOSEREF(res); return result; @@ -597,9 +593,7 @@ __Pyx_CyFunction_get_is_coroutine(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObje if (is_coroutine) { PYOBJECT_TYPE load_marker_temp = PYOBJECT_GLOBAL_LOAD(PYIDENT("_is_coroutine")); PyObject *module, *fromlist, *marker = HPY_LEGACY_OBJECT_AS(load_marker_temp); -#if CYTHON_USING_HPY - PYOBJECT_CLOSEREF(load_marker_temp); -#endif + PYOBJECT_GLOBAL_CLOSEREF(load_marker_temp); fromlist = PyList_New(1); if (unlikely(!fromlist)) return API_NULL_VALUE; Py_INCREF(marker); @@ -614,9 +608,7 @@ __Pyx_CyFunction_get_is_coroutine(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObje #endif PYOBJECT_TYPE load_asyncio_temp = PYOBJECT_GLOBAL_LOAD(PYIDENT("asyncio.coroutines")); module = PyImport_ImportModuleLevelObject(HPY_LEGACY_OBJECT_AS(load_asyncio_temp), NULL, NULL, fromlist, 0); -#if CYTHON_USING_HPY - PYOBJECT_CLOSEREF(load_asyncio_temp); -#endif + PYOBJECT_GLOBAL_CLOSEREF(load_asyncio_temp); Py_DECREF(fromlist); if (unlikely(!module)) goto ignore; PYOBJECT_FIELD_STORE(op, struct_op->func_is_coroutine, __Pyx_PyObject_GetAttrStr(HPY_LEGACY_OBJECT_FROM(module), HPY_LEGACY_OBJECT_FROM(marker))); diff --git a/Cython/Utility/Exceptions.c b/Cython/Utility/Exceptions.c index 8415de6c8a6..87912dc13cf 100644 --- a/Cython/Utility/Exceptions.c +++ b/Cython/Utility/Exceptions.c @@ -1015,9 +1015,7 @@ static void __Pyx_AddTraceback(HPY_CONTEXT_FIRST_ARG_DEF const char *funcname, i HPY_LEGACY_OBJECT_AS(load_moddict_temp), /*PyObject *globals,*/ 0 /*PyObject *locals*/ ); -#if CYTHON_USING_HPY - PYOBJECT_CLOSEREF(load_moddict_temp); -#endif + PYOBJECT_GLOBAL_CLOSEREF(load_moddict_temp); if (!py_frame) goto bad; __Pyx_PyFrame_SetLineNumber(py_frame, py_line); PyTraceBack_Here(py_frame); diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index e21a4699aa1..1ecad363125 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1435,9 +1435,7 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx__GetModuleGlobalName(HPY_CONTEXT_FIRST_ PYOBJECT_TYPE load_moddict_temp = PYOBJECT_GLOBAL_LOAD($moddict_cname); result = PYOBJECT_GET_ITEM(load_moddict_temp, name); __PYX_UPDATE_DICT_CACHE(load_moddict_temp, result, *dict_cached_value, *dict_version) -#if CYTHON_USING_HPY - PYOBJECT_CLOSEREF(load_moddict_temp); -#endif + PYOBJECT_GLOBAL_CLOSEREF(load_moddict_temp); if (likely_object(result)) { return __Pyx_hNewRef(result); } @@ -2840,9 +2838,7 @@ __Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_DEF PyTypeObject* tp) { PYOBJECT_TYPE load_name_temp = PYOBJECT_GLOBAL_LOAD(PYIDENT("__name__")); PYOBJECT_TYPE name = __Pyx_PyObject_GetAttrStr(HPY_LEGACY_OBJECT_FROM((PyObject *)tp), load_name_temp); -#if CYTHON_USING_HPY - PYOBJECT_CLOSEREF(load_name_temp); -#endif + PYOBJECT_GLOBAL_CLOSEREF(load_name_temp); if (unlikely(API_IS_NULL(name)) || unlikely(!PyUnicode_Check(HPY_LEGACY_OBJECT_AS(name)))) { PyErr_Clear(); #if !CYTHON_USING_HPY From 9cf7a83727ccd75d0fbb42aa6db58fd51651aa6c Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Mon, 2 Oct 2023 12:16:20 +0200 Subject: [PATCH 177/286] Add options to globally specify API and ABI --- runtests.py | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/runtests.py b/runtests.py index e179cffb40a..28a4d031636 100755 --- a/runtests.py +++ b/runtests.py @@ -244,6 +244,8 @@ def update_linetrace_extension(ext): return ext def update_hpy_extension(ext): + if getattr(ext, 'is_cython_hpy_ext', False): + return ext.define_macros.append(('HPY', 1)) import hpy.devel # This should be set up by using the setuptools entrypoint @@ -255,12 +257,12 @@ def update_hpy_extension(ext): hpy_ext.hpydevel = hpy.devel.HPyDevel() dist = get_distutils_distro() if not hasattr(dist, 'hpy_abi'): - # can be 'cpython' or 'universal' - # for now, always use 'cpython' + # can be 'cpython', 'hybrid', or 'universal' dist.hpy_abi = HPY_ABI dist.hpy_use_static_libs = False hpy_ext.distribution = dist hpy_ext._finalize_hpy_ext(ext) + ext.is_cython_hpy_ext = True return ext def update_numpy_extension(ext, set_api17_macro=True): @@ -460,6 +462,7 @@ def get_openmp_compiler_flags(language): COMPILER_HAS_INT128 = False OPENMP_C_COMPILER_FLAGS = get_openmp_compiler_flags('c') OPENMP_CPP_COMPILER_FLAGS = get_openmp_compiler_flags('cpp') +PYTHON_API = None HPY_ABI = None # Return this from the EXT_EXTRAS matcher callback to exclude the extension @@ -1315,6 +1318,9 @@ def run_distutils(self, test_directory, module, workdir, incdir, if 'traceback' not in self.tags['tag']: extension.define_macros.append(("CYTHON_CLINE_IN_TRACEBACK", 1)) + if PYTHON_API == 'hpy': + update_hpy_extension(extension) + # Allow tests to be incrementally enabled with Py_LIMITED_API set. # This is intended to be temporary while limited API support # is improved. Eventually we'll want to move to excluding tests @@ -2451,8 +2457,10 @@ def main(): help="do not capture stdout, stderr in srctree tests. Makes pdb.set_trace interactive") parser.add_option("--limited-api", dest="limited_api", default=False, action="store_true", help="Compiles Cython using CPython's LIMITED_API") - parser.add_option("--hpy-abi", dest="hpy_abi", default="cpython", action="store", - help="Select the HPy ABI to use when running tests.") + parser.add_option("--hpy-abi", dest="hpy_abi", default=None, action="store", + help="Select the HPy ABI to use when compiling and running tests.") + parser.add_option("--api", dest="api", default="capi", action="store", + help="Select the API to use when compiling and running tests.") options, cmd_args = parser.parse_args(args) @@ -2806,10 +2814,23 @@ def runtests(options, cmd_args, coverage=None): if options.compiler: COMPILER = options.compiler + global PYTHON_API global HPY_ABI - HPY_ABI = options.hpy_abi + if options.hpy_abi is not None: + HPY_ABI = options.hpy_abi + else: + HPY_ABI = 'cpython' sys.stderr.write("HPy ABI: '%s'\n" % HPY_ABI) + if options.api not in ('capi', 'hpy'): + sys.stderr.write("Unknown API requested: '%s'" % PYTHON_API) + sys.exit(1) + elif options.hpy_abi is not None and options.api != 'hpy': + PYTHON_API = 'hpy' + else: + PYTHON_API = options.api + sys.stderr.write("Using API: '%s'\n" % PYTHON_API) + selected_backends = [ name.strip() for name in options.backends.split(',') if name.strip() ] backends = [] for backend in selected_backends: From d67ca1d4bcfa0828f2578a1e12a4a17028f89942 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 18 Sep 2023 10:34:11 +0200 Subject: [PATCH 178/286] Made binary operations work on HPy for PyObjects --- Cython/Compiler/ExprNodes.py | 44 ++++++++++++++++++++++++++++++++++-- Cython/Compiler/Nodes.py | 2 ++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index f44debc55e3..6c94a9c771e 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -1235,7 +1235,7 @@ class NoneNode(PyConstNode): # The constant value None is_none = 1 - value = "Py_None" + value = "API_NONE_VALUE" constant_result = None @@ -12055,9 +12055,10 @@ def generate_result_code(self, code): self.operand2.pythran_result())) elif type1.is_pyobject or type2.is_pyobject: function = self.py_operation_function(code) - extra_args = ", Py_None" if self.operator == '**' else "" + extra_args = ", API_NONE_VALUE" if self.operator == '**' else "" op1_result = self.operand1.py_result() if type1.is_pyobject else self.operand1.result() op2_result = self.operand2.py_result() if type2.is_pyobject else self.operand2.result() + code.putln('#if !CYTHON_USING_HPY') code.putln( "%s = %s(%s, %s%s); %s" % ( self.result(), @@ -12067,6 +12068,20 @@ def generate_result_code(self, code): extra_args, code.error_goto_if_null(self.result(), self.pos))) self.generate_gotref(code) + code.putln("#else") + function = self.hpy_operation_function(code) + extra_args = ", API_NONE_VALUE" if self.operator == '**' else "" + op1_result = self.operand1.py_result() if type1.is_pyobject else self.operand1.result() + op2_result = self.operand2.py_result() if type2.is_pyobject else self.operand2.result() + code.putln( + "%s = %s(HPY_CONTEXT_FIRST_ARG_CALL %s, %s%s); %s" % ( + self.result(), + function, + op1_result, + op2_result, + extra_args, + code.error_goto_if_null_object(self.result(), self.pos))) + code.putln("#endif") elif self.is_temp: # C++ overloaded operators with exception values are currently all # handled through temporaries. @@ -12098,6 +12113,9 @@ def analyse_types(self, env): def py_operation_function(self, code): return "" + def hpy_operation_function(self, code): + return "" + def calculate_result_code(self): return "(%s %s %s)" % ( self.operand1.result(), @@ -12244,6 +12262,12 @@ def py_operation_function(self, code): if self.inplace: function_name = function_name.replace('PyNumber_', 'PyNumber_InPlace') return function_name + + def hpy_operation_function(self, code): + function_name = self.hpy_functions[self.operator] + if self.inplace: + function_name = function_name.replace('PyNumber_', 'PyNumber_InPlace') + return function_name py_functions = { "|": "PyNumber_Or", @@ -12261,6 +12285,22 @@ def py_operation_function(self, code): "**": "PyNumber_Power", } + hpy_functions = { + "|": "HPy_Or", + "^": "HPy_Xor", + "&": "HPy_And", + "<<": "HPy_Lshift", + ">>": "HPy_Rshift", + "+": "HPy_Add", + "-": "HPy_Subtract", + "*": "HPy_Multiply", + "@": "HPy_MatrixMultiply", + "/": "HPy_TrueDivide", + "//": "HPy_FloorDivide", + "%": "HPy_Remainder", + "**": "HPy_Power", + } + overflow_op_names = { "+": "add", "-": "sub", diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 92251000cbe..7cbaaa18065 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -2279,6 +2279,8 @@ def assure_gil(code_path, code=code): err_val = default_retval if err_val is not None: if err_val != Naming.retval_cname: + if err_val == "NULL": + err_val = "API_NULL_VALUE" code.putln("%s = %s;" % (Naming.retval_cname, err_val)) elif not return_type.is_void: code.putln("__Pyx_pretend_to_initialize(&%s);" % Naming.retval_cname) From 74d70f1b3b9db2a86f33b6ec392e39998ed169f6 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 18 Sep 2023 14:21:52 +0200 Subject: [PATCH 179/286] Range()-based for loops now supported --- Cython/Compiler/Code.py | 6 ++-- Cython/Compiler/ExprNodes.py | 4 +-- Cython/Compiler/ModuleNode.py | 2 +- Cython/Compiler/Nodes.py | 4 +-- Cython/Compiler/PyrexTypes.py | 2 +- Cython/Utility/ObjectHandling.c | 10 ++++--- Cython/Utility/Optimize.c | 30 +++++++++---------- Cython/Utility/TypeConversion.c | 53 ++++++++++++++++++++++----------- 8 files changed, 66 insertions(+), 45 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index cc23cfc10a8..25f47ad88a7 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1202,7 +1202,7 @@ def initialize_main_c_code(self): else: w = self.parts['cached_builtins'] w.enter_cfunc_scope() - w.putln("static CYTHON_SMALL_CODE int __Pyx_InitCachedBuiltins(void) {") + w.putln("static CYTHON_SMALL_CODE int __Pyx_InitCachedBuiltins(HPY_CONTEXT_ONLY_ARG_DEF) {") w = self.parts['cached_constants'] w.enter_cfunc_scope() @@ -1331,7 +1331,7 @@ def close_global_decls(self): w.exit_cfunc_scope() def put_pyobject_decl(self, entry): - self['global_var'].putln("static PyObject *%s;" % entry.cname) + self['global_var'].putln("static PYOBJECT_GLOBAL_TYPE %s;" % entry.cname) # constant handling at code generation time @@ -1516,7 +1516,7 @@ def put_cached_builtin_init(self, pos, name, cname): interned_cname = self.get_interned_identifier(name).cname self.use_utility_code( UtilityCode.load_cached("GetBuiltinName", "ObjectHandling.c")) - w.putln('%s = __Pyx_GetBuiltinName(HPY_CONTEXT_FIRST_ARG_CALL %s); if (!%s) %s' % ( + w.putln('PYOBJECT_GLOBAL_STORE(%s, __Pyx_GetBuiltinName(HPY_CONTEXT_FIRST_ARG_CALL PYOBJECT_GLOBAL_LOAD(%s))); if (API_IS_NULL(%s)) %s' % ( cname, interned_cname, cname, diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 6c94a9c771e..fed94ebcb37 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -2467,7 +2467,7 @@ def generate_result_code(self, code): code.globalstate.use_utility_code( UtilityCode.load_cached("GetBuiltinName", "ObjectHandling.c")) code.putln( - '%s = __Pyx_GetBuiltinName(HPY_CONTEXT_FIRST_ARG_CALL %s); %s' % ( + '%s = __Pyx_GetBuiltinName(HPY_CONTEXT_FIRST_ARG_CALL PYOBJECT_GLOBAL_LOAD(%s)); %s' % ( self.result(), interned_cname, code.error_goto_if_null_object(self.result(), self.pos))) @@ -14427,7 +14427,7 @@ def generate_result_code(self, code): self.arg.result(), self.result(), self.target_type), - code.error_goto_if_null(self.result(), self.pos))) + code.error_goto_if_null_object(self.result(), self.pos))) self.generate_gotref(code) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 7f8931b8898..7c2b3f53331 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -3188,7 +3188,7 @@ def generate_module_init_func(self, imported_modules, env, code): if Options.cache_builtins: code.putln("/*--- Builtin init code ---*/") - code.put_error_if_neg(self.pos, "__Pyx_InitCachedBuiltins()") + code.put_error_if_neg(self.pos, "__Pyx_InitCachedBuiltins(HPY_CONTEXT_ONLY_ARG_CALL)") code.putln("/*--- Constants init code ---*/") code.put_error_if_neg(self.pos, "__Pyx_InitCachedConstants(HPY_CONTEXT_ONLY_ARG_CALL)") diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 7cbaaa18065..b6f7a37fd12 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -4584,11 +4584,11 @@ def generate_arg_conversion_to_pyobject(self, arg, code): old_type = arg.hdr_type func = old_type.to_py_function if func: - code.putln("%s = %s(%s); %s" % ( + code.putln("%s = %s(HPY_CONTEXT_FIRST_ARG_CALL %s); %s" % ( arg.entry.cname, func, arg.hdr_cname, - code.error_goto_if_null(arg.entry.cname, arg.pos))) + code.error_goto_if_null_object(arg.entry.cname, arg.pos))) code.put_var_gotref(arg.entry) else: error(arg.pos, "Cannot convert argument of type '%s' to Python object" % old_type) diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 06d4b719760..72888583cd2 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -1759,7 +1759,7 @@ def to_py_call_code(self, source_code, result_code, result_type, to_py_function= func = func.replace("Object", result_type_name.title(), 1) elif result_type_name == 'bytearray': func = func.replace("Object", "ByteArray", 1) - return '%s = %s(%s)' % ( + return '%s = %s(HPY_CONTEXT_FIRST_ARG_CALL %s)' % ( result_code, func, source_code or 'NULL') diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 1ecad363125..5d1741cddfd 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1554,20 +1554,22 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStrNoError(HPY_CONTEXT_ /////////////// PyObjectGetAttrStr.proto /////////////// #if CYTHON_USE_TYPE_SLOTS -static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStr(PyObject* obj, PyObject* attr_name);/*proto*/ +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStr(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, PYOBJECT_TYPE attr_name);/*proto*/ #else -#define __Pyx_PyObject_GetAttrStr(o,n) PYOBJECT_GET_ATTR(o,n) //Needed to turn this into a function macro to be able to pass the context properly +#define __Pyx_PyObject_GetAttrStr(o,n) DICT_GET_ITEM(o,n) //Needed to turn this into a function macro to be able to pass the context properly #define __Pyx_PyObject_GetAttrStr_legacy(o,n) PyObject_GetAttr(o,n) //Used for functions where the context isn't reachable yet #endif /////////////// PyObjectGetAttrStr /////////////// #if CYTHON_USE_TYPE_SLOTS -static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStr(PyObject* obj, PyObject* attr_name) { +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStr(PYOBJECT_TYPE obj, PYOBJECT_TYPE attr_name) { +#if !CYTHON_USING_HPY PyTypeObject* tp = Py_TYPE(obj); if (likely(tp->tp_getattro)) return tp->tp_getattro(obj, attr_name); - return PyObject_GetAttr(obj, attr_name); +#endif + return DICT_GET_ITEM(obj, attr_name); } #endif diff --git a/Cython/Utility/Optimize.c b/Cython/Utility/Optimize.c index 444ccce91fd..420e56fd273 100644 --- a/Cython/Utility/Optimize.c +++ b/Cython/Utility/Optimize.c @@ -1081,13 +1081,13 @@ static PyObject* __Pyx__PyNumber_PowerOf2(PyObject *two, PyObject *exp, PyObject /////////////// PyIntCompare.proto /////////////// -{{py: c_ret_type = 'PyObject*' if ret_type.is_pyobject else 'int'}} -static CYTHON_INLINE {{c_ret_type}} __Pyx_PyInt_{{'' if ret_type.is_pyobject else 'Bool'}}{{op}}{{order}}(PyObject *op1, PyObject *op2, long intval, long inplace); /*proto*/ +{{py: c_ret_type = 'PYOBJECT_TYPE' if ret_type.is_pyobject else 'int'}} +static CYTHON_INLINE {{c_ret_type}} __Pyx_PyInt_{{'' if ret_type.is_pyobject else 'Bool'}}{{op}}{{order}}(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE op1, PYOBJECT_TYPE op2, long intval, long inplace); /*proto*/ /////////////// PyIntCompare /////////////// {{py: pyval, ival = ('op2', 'b') if order == 'CObj' else ('op1', 'a') }} -{{py: c_ret_type = 'PyObject*' if ret_type.is_pyobject else 'int'}} +{{py: c_ret_type = 'PYOBJECT_TYPE' if ret_type.is_pyobject else 'int'}} {{py: return_true = 'Py_RETURN_TRUE' if ret_type.is_pyobject else 'return 1'}} {{py: return_false = 'Py_RETURN_FALSE' if ret_type.is_pyobject else 'return 0'}} {{py: slot_name = op.lower() }} @@ -1101,7 +1101,7 @@ return_compare = ( ) }} -static CYTHON_INLINE {{c_ret_type}} __Pyx_PyInt_{{'' if ret_type.is_pyobject else 'Bool'}}{{op}}{{order}}(PyObject *op1, PyObject *op2, long intval, long inplace) { +static CYTHON_INLINE {{c_ret_type}} __Pyx_PyInt_{{'' if ret_type.is_pyobject else 'Bool'}}{{op}}{{order}}(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE op1, PYOBJECT_TYPE op2, long intval, long inplace) { CYTHON_MAYBE_UNUSED_VAR(intval); CYTHON_UNUSED_VAR(inplace); if (op1 == op2) { @@ -1157,9 +1157,9 @@ static CYTHON_INLINE {{c_ret_type}} __Pyx_PyInt_{{'' if ret_type.is_pyobject els /////////////// PyIntBinop.proto /////////////// -{{py: c_ret_type = 'PyObject*' if ret_type.is_pyobject else 'int'}} +{{py: c_ret_type = 'PYOBJECT_TYPE' if ret_type.is_pyobject else 'int'}} #if !CYTHON_COMPILING_IN_PYPY -static {{c_ret_type}} __Pyx_PyInt_{{'' if ret_type.is_pyobject else 'Bool'}}{{op}}{{order}}(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check); /*proto*/ +static {{c_ret_type}} __Pyx_PyInt_{{'' if ret_type.is_pyobject else 'Bool'}}{{op}}{{order}}(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE op1, PYOBJECT_TYPE op2, long intval, int inplace, int zerodivision_check); /*proto*/ #else #define __Pyx_PyInt_{{'' if ret_type.is_pyobject else 'Bool'}}{{op}}{{order}}(op1, op2, intval, inplace, zerodivision_check) \ {{if op in ('Eq', 'Ne')}}{{'' if ret_type.is_pyobject else '__Pyx_PyObject_IsTrueAndDecref'}}(PyObject_RichCompare(op1, op2, Py_{{op.upper()}})) @@ -1172,7 +1172,7 @@ static {{c_ret_type}} __Pyx_PyInt_{{'' if ret_type.is_pyobject else 'Bool'}}{{op #if !CYTHON_COMPILING_IN_PYPY {{py: from Cython.Utility import pylong_join }} {{py: pyval, ival = ('op2', 'b') if order == 'CObj' else ('op1', 'a') }} -{{py: c_ret_type = 'PyObject*' if ret_type.is_pyobject else 'int'}} +{{py: c_ret_type = 'PYOBJECT_TYPE' if ret_type.is_pyobject else 'int'}} {{py: return_true = 'Py_RETURN_TRUE' if ret_type.is_pyobject else 'return 1'}} {{py: return_false = 'Py_RETURN_FALSE' if ret_type.is_pyobject else 'return 0'}} {{py: slot_name = {'TrueDivide': 'true_divide', 'FloorDivide': 'floor_divide'}.get(op, op.lower()) }} @@ -1194,7 +1194,7 @@ def zerodiv_check(operand, optype='integer', _is_mod=op == 'Remainder', _needs_c ) if _needs_check else '') }} -static {{c_ret_type}} {{cfunc_name}}(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check) { +static {{c_ret_type}} {{cfunc_name}}(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE op1, PYOBJECT_TYPE op2, long intval, int inplace, int zerodivision_check) { CYTHON_MAYBE_UNUSED_VAR(intval); CYTHON_MAYBE_UNUSED_VAR(inplace); CYTHON_UNUSED_VAR(zerodivision_check); @@ -1221,7 +1221,7 @@ static {{c_ret_type}} {{cfunc_name}}(PyObject *op1, PyObject *op2, long intval, // Calling PyLong_CompactValue() requires the PyLong value to be compact, we only need the last digit. long last_digit = (long) __Pyx_PyLong_Digits({{pyval}})[0]; long result = intval & (likely(__Pyx_PyLong_IsPos({{pyval}})) ? last_digit : (PyLong_MASK - last_digit + 1)); - return PyLong_FromLong(result); + return HPY_LEGACY_OBJECT_FROM(PyLong_FromLong(result)); } {{endif}} // special cases for 0: + - * % / // | ^ & >> << @@ -1282,7 +1282,7 @@ static {{c_ret_type}} {{cfunc_name}}(PyObject *op1, PyObject *op2, long intval, default: {{return_false if op == 'Eq' else return_true}}; #endif {{else}} - default: return PyLong_Type.tp_as_number->nb_{{slot_name}}(op1, op2); + default: return HPY_LEGACY_OBJECT_FROM(PyLong_Type.tp_as_number->nb_{{slot_name}}(op1, op2)); {{endif}} } } @@ -1300,7 +1300,7 @@ static {{c_ret_type}} {{cfunc_name}}(PyObject *op1, PyObject *op2, long intval, ll{{ival}} = {{ival}}; goto long_long; #else - return PyLong_Type.tp_as_number->nb_{{slot_name}}(op1, op2); + return HPY_LEGACY_OBJECT_FROM(PyLong_Type.tp_as_number->nb_{{slot_name}}(op1, op2)); #endif {{elif c_op == '%'}} // see CMath.c :: ModInt utility code @@ -1309,7 +1309,7 @@ static {{c_ret_type}} {{cfunc_name}}(PyObject *op1, PyObject *op2, long intval, {{elif op == 'TrueDivide'}} if ((8 * sizeof(long) <= 53 || likely(labs({{ival}}) <= ((PY_LONG_LONG)1 << 53))) || __Pyx_PyLong_DigitCount({{pyval}}) <= 52 / PyLong_SHIFT) { - return PyFloat_FromDouble((double)a / (double)b); + return HPY_LEGACY_OBJECT_FROM(PyFloat_FromDouble((double)a / (double)b)); } return PyLong_Type.tp_as_number->nb_{{slot_name}}(op1, op2); {{elif op == 'FloorDivide'}} @@ -1334,7 +1334,7 @@ static {{c_ret_type}} {{cfunc_name}}(PyObject *op1, PyObject *op2, long intval, #endif {{endif}} {{endif}} - return PyLong_FromLong(x); + return HPY_LEGACY_OBJECT_FROM(PyLong_FromLong(x)); {{if op != 'TrueDivide'}} #ifdef HAVE_LONG_LONG @@ -1358,7 +1358,7 @@ static {{c_ret_type}} {{cfunc_name}}(PyObject *op1, PyObject *op2, long intval, if (likely(lla == llx >> llb)) /* then execute 'return' below */ {{endif}} {{endif}} - return PyLong_FromLongLong(llx); + return HPY_LEGACY_OBJECT_FROM(PyLong_FromLongLong(llx)); #endif {{endif}}{{# if op != 'TrueDivide' #}} {{endif}}{{# if op in ('Eq', 'Ne') #}} @@ -1379,7 +1379,7 @@ static {{c_ret_type}} {{cfunc_name}}(PyObject *op1, PyObject *op2, long intval, double result; {{zerodiv_check('b', 'float')}} result = ((double)a) {{c_op}} (double)b; - return PyFloat_FromDouble(result); + return HPY_LEGACY_OBJECT_FROM(PyFloat_FromDouble(result)); {{endif}} } {{endif}} diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index 5ac360fea5f..a1c356490cc 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -677,13 +677,13 @@ static CYTHON_INLINE Py_UNICODE __Pyx_PyObject_AsPy_UNICODE(PyObject* x) { /////////////// CIntToPy.proto /////////////// -static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value); +static CYTHON_INLINE PYOBJECT_TYPE {{TO_PY_FUNCTION}}(HPY_CONTEXT_FIRST_ARG_DEF {{TYPE}} value); /////////////// CIntToPy /////////////// //@requires: GCCDiagnostics //@requires: ObjectHandling.c::PyObjectVectorCallKwBuilder -static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value) { +static CYTHON_INLINE PYOBJECT_TYPE {{TO_PY_FUNCTION}}(HPY_CONTEXT_FIRST_ARG_DEF {{TYPE}} value) { #ifdef __Pyx_HAS_GCC_DIAGNOSTIC #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" @@ -695,20 +695,20 @@ static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value) { const int is_unsigned = neg_one > const_zero; if (is_unsigned) { if (sizeof({{TYPE}}) < sizeof(long)) { - return PyInt_FromLong((long) value); + return PYOBJECT_FROM_LONG((long) value); } else if (sizeof({{TYPE}}) <= sizeof(unsigned long)) { - return PyLong_FromUnsignedLong((unsigned long) value); + return HPY_LEGACY_OBJECT_FROM(PyLong_FromUnsignedLong((unsigned long) value)); #ifdef HAVE_LONG_LONG } else if (sizeof({{TYPE}}) <= sizeof(unsigned PY_LONG_LONG)) { - return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG) value); + return HPY_LEGACY_OBJECT_FROM(PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG) value)); #endif } } else { if (sizeof({{TYPE}}) <= sizeof(long)) { - return PyInt_FromLong((long) value); + return PYOBJECT_FROM_LONG((long) value); #ifdef HAVE_LONG_LONG } else if (sizeof({{TYPE}}) <= sizeof(PY_LONG_LONG)) { - return PyLong_FromLongLong((PY_LONG_LONG) value); + return HPY_LEGACY_OBJECT_FROM(PyLong_FromLongLong((PY_LONG_LONG) value)); #endif } } @@ -720,14 +720,31 @@ static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value) { little, !is_unsigned); #else // call int.from_bytes() - PyObject *from_bytes, *result = NULL, *kwds = NULL; - PyObject *py_bytes = NULL, *order_str = NULL; - from_bytes = PyObject_GetAttrString((PyObject*)&PyLong_Type, "from_bytes"); - if (!from_bytes) return NULL; - py_bytes = PyBytes_FromStringAndSize((char*)bytes, sizeof({{TYPE}})); - if (!py_bytes) goto limited_bad; + PYOBJECT_TYPE from_bytes; + PYOBJECT_TYPE result = API_NULL_VALUE; + PYOBJECT_TYPE py_bytes = API_NULL_VALUE; + PYOBJECT_TYPE arg_tuple = API_NULL_VALUE; + PYOBJECT_TYPE kwds = API_NULL_VALUE; + PYOBJECT_TYPE order_str = API_NULL_VALUE; + from_bytes = PYOBJECT_GET_ATTR_STR(HPY_LEGACY_OBJECT_FROM((PyObject*)&PyLong_Type), "from_bytes"); + if (API_IS_NULL(from_bytes)) return API_NULL_VALUE; + py_bytes = BYTES_FROM_STR_AND_SIZE((char*)bytes, sizeof({{TYPE}})); + if (API_IS_NULL(py_bytes)) goto limited_bad; // I'm deliberately not using PYIDENT here because this code path is very unlikely // to ever run so it seems a pessimization mostly. +#if CYTHON_USING_HPY + order_str = PYOBJECT_FROM_STRING(little ? "little" : "big"); + if (API_IS_NULL(order_str)) goto limited_bad; + arg_tuple = TUPLE_PACK(2, py_bytes, order_str); + if (API_IS_NULL(arg_tuple)) goto limited_bad; + if (!is_unsigned) { + // default is signed=False + kwds = DICT_NEW(); + if (API_IS_NULL(kwds)) goto limited_bad; + if (DICT_SET_ITEM_STR(kwds, "signed", __Pyx_hNewRef(Py_True))) goto limited_bad; + } + result = API_CALL_FUNC(from_bytes, &arg_tuple, 2, kwds); +#else order_str = PyUnicode_FromString(little ? "little" : "big"); if (!order_str) goto limited_bad; { @@ -739,12 +756,14 @@ static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value) { } result = __Pyx_Object_Vectorcall_CallFromBuilder(from_bytes, args+1, 2 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET, kwds); } +#endif limited_bad: - Py_XDECREF(kwds); - Py_XDECREF(order_str); - Py_XDECREF(py_bytes); - Py_XDECREF(from_bytes); + Py_XCLOSEREF(from_bytes); + Py_XCLOSEREF(py_bytes); + Py_XCLOSEREF(order_str); + Py_XCLOSEREF(arg_tuple); + Py_XCLOSEREF(kwds); return result; #endif } From f13df7698cd14eabc5e636416167d28ae8ba3944 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 18 Sep 2023 14:34:35 +0200 Subject: [PATCH 180/286] Made forloops based on tuples work on HPy --- Cython/Compiler/ExprNodes.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index fed94ebcb37..f1b46ce59e5 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -3072,11 +3072,13 @@ def generate_result_code(self, code): self.sequence.py_result())) if is_builtin_sequence or self.may_be_a_sequence: + code.putln("#if !CYTHON_USING_HPY") code.putln("%s = %s; __Pyx_INCREF(%s);" % ( self.result(), self.sequence.py_result(), self.result(), )) + code.putln("#endif") self.counter_cname = code.funcstate.allocate_temp( PyrexTypes.c_py_ssize_t_type, manage_ref=False) if self.reversed: @@ -3091,9 +3093,15 @@ def generate_result_code(self, code): code.putln("--%s;" % self.counter_cname) # len -> last item else: init_value = '0' + code.putln("#if !CYTHON_USING_HPY") code.putln("%s = __Pyx_NewRef(%s);" % ( self.result(), self.sequence.py_result())) + code.putln("#else") + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % ( + self.result(), + self.sequence.py_result())) + code.putln("#endif") code.putln("%s = 0;" % self.counter_cname) if not is_builtin_sequence: @@ -3162,13 +3170,13 @@ def generate_next_sequence_item(self, test_name, result_name, code): )) code.putln("#else") code.putln( - "%s = __Pyx_PySequence_ITEM(%s, %s); %s%s; %s" % ( + "%s = TUPLE_GET_ITEM(%s, %s); %s%s; %s" % ( result_name, self.py_result(), self.counter_cname, self.counter_cname, inc_dec, - code.error_goto_if_null(result_name, self.pos))) + code.error_goto_if_null_object(result_name, self.pos))) code.put_gotref(result_name, py_object_type) code.putln("#endif") From dc508e63b3da87cc62422777f16ee50606e3d52f Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Tue, 19 Sep 2023 13:28:17 +0200 Subject: [PATCH 181/286] Integer variables can now be declared inside functions --- Cython/Compiler/Code.py | 4 ++-- Cython/Compiler/ExprNodes.py | 27 ++++++++++++++++++++++----- Cython/Utility/CythonFunction.c | 23 +++++++++++++++++++++-- Cython/Utility/Optimize.c | 10 +++++++--- Cython/Utility/TypeConversion.c | 2 +- 5 files changed, 53 insertions(+), 13 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 25f47ad88a7..634a96a9e35 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -2319,9 +2319,9 @@ def put_init_to_py_none(self, cname, type, nanny=True): from .PyrexTypes import py_object_type, typecast py_none = typecast(type, py_object_type, "Py_None") if nanny: - self.putln("%s = __Pyx_NewRef(Py_None);" % (cname)) + self.putln("%s = __Pyx_hNewRef(API_NONE_VALUE);" % (cname)) else: - self.putln("%s = Py_NewRef(Py_None);" % (cname)) + self.putln("%s = PYOBJECT_NEWREF(API_NONE_VALUE);" % (cname)) def put_init_var_to_py_none(self, entry, template = "%s", nanny=True): code = template % entry.cname diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index f1b46ce59e5..6da90678ec6 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -2660,7 +2660,10 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, if is_pythran_expr(self.type): code.putln('new (&%s) decltype(%s){%s};' % (self.result(), self.result(), result)) elif result != self.result(): - code.putln('%s = %s;' % (self.result(), result)) + if hasattr(rhs, "is_global") and rhs.is_global: + code.putln('%s = PYOBJECT_GLOBAL_LOAD(%s);' % (self.result(), result)) + else: + code.putln('%s = %s;' % (self.result(), result)) if debug_disposal_code: print("NameNode.generate_assignment_code:") print("...generating post-assignment code for %s" % rhs) @@ -6428,7 +6431,18 @@ def c_call_code(self): for actual_arg in self.args[len(formal_args):]: arg_list_code.append(actual_arg.move_result_rhs()) - result = "%s(%s)" % (self.function.result(), ', '.join(arg_list_code)) + arg_string = "" + for arg in arg_list_code: + if arg.startswith("__pyx_") and not arg.startswith("__pyx_t_") and not arg.startswith("__pyx_v_"): + arg_string = arg_string + "PYOBJECT_GLOBAL_LOAD(" + arg + ")" #temp fix for globals and non-globals being handled by the same code + + else: + arg_string = arg_string + "%s" % arg + arg_string = arg_string + ", " + arg_string = arg_string[0:-2] + + + result = "%s(HPY_CONTEXT_FIRST_ARG_CALL %s)" % (self.function.result(), arg_string) return result def is_c_result_required(self): @@ -6510,6 +6524,7 @@ def generate_result_code(self, code): elif func_type.is_cfunction: nogil = not code.funcstate.gil_owned if self.has_optional_args: + code.putln("//Has Optional Args") actual_nargs = len(self.args) expected_nargs = len(func_type.args) - func_type.optional_arg_count self.opt_arg_struct = code.funcstate.allocate_temp( @@ -6526,11 +6541,12 @@ def generate_result_code(self, code): actual_arg.result_as(formal_arg.type))) exc_checks = [] if self.type.is_pyobject and self.is_temp: - exc_checks.append("!%s" % self.result()) + exc_checks.append("API_IS_NULL(%s)" % self.result()) elif self.type.is_memoryviewslice: assert self.is_temp exc_checks.append(self.type.error_condition(self.result())) elif func_type.exception_check != '+': + code.putln("//Exception Check is not +") exc_val = func_type.exception_value exc_check = func_type.exception_check if exc_val is not None: @@ -6549,7 +6565,8 @@ def generate_result_code(self, code): else: exc_checks.append("PyErr_Occurred()") if self.is_temp or exc_checks: - rhs = self.c_call_code() + rhs = self.c_call_code(code) + code.putln("//aaa %s" % rhs) if self.result(): lhs = "%s = " % self.result() if self.is_temp and self.type.is_pyobject: @@ -6640,7 +6657,7 @@ def generate_evaluation_code(self, code): else: function = code.funcstate.allocate_temp(py_object_type, manage_ref=True) self.function.make_owned_reference(code) - code.put("%s = %s; " % (function, self.function.py_result())) + code.put("%s = %s;" % (function, self.function.py_result())) self.function.generate_disposal_code(code) self.function.free_temps(code) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 0cba2eeec6c..2a2d56bff20 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -104,7 +104,9 @@ static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFun PYOBJECT_TYPE module, PYOBJECT_TYPE globals, PYOBJECT_TYPE code); -static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(__pyx_CyFunctionObject* f, PyObject* classobj); +static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(HPY_CONTEXT_FIRST_ARG_DEF + __pyx_CyFunctionObject_FuncDef f, + PYOBJECT_TYPE classobj); static CYTHON_INLINE void *__Pyx_CyFunction_InitDefaults(PyObject *m, size_t size, int pyobjects); @@ -160,17 +162,34 @@ static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void *cfunc) } #endif -static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(__pyx_CyFunctionObject* f, PyObject* classobj) { +#if CYTHON_COMPILING_IN_LIMITED_API +static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void *cfunc) { + if (__Pyx_CyFunction_Check(func)) { + return PyCFunction_GetFunction(((__pyx_CyFunctionObject*)func)->func) == (PyCFunction) cfunc; + } else if (PyCFunction_Check(func)) { + return PyCFunction_GetFunction(func) == (PyCFunction) cfunc; + } + return 0; +} +#else +static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void *cfunc) { + return __Pyx_CyOrPyCFunction_Check(func) && __Pyx_CyOrPyCFunction_GET_FUNCTION(func) == (PyCFunction) cfunc; +} +#endif + +static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef f, PYOBJECT_TYPE classobj) { #if PY_VERSION_HEX < 0x030900B1 || (CYTHON_COMPILING_IN_LIMITED_API && !CYTHON_USING_HPY) __Pyx_Py_XDECREF_SET( __Pyx_CyFunction_GetClassObj(f), ((classobj) ? __Pyx_NewRef(classobj) : NULL)); #else +#if !CYTHON_USING_HPY //Need to figure out how to port this behaviour in HPy __Pyx_Py_XDECREF_SET( // assigning to "mm_class", which is a "PyTypeObject*" ((PyCMethodObject *) (f))->mm_class, (PyTypeObject*)((classobj) ? __Pyx_NewRef(classobj) : NULL)); #endif +#endif } #if CYTHON_USING_HPY diff --git a/Cython/Utility/Optimize.c b/Cython/Utility/Optimize.c index 420e56fd273..8d30a1d5c3a 100644 --- a/Cython/Utility/Optimize.c +++ b/Cython/Utility/Optimize.c @@ -1366,9 +1366,9 @@ static {{c_ret_type}} {{cfunc_name}}(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE op1 #endif {{if c_op in '+-*' or op in ('TrueDivide', 'Eq', 'Ne')}} - if (PyFloat_CheckExact({{pyval}})) { + if (PyFloat_CheckExact(HPY_LEGACY_OBJECT_AS({{pyval}}))) { const long {{'a' if order == 'CObj' else 'b'}} = intval; - double {{ival}} = __Pyx_PyFloat_AS_DOUBLE({{pyval}}); + double {{ival}} = __Pyx_PyFloat_AS_DOUBLE(HPY_LEGACY_OBJECT_AS({{pyval}})); {{if op in ('Eq', 'Ne')}} if ((double)a {{c_op}} (double)b) { {{return_true}}; @@ -1388,7 +1388,11 @@ static {{c_ret_type}} {{cfunc_name}}(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE op1 return {{'' if ret_type.is_pyobject else '__Pyx_PyObject_IsTrueAndDecref'}}( PyObject_RichCompare(op1, op2, Py_{{op.upper()}})); {{else}} - return (inplace ? PyNumber_InPlace{{op}} : PyNumber_{{op}})(op1, op2); +#if CYTHON_USING_HPY + return HPy_{{op}}(HPY_CONTEXT_CNAME, op1, op2); +#else + return (inplace ? PyNumber_InPlace{{op}} : PyNumber_{{op}})(HPY_LEGACY_OBJECT_AS(op1), HPY_LEGACY_OBJECT_AS(op2)); +#endif {{endif}} } #endif diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index a1c356490cc..a01316625a3 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -110,7 +110,7 @@ static CYTHON_INLINE size_t __Pyx_Py_UNICODE_strlen(const Py_UNICODE *u) #define __Pyx_hNewRef(obj) PYOBJECT_NEWREF(obj) //This will be merged into Pyx_NewRef eventually, but #if CYTHON_USING_HPY will make all calls to this macro use HPy, even if it hasn't been ported yet #define __Pyx_NewRef(obj) Py_NewRef(obj) -#define __Pyx_Owned_Py_None(b) __Pyx_NewRef(Py_None) +#define __Pyx_Owned_Py_None(b) __Pyx_hNewRef(API_NONE_VALUE) static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyBool_FromLong(HPY_CONTEXT_FIRST_ARG_DEF long b); static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*); static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject*); From 69383636563e82491d61f9413aff13bfd35ab7af Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 20 Sep 2023 10:22:57 +0200 Subject: [PATCH 182/286] Fixed few small issues for benchmarking --- Cython/Compiler/ExprNodes.py | 3 +-- Cython/Utility/ModuleSetupCode.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 6da90678ec6..f043faccbee 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -6565,8 +6565,7 @@ def generate_result_code(self, code): else: exc_checks.append("PyErr_Occurred()") if self.is_temp or exc_checks: - rhs = self.c_call_code(code) - code.putln("//aaa %s" % rhs) + rhs = self.c_call_code() if self.result(): lhs = "%s = " % self.result() if self.is_temp and self.type.is_pyobject: diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 9deaaf2ba00..8581c01cc7a 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -2069,6 +2069,7 @@ static CYTHON_INLINE int __Pyx_Is_Little_Endian(void) #define __Pyx_XGIVEREF(r) #endif /* CYTHON_REFNANNY */ +#if !CYTHON_USING_HPY #define __Pyx_Py_XDECREF_SET(r, v) do { \ PyObject *tmp = (PyObject *) r; \ r = v; Py_XDECREF(tmp); \ @@ -2081,6 +2082,20 @@ static CYTHON_INLINE int __Pyx_Is_Little_Endian(void) PyObject *tmp = (PyObject *) r; \ r = v; __Pyx_DECREF(tmp); \ } while (0) +#else +#define __Pyx_Py_XDECREF_SET(r, v) do { \ + /*PYOBJECT_XCLOSEREF(r);*/ \ + r = v; \ + } while (0) +#define __Pyx_XDECREF_SET(r, v) do { \ + /*PYOBJECT_XCLOSEREF(r);*/ \ + r = v; \ + } while (0) +#define __Pyx_DECREF_SET(r, v) do { \ + /*PYOBJECT_CLOSEREF(r);*/ \ + r = v; \ + } while (0) +#endif #define __Pyx_CLEAR(r) do { PyObject* tmp = ((PyObject*)(r)); r = NULL; __Pyx_DECREF(tmp);} while(0) #define __Pyx_XCLEAR(r) do { if((r) != NULL) {PyObject* tmp = ((PyObject*)(r)); r = NULL; __Pyx_DECREF(tmp);}} while(0) From c6532ee1dfed3e265bfc04bedd6614a796b48513 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Fri, 22 Sep 2023 14:49:18 +0200 Subject: [PATCH 183/286] Fixed an edge case and created more macros for integer conversion --- Cython/Compiler/ExprNodes.py | 3 ++- Cython/Utility/ModuleSetupCode.c | 18 ++++++++++++++++++ Cython/Utility/Optimize.c | 14 +++++++------- Cython/Utility/TypeConversion.c | 8 ++++---- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index f043faccbee..c2edac5cf7d 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -2660,7 +2660,8 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, if is_pythran_expr(self.type): code.putln('new (&%s) decltype(%s){%s};' % (self.result(), self.result(), result)) elif result != self.result(): - if hasattr(rhs, "is_global") and rhs.is_global: + code.putln("//%s" % rhs) + if hasattr(rhs, "is_global") and rhs.is_global and rhs.is_c_literal == False: code.putln('%s = PYOBJECT_GLOBAL_LOAD(%s);' % (self.result(), result)) else: code.putln('%s = %s;' % (self.result(), result)) diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 8581c01cc7a..39ccb3fb440 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -762,10 +762,17 @@ class __Pyx_FakeReference { #define PYERR_EXCEPTIONMATCHES(exc) HPyErr_ExceptionMatches(HPY_CONTEXT_CNAME, (exc)) #define PYOBJECT_FROM_LONG(i) HPyLong_FromLong(HPY_CONTEXT_CNAME, i) + #define PYOBJECT_FROM_LONGLONG(i) HPyLong_FromLongLong(HPY_CONTEXT_CNAME, i) + #define PYOBJECT_FROM_UNSIGNED_LONG(i) HPyLong_FromUnsignedLong(HPY_CONTEXT_CNAME, i) + #define PYOBJECT_FROM_UNSIGNED_LONGLONG(i) HPyLong_FromUnsignedLongLong(HPY_CONTEXT_CNAME, i) #define PYOBJECT_FROM_DOUBLE(f) HPyFloat_FromDouble(HPY_CONTEXT_CNAME, f) #define PYOBJECT_FROM_STRING(s) HPyUnicode_FromString(HPY_CONTEXT_CNAME, s) //not yet needed in C API version #define PYOBJECT_LONG_AS_SSIZE(l) HPyLong_AsSsize_t(HPY_CONTEXT_CNAME, l) + #define PYOBJECT_INT_AS_LONG_NOERROR(l) HPyLong_AsLong(HPY_CONTEXT_CNAME, l) + #define PYOBJECT_FLOAT_AS_DOUBLE(f) HPyFloat_AsDouble(HPY_CONTEXT_CNAME, f) + + #define FLOAT_CHECK_EXACT(f) HPyFloat_Check(HPY_CONTEXT_CNAME, f) #define HPY_LEGACY_OBJECT_FROM(o) HPy_FromPyObject(HPY_CONTEXT_CNAME, o) #define HPY_LEGACY_OBJECT_AS(o) HPy_AsPyObject(HPY_CONTEXT_CNAME, o) @@ -796,6 +803,8 @@ class __Pyx_FakeReference { #define API_VECTORCALLFUNC HPyCallFunction + #define API_RICH_COMPARE(h1, h2, op) HPy_RichCompare(HPY_CONTEXT_CNAME, h1, h2, op) + #define DICT_NEW() HPyDict_New(HPY_CONTEXT_CNAME) #define DICT_GET_ITEM(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) #define DICT_SET_ITEM(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) @@ -859,9 +868,16 @@ class __Pyx_FakeReference { #define API_CALL_FUNC(callable, args, nargs, kwnames) PyObject_Call(HPY_CONTEXT_CNAME, callable, args, kwnames) #define PYOBJECT_FROM_LONG(i) PyInt_FromLong(i) + #define PYOBJECT_FROM_LONGLONG(i) PyLong_FromLongLong(i) + #define PYOBJECT_FROM_UNSIGNED_LONG(i) PyInt_FromUnsignedLong(i) + #define PYOBJECT_FROM_UNSIGNED_LONGLONG(i) PyLong_FromUnsignedLongLong(i) #define PYOBJECT_FROM_DOUBLE(f) PyFloat_FromDouble(f) #define PYOBJECT_LONG_AS_SSIZE(l) PyLong_AsSsize_t(l) + #define PYOBJECT_INT_AS_LONG_NOERROR(l) PyInt_AS_LONG(l) + #define PYOBJECT_FLOAT_AS_DOUBLE(f) PyFloat_AsDouble(f) + + #define FLOAT_CHECK_EXACT(f) HPY_LEGACY_OBJECT_AS(f) #define HPY_LEGACY_OBJECT_FROM(o) o #define HPY_LEGACY_OBJECT_AS(o) o @@ -889,6 +905,8 @@ class __Pyx_FakeReference { #define API_FALSE Py_False #define API_EXC(name) (PyExc_ ## name) + #define API_RICH_COMPARE(h1, h2, op) PyObject_RichCompare(h1, h2, op) + #define DICT_NEW() PyDict_New() #define DICT_GET_ITEM(o, attr_name) PyDict_GetItem(o, attr_name) #define DICT_SET_ITEM(o, attr_name, attr_val) PyDict_SetItem(o, attr_name, attr_val) diff --git a/Cython/Utility/Optimize.c b/Cython/Utility/Optimize.c index 8d30a1d5c3a..39ccd159c76 100644 --- a/Cython/Utility/Optimize.c +++ b/Cython/Utility/Optimize.c @@ -1221,7 +1221,7 @@ static {{c_ret_type}} {{cfunc_name}}(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE op1 // Calling PyLong_CompactValue() requires the PyLong value to be compact, we only need the last digit. long last_digit = (long) __Pyx_PyLong_Digits({{pyval}})[0]; long result = intval & (likely(__Pyx_PyLong_IsPos({{pyval}})) ? last_digit : (PyLong_MASK - last_digit + 1)); - return HPY_LEGACY_OBJECT_FROM(PyLong_FromLong(result)); + return PYOBJECT_FROM_LONG(result); } {{endif}} // special cases for 0: + - * % / // | ^ & >> << @@ -1309,7 +1309,7 @@ static {{c_ret_type}} {{cfunc_name}}(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE op1 {{elif op == 'TrueDivide'}} if ((8 * sizeof(long) <= 53 || likely(labs({{ival}}) <= ((PY_LONG_LONG)1 << 53))) || __Pyx_PyLong_DigitCount({{pyval}}) <= 52 / PyLong_SHIFT) { - return HPY_LEGACY_OBJECT_FROM(PyFloat_FromDouble((double)a / (double)b)); + return PYOBJECT_FROM_DOUBLE((double)a / (double)b); } return PyLong_Type.tp_as_number->nb_{{slot_name}}(op1, op2); {{elif op == 'FloorDivide'}} @@ -1334,7 +1334,7 @@ static {{c_ret_type}} {{cfunc_name}}(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE op1 #endif {{endif}} {{endif}} - return HPY_LEGACY_OBJECT_FROM(PyLong_FromLong(x)); + return PYOBJECT_FROM_LONG(x); {{if op != 'TrueDivide'}} #ifdef HAVE_LONG_LONG @@ -1358,7 +1358,7 @@ static {{c_ret_type}} {{cfunc_name}}(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE op1 if (likely(lla == llx >> llb)) /* then execute 'return' below */ {{endif}} {{endif}} - return HPY_LEGACY_OBJECT_FROM(PyLong_FromLongLong(llx)); + return PYOBJECT_FROM_LONGLONG(llx); #endif {{endif}}{{# if op != 'TrueDivide' #}} {{endif}}{{# if op in ('Eq', 'Ne') #}} @@ -1366,7 +1366,7 @@ static {{c_ret_type}} {{cfunc_name}}(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE op1 #endif {{if c_op in '+-*' or op in ('TrueDivide', 'Eq', 'Ne')}} - if (PyFloat_CheckExact(HPY_LEGACY_OBJECT_AS({{pyval}}))) { + if (FLOAT_CHECK_EXACT({{pyval}})) { const long {{'a' if order == 'CObj' else 'b'}} = intval; double {{ival}} = __Pyx_PyFloat_AS_DOUBLE(HPY_LEGACY_OBJECT_AS({{pyval}})); {{if op in ('Eq', 'Ne')}} @@ -1379,14 +1379,14 @@ static {{c_ret_type}} {{cfunc_name}}(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE op1 double result; {{zerodiv_check('b', 'float')}} result = ((double)a) {{c_op}} (double)b; - return HPY_LEGACY_OBJECT_FROM(PyFloat_FromDouble(result)); + return PYOBJECT_FROM_DOUBLE(result); {{endif}} } {{endif}} {{if op in ('Eq', 'Ne')}} return {{'' if ret_type.is_pyobject else '__Pyx_PyObject_IsTrueAndDecref'}}( - PyObject_RichCompare(op1, op2, Py_{{op.upper()}})); + API_RICH_COMPARE(op1, op2, Py_{{op.upper()}})); {{else}} #if CYTHON_USING_HPY return HPy_{{op}}(HPY_CONTEXT_CNAME, op1, op2); diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index a01316625a3..d4cd1a75907 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -127,7 +127,7 @@ static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject*); #define __Pyx_PyFloat_AsDouble(x) (PyFloat_CheckExact(x) ? PyFloat_AS_DOUBLE(x) : PyFloat_AsDouble(x)) #define __Pyx_PyFloat_AS_DOUBLE(x) PyFloat_AS_DOUBLE(x) #else -#define __Pyx_PyFloat_AsDouble(x) PyFloat_AsDouble(x) +#define __Pyx_PyFloat_AsDouble(x) PYOBJECT_FLOAT_AS_DOUBLE(x) #define __Pyx_PyFloat_AS_DOUBLE(x) PyFloat_AsDouble(x) #endif #define __Pyx_PyFloat_AsFloat(x) ((float) __Pyx_PyFloat_AsDouble(x)) @@ -697,10 +697,10 @@ static CYTHON_INLINE PYOBJECT_TYPE {{TO_PY_FUNCTION}}(HPY_CONTEXT_FIRST_ARG_DEF if (sizeof({{TYPE}}) < sizeof(long)) { return PYOBJECT_FROM_LONG((long) value); } else if (sizeof({{TYPE}}) <= sizeof(unsigned long)) { - return HPY_LEGACY_OBJECT_FROM(PyLong_FromUnsignedLong((unsigned long) value)); + return PYOBJECT_FROM_UNSIGNED_LONG((unsigned long) value); #ifdef HAVE_LONG_LONG } else if (sizeof({{TYPE}}) <= sizeof(unsigned PY_LONG_LONG)) { - return HPY_LEGACY_OBJECT_FROM(PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG) value)); + return PYOBJECT_FROM_UNSIGNED_LONGLONG((unsigned PY_LONG_LONG) value); #endif } } else { @@ -708,7 +708,7 @@ static CYTHON_INLINE PYOBJECT_TYPE {{TO_PY_FUNCTION}}(HPY_CONTEXT_FIRST_ARG_DEF return PYOBJECT_FROM_LONG((long) value); #ifdef HAVE_LONG_LONG } else if (sizeof({{TYPE}}) <= sizeof(PY_LONG_LONG)) { - return HPY_LEGACY_OBJECT_FROM(PyLong_FromLongLong((PY_LONG_LONG) value)); + return PYOBJECT_FROM_LONGLONG((PY_LONG_LONG) value); #endif } } From bfaeb558880f11349f83b205faf2f609fcf378b0 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Fri, 22 Sep 2023 14:53:28 +0200 Subject: [PATCH 184/286] Removed debug comment generation --- Cython/Compiler/ExprNodes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index c2edac5cf7d..090a8abd5e2 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -2660,7 +2660,6 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, if is_pythran_expr(self.type): code.putln('new (&%s) decltype(%s){%s};' % (self.result(), self.result(), result)) elif result != self.result(): - code.putln("//%s" % rhs) if hasattr(rhs, "is_global") and rhs.is_global and rhs.is_c_literal == False: code.putln('%s = PYOBJECT_GLOBAL_LOAD(%s);' % (self.result(), result)) else: From c9befacd439564b5189b98175470d0271ff84d22 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 28 Sep 2023 11:34:39 +0200 Subject: [PATCH 185/286] Fixed issues in merge request --- Cython/Compiler/Code.py | 5 +++++ Cython/Compiler/ExprNodes.py | 12 +++++++----- Cython/Utility/ModuleSetupCode.c | 10 +++++++++- Cython/Utility/ObjectHandling.c | 4 ++-- Cython/Utility/Optimize.c | 2 +- Cython/Utility/TypeConversion.c | 2 +- 6 files changed, 25 insertions(+), 10 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 634a96a9e35..0d8665aa819 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1187,6 +1187,8 @@ def __init__(self, writer, module_node, code_config, common_utility_include_dir= self.cached_cmethods = {} self.initialised_constants = set() + self.const_cname_array = [] + writer.set_global_state(self) self.rootwriter = writer @@ -1419,18 +1421,21 @@ def new_string_const(self, text, byte_string): cname = self.new_string_const_cname(byte_string) c = StringConst(cname, text, byte_string) self.string_const_index[byte_string] = c + self.const_cname_array.append(cname) return c def new_num_const(self, value, py_type, value_code=None): cname = self.new_num_const_cname(value, py_type) c = NumConst(cname, value, py_type, value_code) self.num_const_index[(value, py_type)] = c + self.const_cname_array.append(cname) return c def new_py_const(self, type, prefix=''): cname = self.new_const_cname(prefix) c = PyObjectConst(cname, type) self.py_constants.append(c) + self.const_cname_array.append(cname) return c def new_string_const_cname(self, bytes_value): diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 090a8abd5e2..4afb3755f84 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -2548,6 +2548,7 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, namespace = self.entry.scope.namespace_cname rhs_result = rhs.py_result() load_result_temp = rhs_result + b = False if entry.is_member: # if the entry is a member we have to cheat: SetAttr does not work # on types, so we create a descriptor which is then added to tp_dict. @@ -2556,13 +2557,11 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, setter = 'DICT_SET_ITEM' namespace = Naming.moddict_cname interned_cname = interned_cname - if not rhs_result.startswith("__pyx_t_"): + if rhs_result in code.globalstate.const_cname_array: + b = True code.putln("#if CYTHON_USING_HPY") load_result_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=False) code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_result_temp, rhs_result)) - rhs_result = rhs_result #temp fix for globals and non-globals being handled by the same code - code.putln("PYOBJECT_CLOSEREF(%s);" % load_result_temp) - code.funcstate.release_temp(load_result_temp) code.putln("#endif") elif entry.is_pyclass_attr: # Special-case setting __new__ @@ -2587,6 +2586,9 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, code.putln("PYOBJECT_CLOSEREF(%s);" % load_cname_temp) code.funcstate.release_temp(load_namespace_temp) code.funcstate.release_temp(load_cname_temp) + if rhs_result in code.globalstate.const_cname_array and b: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % load_result_temp) + code.funcstate.release_temp(load_result_temp) code.putln("#else") code.put_error_if_neg( self.pos, @@ -2660,7 +2662,7 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, if is_pythran_expr(self.type): code.putln('new (&%s) decltype(%s){%s};' % (self.result(), self.result(), result)) elif result != self.result(): - if hasattr(rhs, "is_global") and rhs.is_global and rhs.is_c_literal == False: + if result in code.globalstate.const_cname_array: code.putln('%s = PYOBJECT_GLOBAL_LOAD(%s);' % (self.result(), result)) else: code.putln('%s = %s;' % (self.result(), result)) diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 39ccb3fb440..2c506973e1b 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -757,6 +757,9 @@ class __Pyx_FakeReference { #define API_CALL_FUNC(callable, args, nargs, kwnames) HPy_Call(HPY_CONTEXT_CNAME, callable, args, nargs, kwnames) + #define API_INT_TYPE HPY_CONTEXT_CNAME->h_LongType + #define API_LONG_TYPE HPY_CONTEXT_CNAME->h_LongType + #define PYERR_OCCURRED() HPyErr_Occurred(HPY_CONTEXT_CNAME) #define PYERR_CLEAR() HPyErr_Clear(HPY_CONTEXT_CNAME) #define PYERR_EXCEPTIONMATCHES(exc) HPyErr_ExceptionMatches(HPY_CONTEXT_CNAME, (exc)) @@ -784,6 +787,7 @@ class __Pyx_FakeReference { #define TYPE_FROM_MOD_AND_SPEC(m, s, b) HPyType_FromModuleAndSpec(HPY_CONTEXT_CNAME, m, &s, b) #define TYPESPEC_GET(s, field) s.field #define TYPE_CHECK(o) HPy_TypeCheck(HPY_CONTEXT_CNAME, (o), HPY_CONTEXT_CNAME->h_TypeType) + #define TYPE_AS_PYOBJECT(t) t #define API_NULL_VALUE HPy_NULL #define API_DEFAULT_VALUE HPy_NULL @@ -814,7 +818,7 @@ class __Pyx_FakeReference { #define PYOBJECT_GET_ITEM(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) #define PYOBJECT_SET_ITEM(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) - #define PYOBJECT_GET_ATTR(o, attr_name) HPy_GetAttr(HPY_CONTEXT_CNAME, o, attr_name) + #define PYOBJECT_GET_ATTR(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) #define PYOBJECT_SET_ATTR(o, attr_name, attr_val) HPy_SetAttr(HPY_CONTEXT_CNAME, o, attr_name, attr_val) #define PYOBJECT_GET_ATTR_STR(o, attr_name) HPy_GetAttr_s(HPY_CONTEXT_CNAME, o, attr_name) #define PYOBJECT_SET_ATTR_STR(o1, attr_name, o2) HPy_SetAttr_s(HPY_CONTEXT_CNAME, o1, attr_name, o2) @@ -867,6 +871,9 @@ class __Pyx_FakeReference { #define API_CALL_FUNC(callable, args, nargs, kwnames) PyObject_Call(HPY_CONTEXT_CNAME, callable, args, kwnames) + #define API_INT_TYPE PyInt_Type + #define API_LONG_TYPE PyLong_Type + #define PYOBJECT_FROM_LONG(i) PyInt_FromLong(i) #define PYOBJECT_FROM_LONGLONG(i) PyLong_FromLongLong(i) #define PYOBJECT_FROM_UNSIGNED_LONG(i) PyInt_FromUnsignedLong(i) @@ -889,6 +896,7 @@ class __Pyx_FakeReference { #define TYPE_FROM_MOD_AND_SPEC(m, s, b) PyType_FromModuleAndSpec(m, s, b) #define TYPESPEC_GET(s, field) s->field #define TYPE_CHECK(o) PyType_Check(o) + #define TYPE_AS_PYOBJECT(t) (PyObject*)& ## t #define API_NULL_VALUE NULL #define API_DEFAULT_VALUE 0 diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 5d1741cddfd..6a60aae3055 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1556,14 +1556,14 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStrNoError(HPY_CONTEXT_ #if CYTHON_USE_TYPE_SLOTS static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStr(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, PYOBJECT_TYPE attr_name);/*proto*/ #else -#define __Pyx_PyObject_GetAttrStr(o,n) DICT_GET_ITEM(o,n) //Needed to turn this into a function macro to be able to pass the context properly +#define __Pyx_PyObject_GetAttrStr(o,n) PYOBJECT_GET_ATTR(o,n) //Needed to turn this into a function macro to be able to pass the context properly #define __Pyx_PyObject_GetAttrStr_legacy(o,n) PyObject_GetAttr(o,n) //Used for functions where the context isn't reachable yet #endif /////////////// PyObjectGetAttrStr /////////////// #if CYTHON_USE_TYPE_SLOTS -static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStr(PYOBJECT_TYPE obj, PYOBJECT_TYPE attr_name) { +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStr(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, PYOBJECT_TYPE attr_name) { #if !CYTHON_USING_HPY PyTypeObject* tp = Py_TYPE(obj); if (likely(tp->tp_getattro)) diff --git a/Cython/Utility/Optimize.c b/Cython/Utility/Optimize.c index 39ccd159c76..bf6e401c2b1 100644 --- a/Cython/Utility/Optimize.c +++ b/Cython/Utility/Optimize.c @@ -1391,7 +1391,7 @@ static {{c_ret_type}} {{cfunc_name}}(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE op1 #if CYTHON_USING_HPY return HPy_{{op}}(HPY_CONTEXT_CNAME, op1, op2); #else - return (inplace ? PyNumber_InPlace{{op}} : PyNumber_{{op}})(HPY_LEGACY_OBJECT_AS(op1), HPY_LEGACY_OBJECT_AS(op2)); + return (inplace ? PyNumber_InPlace{{op}} : PyNumber_{{op}})(op1, op2); #endif {{endif}} } diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index d4cd1a75907..f64574f6343 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -726,7 +726,7 @@ static CYTHON_INLINE PYOBJECT_TYPE {{TO_PY_FUNCTION}}(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE arg_tuple = API_NULL_VALUE; PYOBJECT_TYPE kwds = API_NULL_VALUE; PYOBJECT_TYPE order_str = API_NULL_VALUE; - from_bytes = PYOBJECT_GET_ATTR_STR(HPY_LEGACY_OBJECT_FROM((PyObject*)&PyLong_Type), "from_bytes"); + from_bytes = PYOBJECT_GET_ATTR_STR(TYPE_AS_PYOBJECT(API_LONG_TYPE), "from_bytes"); if (API_IS_NULL(from_bytes)) return API_NULL_VALUE; py_bytes = BYTES_FROM_STR_AND_SIZE((char*)bytes, sizeof({{TYPE}})); if (API_IS_NULL(py_bytes)) goto limited_bad; From d936cdba0ff3ed5e4a16a856f1fd8532e78ecaad Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 28 Sep 2023 15:32:28 +0200 Subject: [PATCH 186/286] Fixed conflation between PyInt and PyLong types --- Cython/Compiler/Code.py | 4 ++-- Cython/Compiler/ModuleNode.py | 2 +- Cython/Utility/Exceptions.c | 2 +- Cython/Utility/ModuleSetupCode.c | 12 +++++++----- Cython/Utility/Optimize.c | 6 +++--- Cython/Utility/TypeConversion.c | 6 +++--- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 0d8665aa819..74dd50fefc4 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1698,9 +1698,9 @@ def generate_num_constants(self): elif Utils.long_literal(value): function = 'HPY_LEGACY_OBJECT_FROM(PyInt_FromString((char *)"%s", 0, 0))' elif len(value.lstrip('-')) > 4: - function = "PYOBJECT_FROM_LONG(%sL)" + function = "PYOBJECT_INT_FROM_LONG(%sL)" else: - function = "PYOBJECT_FROM_LONG(%s)" + function = "PYOBJECT_INT_FROM_LONG(%s)" init_constants.putln('PYOBJECT_GLOBAL_STORE(%s, %s); %s' % ( cname, function % value_code, init_constants.error_goto_if_null_object(cname, self.module_pos))) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 7c2b3f53331..a251816e23e 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -810,7 +810,7 @@ def generate_module_preamble(self, env, options, cimported_modules, metadata, co self._put_setup_code(code, "CppInitCode") else: self._put_setup_code(code, "CInitCode") - self._put_setup_code(code, "HPyInitCode") + code.put(UtilityCode.load_as_string("HPyInitCode", "HPyUtils.c")[1]) code.put(UtilityCode.load_as_string("HPyHelperFuncs", "HPyUtils.c")[1]) self._put_setup_code(code, "PythonCompatibility") self._put_setup_code(code, "MathInitCode") diff --git a/Cython/Utility/Exceptions.c b/Cython/Utility/Exceptions.c index 87912dc13cf..6b61695edfe 100644 --- a/Cython/Utility/Exceptions.c +++ b/Cython/Utility/Exceptions.c @@ -916,7 +916,7 @@ static void __Pyx_AddTraceback(HPY_CONTEXT_FIRST_ARG_DEF const char *funcname, i code_object = HPY_LEGACY_OBJECT_FROM(Py_CompileString("_getframe()", filename, Py_eval_input)); if (unlikely(API_IS_NULL(code_object))) goto bad; - py_py_line = PYOBJECT_FROM_LONG(py_line); + py_py_line = PYOBJECT_LONG_FROM_LONG(py_line); if (unlikely(API_IS_NULL(py_py_line))) goto bad; py_funcname = PYOBJECT_FROM_STRING(funcname); if (unlikely(API_IS_NULL(py_funcname))) goto bad; diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 2c506973e1b..dd43c040a1d 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -764,8 +764,9 @@ class __Pyx_FakeReference { #define PYERR_CLEAR() HPyErr_Clear(HPY_CONTEXT_CNAME) #define PYERR_EXCEPTIONMATCHES(exc) HPyErr_ExceptionMatches(HPY_CONTEXT_CNAME, (exc)) - #define PYOBJECT_FROM_LONG(i) HPyLong_FromLong(HPY_CONTEXT_CNAME, i) - #define PYOBJECT_FROM_LONGLONG(i) HPyLong_FromLongLong(HPY_CONTEXT_CNAME, i) + #define PYOBJECT_INT_FROM_LONG(i) HPyLong_FromLong(HPY_CONTEXT_CNAME, i) + #define PYOBJECT_LONG_FROM_LONG(i) HPyLong_FromLong(HPY_CONTEXT_CNAME, i) + #define PYOBJECT_LONG_FROM_LONGLONG(i) HPyLong_FromLongLong(HPY_CONTEXT_CNAME, i) #define PYOBJECT_FROM_UNSIGNED_LONG(i) HPyLong_FromUnsignedLong(HPY_CONTEXT_CNAME, i) #define PYOBJECT_FROM_UNSIGNED_LONGLONG(i) HPyLong_FromUnsignedLongLong(HPY_CONTEXT_CNAME, i) #define PYOBJECT_FROM_DOUBLE(f) HPyFloat_FromDouble(HPY_CONTEXT_CNAME, f) @@ -828,7 +829,7 @@ class __Pyx_FakeReference { #define BYTES_FROM_STR_AND_SIZE(str, size) HPyBytes_FromStringAndSize(HPY_CONTEXT_CNAME, str, size) #define TUPLE_CREATE_EMPTY() HPyTuple_FromArray(HPY_CONTEXT_CNAME, NULL, 0) - #define TUPLE_GET_ITEM(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, PYOBJECT_FROM_LONG(pos)) + #define TUPLE_GET_ITEM(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, PYOBJECT_LONG_FROM_LONG(pos)) #define TUPLE_GET_SIZE(h) HPy_Length(HPY_CONTEXT_CNAME, (h)) #define TUPLE_BUILDER_TYPE HPyTupleBuilder #define TUPLE_CREATE_START(target, builder, size) builder = HPyTupleBuilder_New(HPY_CONTEXT_CNAME, size) @@ -874,8 +875,9 @@ class __Pyx_FakeReference { #define API_INT_TYPE PyInt_Type #define API_LONG_TYPE PyLong_Type - #define PYOBJECT_FROM_LONG(i) PyInt_FromLong(i) - #define PYOBJECT_FROM_LONGLONG(i) PyLong_FromLongLong(i) + #define PYOBJECT_INT_FROM_LONG(i) PyInt_FromLong(i) + #define PYOBJECT_LONG_FROM_LONG(i) PyLong_FromLong(i) + #define PYOBJECT_LONG_FROM_LONGLONG(i) PyLong_FromLongLong(i) #define PYOBJECT_FROM_UNSIGNED_LONG(i) PyInt_FromUnsignedLong(i) #define PYOBJECT_FROM_UNSIGNED_LONGLONG(i) PyLong_FromUnsignedLongLong(i) #define PYOBJECT_FROM_DOUBLE(f) PyFloat_FromDouble(f) diff --git a/Cython/Utility/Optimize.c b/Cython/Utility/Optimize.c index bf6e401c2b1..64785a1e2fd 100644 --- a/Cython/Utility/Optimize.c +++ b/Cython/Utility/Optimize.c @@ -1221,7 +1221,7 @@ static {{c_ret_type}} {{cfunc_name}}(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE op1 // Calling PyLong_CompactValue() requires the PyLong value to be compact, we only need the last digit. long last_digit = (long) __Pyx_PyLong_Digits({{pyval}})[0]; long result = intval & (likely(__Pyx_PyLong_IsPos({{pyval}})) ? last_digit : (PyLong_MASK - last_digit + 1)); - return PYOBJECT_FROM_LONG(result); + return PYOBJECT_LONG_FROM_LONG(result); } {{endif}} // special cases for 0: + - * % / // | ^ & >> << @@ -1334,7 +1334,7 @@ static {{c_ret_type}} {{cfunc_name}}(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE op1 #endif {{endif}} {{endif}} - return PYOBJECT_FROM_LONG(x); + return PYOBJECT_LONG_FROM_LONG(x); {{if op != 'TrueDivide'}} #ifdef HAVE_LONG_LONG @@ -1358,7 +1358,7 @@ static {{c_ret_type}} {{cfunc_name}}(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE op1 if (likely(lla == llx >> llb)) /* then execute 'return' below */ {{endif}} {{endif}} - return PYOBJECT_FROM_LONGLONG(llx); + return PYOBJECT_LONG_FROM_LONGLONG(llx); #endif {{endif}}{{# if op != 'TrueDivide' #}} {{endif}}{{# if op in ('Eq', 'Ne') #}} diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index f64574f6343..0cb5e6e569c 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -695,7 +695,7 @@ static CYTHON_INLINE PYOBJECT_TYPE {{TO_PY_FUNCTION}}(HPY_CONTEXT_FIRST_ARG_DEF const int is_unsigned = neg_one > const_zero; if (is_unsigned) { if (sizeof({{TYPE}}) < sizeof(long)) { - return PYOBJECT_FROM_LONG((long) value); + return PYOBJECT_INT_FROM_LONG((long) value); } else if (sizeof({{TYPE}}) <= sizeof(unsigned long)) { return PYOBJECT_FROM_UNSIGNED_LONG((unsigned long) value); #ifdef HAVE_LONG_LONG @@ -705,10 +705,10 @@ static CYTHON_INLINE PYOBJECT_TYPE {{TO_PY_FUNCTION}}(HPY_CONTEXT_FIRST_ARG_DEF } } else { if (sizeof({{TYPE}}) <= sizeof(long)) { - return PYOBJECT_FROM_LONG((long) value); + return PYOBJECT_INT_FROM_LONG((long) value); #ifdef HAVE_LONG_LONG } else if (sizeof({{TYPE}}) <= sizeof(PY_LONG_LONG)) { - return PYOBJECT_FROM_LONGLONG((PY_LONG_LONG) value); + return PYOBJECT_LONG_FROM_LONGLONG((PY_LONG_LONG) value); #endif } } From 32d6c20cd7927bc618f26d7599c7135e3f081734 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 28 Sep 2023 16:12:09 +0200 Subject: [PATCH 187/286] Fixed code loading from wrong file --- Cython/Compiler/ModuleNode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index a251816e23e..7c2b3f53331 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -810,7 +810,7 @@ def generate_module_preamble(self, env, options, cimported_modules, metadata, co self._put_setup_code(code, "CppInitCode") else: self._put_setup_code(code, "CInitCode") - code.put(UtilityCode.load_as_string("HPyInitCode", "HPyUtils.c")[1]) + self._put_setup_code(code, "HPyInitCode") code.put(UtilityCode.load_as_string("HPyHelperFuncs", "HPyUtils.c")[1]) self._put_setup_code(code, "PythonCompatibility") self._put_setup_code(code, "MathInitCode") From 5c09b8f0af47162906d40054ee0487661ded084b Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 2 Oct 2023 14:38:38 +0200 Subject: [PATCH 188/286] Fixed broken API, and added new test for this branch --- Cython/Utility/ModuleSetupCode.c | 2 +- tests/run/hpy_forloop | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tests/run/hpy_forloop diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index dd43c040a1d..a4cf06aee61 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -878,7 +878,7 @@ class __Pyx_FakeReference { #define PYOBJECT_INT_FROM_LONG(i) PyInt_FromLong(i) #define PYOBJECT_LONG_FROM_LONG(i) PyLong_FromLong(i) #define PYOBJECT_LONG_FROM_LONGLONG(i) PyLong_FromLongLong(i) - #define PYOBJECT_FROM_UNSIGNED_LONG(i) PyInt_FromUnsignedLong(i) + #define PYOBJECT_FROM_UNSIGNED_LONG(i) PyLong_FromUnsignedLong(i) #define PYOBJECT_FROM_UNSIGNED_LONGLONG(i) PyLong_FromUnsignedLongLong(i) #define PYOBJECT_FROM_DOUBLE(f) PyFloat_FromDouble(f) diff --git a/tests/run/hpy_forloop b/tests/run/hpy_forloop new file mode 100644 index 00000000000..ea4a9797090 --- /dev/null +++ b/tests/run/hpy_forloop @@ -0,0 +1,9 @@ +#mode: run + +import cython + +def add(): + a = 0 + for i in range(100): + a = a + i + return a \ No newline at end of file From d8a67c1b5b744757dc25b6fd278736bec862690d Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 2 Oct 2023 14:58:53 +0200 Subject: [PATCH 189/286] Fix for function where globals are present but code is not --- Cython/Compiler/ExprNodes.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 4afb3755f84..ceb2f10fad3 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -6406,7 +6406,7 @@ def analyse_c_function_call(self, env): def calculate_result_code(self): return self.c_call_code() - def c_call_code(self): + def c_call_code(self, code=False): func_type = self.function_type() if self.type is PyrexTypes.error_type or not func_type.is_cfunction: return "" @@ -6435,9 +6435,11 @@ def c_call_code(self): arg_string = "" for arg in arg_list_code: - if arg.startswith("__pyx_") and not arg.startswith("__pyx_t_") and not arg.startswith("__pyx_v_"): - arg_string = arg_string + "PYOBJECT_GLOBAL_LOAD(" + arg + ")" #temp fix for globals and non-globals being handled by the same code - + if code: + if arg in code.globalstate.const_cname_array: + arg_string = arg_string + "PYOBJECT_GLOBAL_LOAD(" + arg + ")" #temp fix for globals and non-globals being handled by the same code + else: + arg_string = arg_string + "%s" % arg else: arg_string = arg_string + "%s" % arg arg_string = arg_string + ", " @@ -6567,7 +6569,7 @@ def generate_result_code(self, code): else: exc_checks.append("PyErr_Occurred()") if self.is_temp or exc_checks: - rhs = self.c_call_code() + rhs = self.c_call_code(code=code) if self.result(): lhs = "%s = " % self.result() if self.is_temp and self.type.is_pyobject: From d0c9ac8076d1a34ea6e48c822cee684185a1f0f0 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 2 Oct 2023 15:01:28 +0200 Subject: [PATCH 190/286] Added file extension to new test --- tests/run/{hpy_forloop => hpy_forloop.pyx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/run/{hpy_forloop => hpy_forloop.pyx} (100%) diff --git a/tests/run/hpy_forloop b/tests/run/hpy_forloop.pyx similarity index 100% rename from tests/run/hpy_forloop rename to tests/run/hpy_forloop.pyx From 4f5ea7e02c601c5195a5cedcf18705e7974cde7e Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 2 Oct 2023 17:12:44 +0200 Subject: [PATCH 191/286] Fixed incorrect macro name --- Cython/Utility/TypeConversion.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index 0cb5e6e569c..ae26bc7d2f7 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -759,11 +759,11 @@ static CYTHON_INLINE PYOBJECT_TYPE {{TO_PY_FUNCTION}}(HPY_CONTEXT_FIRST_ARG_DEF #endif limited_bad: - Py_XCLOSEREF(from_bytes); - Py_XCLOSEREF(py_bytes); - Py_XCLOSEREF(order_str); - Py_XCLOSEREF(arg_tuple); - Py_XCLOSEREF(kwds); + PYOBJECT_XCLOSEREF(from_bytes); + PYOBJECT_XCLOSEREF(py_bytes); + PYOBJECT_XCLOSEREF(order_str); + PYOBJECT_XCLOSEREF(arg_tuple); + PYOBJECT_XCLOSEREF(kwds); return result; #endif } From 6758adc99fdef4a3e748fdb08193896efde65343 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Fri, 6 Oct 2023 11:01:21 +0200 Subject: [PATCH 192/286] Fixed function repr causing recursion depth issue, plus fix for decref_set --- Cython/Compiler/Nodes.py | 16 ++++++++-------- Cython/Utility/CythonFunction.c | 13 +++++++++---- Cython/Utility/ModuleSetupCode.c | 9 ++++++--- Cython/Utility/ObjectHandling.c | 6 +++--- 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index b6f7a37fd12..e0b35f32e40 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -6874,15 +6874,15 @@ def generate_execution_code(self, code): value.py_result())) value.generate_disposal_code(code) else: - value.make_owned_reference(code) - if not hasattr(value, "is_global") or not value.is_global: - code.putln("%s = %s;" % ( - Naming.retval_cname, - value.result_as(self.return_type))) - else: + if (hasattr(value, "entry") and hasattr(value.entry, "cname") and value.entry.cname in code.globalstate.const_cname_array) or (hasattr(value, "result_code") and value.result_code in code.globalstate.const_cname_array): + value.make_owned_reference(code) code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % ( - Naming.retval_cname, - value.result_as(self.return_type))) + Naming.retval_cname, + value.result_as(self.return_type))) + else: + code.putln("%s = %s;" % ( + Naming.retval_cname, + value.result_as(self.return_type))) value.generate_post_assignment_code(code) value.free_temps(code) else: diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 2a2d56bff20..061eb5a9bfb 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -200,8 +200,13 @@ __Pyx_CyFunction_doc_get(HPyContext *HPY_CONTEXT_CNAME, __pyx_CyFunctionObject_F CYTHON_UNUSED_VAR(closure); __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); HPy h_doc = HPyField_Load(HPY_CONTEXT_CNAME, op, struct_op->func_doc); + if (HPy_IsNull(h_doc)) { + h_doc = HPyUnicode_FromString(HPY_CONTEXT_CNAME, struct_op->func->meth.doc); + } if (HPy_IsNull(h_doc)) { return HPy_Dup(HPY_CONTEXT_CNAME, HPY_CONTEXT_CNAME->h_None); + } else { + HPyField_Store(HPY_CONTEXT_CNAME, op, &struct_op->func_doc, h_doc); } return h_doc; } @@ -243,7 +248,7 @@ __Pyx_CyFunction_get_doc(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDe return op->func_doc; #else __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); - return PYOBJECT_FIELD_LOAD(op, struct_op->func_doc); + return HPyField_Load(HPY_CONTEXT_CNAME, op, struct_op->func_doc); #endif } @@ -822,6 +827,8 @@ static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFun __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); assert(ml->kind == HPyDef_Kind_Meth); struct_op->func = ml; + PYOBJECT_FIELD_STORE(op, struct_op->func_qualname, qualname); + PYOBJECT_FIELD_STORE(op, struct_op->func_doc, HPyUnicode_FromString(HPY_CONTEXT_CNAME, ml->meth.doc)); #endif /* !CYTHON_USING_HPY */ if (unlikely(API_IS_NULL(op))) return API_NULL_VALUE; @@ -831,8 +838,6 @@ static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFun PYOBJECT_FIELD_STORE(op, struct_op->func_closure, closure); PYOBJECT_FIELD_STORE(op, struct_op->func_dict, API_NULL_VALUE); PYOBJECT_FIELD_STORE(op, struct_op->func_name, API_NULL_VALUE); - PYOBJECT_FIELD_STORE(op, struct_op->func_qualname, qualname); - PYOBJECT_FIELD_STORE(op, struct_op->func_doc, API_NULL_VALUE); PYOBJECT_FIELD_STORE(op, struct_op->func_globals, globals); PYOBJECT_FIELD_STORE(op, struct_op->func_code,code); // Dynamic Default args @@ -1447,7 +1452,7 @@ static PyType_Slot __pyx_CyFunctionType_slots[] = { {Py_tp_methods, (void *)__pyx_CyFunction_methods}, {Py_tp_members, (void *)__pyx_CyFunction_members}, {Py_tp_getset, (void *)__pyx_CyFunction_getsets}, - {Py_tp_descr_get, (void *)__Pyx_PyMethod_New}, +{Py_tp_descr_get, (void *)__Pyx_PyMethod_New}, {0, 0}, }; diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index a4cf06aee61..26384831e46 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -2112,16 +2112,19 @@ static CYTHON_INLINE int __Pyx_Is_Little_Endian(void) } while (0) #else #define __Pyx_Py_XDECREF_SET(r, v) do { \ - /*PYOBJECT_XCLOSEREF(r);*/ \ + HPy tmp = r; \ r = v; \ + PYOBJECT_XCLOSEREF(tmp); \ } while (0) #define __Pyx_XDECREF_SET(r, v) do { \ - /*PYOBJECT_XCLOSEREF(r);*/ \ + HPy tmp = r; \ r = v; \ + PYOBJECT_XCLOSEREF(tmp); \ } while (0) #define __Pyx_DECREF_SET(r, v) do { \ - /*PYOBJECT_CLOSEREF(r);*/ \ + HPy tmp = r; \ r = v; \ + PYOBJECT_XCLOSEREF(tmp); \ } while (0) #endif diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 6a60aae3055..5e31609413a 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1446,17 +1446,17 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx__GetModuleGlobalName(HPY_CONTEXT_FIRST_ //////////////////// GetAttr.proto //////////////////// -static CYTHON_INLINE PyObject *__Pyx_GetAttr(PyObject *, PyObject *); /*proto*/ +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_GetAttr(PYOBJECT_TYPE, PYOBJECT_TYPE); /*proto*/ //////////////////// GetAttr //////////////////// //@requires: PyObjectGetAttrStr -static CYTHON_INLINE PyObject *__Pyx_GetAttr(PyObject *o, PyObject *n) { +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_GetAttr(PYOBJECT_TYPE o, PYOBJECT_TYPE n) { #if CYTHON_USE_TYPE_SLOTS if (likely(PyUnicode_Check(n))) return __Pyx_PyObject_GetAttrStr(o, n); #endif - return PyObject_GetAttr(o, n); + return PYOBJECT_GET_ATTR(o, n); } From 19843d2a5d85cd2fd53679aaec8c6e89aa99599f Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Fri, 29 Sep 2023 13:24:58 +0200 Subject: [PATCH 193/286] Moved macros to HPyUtils and reorganised them --- Cython/Compiler/Code.py | 2 +- Cython/Compiler/ModuleNode.py | 2 +- Cython/Compiler/PyrexTypes.py | 2 +- Cython/Utility/CythonFunction.c | 3 +- Cython/Utility/Exceptions.c | 2 +- Cython/Utility/HPyUtils.c | 298 +++++++++++++++++++++++++++++++ Cython/Utility/ModuleSetupCode.c | 225 ----------------------- Cython/Utility/Optimize.c | 4 +- Cython/Utility/TypeConversion.c | 8 +- 9 files changed, 309 insertions(+), 237 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 74dd50fefc4..ffbfcecd38f 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1692,7 +1692,7 @@ def generate_num_constants(self): self.parts['globals_table'].putln("globals_array[%d] = &%s;" % (self.globals_counter, cname)) self.globals_counter += 1 if py_type == 'float': - function = 'PYOBJECT_FROM_DOUBLE(%s)' + function = 'PYOBJECT_FLOAT_FROM_DOUBLE(%s)' elif py_type == 'long': function = 'HPY_LEGACY_OBJECT_FROM(PyLong_FromString((char *)"%s", 0, 0))' elif Utils.long_literal(value): diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 7c2b3f53331..a251816e23e 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -810,7 +810,7 @@ def generate_module_preamble(self, env, options, cimported_modules, metadata, co self._put_setup_code(code, "CppInitCode") else: self._put_setup_code(code, "CInitCode") - self._put_setup_code(code, "HPyInitCode") + code.put(UtilityCode.load_as_string("HPyInitCode", "HPyUtils.c")[1]) code.put(UtilityCode.load_as_string("HPyHelperFuncs", "HPyUtils.c")[1]) self._put_setup_code(code, "PythonCompatibility") self._put_setup_code(code, "MathInitCode") diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 72888583cd2..b7affa2b426 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -2414,7 +2414,7 @@ def sign_and_name(self): class CFloatType(CNumericType): is_float = 1 - to_py_function = "PYOBJECT_FROM_DOUBLE" + to_py_function = "PYOBJECT_FLOAT_FROM_DOUBLE" from_py_function = "__Pyx_PyFloat_AsDouble" exception_value = -1 diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 061eb5a9bfb..2f97931106c 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -1044,8 +1044,7 @@ __Pyx_CyFunction_repr_impl(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_Func { __pyx_CyFunctionObject *struct_op = op; PyObject *qualname_field = struct_op->func_qualname; -#endif - return UNICODE_FROM_FORMAT("", + return PYOBJECT_UNICODE_FROM_FORMAT("", qualname_field, op); } #endif /* CYTHON_USING_HPY */ diff --git a/Cython/Utility/Exceptions.c b/Cython/Utility/Exceptions.c index 6b61695edfe..95b0f6ced58 100644 --- a/Cython/Utility/Exceptions.c +++ b/Cython/Utility/Exceptions.c @@ -918,7 +918,7 @@ static void __Pyx_AddTraceback(HPY_CONTEXT_FIRST_ARG_DEF const char *funcname, i if (unlikely(API_IS_NULL(code_object))) goto bad; py_py_line = PYOBJECT_LONG_FROM_LONG(py_line); if (unlikely(API_IS_NULL(py_py_line))) goto bad; - py_funcname = PYOBJECT_FROM_STRING(funcname); + py_funcname = PYOBJECT_UNICODE_FROM_STRING(funcname); if (unlikely(API_IS_NULL(py_funcname))) goto bad; dict = DICT_NEW(); if (unlikely(API_IS_NULL(dict))) goto bad; diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 53c9374bea8..e77ceaddc50 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -1,3 +1,301 @@ +/////////////// HPyInitCode /////////////// +//@substitute: naming + +#if CYTHON_USING_HPY + //HPy Context Macros + #define HPY_CONTEXT_CNAME $hpy_context_cname + #define HPY_CONTEXT_TYPE HPyContext * + #define HPY_CONTEXT_ONLY_ARG_DEF HPY_CONTEXT_TYPE HPY_CONTEXT_CNAME + #define HPY_CONTEXT_ONLY_ARG_CALL HPY_CONTEXT_CNAME + #define HPY_CONTEXT_FIRST_ARG_DEF HPY_CONTEXT_TYPE HPY_CONTEXT_CNAME, + #define HPY_CONTEXT_FIRST_ARG_CALL HPY_CONTEXT_CNAME, + + //Handle/PyObject Macros + #define PYOBJECT_TYPE HPy + #define PYTYPEOBJECT_TYPE HPy + #define PYOBJECT_FIELD_TYPE HPyField + #define PYOBJECT_FIELD_STORE(owner, field, h) HPyField_Store(HPY_CONTEXT_CNAME, owner, &field, h) + #define PYOBJECT_FIELD_LOAD(owner, field) HPyField_Load(HPY_CONTEXT_CNAME, owner, field) + #define PYOBJECT_GLOBAL_TYPE HPyGlobal + #define PYTYPEOBJECT_GLOBAL_TYPE HPyGlobal + #define PYOBJECT_GLOBAL_STORE(global, h) HPyGlobal_Store(HPY_CONTEXT_CNAME, &global, h) + #define PYOBJECT_GLOBAL_LOAD(global) HPyGlobal_Load(HPY_CONTEXT_CNAME, global) + #define CAPI_IS_POINTER + #define CAPI_NEEDS_DEREFERENCE + + //Create New and Close References + #define PYOBJECT_NEWREF(h) HPy_Dup(HPY_CONTEXT_CNAME, h) + #define PYOBJECT_XNEWREF(h) HPy_Dup(HPY_CONTEXT_CNAME, h) + #define PYOBJECT_CLOSEREF(h) HPy_Close(HPY_CONTEXT_CNAME, h) + #define PYOBJECT_XCLOSEREF(h) HPy_Close(HPY_CONTEXT_CNAME, h) + #define PYOBJECT_GLOBAL_CLOSEREF(ref) HPy_Close(HPY_CONTEXT_CNAME, ref) + #define REFNANNY_CLOSEREF(func, h) PYOBJECT_CLOSEREF(h) + + //HPy to/from PyObject Functions + #define HPY_LEGACY_OBJECT_FROM(o) HPy_FromPyObject(HPY_CONTEXT_CNAME, o) + #define HPY_LEGACY_OBJECT_AS(o) HPy_AsPyObject(HPY_CONTEXT_CNAME, o) + #define HPY_LEGACY_OBJECT_ARRAY_AS(o, ssize) HPy_AsPyObjectArray(HPY_CONTEXT_CNAME, o, ssize) + + //NULL/None Values and functions + #define API_NULL_VALUE HPy_NULL + #define API_DEFAULT_VALUE HPy_NULL + #define API_IS_NULL(h) HPy_IsNull(h) + #define API_IS_NOT_NULL(h) !HPy_IsNull(h) + #define API_NONE_VALUE HPY_CONTEXT_CNAME->h_None + #define API_ASSIGN_NONE HPy_Dup(HPY_CONTEXT_CNAME, HPY_CONTEXT_CNAME->h_None) + + //Boolean Values and Functions + #define API_TRUE HPY_CONTEXT_CNAME->h_True + #define API_FALSE HPY_CONTEXT_CNAME->h_False + #define API_IS_TRUE(h) HPy_IsTrue(HPY_CONTEXT_CNAME, h) + #define API_IS_FALSE(h) !HPy_IsTrue(HPY_CONTEXT_CNAME, h) + + //General Methods + #define API_IS_EQUAL(a, b) HPy_Is(HPY_CONTEXT_CNAME, a, b) + #define API_RICH_COMPARE(h1, h2, op) HPy_RichCompare(HPY_CONTEXT_CNAME, h1, h2, op) + + //API Call Macros + #define API_CALL_FUNC(callable, args, nargs, kwnames) HPy_Call(HPY_CONTEXT_CNAME, callable, args, nargs, kwnames) + + //Type Objects + #define API_INT_TYPE HPY_CONTEXT_CNAME->h_LongType + #define API_LONG_TYPE HPY_CONTEXT_CNAME->h_LongType + #define API_SSIZE_T HPy_ssize_t + + //Type Checks + #define FLOAT_CHECK_EXACT(f) HPyFloat_Check(HPY_CONTEXT_CNAME, f) + #define DICT_CHECK(o) HPyDict_Check(HPY_CONTEXT_CNAME, o) + + //Integer Type - From + #define PYOBJECT_INT_FROM_LONG(i) HPyLong_FromLong(HPY_CONTEXT_CNAME, i) + #define PYOBJECT_INT_FROM_UNSIGNED_LONG(i) HPyLong_FromUnsignedLong(HPY_CONTEXT_CNAME, i) + + //Integer Type - To + #define PYOBJECT_INT_AS_LONG_NOERROR(l) HPyLong_AsLong(HPY_CONTEXT_CNAME, l) + + //Long Type - From + #define PYOBJECT_LONG_FROM_LONG(i) HPyLong_FromLong(HPY_CONTEXT_CNAME, i) + #define PYOBJECT_LONG_FROM_LONGLONG(i) HPyLong_FromLongLong(HPY_CONTEXT_CNAME, i) + #define PYOBJECT_LONG_FROM_UNSIGNED_LONGLONG(i) HPyLong_FromUnsignedLongLong(HPY_CONTEXT_CNAME, i) + + //Long Type - To + #define PYOBJECT_LONG_AS_SSIZE(l) HPyLong_AsSsize_t(HPY_CONTEXT_CNAME, l) + + //Float Type - From + #define PYOBJECT_FLOAT_FROM_DOUBLE(f) HPyFloat_FromDouble(HPY_CONTEXT_CNAME, f) + + //Float Type - To + #define PYOBJECT_FLOAT_AS_DOUBLE(f) HPyFloat_AsDouble(HPY_CONTEXT_CNAME, f) + + //Unicode Type - From + #define PYOBJECT_UNICODE_FROM_STRING(s) HPyUnicode_FromString(HPY_CONTEXT_CNAME, s) + #define PYOBJECT_UNICODE_FROM_FORMAT(format_str, ...) HPyUnicode_FromFormat(HPY_CONTEXT_CNAME, format_str, __VA_ARGS__) + + //Unicode Type - To + + //Bytes Type - From + #define BYTES_FROM_STR_AND_SIZE(str, size) HPyBytes_FromStringAndSize(HPY_CONTEXT_CNAME, str, size) + + //Bytes Type - To + + //Dict Type + #define DICT_NEW() HPyDict_New(HPY_CONTEXT_CNAME) + #define DICT_GET_ITEM(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) + #define DICT_SET_ITEM(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) + #define DICT_GET_ITEM_STR(o, attr_name) HPy_GetItem_s(HPY_CONTEXT_CNAME, o, attr_name) + #define DICT_SET_ITEM_STR(o, attr_name, attr_val) HPy_SetItem_s(HPY_CONTEXT_CNAME, o, attr_name, attr_val) + + //Sequence Type + + //Tuple Type + #define TUPLE_CREATE_EMPTY() HPyTuple_FromArray(HPY_CONTEXT_CNAME, NULL, 0) + #define TUPLE_GET_ITEM(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, PYOBJECT_LONG_FROM_LONG(pos)) + #define TUPLE_GET_SIZE(h) HPy_Length(HPY_CONTEXT_CNAME, (h)) + #define TUPLE_BUILDER_TYPE HPyTupleBuilder + #define TUPLE_CREATE_START(target, builder, size) builder = HPyTupleBuilder_New(HPY_CONTEXT_CNAME, size) + #define TUPLE_CREATE_ASSIGN(tuple, builder, index, item) HPyTupleBuilder_Set(HPY_CONTEXT_CNAME, builder, index, item) + #define TUPLE_CREATE_FINALISE(target, builder) target = HPyTupleBuilder_Build(HPY_CONTEXT_CNAME, builder); + #define TUPLE_PACK(num_args, ...) HPyTuple_Pack(HPY_CONTEXT_CNAME, num_args, __VA_ARGS__) + + //List Type + + //PyObject/HPy Handle Type + #define PYOBJECT_GET_ITEM(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) + #define PYOBJECT_SET_ITEM(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) + #define PYOBJECT_GET_ATTR(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) + #define PYOBJECT_SET_ATTR(o, attr_name, attr_val) HPy_SetAttr(HPY_CONTEXT_CNAME, o, attr_name, attr_val) + #define PYOBJECT_GET_ATTR_STR(o, attr_name) HPy_GetAttr_s(HPY_CONTEXT_CNAME, o, attr_name) + #define PYOBJECT_SET_ATTR_STR(o1, attr_name, o2) HPy_SetAttr_s(HPY_CONTEXT_CNAME, o1, attr_name, o2) + + //Type Type + #define TYPESPEC_TYPE HPyType_Spec + #define TYPE_FROM_MOD_AND_SPEC(m, s, b) HPyType_FromModuleAndSpec(HPY_CONTEXT_CNAME, m, &s, b) + #define TYPESPEC_GET(s, field) s.field + #define TYPE_CHECK(o) HPy_TypeCheck(HPY_CONTEXT_CNAME, (o), HPY_CONTEXT_CNAME->h_TypeType) + #define TYPE_AS_PYOBJECT(t) t + + + //Error & Exception Macros + #define PYERR_OCCURRED() HPyErr_Occurred(HPY_CONTEXT_CNAME) + #define PYERR_CLEAR() HPyErr_Clear(HPY_CONTEXT_CNAME) + #define PYERR_EXCEPTIONMATCHES(exc) HPyErr_ExceptionMatches(HPY_CONTEXT_CNAME, (exc)) + #define API_EXC(name) (HPY_CONTEXT_CNAME->h_ ## name) + + //Module Macros + #define PYMODULEDEF_TYPE HPyModuleDef + #define PYMODULE_GETDICT_ATTR(mod) HPy_GetAttr_s(HPY_CONTEXT_CNAME, mod, "__dict__") + + //Function Macros + #define PYMETHODDEF_TYPE HPyDef + +#else + //HPy Context Macros + #define HPY_CONTEXT_CNAME + #define HPY_CONTEXT_ONLY_ARG_DEF void + #define HPY_CONTEXT_ONLY_ARG_CALL + #define HPY_CONTEXT_FIRST_ARG_DEF + #define HPY_CONTEXT_FIRST_ARG_CALL + + //Handle/PyObject Macros + #define PYOBJECT_TYPE PyObject * + #define PYTYPEOBJECT_TYPE PyTypeObject * + #define PYOBJECT_FIELD_TYPE PyObject * + #define PYOBJECT_FIELD_STORE(owner, field, h) field = h + #define PYOBJECT_FIELD_LOAD(owner, field) field + #define PYOBJECT_GLOBAL_TYPE PyObject * + #define PYTYPEOBJECT_GLOBAL_TYPE PyTypeObject * + #define PYOBJECT_GLOBAL_STORE(global, h) global = h + #define PYOBJECT_GLOBAL_LOAD(global) global + #define CAPI_IS_POINTER * //Some types are sometimes pointers and sometimes not (i.e. PyModuleDef) where the type is always the same in HPy + #define CAPI_NEEDS_DEREFERENCE & + + //Create New and Close References + #define PYOBJECT_NEWREF(h) Py_NewRef(h) + #define PYOBJECT_XNEWREF(h) Py_XNewRef(h) + #define PYOBJECT_CLOSEREF(h) Py_DECREF(h) + #define PYOBJECT_XCLOSEREF(h) Py_XDECREF(h) + #define PYOBJECT_GLOBAL_CLOSEREF(ref) /* nop */ + #define REFNANNY_CLOSEREF(func, h) func(h) + + //HPy to/from PyObject Functions + #define HPY_LEGACY_OBJECT_FROM(o) o + #define HPY_LEGACY_OBJECT_AS(o) o + #define HPY_LEGACY_OBJECT_ARRAY_AS(o, ssize) o + + //NULL/None Values and functions + #define API_NULL_VALUE NULL + #define API_DEFAULT_VALUE 0 + #define API_IS_NULL(h) !h + #define API_IS_NOT_NULL(h) h + #define API_NONE_VALUE Py_None + #define API_ASSIGN_NONE Py_None + + //Boolean Values and Functions + #define API_TRUE Py_True + #define API_FALSE Py_False + #define API_IS_TRUE(h) PyObject_IsTrue(h) + #define API_IS_FALSE(h) !PyObject_Not(h) + + //General Methods + #define API_IS_EQUAL(a, b) a==b + #define API_RICH_COMPARE(h1, h2, op) PyObject_RichCompare(h1, h2, op) + + //API Call Macros + #define API_CALL_FUNC(callable, args, nargs, kwnames) PyObject_Call(HPY_CONTEXT_CNAME, callable, args, kwnames) + + //Type Objects + #define API_INT_TYPE PyInt_Type + #define API_LONG_TYPE PyLong_Type + #define API_SSIZE_T Py_ssize_t + + //Number Type Checks + #define FLOAT_CHECK_EXACT(f) PyFloat_CheckExact(f) + #define DICT_CHECK(o) PyDict_Check(o) + + + //Integer Type - From + #define PYOBJECT_INT_FROM_LONG(i) PyInt_FromLong(i) + #define PYOBJECT_INT_FROM_UNSIGNED_LONG(i) PyInt_FromUnsignedLong(i) + + //Integer Type - To + #define PYOBJECT_INT_AS_LONG_NOERROR(l) PyInt_AS_LONG(l) + + //Long Type - From + #define PYOBJECT_LONG_FROM_LONG(i) PyLong_FromLong(i) + #define PYOBJECT_LONG_FROM_LONGLONG(i) PyLong_FromLongLong(i) + #define PYOBJECT_LONG_FROM_UNSIGNED_LONGLONG(i) PyLong_FromUnsignedLongLong(i) + + //Long Type - To + #define PYOBJECT_LONG_AS_SSIZE(l) PyLong_AsSsize_t(l) + + //Float Type - From + #define PYOBJECT_FLOAT_FROM_DOUBLE(f) PyFloat_FromDouble(f) + + //Float Type - To + #define PYOBJECT_FLOAT_AS_DOUBLE(f) PyFloat_AsDouble(f) + + //Unicode Type - From + #define PYOBJECT_UNICODE_FROM_STRING(s) PyUnicode_FromString(s) + #define PYOBJECT_UNICODE_FROM_FORMAT(format_str, ...) PyUnicode_FromFormat(format_str, __VA_ARGS__) + + //Unicode Type - To + + //Bytes Type - From + #define BYTES_FROM_STR_AND_SIZE(str, size) PyBytes_FromStringAndSize(str, size) + + //Bytes Type - To + + //Dict Type + #define DICT_NEW() PyDict_New() + #define DICT_GET_ITEM(o, attr_name) PyDict_GetItem(o, attr_name) + #define DICT_SET_ITEM(o, attr_name, attr_val) PyDict_SetItem(o, attr_name, attr_val) + #define DICT_GET_ITEM_STR(o, attr_name) PyDict_GetItemString(o, attr_name) + #define DICT_SET_ITEM_STR(o, attr_name, attr_val) PyDict_SetItemString(o, attr_name, attr_val) + + //Sequence Type + + //Tuple Type + #define TUPLE_CREATE_EMPTY() PyTuple_New(0) + #define TUPLE_GET_ITEM(h, pos) __Pyx_PySequence_ITEM(HPY_CONTEXT_CNAME, h, pos) + #define TUPLE_GET_SIZE(h) PyTuple_GET_SIZE(h) + #define TUPLE_BUILDER_TYPE PyObject * //Not used, just needed to prevent errors + #define TUPLE_CREATE_START(target, builder, size) target=PyTuple_New(size) + #define TUPLE_CREATE_ASSIGN(tuple, builder, index, item) __Pyx_PyTuple_SET_ITEM(tuple, index, item) + #define TUPLE_CREATE_FINALISE(target, null) + #define TUPLE_PACK(num_args, ...) PyTuple_Pack(num_args, __VA_ARGS__) + + //List Type + + //PyObject/HPy Handle Type + #define PYOBJECT_GET_ITEM(o, attr_name) PyObject_GetItem(HPY_CONTEXT_CNAME, o, attr_name) + #define PYOBJECT_SET_ITEM(o, attr_name, attr_val) PyObject_SetItem(o, attr_name, attr_val) + #define PYOBJECT_GET_ATTR(o, attr_name) PyObject_GetAttr(o, attr_name) + #define PYOBJECT_SET_ATTR(o, attr_name, attr_val) PyObject_SetAttr(o, attr_name, attr_val) + #define PYOBJECT_GET_ATTR_STR(o, attr_name) PyObject_GetAttrString(o, attr_name) + #define PYOBJECT_SET_ATTR_STR(o1, attr_name, o2) PyObject_SetAttrString(o1, attr_name, o2) + + //Type Type + #define TYPESPEC_TYPE PyType_Spec + #define TYPE_FROM_MOD_AND_SPEC(m, s, b) PyType_FromModuleAndSpec(m, s, b) + #define TYPESPEC_GET(s, field) s->field + #define TYPE_CHECK(o) PyType_Check(o) + #define TYPE_AS_PYOBJECT(t) (PyObject*)&##t + + //Error & Exception Macros + #define PYERR_OCCURRED() (!!PyErr_Occurred()) + #define PYERR_CLEAR() PyErr_Clear() + #define PYERR_EXCEPTIONMATCHES(exc) PyErr_ExceptionMatches((exc)) + #define API_EXC(name) (PyExc_ ## name) + + //Module Macros + #define PYMODULEDEF_TYPE struct PyModuleDef + #define PYMODULE_GETDICT_ATTR(mod) PyModule_GetDict(mod) + + //Function Macros + #define PYMETHODDEF_TYPE PyMethodDef + +#endif + //////////////////// HPyHelperFuncs.proto //////////////////// #if CYTHON_USING_HPY diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 26384831e46..f8146efe243 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -723,231 +723,6 @@ class __Pyx_FakeReference { }; -/////////////// HPyInitCode /////////////// -//@substitute: naming - -#if CYTHON_USING_HPY - #define HPY_CONTEXT_CNAME $hpy_context_cname - #define HPY_CONTEXT_TYPE HPyContext * - #define HPY_CONTEXT_ONLY_ARG_DEF HPY_CONTEXT_TYPE HPY_CONTEXT_CNAME - #define HPY_CONTEXT_ONLY_ARG_CALL HPY_CONTEXT_CNAME - #define HPY_CONTEXT_FIRST_ARG_DEF HPY_CONTEXT_TYPE HPY_CONTEXT_CNAME, - #define HPY_CONTEXT_FIRST_ARG_CALL HPY_CONTEXT_CNAME, - - #define API_SSIZE_T HPy_ssize_t - - #define PYOBJECT_TYPE HPy - #define PYTYPEOBJECT_TYPE HPy - #define PYOBJECT_FIELD_TYPE HPyField - #define PYOBJECT_FIELD_STORE(owner, field, h) HPyField_Store(HPY_CONTEXT_CNAME, owner, &field, h) - #define PYOBJECT_FIELD_LOAD(owner, field) HPyField_Load(HPY_CONTEXT_CNAME, owner, field) - #define PYOBJECT_GLOBAL_TYPE HPyGlobal - #define PYTYPEOBJECT_GLOBAL_TYPE HPyGlobal - #define PYOBJECT_GLOBAL_STORE(global, h) HPyGlobal_Store(HPY_CONTEXT_CNAME, &global, h) - #define PYOBJECT_GLOBAL_LOAD(global) HPyGlobal_Load(HPY_CONTEXT_CNAME, global) - #define PYOBJECT_GLOBAL_CLOSEREF(ref) HPy_Close(HPY_CONTEXT_CNAME, ref) - #define CAPI_IS_POINTER - #define CAPI_NEEDS_DEREFERENCE - - #define PYOBJECT_NEWREF(h) HPy_Dup(HPY_CONTEXT_CNAME, h) - #define PYOBJECT_XNEWREF(h) HPy_Dup(HPY_CONTEXT_CNAME, h) - #define PYOBJECT_CLOSEREF(h) HPy_Close(HPY_CONTEXT_CNAME, h) - #define PYOBJECT_XCLOSEREF(h) HPy_Close(HPY_CONTEXT_CNAME, h) - #define REFNANNY_CLOSEREF(func, h) PYOBJECT_CLOSEREF(h) - - #define API_CALL_FUNC(callable, args, nargs, kwnames) HPy_Call(HPY_CONTEXT_CNAME, callable, args, nargs, kwnames) - - #define API_INT_TYPE HPY_CONTEXT_CNAME->h_LongType - #define API_LONG_TYPE HPY_CONTEXT_CNAME->h_LongType - - #define PYERR_OCCURRED() HPyErr_Occurred(HPY_CONTEXT_CNAME) - #define PYERR_CLEAR() HPyErr_Clear(HPY_CONTEXT_CNAME) - #define PYERR_EXCEPTIONMATCHES(exc) HPyErr_ExceptionMatches(HPY_CONTEXT_CNAME, (exc)) - - #define PYOBJECT_INT_FROM_LONG(i) HPyLong_FromLong(HPY_CONTEXT_CNAME, i) - #define PYOBJECT_LONG_FROM_LONG(i) HPyLong_FromLong(HPY_CONTEXT_CNAME, i) - #define PYOBJECT_LONG_FROM_LONGLONG(i) HPyLong_FromLongLong(HPY_CONTEXT_CNAME, i) - #define PYOBJECT_FROM_UNSIGNED_LONG(i) HPyLong_FromUnsignedLong(HPY_CONTEXT_CNAME, i) - #define PYOBJECT_FROM_UNSIGNED_LONGLONG(i) HPyLong_FromUnsignedLongLong(HPY_CONTEXT_CNAME, i) - #define PYOBJECT_FROM_DOUBLE(f) HPyFloat_FromDouble(HPY_CONTEXT_CNAME, f) - #define PYOBJECT_FROM_STRING(s) HPyUnicode_FromString(HPY_CONTEXT_CNAME, s) //not yet needed in C API version - - #define PYOBJECT_LONG_AS_SSIZE(l) HPyLong_AsSsize_t(HPY_CONTEXT_CNAME, l) - #define PYOBJECT_INT_AS_LONG_NOERROR(l) HPyLong_AsLong(HPY_CONTEXT_CNAME, l) - #define PYOBJECT_FLOAT_AS_DOUBLE(f) HPyFloat_AsDouble(HPY_CONTEXT_CNAME, f) - - #define FLOAT_CHECK_EXACT(f) HPyFloat_Check(HPY_CONTEXT_CNAME, f) - - #define HPY_LEGACY_OBJECT_FROM(o) HPy_FromPyObject(HPY_CONTEXT_CNAME, o) - #define HPY_LEGACY_OBJECT_AS(o) HPy_AsPyObject(HPY_CONTEXT_CNAME, o) - #define HPY_LEGACY_OBJECT_ARRAY_AS(o, ssize) HPy_AsPyObjectArray(HPY_CONTEXT_CNAME, o, ssize) - - #define PYMODULEDEF_TYPE HPyModuleDef - #define PYMETHODDEF_TYPE HPyDef - #define TYPESPEC_TYPE HPyType_Spec - #define TYPE_FROM_MOD_AND_SPEC(m, s, b) HPyType_FromModuleAndSpec(HPY_CONTEXT_CNAME, m, &s, b) - #define TYPESPEC_GET(s, field) s.field - #define TYPE_CHECK(o) HPy_TypeCheck(HPY_CONTEXT_CNAME, (o), HPY_CONTEXT_CNAME->h_TypeType) - #define TYPE_AS_PYOBJECT(t) t - - #define API_NULL_VALUE HPy_NULL - #define API_DEFAULT_VALUE HPy_NULL - #define API_IS_NULL(h) HPy_IsNull(h) - #define API_IS_NOT_NULL(h) !HPy_IsNull(h) - #define API_IS_EQUAL(a, b) HPy_Is(HPY_CONTEXT_CNAME, a, b) - #define API_ASSIGN_NONE HPy_Dup(HPY_CONTEXT_CNAME, HPY_CONTEXT_CNAME->h_None) - #define API_IS_TRUE(h) HPy_IsTrue(HPY_CONTEXT_CNAME, h) - #define API_IS_FALSE(h) !HPy_IsTrue(HPY_CONTEXT_CNAME, h) - - /* constants */ - #define API_NONE_VALUE HPY_CONTEXT_CNAME->h_None - #define API_TRUE HPY_CONTEXT_CNAME->h_True - #define API_FALSE HPY_CONTEXT_CNAME->h_False - #define API_EXC_ATTRIBUTEERROR HPY_CONTEXT_CNAME->h_AttributeError - #define API_EXC(name) (HPY_CONTEXT_CNAME->h_ ## name) - - #define API_VECTORCALLFUNC HPyCallFunction - - #define API_RICH_COMPARE(h1, h2, op) HPy_RichCompare(HPY_CONTEXT_CNAME, h1, h2, op) - - #define DICT_NEW() HPyDict_New(HPY_CONTEXT_CNAME) - #define DICT_GET_ITEM(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) - #define DICT_SET_ITEM(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) - #define DICT_GET_ITEM_STR(o, attr_name) HPy_GetItem_s(HPY_CONTEXT_CNAME, o, attr_name) - #define DICT_SET_ITEM_STR(o, attr_name, attr_val) HPy_SetItem_s(HPY_CONTEXT_CNAME, o, attr_name, attr_val) - #define DICT_CHECK(o) HPyDict_Check(HPY_CONTEXT_CNAME, o) - - #define PYOBJECT_GET_ITEM(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) - #define PYOBJECT_SET_ITEM(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) - #define PYOBJECT_GET_ATTR(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) - #define PYOBJECT_SET_ATTR(o, attr_name, attr_val) HPy_SetAttr(HPY_CONTEXT_CNAME, o, attr_name, attr_val) - #define PYOBJECT_GET_ATTR_STR(o, attr_name) HPy_GetAttr_s(HPY_CONTEXT_CNAME, o, attr_name) - #define PYOBJECT_SET_ATTR_STR(o1, attr_name, o2) HPy_SetAttr_s(HPY_CONTEXT_CNAME, o1, attr_name, o2) - - #define PYMODULE_GETDICT_ATTR(mod) HPy_GetAttr_s(HPY_CONTEXT_CNAME, mod, "__dict__") - - #define BYTES_FROM_STR_AND_SIZE(str, size) HPyBytes_FromStringAndSize(HPY_CONTEXT_CNAME, str, size) - - #define TUPLE_CREATE_EMPTY() HPyTuple_FromArray(HPY_CONTEXT_CNAME, NULL, 0) - #define TUPLE_GET_ITEM(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, PYOBJECT_LONG_FROM_LONG(pos)) - #define TUPLE_GET_SIZE(h) HPy_Length(HPY_CONTEXT_CNAME, (h)) - #define TUPLE_BUILDER_TYPE HPyTupleBuilder - #define TUPLE_CREATE_START(target, builder, size) builder = HPyTupleBuilder_New(HPY_CONTEXT_CNAME, size) - #define TUPLE_CREATE_ASSIGN(tuple, builder, index, item) HPyTupleBuilder_Set(HPY_CONTEXT_CNAME, builder, index, item) - #define TUPLE_CREATE_FINALISE(target, builder) target = HPyTupleBuilder_Build(HPY_CONTEXT_CNAME, builder); - #define TUPLE_PACK(num_args, ...) HPyTuple_Pack(HPY_CONTEXT_CNAME, num_args, __VA_ARGS__) - - #define UNICODE_FROM_FORMAT(format_str, ...) HPyUnicode_FromFormat(HPY_CONTEXT_CNAME, format_str, __VA_ARGS__) -#else - #define HPY_CONTEXT_CNAME - #define HPY_CONTEXT_ONLY_ARG_DEF void - #define HPY_CONTEXT_ONLY_ARG_CALL - #define HPY_CONTEXT_FIRST_ARG_DEF - #define HPY_CONTEXT_FIRST_ARG_CALL - - #define API_SSIZE_T Py_ssize_t - - #define PYOBJECT_TYPE PyObject * - #define PYTYPEOBJECT_TYPE PyTypeObject * - #define PYOBJECT_FIELD_TYPE PyObject * - #define PYOBJECT_FIELD_STORE(owner, field, h) field = h - #define PYOBJECT_FIELD_LOAD(owner, field) field - #define PYOBJECT_GLOBAL_TYPE PyObject * - #define PYTYPEOBJECT_GLOBAL_TYPE PyTypeObject * - #define PYOBJECT_GLOBAL_STORE(global, h) global = h - #define PYOBJECT_GLOBAL_LOAD(global) global - #define PYOBJECT_GLOBAL_CLOSEREF(ref) /* nop */ - #define CAPI_IS_POINTER * //Some types are sometimes pointers and sometimes not (i.e. PyModuleDef) where the type is always the same in HPy - #define CAPI_NEEDS_DEREFERENCE & - - #define PYOBJECT_NEWREF(h) Py_NewRef(h) - #define PYOBJECT_XNEWREF(h) Py_XNewRef(h) - #define PYOBJECT_CLOSEREF(h) Py_DECREF(h) - #define PYOBJECT_XCLOSEREF(h) Py_XDECREF(h) - #define REFNANNY_CLOSEREF(func, h) func(h) - - #define PYERR_OCCURRED() (!!PyErr_Occurred()) - #define PYERR_CLEAR() PyErr_Clear() - #define PYERR_EXCEPTIONMATCHES(exc) PyErr_ExceptionMatches((exc)) - - #define API_CALL_FUNC(callable, args, nargs, kwnames) PyObject_Call(HPY_CONTEXT_CNAME, callable, args, kwnames) - - #define API_INT_TYPE PyInt_Type - #define API_LONG_TYPE PyLong_Type - - #define PYOBJECT_INT_FROM_LONG(i) PyInt_FromLong(i) - #define PYOBJECT_LONG_FROM_LONG(i) PyLong_FromLong(i) - #define PYOBJECT_LONG_FROM_LONGLONG(i) PyLong_FromLongLong(i) - #define PYOBJECT_FROM_UNSIGNED_LONG(i) PyLong_FromUnsignedLong(i) - #define PYOBJECT_FROM_UNSIGNED_LONGLONG(i) PyLong_FromUnsignedLongLong(i) - #define PYOBJECT_FROM_DOUBLE(f) PyFloat_FromDouble(f) - - #define PYOBJECT_LONG_AS_SSIZE(l) PyLong_AsSsize_t(l) - #define PYOBJECT_INT_AS_LONG_NOERROR(l) PyInt_AS_LONG(l) - #define PYOBJECT_FLOAT_AS_DOUBLE(f) PyFloat_AsDouble(f) - - #define FLOAT_CHECK_EXACT(f) HPY_LEGACY_OBJECT_AS(f) - - #define HPY_LEGACY_OBJECT_FROM(o) o - #define HPY_LEGACY_OBJECT_AS(o) o - #define HPY_LEGACY_OBJECT_ARRAY_AS(o, ssize) o - - #define PYMODULEDEF_TYPE struct PyModuleDef - #define PYMETHODDEF_TYPE PyMethodDef - #define TYPESPEC_TYPE PyType_Spec - #define TYPE_FROM_MOD_AND_SPEC(m, s, b) PyType_FromModuleAndSpec(m, s, b) - #define TYPESPEC_GET(s, field) s->field - #define TYPE_CHECK(o) PyType_Check(o) - #define TYPE_AS_PYOBJECT(t) (PyObject*)& ## t - - #define API_NULL_VALUE NULL - #define API_DEFAULT_VALUE 0 - #define API_IS_NULL(h) !h //Both are here as otherwise we would get !!h for API_IS_NOT_NULL, which is hard to read - but it can be made so if necessary - #define API_IS_NOT_NULL(h) h - #define API_IS_EQUAL(a, b) a==b - #define API_ASSIGN_NONE Py_None - #define API_IS_TRUE(h) PyObject_IsTrue(h) - #define API_IS_FALSE(h) !PyObject_Not(h) - - /* constants */ - #define API_NONE_VALUE Py_None - #define API_TRUE Py_True - #define API_FALSE Py_False - #define API_EXC(name) (PyExc_ ## name) - - #define API_RICH_COMPARE(h1, h2, op) PyObject_RichCompare(h1, h2, op) - - #define DICT_NEW() PyDict_New() - #define DICT_GET_ITEM(o, attr_name) PyDict_GetItem(o, attr_name) - #define DICT_SET_ITEM(o, attr_name, attr_val) PyDict_SetItem(o, attr_name, attr_val) - #define DICT_GET_ITEM_STR(o, attr_name) PyDict_GetItemString(o, attr_name) - #define DICT_SET_ITEM_STR(o, attr_name, attr_val) PyDict_SetItemString(o, attr_name, attr_val) - #define DICT_CHECK(o) PyDict_Check(o) - - #define PYOBJECT_GET_ITEM(o, attr_name) PyObject_GetItem(HPY_CONTEXT_CNAME, o, attr_name) - #define PYOBJECT_SET_ITEM(o, attr_name, attr_val) PyObject_SetItem(o, attr_name, attr_val) - #define PYOBJECT_GET_ATTR(o, attr_name) PyObject_GetAttr(o, attr_name) - #define PYOBJECT_SET_ATTR(o, attr_name, attr_val) PyObject_SetAttr(o, attr_name, attr_val) - #define PYOBJECT_GET_ATTR_STR(o, attr_name) PyObject_GetAttrString(o, attr_name) - #define PYOBJECT_SET_ATTR_STR(o1, attr_name, o2) PyObject_SetAttrString(o1, attr_name, o2) - - #define PYMODULE_GETDICT_ATTR(mod) PyModule_GetDict(mod) - - #define BYTES_FROM_STR_AND_SIZE(str, size) PyBytes_FromStringAndSize(str, size) - - #define TUPLE_CREATE_EMPTY() PyTuple_New(0) - #define TUPLE_GET_ITEM(h, pos) __Pyx_PySequence_ITEM(HPY_CONTEXT_CNAME, h, pos) - #define TUPLE_GET_SIZE(h) PyTuple_GET_SIZE(h) - #define TUPLE_BUILDER_TYPE PyObject * //Not used, just needed to prevent errors - #define TUPLE_CREATE_START(target, builder, size) target=PyTuple_New(size) - #define TUPLE_CREATE_ASSIGN(tuple, builder, index, item) __Pyx_PyTuple_SET_ITEM(tuple, index, item) - #define TUPLE_CREATE_FINALISE(target, null) - #define TUPLE_PACK(num_args, ...) PyTuple_Pack(num_args, __VA_ARGS__) - - #define UNICODE_FROM_FORMAT(format_str, ...) PyUnicode_FromFormat(format_str, __VA_ARGS__) - -#endif - /////////////// PythonCompatibility /////////////// #define __PYX_BUILD_PY_SSIZE_T "n" diff --git a/Cython/Utility/Optimize.c b/Cython/Utility/Optimize.c index 64785a1e2fd..a54e95f889f 100644 --- a/Cython/Utility/Optimize.c +++ b/Cython/Utility/Optimize.c @@ -1309,7 +1309,7 @@ static {{c_ret_type}} {{cfunc_name}}(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE op1 {{elif op == 'TrueDivide'}} if ((8 * sizeof(long) <= 53 || likely(labs({{ival}}) <= ((PY_LONG_LONG)1 << 53))) || __Pyx_PyLong_DigitCount({{pyval}}) <= 52 / PyLong_SHIFT) { - return PYOBJECT_FROM_DOUBLE((double)a / (double)b); + return PYOBJECT_FLOAT_FROM_DOUBLE((double)a / (double)b); } return PyLong_Type.tp_as_number->nb_{{slot_name}}(op1, op2); {{elif op == 'FloorDivide'}} @@ -1379,7 +1379,7 @@ static {{c_ret_type}} {{cfunc_name}}(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE op1 double result; {{zerodiv_check('b', 'float')}} result = ((double)a) {{c_op}} (double)b; - return PYOBJECT_FROM_DOUBLE(result); + return PYOBJECT_FLOAT_FROM_DOUBLE(result); {{endif}} } {{endif}} diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index ae26bc7d2f7..018c7d903a1 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -469,7 +469,7 @@ static CYTHON_INLINE PyObject* __Pyx__PyNumber_Float(PyObject* obj) { #if CYTHON_USE_PYLONG_INTERNALS no_error: #endif - return PYOBJECT_FROM_DOUBLE(val); + return PYOBJECT_FLOAT_FROM_DOUBLE(val); } /////////////// GCCDiagnostics.proto /////////////// @@ -697,10 +697,10 @@ static CYTHON_INLINE PYOBJECT_TYPE {{TO_PY_FUNCTION}}(HPY_CONTEXT_FIRST_ARG_DEF if (sizeof({{TYPE}}) < sizeof(long)) { return PYOBJECT_INT_FROM_LONG((long) value); } else if (sizeof({{TYPE}}) <= sizeof(unsigned long)) { - return PYOBJECT_FROM_UNSIGNED_LONG((unsigned long) value); + return PYOBJECT_INT_FROM_UNSIGNED_LONG((unsigned long) value); #ifdef HAVE_LONG_LONG } else if (sizeof({{TYPE}}) <= sizeof(unsigned PY_LONG_LONG)) { - return PYOBJECT_FROM_UNSIGNED_LONGLONG((unsigned PY_LONG_LONG) value); + return PYOBJECT_LONG_FROM_UNSIGNED_LONGLONG((unsigned PY_LONG_LONG) value); #endif } } else { @@ -733,7 +733,7 @@ static CYTHON_INLINE PYOBJECT_TYPE {{TO_PY_FUNCTION}}(HPY_CONTEXT_FIRST_ARG_DEF // I'm deliberately not using PYIDENT here because this code path is very unlikely // to ever run so it seems a pessimization mostly. #if CYTHON_USING_HPY - order_str = PYOBJECT_FROM_STRING(little ? "little" : "big"); + order_str = PYOBJECT_UNICODE_FROM_STRING(little ? "little" : "big"); if (API_IS_NULL(order_str)) goto limited_bad; arg_tuple = TUPLE_PACK(2, py_bytes, order_str); if (API_IS_NULL(arg_tuple)) goto limited_bad; From 302274c12d328dca9c914fed45b420d51ec73563 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Fri, 6 Oct 2023 13:42:53 +0200 Subject: [PATCH 194/286] Added descr_get legacy slot to HPy Functions (for doctests) --- Cython/Utility/CythonFunction.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 2f97931106c..9cba10a85fa 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -58,6 +58,7 @@ typedef struct { __pyx_vectorcallfunc func_vectorcall; #endif #else /* !CYTHON_USING_HPY */ + PyObject_HEAD HPyDef *func; #endif /* !CYTHON_USING_HPY */ PYOBJECT_FIELD_TYPE func_self; @@ -86,7 +87,7 @@ typedef struct { } __pyx_CyFunctionObject; #if CYTHON_USING_HPY -HPyType_HELPERS(__pyx_CyFunctionObject) +HPyType_LEGACY_HELPERS(__pyx_CyFunctionObject) #endif #undef __Pyx_CyOrPyCFunction_Check @@ -104,8 +105,8 @@ static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFun PYOBJECT_TYPE module, PYOBJECT_TYPE globals, PYOBJECT_TYPE code); -static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(HPY_CONTEXT_FIRST_ARG_DEF - __pyx_CyFunctionObject_FuncDef f, +static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(HPY_CONTEXT_FIRST_ARG_DEF + __pyx_CyFunctionObject_FuncDef f, PYOBJECT_TYPE classobj); static CYTHON_INLINE void *__Pyx_CyFunction_InitDefaults(PyObject *m, size_t size, @@ -1432,13 +1433,18 @@ static HPyDef *__pyx_CyFunctionType_HPyDefines[] = { NULL }; +static PyType_Slot __pyx_CyFunctionType_legacy_slots[] = { + {Py_tp_descr_get, (void *)__Pyx_PyMethod_New}, + {0, 0}, +}; + static HPyType_Spec __pyx_CyFunctionType_spec = { __PYX_TYPE_MODULE_PREFIX "cython_function_or_method", .basicsize = sizeof(__pyx_CyFunctionObject), .itemsize = 0, .flags = HPy_TPFLAGS_DEFAULT | HPy_TPFLAGS_HAVE_GC | HPy_TPFLAGS_BASETYPE, /*tp_flags*/ .builtin_shape = SHAPE(__pyx_CyFunctionObject), - .legacy_slots = 0, + .legacy_slots = __pyx_CyFunctionType_legacy_slots, .defines = __pyx_CyFunctionType_HPyDefines }; #else /* CYTHON_USING_HPY */ From 7945631b56de5d9c216c9ae5bda9e79198100524 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Mon, 9 Oct 2023 17:22:55 +0200 Subject: [PATCH 195/286] Fix missing newref --- Cython/Compiler/Nodes.py | 2 +- Cython/Utility/CythonFunction.c | 3 ++- Cython/Utility/ObjectHandling.c | 19 +++++++++++++++++++ tests/run/hpy_forloop.pyx | 8 ++++++-- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index e0b35f32e40..6b46ad5ca3e 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -6880,7 +6880,7 @@ def generate_execution_code(self, code): Naming.retval_cname, value.result_as(self.return_type))) else: - code.putln("%s = %s;" % ( + code.putln("%s = PYOBJECT_NEWREF(%s);" % ( Naming.retval_cname, value.result_as(self.return_type))) value.generate_post_assignment_code(code) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 9cba10a85fa..925a71cac22 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -1430,11 +1430,12 @@ static HPyDef *__pyx_CyFunctionType_HPyDefines[] = { // {Py_tp_getset, (void *)__pyx_CyFunction_getsets}, &__Pyx_CyFunction_doc, // {Py_tp_descr_get, (void *)__Pyx_PyMethod_New}, + &__pyx_CyFunction_descr_get, NULL }; static PyType_Slot __pyx_CyFunctionType_legacy_slots[] = { - {Py_tp_descr_get, (void *)__Pyx_PyMethod_New}, + // {Py_tp_descr_get, (void *)__Pyx_PyMethod_New}, {0, 0}, }; diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 5e31609413a..ffb24b4dc54 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -2633,6 +2633,24 @@ static CYTHON_INLINE int __Pyx_object_dict_version_matches(PyObject* obj, PY_UIN /////////////// PyMethodNew.proto /////////////// +#if CYTHON_USING_HPY +HPyDef_SLOT(__pyx_CyFunction_descr_get, HPy_tp_descr_get) +static HPy __pyx_CyFunction_descr_get_impl(HPyContext *ctx, HPy func, HPy self, HPy typ) { + HPy typesModule=HPy_NULL, methodType=HPy_NULL, result=HPy_NULL; + CYTHON_UNUSED_VAR(typ); + if (HPy_IsNull(self)) + return HPy_Dup(ctx, func); + typesModule = HPyImport_ImportModule(ctx, "types"); + if (HPy_IsNull(typesModule)) return HPy_NULL; + methodType = HPy_GetAttr_s(ctx, typesModule, "MethodType"); + HPy_Close(ctx, typesModule); + if (HPy_IsNull(methodType)) return HPy_NULL; + HPy args[] = { func, self }; + result = HPy_Call(ctx, methodType, args, 2, HPy_NULL); + HPy_Close(ctx, methodType); + return result; +} +#else #if CYTHON_COMPILING_IN_LIMITED_API static PyObject *__Pyx_PyMethod_New(PyObject *func, PyObject *self, PyObject *typ) { PyObject *typesModule=NULL, *methodType=NULL, *result=NULL; @@ -2658,6 +2676,7 @@ static PyObject *__Pyx_PyMethod_New(PyObject *func, PyObject *self, PyObject *ty return PyMethod_New(func, self); } #endif +#endif ///////////// PyMethodNew2Arg.proto ///////////// diff --git a/tests/run/hpy_forloop.pyx b/tests/run/hpy_forloop.pyx index ea4a9797090..862830d7a03 100644 --- a/tests/run/hpy_forloop.pyx +++ b/tests/run/hpy_forloop.pyx @@ -3,7 +3,11 @@ import cython def add(): + """ + >>> add() + 49999995000000 + """ a = 0 - for i in range(100): + for i in range(10000000): a = a + i - return a \ No newline at end of file + return a From 8c0f6cfb107bb50abf784d4fe51e746e19977926 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Tue, 10 Oct 2023 19:48:42 +0200 Subject: [PATCH 196/286] Cleanup --- Cython/Utility/CythonFunction.c | 14 ++++++-------- Cython/Utility/ObjectHandling.c | 3 +-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 925a71cac22..c2f602d668f 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -58,7 +58,6 @@ typedef struct { __pyx_vectorcallfunc func_vectorcall; #endif #else /* !CYTHON_USING_HPY */ - PyObject_HEAD HPyDef *func; #endif /* !CYTHON_USING_HPY */ PYOBJECT_FIELD_TYPE func_self; @@ -87,7 +86,7 @@ typedef struct { } __pyx_CyFunctionObject; #if CYTHON_USING_HPY -HPyType_LEGACY_HELPERS(__pyx_CyFunctionObject) +HPyType_HELPERS(__pyx_CyFunctionObject) #endif #undef __Pyx_CyOrPyCFunction_Check @@ -1419,6 +1418,11 @@ static HPy __Pyx_CyFunction_call_impl(HPyContext *HPY_CONTEXT_CNAME, HPy func, c #if CYTHON_USE_TYPE_SPECS #if CYTHON_USING_HPY +HPyDef_SLOT(__pyx_CyFunction_descr_get, HPy_tp_descr_get) +static HPy __pyx_CyFunction_descr_get_impl(HPyContext *ctx, HPy func, HPy self, HPy typ) { + __Pyx_PyMethod_New(ctx, func, self, typ); +} + static HPyDef *__pyx_CyFunctionType_HPyDefines[] = { &__Pyx_CyFunction_repr, &__Pyx_CyFunction_call, @@ -1434,18 +1438,12 @@ static HPyDef *__pyx_CyFunctionType_HPyDefines[] = { NULL }; -static PyType_Slot __pyx_CyFunctionType_legacy_slots[] = { - // {Py_tp_descr_get, (void *)__Pyx_PyMethod_New}, - {0, 0}, -}; - static HPyType_Spec __pyx_CyFunctionType_spec = { __PYX_TYPE_MODULE_PREFIX "cython_function_or_method", .basicsize = sizeof(__pyx_CyFunctionObject), .itemsize = 0, .flags = HPy_TPFLAGS_DEFAULT | HPy_TPFLAGS_HAVE_GC | HPy_TPFLAGS_BASETYPE, /*tp_flags*/ .builtin_shape = SHAPE(__pyx_CyFunctionObject), - .legacy_slots = __pyx_CyFunctionType_legacy_slots, .defines = __pyx_CyFunctionType_HPyDefines }; #else /* CYTHON_USING_HPY */ diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index ffb24b4dc54..31a4b2d6faa 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -2634,8 +2634,7 @@ static CYTHON_INLINE int __Pyx_object_dict_version_matches(PyObject* obj, PY_UIN /////////////// PyMethodNew.proto /////////////// #if CYTHON_USING_HPY -HPyDef_SLOT(__pyx_CyFunction_descr_get, HPy_tp_descr_get) -static HPy __pyx_CyFunction_descr_get_impl(HPyContext *ctx, HPy func, HPy self, HPy typ) { +static HPy __Pyx_PyMethod_New(HPyContext *ctx, HPy func, HPy self, HPy typ) { HPy typesModule=HPy_NULL, methodType=HPy_NULL, result=HPy_NULL; CYTHON_UNUSED_VAR(typ); if (HPy_IsNull(self)) From a43d96deebb3f5be30a79fb50d21b4be2a6195c7 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Fri, 29 Sep 2023 14:27:11 +0200 Subject: [PATCH 197/286] FIrst steps towards one-arg functions --- Cython/Compiler/Code.py | 6 ++--- Cython/Compiler/ExprNodes.py | 18 ++++++++----- Cython/Compiler/Nodes.py | 43 +++++++++++++++++++++++------- Cython/Compiler/TypeSlots.py | 4 +-- Cython/Utility/FunctionArguments.c | 16 +++++------ 5 files changed, 58 insertions(+), 29 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index ffbfcecd38f..6c804e2cd0f 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -2156,10 +2156,10 @@ def put_var_declaration(self, entry, storage_class="", entry.type.is_global = False self.put(entry.type.declaration_code( entry.cname, dll_linkage=dll_linkage)) - if entry.init is not None: - self.put_safe(" = %s" % entry.type.literal_code(entry.init)) - elif entry.type.is_pyobject: + if entry.type.is_pyobject: self.put(" = API_NULL_VALUE") + elif entry.init is not None: + self.put_safe(" = %s" % entry.type.literal_code(entry.init)) self.putln(";") self.funcstate.scope.use_entry_utility_code(entry) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index ceb2f10fad3..b9635828156 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -4557,7 +4557,7 @@ def generate_result_code(self, code): utility_code = None error_value = None if self.type.is_pyobject: - error_value = 'NULL' + error_value = 'API_NULL_VALUE' if self.index.type.is_int: if self.base.type is list_type: function = "__Pyx_GetItemInt_List" @@ -4609,7 +4609,7 @@ def generate_result_code(self, code): self.result() if self.type.is_pyobject else None, self.exception_value, self.in_nogil_context) else: - error_check = '!%s' if error_value == 'NULL' else '%%s == %s' % error_value + error_check = 'API_IS_NULL(%s)' if error_value == 'NULL' else '%%s == %s' % error_value code.putln( "%s = %s(%s, %s%s); %s" % ( self.result(), @@ -6518,12 +6518,18 @@ def generate_result_code(self, code): arg_code = self.arg_tuple.py_result() code.globalstate.use_utility_code(UtilityCode.load_cached( "PyObjectCall", "ObjectHandling.c")) + func_result = self.function.py_result() + if func_result.startswith("__pyx_") and not func_result.startswith("__pyx_t_") and not func_result.startswith("__pyx_v_"): + func_result = "PYOBJECT_GLOBAL_LOAD(" + func_result + ")" + load_arg_code = arg_code + if load_arg_code.startswith("__pyx_") and not load_arg_code.startswith("__pyx_t_") and not load_arg_code.startswith("__pyx_v_"): + load_arg_code = "PYOBJECT_GLOBAL_LOAD(" + load_arg_code + ")" code.putln( - "%s = __Pyx_PyObject_Call(%s, %s, NULL); %s" % ( + "%s = HPY_LEGACY_OBJECT_FROM(__Pyx_PyObject_Call(HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), NULL)); %s" % ( self.result(), - self.function.py_result(), - arg_code, - code.error_goto_if_null(self.result(), self.pos))) + func_result, + load_arg_code, + code.error_goto_if_null_object(self.result(), self.pos))) self.generate_gotref(code) elif func_type.is_cfunction: nogil = not code.funcstate.gil_owned diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 6b46ad5ca3e..57853a7c392 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -3778,7 +3778,7 @@ def generate_function_header(self, code, with_pymethdef, proto_only=0): varargs_args = "PYOBJECT_TYPE %s, PYOBJECT_TYPE %s" % ( Naming.args_cname, Naming.kwds_cname) if sig.use_fastcall: - fastcall_args = "PYOBJECT_TYPE const *%s, API_SSIZE_T %s, PYOBJECT_TYPE %s" % ( + fastcall_args = "PYOBJECT_TYPE const *%s, size_t %s, PYOBJECT_TYPE %s" % ( Naming.args_cname, Naming.nargs_cname, Naming.kwds_cname) arg_code_list.append( "\n#if CYTHON_METH_FASTCALL\n%s\n#else\n%s\n#endif\n" % ( @@ -3942,7 +3942,7 @@ def generate_argument_parsing_code(self, env, code, decl_code): code.put_var_xdecref(arg.entry) code.put_add_traceback(self.target.entry.qualified_name) code.put_finish_refcount_context() - code.putln("return %s;" % self.error_value()) + code.putln("return %s;//returns error value" % self.error_value()) if code.label_used(end_label): code.put_label(end_label) @@ -4077,11 +4077,24 @@ def generate_tuple_and_keyword_parsing_code(self, args, code, decl_code): code.putln('{') all_args = tuple(positional_args) + tuple(kw_only_args) non_posonly_args = [arg for arg in all_args if not arg.pos_only] - non_pos_args_id = ','.join( - ['&%s' % code.intern_identifier(arg.entry.name) for arg in non_posonly_args] + ['0']) - code.putln("PyObject **%s[] = {%s};" % ( + non_pos_args_id = "" + loaded_args_arr = [] + for arg in non_posonly_args: + arg_str = code.intern_identifier(arg.entry.name) + if not arg_str.startswith("__pyx_t_"): + temp_load_arg = code.funcstate.allocate_temp(py_object_type, manage_ref=False) + loaded_args_arr.append(temp_load_arg) + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_arg, arg_str)) + non_pos_args_id = non_pos_args_id + "&%s, " % temp_load_arg + else: + non_pos_args_id = non_pos_args_id + "&%s, " % arg_str + non_pos_args_id = non_pos_args_id + '0' + code.putln("PyObject **%s[] = {%s};////////" % ( Naming.pykwdlist_cname, non_pos_args_id)) + + for arg in loaded_args_arr: + code.funcstate.release_temp(arg) # Before being converted and assigned to the target variables, # borrowed references to all unpacked argument values are @@ -4109,7 +4122,7 @@ def generate_tuple_and_keyword_parsing_code(self, args, code, decl_code): kw_unpacking_condition = "likely(%s)" % kw_unpacking_condition # --- optimised code when we receive keyword arguments - code.putln("if (%s) {" % kw_unpacking_condition) + code.putln("if (API_IS_NOT_NULL(%s)) {" % kw_unpacking_condition) if accept_kwd_args: self.generate_keyword_unpacking_code( @@ -4285,7 +4298,7 @@ def generate_argument_values_setup_code(self, args, code, decl_code): # the 'values' array collects references to arguments # before doing any type coercion etc.. Whether they are borrowed or not # depends on the compilation options. - decl_code.putln("PyObject* values[%d] = {%s};" % ( + decl_code.putln("PYOBJECT_TYPE values[%d] = {%s};" % ( max_args, ','.join('0'*max_args))) if self.target.defaults_struct: @@ -4402,8 +4415,18 @@ def generate_keyword_unpacking_code(self, min_positional_args, max_positional_ar code.putln('else if (unlikely(PyErr_Occurred())) %s' % code.error_goto(self.pos)) code.putln('}') else: - code.putln('if (likely((values[%d] = __Pyx_GetKwValue_%s(%s, %s, %s)) != 0)) {' % ( - i, self.signature.fastvar, Naming.kwds_cname, Naming.kwvalues_cname, pystring_cname)) + temp_load_pystr = code.funcstate.allocate_temp(py_object_type, manage_ref=False) + if pystring_cname.startswith("__pyx_t") or pystring_cname.startswith("__pyx_v"): + pystr_is_global = False + code.putln("%s = %s;" % (temp_load_pystr, pystring_cname)) + else: + pystr_is_global = True + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_pystr, pystring_cname)) + code.putln('if (likely(API_IS_NOT_NULL(values[%d] = __Pyx_GetKwValue_%s(HPY_CONTEXT_FIRST_ARG_CALL %s, %s, %s)))) {' % ( + i, self.signature.fastvar, Naming.kwds_cname, Naming.kwvalues_cname, temp_load_pystr)) + if pystr_is_global: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_pystr) #This should also be closed in the other else statements, but as they are errors it doesn't matter too much + code.funcstate.release_temp(temp_load_pystr) code.putln('(void)__Pyx_Arg_NewRef_%s(values[%d]);' % (self.signature.fastvar, i)) code.putln('kw_args--;') code.putln('}') @@ -4485,7 +4508,7 @@ def generate_keyword_unpacking_code(self, min_positional_args, max_positional_ar values_array = 'values' code.globalstate.use_utility_code( UtilityCode.load_cached("ParseKeywords", "FunctionArguments.c")) - code.putln('if (unlikely(__Pyx_ParseOptionalKeywords(%s, %s, %s, %s, %s, %s, %s) < 0)) %s' % ( + code.putln('if (unlikely(__Pyx_ParseOptionalKeywords(HPY_LEGACY_OBJECT_AS(%s), %s, %s, %s, %s, %s, %s) < 0)) %s' % ( Naming.kwds_cname, Naming.kwvalues_cname, Naming.pykwdlist_cname, diff --git a/Cython/Compiler/TypeSlots.py b/Cython/Compiler/TypeSlots.py index bd38ef7dcb2..833cd544298 100644 --- a/Cython/Compiler/TypeSlots.py +++ b/Cython/Compiler/TypeSlots.py @@ -79,8 +79,8 @@ class Signature: type_to_format_map = {type_: format_ for format_, type_ in format_map.items()} error_value_map = { - 'O': "NULL", - 'T': "NULL", + 'O': "API_NULL_VALUE", + 'T': "API_NULL_VALUE", 'i': "-1", 'b': "-1", 'l': "-1", diff --git a/Cython/Utility/FunctionArguments.c b/Cython/Utility/FunctionArguments.c index c998922a234..353066be1a1 100644 --- a/Cython/Utility/FunctionArguments.c +++ b/Cython/Utility/FunctionArguments.c @@ -475,9 +475,9 @@ static int __Pyx_MergeKeywords(PyObject *kwdict, PyObject *source_mapping) { #define __Pyx_KwargsAsDict_VARARGS(kw, kwvalues) PyDict_Copy(kw) #if CYTHON_METH_FASTCALL #define __Pyx_Arg_FASTCALL(args, i) args[i] - #define __Pyx_NumKwargs_FASTCALL(kwds) PyTuple_GET_SIZE(kwds) + #define __Pyx_NumKwargs_FASTCALL(kwds) TUPLE_GET_SIZE(kwds) #define __Pyx_KwValues_FASTCALL(args, nargs) ((args) + (nargs)) - static CYTHON_INLINE PyObject * __Pyx_GetKwValue_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues, PyObject *s); + static CYTHON_INLINE PYOBJECT_TYPE __Pyx_GetKwValue_FASTCALL(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE kwnames, PYOBJECT_TYPE const *kwvalues, PYOBJECT_TYPE s); #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000 CYTHON_UNUSED static PyObject *__Pyx_KwargsAsDict_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues);/*proto*/ #else @@ -514,26 +514,26 @@ static int __Pyx_MergeKeywords(PyObject *kwdict, PyObject *source_mapping) { // kwnames: tuple with names of keyword arguments // kwvalues: C array with values of keyword arguments // s: str with the keyword name to look for -static CYTHON_INLINE PyObject * __Pyx_GetKwValue_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues, PyObject *s) +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_GetKwValue_FASTCALL(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE kwnames, PYOBJECT_TYPE const *kwvalues, PYOBJECT_TYPE s) { // Search the kwnames array for s and return the corresponding value. // We do two loops: a first one to compare pointers (which will find a // match if the name in kwnames is interned, given that s is interned // by Cython). A second loop compares the actual strings. - Py_ssize_t i, n = PyTuple_GET_SIZE(kwnames); + API_SSIZE_T i, n = TUPLE_GET_SIZE(kwnames); for (i = 0; i < n; i++) { - if (s == PyTuple_GET_ITEM(kwnames, i)) return kwvalues[i]; + if (API_IS_EQUAL(s, TUPLE_GET_ITEM(kwnames, i))) return kwvalues[i]; } for (i = 0; i < n; i++) { - int eq = __Pyx_PyUnicode_Equals(s, PyTuple_GET_ITEM(kwnames, i), Py_EQ); + int eq = __Pyx_PyUnicode_Equals(HPY_LEGACY_OBJECT_AS(s), HPY_LEGACY_OBJECT_AS(TUPLE_GET_ITEM(kwnames, i)), Py_EQ); if (unlikely(eq != 0)) { - if (unlikely(eq < 0)) return NULL; /* error */ + if (unlikely(eq < 0)) return API_NULL_VALUE; /* error */ return kwvalues[i]; } } - return NULL; /* not found (no exception set) */ + return API_NULL_VALUE; /* not found (no exception set) */ } #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000 From cab2d9824d8d85b853cb08763d13ca19d252da49 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 2 Oct 2023 15:56:08 +0200 Subject: [PATCH 198/286] Multiple argument functions seem to be working --- Cython/Utility/CythonFunction.c | 37 +++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index c2f602d668f..bcdfdee619c 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -125,6 +125,7 @@ static int __pyx_CyFunction_init(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_GLOBAL_TYPE #if CYTHON_USING_HPY HPyDef_CALL_FUNCTION(__Pyx_CyFunction_hpycall_NOARGS) +HPyDef_CALL_FUNCTION(__Pyx_CyFunction_hpycall_KEYWORDS) #else /* CYTHON_USING_HPY */ #if CYTHON_METH_FASTCALL static PyObject * __Pyx_CyFunction_Vectorcall_NOARGS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); @@ -856,6 +857,9 @@ static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFun case HPyFunc_NOARGS: HPy_SetCallFunction(HPY_CONTEXT_CNAME, op, &__Pyx_CyFunction_hpycall_NOARGS); break; + case HPyFunc_KEYWORDS: + HPy_SetCallFunction(HPY_CONTEXT_CNAME, op, &__Pyx_CyFunction_hpycall_KEYWORDS); + break; default: HPyErr_SetString(HPY_CONTEXT_CNAME, API_EXC(TypeError), "unknown signature"); return HPy_NULL; @@ -1285,6 +1289,37 @@ static HPy __Pyx_CyFunction_hpycall_NOARGS_impl(HPyContext *HPY_CONTEXT_CNAME, H } return result; } + +static HPy __Pyx_CyFunction_hpycall_KEYWORDS_impl(HPyContext *HPY_CONTEXT_CNAME, HPy func, const HPy *args, size_t nargs, HPy kwnames) +{ + __pyx_CyFunctionObject *cyfunc = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, func); + HPyDef *def = cyfunc->func; + assert(def->kind == HPyDef_Kind_Meth); + HPy result, self; + int self_needs_close = 0; + + switch (__Pyx_CyFunction_Vectorcall_CheckArgs(HPY_CONTEXT_CNAME, cyfunc, nargs, kwnames)) { + case 1: + self = args[0]; + args += 1; + nargs -= 1; + break; + case 0: + self = HPyField_Load(HPY_CONTEXT_CNAME, func, cyfunc->func_self); + self_needs_close = 1; + break; + default: + return HPy_NULL; + } + + HPyFunc_keywords func_keywords = def->meth.impl; + result = func_keywords(HPY_CONTEXT_CNAME, self, args, nargs, kwnames); + if (self_needs_close) { + HPy_Close(HPY_CONTEXT_CNAME, self); + } + return result; +} + #else /* CYTHON_USING_HPY */ static PyObject * __Pyx_CyFunction_Vectorcall_NOARGS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { @@ -1317,9 +1352,7 @@ static PyObject * __Pyx_CyFunction_Vectorcall_NOARGS(PyObject *func, PyObject *c } return def->ml_meth(self, NULL); } -#endif /* CYTHON_USING_HPY */ -#if !CYTHON_USING_HPY static PyObject * __Pyx_CyFunction_Vectorcall_O(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func; From a1664a9d1359ec3260d06b665fe6d9d70adebdcd Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 9 Oct 2023 13:27:49 +0200 Subject: [PATCH 199/286] Fixed (sort of) multiple initialisation in CodeObjectNode --- Cython/Compiler/ExprNodes.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index b9635828156..fdde58f3d26 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -10346,16 +10346,16 @@ def generate_result_code(self, code): elif self.def_node.is_generator: flags.append('CO_GENERATOR') - load_empty_bytes_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True) - load_empty_tuple_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True) - load_varnames_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True) - load_filepath_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True) - load_funcname_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True) + load_empty_bytes_temp = code.funcstate.allocate_temp(py_object_type, True) + load_empty_tuple_temp = code.funcstate.allocate_temp(py_object_type, True) + load_varnames_temp = code.funcstate.allocate_temp(py_object_type, True) + load_filepath_temp = code.funcstate.allocate_temp(py_object_type, True) + load_funcname_temp = code.funcstate.allocate_temp(py_object_type, True) code.putln("#if CYTHON_USING_HPY") #Type decls are temporary, still need to figure out how to get them to be declared automatically - + code.putln("{") code.putln("PYOBJECT_TYPE %s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_empty_bytes_temp, Naming.empty_bytes)) code.putln("PYOBJECT_TYPE %s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_empty_tuple_temp, Naming.empty_tuple)) code.putln("PYOBJECT_TYPE %s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_varnames_temp, self.varnames.result())) @@ -10391,6 +10391,8 @@ def generate_result_code(self, code): code.putln("PYOBJECT_CLOSEREF(%s);" % load_varnames_temp) code.putln("PYOBJECT_CLOSEREF(%s);" % load_filepath_temp) code.putln("PYOBJECT_CLOSEREF(%s);" % load_funcname_temp) + + code.putln("}") code.putln("#else") From 6ac9a41850317fc4b3e137ac2a0af00bf8e527d2 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 18 Oct 2023 11:56:54 +0200 Subject: [PATCH 200/286] Added test for HPy args --- tests/run/hpy_args.pyx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/run/hpy_args.pyx diff --git a/tests/run/hpy_args.pyx b/tests/run/hpy_args.pyx new file mode 100644 index 00000000000..796b18da284 --- /dev/null +++ b/tests/run/hpy_args.pyx @@ -0,0 +1,18 @@ +#mode: run + +def test_arg(arg): + """ + >>> test_arg(1) + 1 + >>> test_arg(1.25) + 1.25 + >>> test_arg(True) + True + >>> test_arg("HPy") + 'HPy' + >>> test_arg((1,2)) + (1, 2) + >>> test_arg(None) + + """ + return arg From 8e7b73c23c2dce354a2526a16093b9811e5b232d Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 18 Oct 2023 13:47:13 +0200 Subject: [PATCH 201/286] Fixed issues in PR --- Cython/Compiler/ExprNodes.py | 27 +++++++++++++++++++-------- Cython/Compiler/Nodes.py | 11 +++++------ 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index fdde58f3d26..a13ad92b2f8 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -4557,7 +4557,7 @@ def generate_result_code(self, code): utility_code = None error_value = None if self.type.is_pyobject: - error_value = 'API_NULL_VALUE' + error_value = 'NULL' if self.index.type.is_int: if self.base.type is list_type: function = "__Pyx_GetItemInt_List" @@ -6519,17 +6519,28 @@ def generate_result_code(self, code): code.globalstate.use_utility_code(UtilityCode.load_cached( "PyObjectCall", "ObjectHandling.c")) func_result = self.function.py_result() - if func_result.startswith("__pyx_") and not func_result.startswith("__pyx_t_") and not func_result.startswith("__pyx_v_"): - func_result = "PYOBJECT_GLOBAL_LOAD(" + func_result + ")" - load_arg_code = arg_code - if load_arg_code.startswith("__pyx_") and not load_arg_code.startswith("__pyx_t_") and not load_arg_code.startswith("__pyx_v_"): - load_arg_code = "PYOBJECT_GLOBAL_LOAD(" + load_arg_code + ")" + tmp_func_result = code.funcstate.allocate_temp(py_object_type, False) + if func_result in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_func_result, func_result)) + else: + code.putln("%s = %s;" % (tmp_func_result, func_result)) + tmp_arg_code = code.funcstate.allocate_temp(py_object_type, False) + if arg_code in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_arg_code, arg_code)) + else: + code.putln("%s = %s;" % (tmp_arg_code, arg_code)) code.putln( "%s = HPY_LEGACY_OBJECT_FROM(__Pyx_PyObject_Call(HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), NULL)); %s" % ( self.result(), - func_result, - load_arg_code, + tmp_func_result, + tmp_arg_code, code.error_goto_if_null_object(self.result(), self.pos))) + if func_result in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_func_result) + if arg_code in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_arg_code) + code.funcstate.release_temp(tmp_func_result) + code.funcstate.release_temp(tmp_arg_code) self.generate_gotref(code) elif func_type.is_cfunction: nogil = not code.funcstate.gil_owned diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 57853a7c392..3381deb206f 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -3942,7 +3942,7 @@ def generate_argument_parsing_code(self, env, code, decl_code): code.put_var_xdecref(arg.entry) code.put_add_traceback(self.target.entry.qualified_name) code.put_finish_refcount_context() - code.putln("return %s;//returns error value" % self.error_value()) + code.putln("return %s;" % self.error_value()) if code.label_used(end_label): code.put_label(end_label) @@ -4089,11 +4089,12 @@ def generate_tuple_and_keyword_parsing_code(self, args, code, decl_code): else: non_pos_args_id = non_pos_args_id + "&%s, " % arg_str non_pos_args_id = non_pos_args_id + '0' - code.putln("PyObject **%s[] = {%s};////////" % ( + code.putln("PyObject **%s[] = {%s};" % ( Naming.pykwdlist_cname, non_pos_args_id)) for arg in loaded_args_arr: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % arg) code.funcstate.release_temp(arg) # Before being converted and assigned to the target variables, @@ -4416,15 +4417,13 @@ def generate_keyword_unpacking_code(self, min_positional_args, max_positional_ar code.putln('}') else: temp_load_pystr = code.funcstate.allocate_temp(py_object_type, manage_ref=False) - if pystring_cname.startswith("__pyx_t") or pystring_cname.startswith("__pyx_v"): - pystr_is_global = False + if pystring_cname in code.globalstate.const_cname_array: code.putln("%s = %s;" % (temp_load_pystr, pystring_cname)) else: - pystr_is_global = True code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_pystr, pystring_cname)) code.putln('if (likely(API_IS_NOT_NULL(values[%d] = __Pyx_GetKwValue_%s(HPY_CONTEXT_FIRST_ARG_CALL %s, %s, %s)))) {' % ( i, self.signature.fastvar, Naming.kwds_cname, Naming.kwvalues_cname, temp_load_pystr)) - if pystr_is_global: + if pystring_cname in code.globalstate.const_cname_array: code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_pystr) #This should also be closed in the other else statements, but as they are errors it doesn't matter too much code.funcstate.release_temp(temp_load_pystr) code.putln('(void)__Pyx_Arg_NewRef_%s(values[%d]);' % (self.signature.fastvar, i)) From e7c5ec13e1d75a71b16d5724db811cc881da8e60 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 21 Sep 2023 09:17:31 +0200 Subject: [PATCH 202/286] Dictionaries have been ported --- Cython/Compiler/ExprNodes.py | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index a13ad92b2f8..2b452f18b43 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -1214,6 +1214,7 @@ class PyConstNode(AtomicExprNode): is_literal = 1 type = py_object_type nogil_check = None + is_global = True def is_simple(self): return 1 @@ -1270,6 +1271,8 @@ class ConstNode(AtomicExprNode): is_literal = 1 nogil_check = None + is_global = True + def is_simple(self): return 1 @@ -1605,6 +1608,7 @@ class BytesNode(ConstNode): is_string_literal = True # start off as Python 'bytes' to support len() in O(1) type = bytes_type + is_global = True def calculate_constant_result(self): self.constant_result = self.value @@ -1695,6 +1699,7 @@ class UnicodeNode(ConstNode): is_string_literal = True bytes_value = None type = unicode_type + is_global = True def calculate_constant_result(self): self.constant_result = self.value @@ -9567,10 +9572,32 @@ def generate_evaluation_code(self, code): code.error_goto(item.pos))) code.putln("} else {") - code.put_error_if_neg(self.pos, "PyDict_SetItem(%s, %s, %s)" % ( + temp_load_key = code.funcstate.allocate_temp(py_object_type, manage_ref=False) + code.putln("//%s" % item.key) + code.putln("//%s" % item.key.type) + if hasattr(item.key, "is_global") and item.key.is_global: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_key, item.key.py_result())) + else: + code.putln("%s = %s;" % (temp_load_key, item.key.py_result())) + + temp_load_value = code.funcstate.allocate_temp(py_object_type, manage_ref=False) + if hasattr(item.value, "is_global") and item.value.is_global: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_value, item.value.py_result())) + else: + code.putln("%s = %s;" % (temp_load_value, item.value.py_result())) + code.put_error_if_neg(self.pos, "DICT_SET_ITEM(%s, %s, %s)" % ( self.result(), - item.key.py_result(), - item.value.py_result())) + temp_load_key, + temp_load_value)) + + if hasattr(item.key, "is_global") and item.key.is_global: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_key) + code.funcstate.release_temp(temp_load_key) + + if hasattr(item.value, "is_global") and item.value.is_global: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_value) + code.funcstate.release_temp(temp_load_value) + if self.reject_duplicates and keys_seen is None: code.putln('}') if self.exclude_null_values: From 19a611d401ac6f58c1d15f53e49ff406b56d1c1a Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 21 Sep 2023 10:53:06 +0200 Subject: [PATCH 203/286] Fixed calling attributes (or most attributes at least) --- Cython/Compiler/ExprNodes.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 2b452f18b43..c9da331152c 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -7965,13 +7965,22 @@ def generate_result_code(self, code): code.globalstate.use_utility_code( UtilityCode.load_cached("PyObjectGetAttrStr", "ObjectHandling.c")) lookup_func_name = '__Pyx_PyObject_GetAttrStr' + temp_load_attr = code.funcstate.allocate_temp(py_object_type, manage_ref=False) + attr_str = code.intern_identifier(self.attribute) + if attr_str.startswith("__pyx_int_") or attr_str.startswith("__pyx_k_") or attr_str.startswith("__pyx_n_s_"): + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_attr, attr_str)) + else: + code.putln("%s = %s;" % (temp_load_attr, attr_str)) code.putln( '%s = %s(%s, %s); %s' % ( self.result(), lookup_func_name, self.obj.py_result(), - code.intern_identifier(self.attribute), - code.error_goto_if_null(self.result(), self.pos))) + temp_load_attr, + code.error_goto_if_null_object(self.result(), self.pos))) + if not attr_str.startswith("__pyx_t_"): + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s)" % temp_load_attr) + code.funcstate.release_temp(temp_load_attr) self.generate_gotref(code) elif self.type.is_memoryviewslice: if self.is_memslice_transpose: From be20bd0dd2fa32144a82fd332e4af2d4a832bd61 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 21 Sep 2023 15:06:51 +0200 Subject: [PATCH 204/286] Ported more dict functionality --- Cython/Compiler/ExprNodes.py | 37 +++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index c9da331152c..d546c8097ae 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -4626,7 +4626,11 @@ def generate_result_code(self, code): if self.type.is_pyobject: self.generate_gotref(code) - def generate_setitem_code(self, value_code, code): + def generate_setitem_code(self, value, code): + if self.type.is_pyobject: + value_code = value.py_result() + elif self.base.type is bytearray_type: + value_code = self._check_byte_value(code, value) if self.index.type.is_int: if self.base.type is bytearray_type: code.globalstate.use_utility_code( @@ -4640,7 +4644,7 @@ def generate_setitem_code(self, value_code, code): else: index_code = self.index.py_result() if self.base.type is dict_type: - function = "PyDict_SetItem" + function = "DICT_SET_ITEM" # It would seem that we could specialized lists/tuples, but that # shouldn't happen here. # Both PyList_SetItem() and PyTuple_SetItem() take a Py_ssize_t as @@ -4649,25 +4653,44 @@ def generate_setitem_code(self, value_code, code): # and raise a TypeError when trying to set their entries # (PyTuple_SetItem() is for creating new tuples from scratch). else: - function = "PyObject_SetItem" + function = "PYOBJECT_SET_ITEM" + + temp_load_index = code.funcstate.allocate_temp(py_object_type, manage_ref=False) + if hasattr(self.index, "is_global") and self.index.is_global: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_index, index_code)) + else: + code.putln("%s = %s;" % (temp_load_index, index_code)) + temp_load_value = code.funcstate.allocate_temp(py_object_type, manage_ref=False) + if hasattr(value, "is_global") and value.is_global: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_value, value_code)) + else: + code.putln("%s = %s;" % (temp_load_value, value_code)) + code.putln(code.error_goto_if_neg( "%s(%s, %s, %s%s)" % ( function, self.base.py_result(), - index_code, - value_code, + temp_load_index, + temp_load_value, self.extra_index_params(code)), self.pos)) + if hasattr(self.index, "is_global") and self.index.is_global: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_index) + code.funcstate.release_temp(temp_load_index) + if hasattr(value, "is_global") and value.is_global: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_value) + code.funcstate.release_temp(temp_load_value) + def generate_assignment_code(self, rhs, code, overloaded_assignment=False, exception_check=None, exception_value=None): self.generate_subexpr_evaluation_code(code) if self.type.is_pyobject: - self.generate_setitem_code(rhs.py_result(), code) + self.generate_setitem_code(rhs, code) elif self.base.type is bytearray_type: value_code = self._check_byte_value(code, rhs) - self.generate_setitem_code(value_code, code) + self.generate_setitem_code(rhs, code) elif self.base.type.is_cpp_class and self.exception_check and self.exception_check == '+': if overloaded_assignment and exception_check and self.exception_value != exception_value: # Handle the case that both the index operator and the assignment From 71a3403adde57a16b5509e27f29b854674ee4bc8 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 12 Oct 2023 16:55:56 +0200 Subject: [PATCH 205/286] Work towards making dict_getitem test pass --- Cython/Compiler/Code.py | 2 +- Cython/Compiler/ExprNodes.py | 4 ++-- Cython/Compiler/ModuleNode.py | 10 +++++----- Cython/Compiler/Nodes.py | 16 ++++++++-------- Cython/Compiler/PyrexTypes.py | 10 +++++----- Cython/Compiler/Symtab.py | 2 +- Cython/Utility/FunctionArguments.c | 12 ++++++------ Cython/Utility/HPyUtils.c | 2 ++ Cython/Utility/ModuleSetupCode.c | 5 +++++ Cython/Utility/ObjectHandling.c | 6 +++--- Cython/Utility/TypeConversion.c | 2 +- 11 files changed, 39 insertions(+), 32 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 6c804e2cd0f..c4139b20a71 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -2322,7 +2322,7 @@ def put_var_xdecrefs_clear(self, entries): def put_init_to_py_none(self, cname, type, nanny=True): from .PyrexTypes import py_object_type, typecast - py_none = typecast(type, py_object_type, "Py_None") + py_none = typecast(type, py_object_type, "API_NONE_VALUE") if nanny: self.putln("%s = __Pyx_hNewRef(API_NONE_VALUE);" % (cname)) else: diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index d546c8097ae..294731d7d3f 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -10354,7 +10354,7 @@ class InnerFunctionNode(PyCFunctionNode): def closure_result_code(self): if self.needs_closure_code: return "((PyObject*)%s)" % Naming.cur_scope_cname - return "NULL" + return "API_NULL_VALUE" class CodeObjectNode(ExprNode): @@ -14435,7 +14435,7 @@ def generate_if_needed(cls, arg, code, exception_message, def put_nonecheck(self, code): code.putln( - "if (unlikely(%s == Py_None)) {" % self.condition()) + "if (unlikely(API_IS_EQUAL(%s, API_NONE_VALUE))) {" % self.condition()) if self.in_nogil_context: code.put_ensure_gil() diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index a251816e23e..d6ee5816f82 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -439,7 +439,7 @@ def api_entries(entries, pxd=0): def generate_cclass_header_code(self, type, h_code): h_code.putln("%s %s %s;" % ( Naming.extern_c_macro, - PyrexTypes.public_decl("PyTypeObject", "DL_IMPORT"), + PyrexTypes.public_decl("PYTYPEOBJECT_TYPE", "DL_IMPORT"), type.typeobj_cname)) def generate_cclass_include_code(self, type, i_code): @@ -1215,12 +1215,12 @@ def generate_typeobj_predeclaration(self, entry, code): if entry.visibility == 'extern' and not entry.in_cinclude: code.putln("%s %s %s;" % ( Naming.extern_c_macro, - PyrexTypes.public_decl("PyTypeObject", "DL_IMPORT"), + PyrexTypes.public_decl("PYTYPEOBJECT_TYPE", "DL_IMPORT"), name)) elif entry.visibility == 'public': code.putln("%s %s %s;" % ( Naming.extern_c_macro, - PyrexTypes.public_decl("PyTypeObject", "DL_EXPORT"), + PyrexTypes.public_decl("PYTYPEOBJECT_TYPE", "DL_EXPORT"), name)) # ??? Do we really need the rest of this? ??? #else: @@ -1666,7 +1666,7 @@ def generate_new_function(self, scope, code, cclass_entry): if needs_error_cleanup: code.putln("bad:") code.put_decref_clear("o", py_object_type, nanny=False) - code.putln("return NULL;") + code.putln("return API_NULL_VALUE;") code.putln( "}") @@ -3233,7 +3233,7 @@ def generate_module_init_func(self, imported_modules, env, code): if profile or linetrace: code.funcstate.can_trace = False - code.put_trace_return("Py_None", nogil=not code.funcstate.gil_owned) + code.put_trace_return("API_NONE_VALUE", nogil=not code.funcstate.gil_owned) code.putln() code.putln("/*--- Wrapped vars code ---*/") diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 3381deb206f..6bf4dd8fa43 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -2095,7 +2095,7 @@ def generate_function_definitions(self, env, code): # Scope unconditionally DECREFed on return. code.putln("%s = __Pyx_NewRef(%s);" % ( Naming.cur_scope_cname, - lenv.scope_class.type.cast_code("Py_None"))) + lenv.scope_class.type.cast_code("API_NONE_VALUE"))) code.putln(code.error_goto(self.pos)) code.putln("} else {") code.put_gotref(Naming.cur_scope_cname, lenv.scope_class.type) @@ -3845,7 +3845,7 @@ def generate_argument_declarations(self, env, code): for arg in self.args: if arg.is_generic: if arg.needs_conversion: - code.putln("PyObject *%s = 0;" % arg.hdr_cname) + code.putln("PYOBJECT_TYPE %s = API_DEFAULT_VALUE;" % arg.hdr_cname) else: code.put_var_declaration(arg.entry) for entry in env.var_entries: @@ -4791,7 +4791,7 @@ def generate_function_definitions(self, env, code): self.generate_function_header(code) closure_init_code = code.insertion_point() # ----- Local variables - code.putln("PyObject *%s = NULL;" % Naming.retval_cname) + code.putln("PYOBJECT_TYPE %s = API_NULL_VALUE;" % Naming.retval_cname) tempvardecl_code = code.insertion_point() code.put_declare_refcount_context() code.put_setup_refcount_context(self.entry.name or self.entry.qualified_name) @@ -4905,10 +4905,10 @@ def generate_function_definitions(self, env, code): resume_code.putln("case %d: goto %s;" % (i, label)) resume_code.putln("default: /* CPython raises the right error here */") if profile or linetrace: - resume_code.put_trace_return("Py_None", + resume_code.put_trace_return("API_NONE_VALUE", nogil=not code.funcstate.gil_owned) resume_code.put_finish_refcount_context() - resume_code.putln("return NULL;") + resume_code.putln("return API_NULL_VALUE;") resume_code.putln("}") code.exit_cfunc_scope() @@ -4956,9 +4956,9 @@ def generate_execution_code(self, code): # Check to see if we are an extension type if self.py_func.is_module_scope: - self_arg = "((PyObject *)%s)" % Naming.module_cname + self_arg = "((PYOBJECT_TYPE)%s)" % Naming.module_cname else: - self_arg = "((PyObject *)%s)" % self.args[0].cname + self_arg = "((PYOBJECT_TYPE)%s)" % self.args[0].cname code.putln("/* Check if called by wrapper */") code.putln("if (unlikely(%s)) ;" % Naming.skip_dispatch_cname) code.putln("/* Check if overridden in Python */") @@ -6914,7 +6914,7 @@ def generate_execution_code(self, code): code.globalstate.use_utility_code( UtilityCode.load_cached("StopAsyncIteration", "Coroutine.c")) code.put("PyErr_SetNone(__Pyx_PyExc_StopAsyncIteration); ") - code.putln("%s = NULL;" % Naming.retval_cname) + code.putln("%s = API_NULL_VALUE;" % Naming.retval_cname) else: code.put_init_to_py_none(Naming.retval_cname, self.return_type) elif self.return_type.is_returncode: diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index b7affa2b426..1793dc9c0bf 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -1452,7 +1452,7 @@ class BuiltinObjectType(PyObjectType): vtabptr_cname = None typedef_flag = True is_external = True - decl_type = 'PyObject' + decl_type = 'PYOBJECT_TYPE' def __init__(self, name, cname, objstruct_cname=None): self.name = name @@ -1556,17 +1556,17 @@ def declaration_code(self, entity_code, base_code = self.name else: base_code = public_decl(self.decl_type, dll_linkage) - entity_code = "*%s" % entity_code + entity_code = "%s" % entity_code return self.base_declaration_code(base_code, entity_code) def as_pyobject(self, cname): - if self.decl_type == 'PyObject': + if self.decl_type == 'PYOBJECT_TYPE': return cname else: - return "(PyObject *)" + cname + return "(PYOBJECT_TYPE)" + cname def cast_code(self, expr_code, to_object_struct = False): - return "((%s*)%s)" % ( + return "((%s)%s)" % ( to_object_struct and self.objstruct_cname or self.decl_type, # self.objstruct_cname may be None expr_code) diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index f8706f6268b..9e77a36bb71 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -1916,7 +1916,7 @@ def declare_arg(self, name, type, pos): entry = self.declare(name, cname, type, pos, 'private') entry.is_variable = 1 if type.is_pyobject: - entry.init = "0" + entry.init = "API_NULL_VALUE" entry.is_arg = 1 #entry.borrowed = 1 # Not using borrowed arg refs for now self.arg_entries.append(entry) diff --git a/Cython/Utility/FunctionArguments.c b/Cython/Utility/FunctionArguments.c index 353066be1a1..b74e772f42d 100644 --- a/Cython/Utility/FunctionArguments.c +++ b/Cython/Utility/FunctionArguments.c @@ -4,26 +4,26 @@ // Exact is 0 (False), 1 (True) or 2 (True and from annotation) // The latter gives a small amount of extra error diagnostics #define __Pyx_ArgTypeTest(obj, type, none_allowed, name, exact) \ - ((likely(__Pyx_IS_TYPE(obj, type) | (none_allowed && (obj == Py_None)))) ? 1 : \ + ((likely(__Pyx_IS_TYPE(obj, type) | (none_allowed && (API_IS_EQUAL(obj, API_NONE_VALUE))))) ? 1 : \ __Pyx__ArgTypeTest(obj, type, name, exact)) -static int __Pyx__ArgTypeTest(PyObject *obj, PyTypeObject *type, const char *name, int exact); /*proto*/ +static int __Pyx__ArgTypeTest(PYOBJECT_TYPE obj, PYTYPEOBJECT_TYPE type, const char *name, int exact); /*proto*/ //////////////////// ArgTypeTest //////////////////// //@substitute: naming -static int __Pyx__ArgTypeTest(PyObject *obj, PyTypeObject *type, const char *name, int exact) +static int __Pyx__ArgTypeTest(PYOBJECT_TYPE obj, PYTYPEOBJECT_TYPE type, const char *name, int exact) { __Pyx_TypeName type_name; __Pyx_TypeName obj_type_name; - PyObject *extra_info = $empty_unicode; + PYOBJECT_TYPE extra_info = PYOBJECT_GLOBAL_LOAD($empty_unicode); int from_annotation_subclass = 0; if (unlikely(!type)) { PyErr_SetString(PyExc_SystemError, "Missing type object"); return 0; } else if (!exact) { - if (likely(__Pyx_TypeCheck(obj, type))) return 1; + if (likely(__Pyx_TypeCheck(HPY_LEGACY_OBJECT_AS(obj), HPY_LEGACY_OBJECT_AS(type)))) return 1; } else if (exact == 2) { // type from annotation if (__Pyx_TypeCheck(obj, type)) { @@ -32,7 +32,7 @@ static int __Pyx__ArgTypeTest(PyObject *obj, PyTypeObject *type, const char *nam } } type_name = __Pyx_PyType_GetName(type); - obj_type_name = __Pyx_PyType_GetName(Py_TYPE(obj)); + obj_type_name = __Pyx_PyType_GetName(GET_TYPE(obj)); PyErr_Format(PyExc_TypeError, "Argument '%.200s' has incorrect type (expected " __Pyx_FMT_TYPENAME ", got " __Pyx_FMT_TYPENAME ")" diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index e77ceaddc50..0f15eb17f98 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -133,6 +133,7 @@ #define TYPESPEC_GET(s, field) s.field #define TYPE_CHECK(o) HPy_TypeCheck(HPY_CONTEXT_CNAME, (o), HPY_CONTEXT_CNAME->h_TypeType) #define TYPE_AS_PYOBJECT(t) t + #define GET_TYPE(o) HPy_Type(HPY_CONTEXT_CNAME, o) //Error & Exception Macros @@ -280,6 +281,7 @@ #define TYPESPEC_GET(s, field) s->field #define TYPE_CHECK(o) PyType_Check(o) #define TYPE_AS_PYOBJECT(t) (PyObject*)&##t + #define GET_TYPE(o) Py_TYPE(o) //Error & Exception Macros #define PYERR_OCCURRED() (!!PyErr_Occurred()) diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index f8146efe243..df2dfb40944 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -729,8 +729,13 @@ class __Pyx_FakeReference { #define CYTHON_FORMAT_SSIZE_T "z" // TODO: remove this block +#if CYTHON_USING_HPY + #define __Pyx_BUILTIN_MODULE_NAME "HPY_CONTEXT_CNAME->h_Builtins" + #define __Pyx_DefaultClassType HPyType_Type +#else #define __Pyx_BUILTIN_MODULE_NAME "builtins" #define __Pyx_DefaultClassType PyType_Type +#endif #if CYTHON_COMPILING_IN_LIMITED_API // Note that the limited API doesn't know about PyCodeObject, so the type of this diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 31a4b2d6faa..63d79f43a8c 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -2839,7 +2839,7 @@ static CYTHON_INLINE PyObject* __Pyx_PySequence_Multiply(PyObject *seq, Py_ssize #if CYTHON_COMPILING_IN_LIMITED_API typedef PYOBJECT_TYPE __Pyx_TypeName; #define __Pyx_FMT_TYPENAME "%U" -static __Pyx_TypeName __Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_DEF PyTypeObject* tp); /*proto*/ +static __Pyx_TypeName __Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_DEF PYTYPEOBJECT_TYPE tp); /*proto*/ #define __Pyx_DECREF_TypeName(obj) PYOBJECT_XCLOSEREF(obj) #else typedef const char *__Pyx_TypeName; @@ -2854,10 +2854,10 @@ typedef const char *__Pyx_TypeName; #if CYTHON_COMPILING_IN_LIMITED_API static __Pyx_TypeName -__Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_DEF PyTypeObject* tp) +__Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_DEF PYTYPEOBJECT_TYPE tp) { PYOBJECT_TYPE load_name_temp = PYOBJECT_GLOBAL_LOAD(PYIDENT("__name__")); - PYOBJECT_TYPE name = __Pyx_PyObject_GetAttrStr(HPY_LEGACY_OBJECT_FROM((PyObject *)tp), load_name_temp); + PYOBJECT_TYPE name = __Pyx_PyObject_GetAttrStr(tp, load_name_temp); PYOBJECT_GLOBAL_CLOSEREF(load_name_temp); if (unlikely(API_IS_NULL(name)) || unlikely(!PyUnicode_Check(HPY_LEGACY_OBJECT_AS(name)))) { PyErr_Clear(); diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index 018c7d903a1..477b5cbd7d3 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -319,7 +319,7 @@ static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject* x) { } static PyObject* __Pyx_PyNumber_IntOrLongWrongResultType(HPY_CONTEXT_FIRST_ARG_DEF PyObject* result, const char* type_name) { - __Pyx_TypeName result_type_name = __Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_CALL Py_TYPE(result)); + __Pyx_TypeName result_type_name = __Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_CALL GET_TYPE(HPY_LEGACY_OBJECT_FROM(result))); if (PyLong_Check(result)) { // CPython issue #17576: warn if 'result' not of exact type int. if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, From a3cf06dc04bb7e229105024a48ca90e1c5a1b4ca Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 16 Oct 2023 16:30:36 +0200 Subject: [PATCH 206/286] Fixed compile errors on cpython ABI and segfault on C API for dict_getitem --- Cython/Compiler/Code.py | 7 ++-- Cython/Compiler/ExprNodes.py | 43 +++++++++++++++------- Cython/Compiler/ModuleNode.py | 9 +++-- Cython/Compiler/Nodes.py | 36 ++++++++++++++----- Cython/Compiler/Optimize.py | 2 +- Cython/Compiler/PyrexTypes.py | 6 ++-- Cython/Utility/FunctionArguments.c | 12 +++---- Cython/Utility/HPyUtils.c | 22 +++++++++--- Cython/Utility/ObjectHandling.c | 57 ++++++++++++++++++++---------- 9 files changed, 132 insertions(+), 62 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index c4139b20a71..b886f6a486a 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1412,6 +1412,7 @@ def get_py_string_const(self, text, identifier=None, c_string = self.get_string_const(text) py_string = c_string.get_py_string_const( text.encoding, identifier, is_str, py3str_cstring) + self.const_cname_array.append(py_string.cname) return py_string def get_interned_identifier(self, text): @@ -1421,21 +1422,21 @@ def new_string_const(self, text, byte_string): cname = self.new_string_const_cname(byte_string) c = StringConst(cname, text, byte_string) self.string_const_index[byte_string] = c - self.const_cname_array.append(cname) + self.const_cname_array.append(c.cname) return c def new_num_const(self, value, py_type, value_code=None): cname = self.new_num_const_cname(value, py_type) c = NumConst(cname, value, py_type, value_code) self.num_const_index[(value, py_type)] = c - self.const_cname_array.append(cname) + self.const_cname_array.append(c.cname) return c def new_py_const(self, type, prefix=''): cname = self.new_const_cname(prefix) c = PyObjectConst(cname, type) self.py_constants.append(c) - self.const_cname_array.append(cname) + self.const_cname_array.append(c.cname) return c def new_string_const_cname(self, bytes_value): diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 294731d7d3f..c613fe194db 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -4614,7 +4614,17 @@ def generate_result_code(self, code): self.result() if self.type.is_pyobject else None, self.exception_value, self.in_nogil_context) else: - error_check = 'API_IS_NULL(%s)' if error_value == 'NULL' else '%%s == %s' % error_value + error_check = '!%s' if error_value == 'NULL' else 'API_IS_EQUAL(%%s, %s)' % error_value + code.putln("#if CYTHON_USING_HPY") + code.putln( + "%s = %s(HPY_CONTEXT_CNAME, %s, %s%s); %s" % ( + self.result(), + function, + self.base.py_result(), + index_code, + self.extra_index_params(code), + code.error_goto_if(error_check % self.result(), self.pos))) + code.putln("#else") code.putln( "%s = %s(%s, %s%s); %s" % ( self.result(), @@ -4623,6 +4633,7 @@ def generate_result_code(self, code): index_code, self.extra_index_params(code), code.error_goto_if(error_check % self.result(), self.pos))) + code.putln("#endif") if self.type.is_pyobject: self.generate_gotref(code) @@ -8384,13 +8395,12 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): arg.generate_giveref(code) code.putln("#endif") if self.type is tuple_type: - code.putln("if (%s(%s, %s, %s, %s)) %s;" % ( + code.putln("%s(%s, %s, %s, %s);" % ( set_item_func, target, tmp_builder, (offset and i) and ('%s + %s' % (offset, i)) or (offset or i), - arg.py_result(), - code.error_goto(self.pos))) + arg.py_result())) else: code.putln("if (%s(%s, %s, %s)) %s;" % ( set_item_func, @@ -13700,7 +13710,7 @@ def find_special_bool_compare_function(self, env, operand1, result_is_bool=False def generate_operation_code(self, code, result_code, operand1, op, operand2): if self.type.is_pyobject: - error_clause = code.error_goto_if_null + error_clause = code.error_goto_if_null_object got_ref = "__Pyx_XGOTREF(%s); " % result_code if self.special_bool_cmp_function: code.globalstate.use_utility_code( @@ -13740,9 +13750,9 @@ def generate_operation_code(self, code, result_code, elif operand1.type.is_pyobject and op not in ('is', 'is_not'): assert op not in ('in', 'not_in'), op assert self.type.is_pyobject or self.type is PyrexTypes.c_bint_type - code.putln("%s = PyObject_RichCompare%s(%s, %s, %s); %s%s" % ( + code.putln("%s = API_RICH_COMPARE%s(%s, %s, %s); %s%s" % ( result_code, - "" if self.type.is_pyobject else "Bool", + "" if self.type.is_pyobject else "_BOOL", operand1.py_result(), operand2.py_result(), richcmp_constants[op], @@ -13770,12 +13780,19 @@ def generate_operation_code(self, code, result_code, common_type = type1 code1 = operand1.result_as(common_type) code2 = operand2.result_as(common_type) - statement = "%s = %s(%s %s %s);" % ( - result_code, - coerce_result, - code1, - self.c_operator(op), - code2) + op_value = self.c_operator(op) + if op_value == "==" and operand1.type == py_object_type and operand2.type == py_object_type: + statement = "%s = API_IS_EQUAL(%s, %s);" % ( + result_code, + code1, + code2) + else: + statement = "%s = %s(%s %s %s);" % ( + result_code, + coerce_result, + code1, + self.c_operator(op), + code2) if self.is_cpp_comparison() and self.exception_check == '+': translate_cpp_exception( code, diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index d6ee5816f82..f5880b09e86 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -439,7 +439,7 @@ def api_entries(entries, pxd=0): def generate_cclass_header_code(self, type, h_code): h_code.putln("%s %s %s;" % ( Naming.extern_c_macro, - PyrexTypes.public_decl("PYTYPEOBJECT_TYPE", "DL_IMPORT"), + PyrexTypes.public_decl("PyTypeObject", "DL_IMPORT"), type.typeobj_cname)) def generate_cclass_include_code(self, type, i_code): @@ -1215,12 +1215,12 @@ def generate_typeobj_predeclaration(self, entry, code): if entry.visibility == 'extern' and not entry.in_cinclude: code.putln("%s %s %s;" % ( Naming.extern_c_macro, - PyrexTypes.public_decl("PYTYPEOBJECT_TYPE", "DL_IMPORT"), + PyrexTypes.public_decl("PyTypeObject", "DL_IMPORT"), name)) elif entry.visibility == 'public': code.putln("%s %s %s;" % ( Naming.extern_c_macro, - PyrexTypes.public_decl("PYTYPEOBJECT_TYPE", "DL_EXPORT"), + PyrexTypes.public_decl("PyTypeObject", "DL_EXPORT"), name)) # ??? Do we really need the rest of this? ??? #else: @@ -3422,6 +3422,9 @@ def generate_module_import_setup(self, env, code): code.putln("PYOBJECT_TYPE modules = HPY_LEGACY_OBJECT_FROM(PyImport_GetModuleDict()); %s" % ##TODO: Figure out how to get module dict code.error_goto_if_null_object("modules", self.pos)) code.putln('if (API_IS_NULL(DICT_GET_ITEM_STR(modules, %s))) {' % fq_module_name_cstring) + code.putln('#if CYTHON_USING_HPY') + code.putln('HPyErr_Clear(HPY_CONTEXT_CNAME);') + code.putln('#endif') code.putln(code.error_goto_if_neg('DICT_SET_ITEM_STR(modules, %s, %s)' % ( fq_module_name_cstring, Naming.pymodinit_module_arg), self.pos)) code.putln("}") diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 6bf4dd8fa43..bc3ff57a4f9 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -2455,6 +2455,16 @@ def generate_arg_type_test(self, arg, code): # 2 is used to indicate that the type is from the annotation # and provide a little extra info on failure. exact = 2 if arg.type_from_annotation else 1 + code.putln("#if CYTHON_USING_HPY") + code.putln( + 'if (unlikely(!__Pyx_ArgTypeTest(%s, HPY_LEGACY_OBJECT_FROM((PyObject*)%s), %d, %s, %s))) %s' % ( + arg.entry.cname, + typeptr_cname, + arg.accept_none, + arg.name_cstring, + exact, + code.error_goto(arg.pos))) + code.putln("#else") code.putln( 'if (unlikely(!__Pyx_ArgTypeTest(%s, %s, %d, %s, %s))) %s' % ( arg_code, @@ -2463,6 +2473,7 @@ def generate_arg_type_test(self, arg, code): arg.name_cstring, exact, code.error_goto(arg.pos))) + code.putln("#endif") else: error(arg.pos, "Cannot test type of extern C class without type object name specification") @@ -4135,7 +4146,7 @@ def generate_tuple_and_keyword_parsing_code(self, args, code, decl_code): # the kw-args dict passed is non-empty (which it will be, since kw_unpacking_condition is true) code.globalstate.use_utility_code( UtilityCode.load_cached("ParseKeywords", "FunctionArguments.c")) - code.putln('if (likely(__Pyx_ParseOptionalKeywords(%s, %s, %s, %s, %s, %s, %s) < 0)) %s' % ( + code.putln('if (likely(__Pyx_ParseOptionalKeywords(HPY_LEGACY_OBJECT_AS(%s), %s, %s, %s, %s, %s, %s) < 0)) %s' % ( Naming.kwds_cname, Naming.kwvalues_cname, Naming.pykwdlist_cname, @@ -4250,6 +4261,7 @@ def generate_arg_assignment(self, arg, item, code): if arg.default: # C-typed default arguments must be handled here code.putln('if (%s) {' % item) + item = "HPY_LEGACY_OBJECT_AS(%s)" % item code.putln(arg.type.from_py_call_code( item, arg.entry.cname, arg.pos, code)) if arg.default: @@ -4300,7 +4312,7 @@ def generate_argument_values_setup_code(self, args, code, decl_code): # before doing any type coercion etc.. Whether they are borrowed or not # depends on the compilation options. decl_code.putln("PYOBJECT_TYPE values[%d] = {%s};" % ( - max_args, ','.join('0'*max_args))) + max_args, ','.join(['API_DEFAULT_VALUE']*max_args))) if self.target.defaults_struct: code.putln('%s *%s = __Pyx_CyFunction_Defaults(%s, %s);' % ( @@ -4416,16 +4428,16 @@ def generate_keyword_unpacking_code(self, min_positional_args, max_positional_ar code.putln('else if (unlikely(PyErr_Occurred())) %s' % code.error_goto(self.pos)) code.putln('}') else: - temp_load_pystr = code.funcstate.allocate_temp(py_object_type, manage_ref=False) + tmp_load_pystr = code.funcstate.allocate_temp(py_object_type, False) if pystring_cname in code.globalstate.const_cname_array: - code.putln("%s = %s;" % (temp_load_pystr, pystring_cname)) + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_pystr, pystring_cname)) else: - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_pystr, pystring_cname)) + code.putln("%s = %s;" % (tmp_load_pystr, pystring_cname)) code.putln('if (likely(API_IS_NOT_NULL(values[%d] = __Pyx_GetKwValue_%s(HPY_CONTEXT_FIRST_ARG_CALL %s, %s, %s)))) {' % ( - i, self.signature.fastvar, Naming.kwds_cname, Naming.kwvalues_cname, temp_load_pystr)) + i, self.signature.fastvar, Naming.kwds_cname, Naming.kwvalues_cname, tmp_load_pystr)) if pystring_cname in code.globalstate.const_cname_array: - code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_pystr) #This should also be closed in the other else statements, but as they are errors it doesn't matter too much - code.funcstate.release_temp(temp_load_pystr) + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_load_pystr) + code.funcstate.release_temp(tmp_load_pystr) code.putln('(void)__Pyx_Arg_NewRef_%s(values[%d]);' % (self.signature.fastvar, i)) code.putln('kw_args--;') code.putln('}') @@ -6896,15 +6908,21 @@ def generate_execution_code(self, code): value.py_result())) value.generate_disposal_code(code) else: + value.make_owned_reference(code) if (hasattr(value, "entry") and hasattr(value.entry, "cname") and value.entry.cname in code.globalstate.const_cname_array) or (hasattr(value, "result_code") and value.result_code in code.globalstate.const_cname_array): - value.make_owned_reference(code) code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % ( Naming.retval_cname, value.result_as(self.return_type))) else: + code.putln("#if CYTHON_USING_HPY") code.putln("%s = PYOBJECT_NEWREF(%s);" % ( Naming.retval_cname, value.result_as(self.return_type))) + code.putln("#else") + code.putln("%s = %s;" % ( + Naming.retval_cname, + value.result_as(self.return_type))) + code.putln("#endif") value.generate_post_assignment_code(code) value.free_temps(code) else: diff --git a/Cython/Compiler/Optimize.py b/Cython/Compiler/Optimize.py index f5c5b8b71f6..b91d3e9497f 100644 --- a/Cython/Compiler/Optimize.py +++ b/Cython/Compiler/Optimize.py @@ -2866,7 +2866,7 @@ def _handle_simple_function_type(self, node, function, pos_args): if len(pos_args) != 1: return node node = ExprNodes.PythonCapiCallNode( - node.pos, "Py_TYPE", self.Pyx_Type_func_type, + node.pos, "GET_TYPE", self.Pyx_Type_func_type, args = pos_args, is_temp = False) return ExprNodes.CastNode(node, PyrexTypes.py_object_type) diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 1793dc9c0bf..eeef926163e 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -352,7 +352,7 @@ def _assign_from_py_code(self, source_code, result_code, error_pos, code, from_py_function=None, error_condition=None, extra_args=None, special_none_cvalue=None): args = ', ' + ', '.join('%s' % arg for arg in extra_args) if extra_args else '' - convert_call = "%s(%s%s)" % ( + convert_call = "%s(HPY_CONTEXT_FIRST_ARG_CALL %s%s)" % ( from_py_function or self.from_py_function, source_code, args, @@ -1556,7 +1556,7 @@ def declaration_code(self, entity_code, base_code = self.name else: base_code = public_decl(self.decl_type, dll_linkage) - entity_code = "%s" % entity_code + entity_code = "CAPI_IS_POINTER %s" % entity_code return self.base_declaration_code(base_code, entity_code) def as_pyobject(self, cname): @@ -1566,7 +1566,7 @@ def as_pyobject(self, cname): return "(PYOBJECT_TYPE)" + cname def cast_code(self, expr_code, to_object_struct = False): - return "((%s)%s)" % ( + return "((%s CAPI_IS_POINTER)%s)" % ( to_object_struct and self.objstruct_cname or self.decl_type, # self.objstruct_cname may be None expr_code) diff --git a/Cython/Utility/FunctionArguments.c b/Cython/Utility/FunctionArguments.c index b74e772f42d..22c0c302419 100644 --- a/Cython/Utility/FunctionArguments.c +++ b/Cython/Utility/FunctionArguments.c @@ -4,15 +4,15 @@ // Exact is 0 (False), 1 (True) or 2 (True and from annotation) // The latter gives a small amount of extra error diagnostics #define __Pyx_ArgTypeTest(obj, type, none_allowed, name, exact) \ - ((likely(__Pyx_IS_TYPE(obj, type) | (none_allowed && (API_IS_EQUAL(obj, API_NONE_VALUE))))) ? 1 : \ - __Pyx__ArgTypeTest(obj, type, name, exact)) + ((likely(OBJ_IS_TYPE(obj, type) | (none_allowed && (API_IS_EQUAL(obj, API_NONE_VALUE))))) ? 1 : \ + __Pyx__ArgTypeTest(HPY_CONTEXT_FIRST_ARG_CALL obj, type, name, exact)) -static int __Pyx__ArgTypeTest(PYOBJECT_TYPE obj, PYTYPEOBJECT_TYPE type, const char *name, int exact); /*proto*/ +static int __Pyx__ArgTypeTest(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, PYTYPEOBJECT_TYPE type, const char *name, int exact); /*proto*/ //////////////////// ArgTypeTest //////////////////// //@substitute: naming -static int __Pyx__ArgTypeTest(PYOBJECT_TYPE obj, PYTYPEOBJECT_TYPE type, const char *name, int exact) +static int __Pyx__ArgTypeTest(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, PYTYPEOBJECT_TYPE type, const char *name, int exact) { __Pyx_TypeName type_name; __Pyx_TypeName obj_type_name; @@ -31,8 +31,8 @@ static int __Pyx__ArgTypeTest(PYOBJECT_TYPE obj, PYTYPEOBJECT_TYPE type, const c extra_info = PYUNICODE("Note that Cython is deliberately stricter than PEP-484 and rejects subclasses of builtin types. If you need to pass subclasses then set the 'annotation_typing' directive to False."); } } - type_name = __Pyx_PyType_GetName(type); - obj_type_name = __Pyx_PyType_GetName(GET_TYPE(obj)); + type_name = __Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_CALL type); + obj_type_name = __Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_CALL GET_TYPE(obj)); PyErr_Format(PyExc_TypeError, "Argument '%.200s' has incorrect type (expected " __Pyx_FMT_TYPENAME ", got " __Pyx_FMT_TYPENAME ")" diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 0f15eb17f98..835b24afcfb 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -53,6 +53,7 @@ //General Methods #define API_IS_EQUAL(a, b) HPy_Is(HPY_CONTEXT_CNAME, a, b) #define API_RICH_COMPARE(h1, h2, op) HPy_RichCompare(HPY_CONTEXT_CNAME, h1, h2, op) + #define API_RICH_COMPARE_BOOL(h1, h2, op) HPy_RichCompareBool(HPY_CONTEXT_CNAME, h1, h2, op) //API Call Macros #define API_CALL_FUNC(callable, args, nargs, kwnames) HPy_Call(HPY_CONTEXT_CNAME, callable, args, nargs, kwnames) @@ -63,8 +64,11 @@ #define API_SSIZE_T HPy_ssize_t //Type Checks - #define FLOAT_CHECK_EXACT(f) HPyFloat_Check(HPY_CONTEXT_CNAME, f) + #define LONG_CHECK(l) HPyNumber_Check(HPY_CONTEXT_CNAME, l) + #define FLOAT_CHECK_EXACT(f) HPyNumber_Check(HPY_CONTEXT_CNAME, f) #define DICT_CHECK(o) HPyDict_Check(HPY_CONTEXT_CNAME, o) + #define DICT_CHECK_EXACT(o) HPyDict_Check(HPY_CONTEXT_CNAME, o) + #define TUPLE_CHECK(o) HPyTuple_Check(HPY_CONTEXT_CNAME, o) //Integer Type - From #define PYOBJECT_INT_FROM_LONG(i) HPyLong_FromLong(HPY_CONTEXT_CNAME, i) @@ -80,6 +84,7 @@ //Long Type - To #define PYOBJECT_LONG_AS_SSIZE(l) HPyLong_AsSsize_t(HPY_CONTEXT_CNAME, l) + #define PYOBJECT_LONG_AS_UNSIGNED_LONG(l) HPyLong_AsUnsignedLong(HPY_CONTEXT_CNAME, l) //Float Type - From #define PYOBJECT_FLOAT_FROM_DOUBLE(f) HPyFloat_FromDouble(HPY_CONTEXT_CNAME, f) @@ -104,6 +109,7 @@ #define DICT_SET_ITEM(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) #define DICT_GET_ITEM_STR(o, attr_name) HPy_GetItem_s(HPY_CONTEXT_CNAME, o, attr_name) #define DICT_SET_ITEM_STR(o, attr_name, attr_val) HPy_SetItem_s(HPY_CONTEXT_CNAME, o, attr_name, attr_val) + #define DICT_GET_ITEM_WITH_ERROR(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) //Sequence Type @@ -134,7 +140,7 @@ #define TYPE_CHECK(o) HPy_TypeCheck(HPY_CONTEXT_CNAME, (o), HPY_CONTEXT_CNAME->h_TypeType) #define TYPE_AS_PYOBJECT(t) t #define GET_TYPE(o) HPy_Type(HPY_CONTEXT_CNAME, o) - + #define OBJ_IS_TYPE(o, t) HPy_TypeCheck(HPY_CONTEXT_CNAME, o, t) //Error & Exception Macros #define PYERR_OCCURRED() HPyErr_Occurred(HPY_CONTEXT_CNAME) @@ -200,6 +206,7 @@ //General Methods #define API_IS_EQUAL(a, b) a==b #define API_RICH_COMPARE(h1, h2, op) PyObject_RichCompare(h1, h2, op) + #define API_RICH_COMPARE_BOOL(h1, h2, op) PyObject_RichCompareBool(h1, h2, op) //API Call Macros #define API_CALL_FUNC(callable, args, nargs, kwnames) PyObject_Call(HPY_CONTEXT_CNAME, callable, args, kwnames) @@ -210,9 +217,11 @@ #define API_SSIZE_T Py_ssize_t //Number Type Checks + #define LONG_CHECK(l) PyLong_Check(l) #define FLOAT_CHECK_EXACT(f) PyFloat_CheckExact(f) #define DICT_CHECK(o) PyDict_Check(o) - + #define DICT_CHECK_EXACT(o) PyDict_CheckExact(o) + #define TUPLE_CHECK(o) PyTuple_Check(o) //Integer Type - From #define PYOBJECT_INT_FROM_LONG(i) PyInt_FromLong(i) @@ -228,6 +237,7 @@ //Long Type - To #define PYOBJECT_LONG_AS_SSIZE(l) PyLong_AsSsize_t(l) + #define PYOBJECT_LONG_AS_UNSIGNED_LONG(l) PyLong_AsUnsignedLong(l) //Float Type - From #define PYOBJECT_FLOAT_FROM_DOUBLE(f) PyFloat_FromDouble(f) @@ -252,12 +262,13 @@ #define DICT_SET_ITEM(o, attr_name, attr_val) PyDict_SetItem(o, attr_name, attr_val) #define DICT_GET_ITEM_STR(o, attr_name) PyDict_GetItemString(o, attr_name) #define DICT_SET_ITEM_STR(o, attr_name, attr_val) PyDict_SetItemString(o, attr_name, attr_val) + #define DICT_GET_ITEM_WITH_ERROR(o, attr_name) PyDict_GetItemWithError(o, attr_name) //Sequence Type //Tuple Type #define TUPLE_CREATE_EMPTY() PyTuple_New(0) - #define TUPLE_GET_ITEM(h, pos) __Pyx_PySequence_ITEM(HPY_CONTEXT_CNAME, h, pos) + #define TUPLE_GET_ITEM(h, pos) __Pyx_PySequence_ITEM(h, pos) #define TUPLE_GET_SIZE(h) PyTuple_GET_SIZE(h) #define TUPLE_BUILDER_TYPE PyObject * //Not used, just needed to prevent errors #define TUPLE_CREATE_START(target, builder, size) target=PyTuple_New(size) @@ -268,7 +279,7 @@ //List Type //PyObject/HPy Handle Type - #define PYOBJECT_GET_ITEM(o, attr_name) PyObject_GetItem(HPY_CONTEXT_CNAME, o, attr_name) + #define PYOBJECT_GET_ITEM(o, attr_name) PyObject_GetItem(o, attr_name) #define PYOBJECT_SET_ITEM(o, attr_name, attr_val) PyObject_SetItem(o, attr_name, attr_val) #define PYOBJECT_GET_ATTR(o, attr_name) PyObject_GetAttr(o, attr_name) #define PYOBJECT_SET_ATTR(o, attr_name, attr_val) PyObject_SetAttr(o, attr_name, attr_val) @@ -282,6 +293,7 @@ #define TYPE_CHECK(o) PyType_Check(o) #define TYPE_AS_PYOBJECT(t) (PyObject*)&##t #define GET_TYPE(o) Py_TYPE(o) + #define OBJ_IS_TYPE(o, t) Py_IS_TYPE(o, t) //Error & Exception Macros #define PYERR_OCCURRED() (!!PyErr_Occurred()) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 63d79f43a8c..6a350c472a7 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -352,52 +352,65 @@ static PyObject *__Pyx_PyObject_GetItem(PyObject *obj, PyObject *key) { /////////////// DictGetItem.proto /////////////// #if !CYTHON_COMPILING_IN_PYPY -static PyObject *__Pyx_PyDict_GetItem(PyObject *d, PyObject* key);/*proto*/ +static PYOBJECT_TYPE __Pyx_PyDict_GetItem(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE d, PYOBJECT_TYPE key);/*proto*/ +#if CYTHON_USING_HPY +#define __Pyx_PyObject_Dict_GetItem(HPY_CONTEXT_CNAME, obj, name) \ + (likely(DICT_CHECK_EXACT(obj)) ? \ + __Pyx_PyDict_GetItem(HPY_CONTEXT_CNAME, obj, name) : PYOBJECT_GET_ITEM(obj, name)) +#else #define __Pyx_PyObject_Dict_GetItem(obj, name) \ - (likely(PyDict_CheckExact(obj)) ? \ - __Pyx_PyDict_GetItem(obj, name) : PyObject_GetItem(obj, name)) + (likely(DICT_CHECK_EXACT(obj)) ? \ + __Pyx_PyDict_GetItem(obj, name) : PYOBJECT_GET_ITEM(obj, name)) +#endif #else -#define __Pyx_PyDict_GetItem(d, key) PyObject_GetItem(d, key) -#define __Pyx_PyObject_Dict_GetItem(obj, name) PyObject_GetItem(obj, name) +#define __Pyx_PyDict_GetItem(HPY_CONTEXT_FIRST_ARG_CALL d, key) PYOBJECT_GET_ITEM(d, key) +#define __Pyx_PyObject_Dict_GetItem(HPY_CONTEXT_FIRST_ARG_CALL obj, name) PYOBJECT_GET_ITEM(obj, name) #endif /////////////// DictGetItem /////////////// #if !CYTHON_COMPILING_IN_PYPY -static PyObject *__Pyx_PyDict_GetItem(PyObject *d, PyObject* key) { - PyObject *value; - value = PyDict_GetItemWithError(d, key); - if (unlikely(!value)) { +static PYOBJECT_TYPE __Pyx_PyDict_GetItem(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE d, PYOBJECT_TYPE key) { + PYOBJECT_TYPE value; + value = DICT_GET_ITEM_WITH_ERROR(d, key); + if (unlikely(API_IS_NULL(value))) { if (!PyErr_Occurred()) { - if (unlikely(PyTuple_Check(key))) { + if (unlikely(TUPLE_CHECK(key))) { // CPython interprets tuples as separate arguments => must wrap them in another tuple. - PyObject* args = PyTuple_Pack(1, key); - if (likely(args)) { - PyErr_SetObject(PyExc_KeyError, args); - Py_DECREF(args); + PYOBJECT_TYPE args = TUPLE_PACK(1, key); + if (likely(API_IS_NOT_NULL(args))) { + PyErr_SetObject(PyExc_KeyError, HPY_LEGACY_OBJECT_AS(args)); + PYOBJECT_CLOSEREF(args); } } else { // Avoid tuple packing if possible. - PyErr_SetObject(PyExc_KeyError, key); + PyErr_SetObject(PyExc_KeyError, HPY_LEGACY_OBJECT_AS(key)); } } - return NULL; + return API_NULL_VALUE; } - Py_INCREF(value); - return value; + return PYOBJECT_NEWREF(value); } #endif /////////////// GetItemInt.proto /////////////// //@substitute: tempita +#if CYTHON_USING_HPY +#define __Pyx_GetItemInt(HPY_CONTEXT_CNAME, o, i, type, is_signed, to_py_func, is_list, wraparound, boundscheck) \ + (__Pyx_fits_Py_ssize_t(i, type, is_signed) ? \ + HPY_LEGACY_OBJECT_FROM(__Pyx_GetItemInt_Fast(HPY_LEGACY_OBJECT_AS(o), (Py_ssize_t)i, is_list, wraparound, boundscheck)) : \ + (is_list ? (PyErr_SetString(PyExc_IndexError, "list index out of range"), HPy_NULL) : \ + HPY_LEGACY_OBJECT_FROM(__Pyx_GetItemInt_Generic(HPY_LEGACY_OBJECT_AS(o), HPY_LEGACY_OBJECT_AS(to_py_func(HPY_CONTEXT_CNAME, i)))))) +#else #define __Pyx_GetItemInt(o, i, type, is_signed, to_py_func, is_list, wraparound, boundscheck) \ (__Pyx_fits_Py_ssize_t(i, type, is_signed) ? \ __Pyx_GetItemInt_Fast(o, (Py_ssize_t)i, is_list, wraparound, boundscheck) : \ (is_list ? (PyErr_SetString(PyExc_IndexError, "list index out of range"), (PyObject*)NULL) : \ __Pyx_GetItemInt_Generic(o, to_py_func(i)))) +#endif {{for type in ['List', 'Tuple']}} #define __Pyx_GetItemInt_{{type}}(o, i, type, is_signed, to_py_func, is_list, wraparound, boundscheck) \ @@ -1569,7 +1582,13 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStr(HPY_CONTEXT_FIRST_A if (likely(tp->tp_getattro)) return tp->tp_getattro(obj, attr_name); #endif - return DICT_GET_ITEM(obj, attr_name); + PYOBJECT_TYPE item = DICT_GET_ITEM(obj, attr_name); +#if CYTHON_USING_HPY + if (HPy_Is(item, HPy_NULL)) { + HPyErr_Clear(HPY_CONTEXT_CNAME); + } +#endif + return item; } #endif From 28cbd459ffb16cc63e23afffd0084515e22dceed Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Tue, 17 Oct 2023 13:38:41 +0200 Subject: [PATCH 207/286] Added error clearing for HPy Dicts --- Cython/Utility/HPyUtils.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 835b24afcfb..57ba2b116df 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -105,11 +105,11 @@ //Dict Type #define DICT_NEW() HPyDict_New(HPY_CONTEXT_CNAME) - #define DICT_GET_ITEM(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) + #define DICT_GET_ITEM(o, attr_name) HPyDict_GetItem(HPY_CONTEXT_CNAME, o, attr_name) #define DICT_SET_ITEM(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) - #define DICT_GET_ITEM_STR(o, attr_name) HPy_GetItem_s(HPY_CONTEXT_CNAME, o, attr_name) + #define DICT_GET_ITEM_STR(o, attr_name) HPyDict_GetItem_s(HPY_CONTEXT_CNAME, o, attr_name) #define DICT_SET_ITEM_STR(o, attr_name, attr_val) HPy_SetItem_s(HPY_CONTEXT_CNAME, o, attr_name, attr_val) - #define DICT_GET_ITEM_WITH_ERROR(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) + #define DICT_GET_ITEM_WITH_ERROR(o, attr_name) HPyDict_GetItem(HPY_CONTEXT_CNAME, o, attr_name) //Sequence Type @@ -355,4 +355,23 @@ HPy HPyType_FromModuleAndSpec(HPyContext *ctx, HPy module, HPyType_Spec *spec, H } return result; } + +static CYTHON_INLINE HPy HPyDict_GetItem(HPyContext *ctx, HPy mp, HPy key) +{ + HPy res = HPy_GetItem(ctx, mp, key); + if (HPy_IsNull(res)) { + HPyErr_Clear(ctx); + } + return res; +} + +static CYTHON_INLINE HPy HPyDict_GetItem_s(HPyContext *ctx, HPy mp, const char *key) +{ + HPy res = HPy_GetItem_s(ctx, mp, key); + if (HPy_IsNull(res)) { + HPyErr_Clear(ctx); + } + return res; +} + #endif From 79abd1fbde6f7c82281cbe52408802c5b9754663 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 23 Oct 2023 16:26:51 +0200 Subject: [PATCH 208/286] Both dict_getitem and fict_get are running now --- Cython/Compiler/Builtin.py | 4 +-- Cython/Compiler/Code.py | 16 ++++++--- Cython/Compiler/ExprNodes.py | 54 +++++++++++++++++++----------- Cython/Compiler/Nodes.py | 9 ++--- Cython/Compiler/Optimize.py | 4 +-- Cython/Utility/Builtins.c | 18 +++++----- Cython/Utility/FunctionArguments.c | 53 ++++++++++++++++------------- Cython/Utility/HPyUtils.c | 13 +++++-- Cython/Utility/Optimize.c | 20 ++++++----- 9 files changed, 116 insertions(+), 75 deletions(-) diff --git a/Cython/Compiler/Builtin.py b/Cython/Compiler/Builtin.py index 55d005f89b6..dac1fce8658 100644 --- a/Cython/Compiler/Builtin.py +++ b/Cython/Compiler/Builtin.py @@ -302,7 +302,7 @@ def declare_in_type(self, self_type): BuiltinMethod("__mul__", "Tz", "T", "__Pyx_PySequence_Multiply", utility_code=UtilityCode.load("PySequenceMultiply", "ObjectHandling.c")), ]), - ("str", "&PyString_Type", [BuiltinMethod("join", "TO", "T", "__Pyx_PyString_Join", + ("str", "API_STRING_TYPE", [BuiltinMethod("join", "TO", "T", "__Pyx_PyString_Join", utility_code=UtilityCode.load("StringJoin", "StringTools.c")), BuiltinMethod("__mul__", "Tz", "T", "__Pyx_PySequence_Multiply", utility_code=UtilityCode.load("PySequenceMultiply", "ObjectHandling.c")), @@ -327,7 +327,7 @@ def declare_in_type(self, self_type): utility_code=UtilityCode.load("PySequenceMultiply", "ObjectHandling.c")), ]), - ("dict", "&PyDict_Type", [BuiltinMethod("__contains__", "TO", "b", "PyDict_Contains"), + ("dict", "API_DICT_TYPE", [BuiltinMethod("__contains__", "TO", "b", "PyDict_Contains"), BuiltinMethod("has_key", "TO", "b", "PyDict_Contains"), BuiltinMethod("items", "T", "O", "__Pyx_PyDict_Items", utility_code=UtilityCode.load("py_dict_items", "Builtins.c")), diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index b886f6a486a..1aa5d8bd94e 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1187,7 +1187,7 @@ def __init__(self, writer, module_node, code_config, common_utility_include_dir= self.cached_cmethods = {} self.initialised_constants = set() - self.const_cname_array = [] + self.const_cname_array = ['__pyx_d', '__pyx_cython_runtime', '__pyx_empty_tuple', '__pyx_empty_bytes', '__pyx_empty_unicode'] writer.set_global_state(self) self.rootwriter = writer @@ -1492,7 +1492,7 @@ def cached_unbound_method_call_code(self, obj_cname, type_cname, method_name, ar self.use_utility_code(UtilityCode.load_cached(utility_code_name, "ObjectHandling.c")) cache_cname = self.get_cached_unbound_method(type_cname, method_name) args = [obj_cname] + arg_cnames - return "__Pyx_%s(&%s, %s)" % ( + return "__Pyx_%s(HPY_CONTEXT_FIRST_ARG_CALL &%s, %s)" % ( utility_code_name, cache_cname, ', '.join(args), @@ -1568,11 +1568,19 @@ def generate_cached_methods_decls(self): decl.putln('static __Pyx_CachedCFunction %s = {0, 0, 0, 0, 0};' % ( cname)) # split type reference storage as it might not be static - init.putln('%s.type = (PyObject*)%s;' % ( - cname, type_cname)) # method name string isn't static in limited api + init.putln('#if CYTHON_USING_HPY') + init.putln("PYOBJECT_TYPE temp_meth_name = PYOBJECT_GLOBAL_LOAD(%s);" % method_name_cname) + init.putln('%s.method_name = &temp_meth_name;' % (cname)) + init.putln('%s.type = %s;' % ( + cname, type_cname)) + init.putln('PYOBJECT_CLOSEREF(temp_meth_name);') + init.putln('#else') + init.putln('%s.type = (PyObject*)&%s;' % ( + cname, type_cname)) init.putln('%s.method_name = &%s;' % ( cname, method_name_cname)) + init.putln('#endif') if Options.generate_cleanup_code: cleanup = self.parts['cleanup_globals'] diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index c613fe194db..39926e3ea24 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -4562,7 +4562,7 @@ def generate_result_code(self, code): utility_code = None error_value = None if self.type.is_pyobject: - error_value = 'NULL' + error_value = 'API_NULL_VALUE' if self.index.type.is_int: if self.base.type is list_type: function = "__Pyx_GetItemInt_List" @@ -4614,7 +4614,7 @@ def generate_result_code(self, code): self.result() if self.type.is_pyobject else None, self.exception_value, self.in_nogil_context) else: - error_check = '!%s' if error_value == 'NULL' else 'API_IS_EQUAL(%%s, %s)' % error_value + error_check = 'API_IS_NULL(%s)' if error_value == 'API_NULL_VALUE' else 'API_IS_EQUAL(%%s, %s)' % error_value code.putln("#if CYTHON_USING_HPY") code.putln( "%s = %s(HPY_CONTEXT_CNAME, %s, %s%s); %s" % ( @@ -6472,20 +6472,22 @@ def c_call_code(self, code=False): for actual_arg in self.args[len(formal_args):]: arg_list_code.append(actual_arg.move_result_rhs()) - arg_string = "" - for arg in arg_list_code: - if code: - if arg in code.globalstate.const_cname_array: - arg_string = arg_string + "PYOBJECT_GLOBAL_LOAD(" + arg + ")" #temp fix for globals and non-globals being handled by the same code + if len(arg_list_code) > 0: + arg_string = "HPY_CONTEXT_FIRST_ARG_CALL " + for arg in arg_list_code: + if code: + if arg in code.globalstate.const_cname_array: + arg_string = arg_string + "PYOBJECT_GLOBAL_LOAD(" + arg + ")" #temp fix for globals and non-globals being handled by the same code + else: + arg_string = arg_string + "%s" % arg else: arg_string = arg_string + "%s" % arg - else: - arg_string = arg_string + "%s" % arg - arg_string = arg_string + ", " - arg_string = arg_string[0:-2] - + arg_string = arg_string + ", " + arg_string = arg_string[0:-2] + else: + arg_string = "HPY_CONTEXT_ONLY_ARG_CALL" - result = "%s(HPY_CONTEXT_FIRST_ARG_CALL %s)" % (self.function.result(), arg_string) + result = "%s(%s)" % (self.function.result(), arg_string) return result def is_c_result_required(self): @@ -6569,7 +6571,7 @@ def generate_result_code(self, code): else: code.putln("%s = %s;" % (tmp_arg_code, arg_code)) code.putln( - "%s = HPY_LEGACY_OBJECT_FROM(__Pyx_PyObject_Call(HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), NULL)); %s" % ( + "%s = HPY_LEGACY_OBJECT_FROM(__Pyx_PyObject_Call(%s, %s, API_NULL_VALUE)); %s" % ( self.result(), tmp_func_result, tmp_arg_code, @@ -7281,13 +7283,22 @@ def generate_result_code(self, code): kwargs = 'NULL' code.globalstate.use_utility_code(UtilityCode.load_cached( "PyObjectCall", "ObjectHandling.c")) + pos_args_cname = self.positional_args.py_result() + tmp_pos_args = code.funcstate.allocate_temp(py_object_type, False) + if pos_args_cname in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_pos_args, pos_args_cname)) + else: + code.putln("%s = %s;" % (tmp_pos_args, pos_args_cname)) code.putln( - "%s = __Pyx_PyObject_Call(%s, %s, %s); %s" % ( + "%s = HPY_LEGACY_OBJECT_FROM(__Pyx_PyObject_Call(%s, HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s))); %s" % ( self.result(), self.function.py_result(), - self.positional_args.py_result(), + tmp_pos_args, kwargs, - code.error_goto_if_null(self.result(), self.pos))) + code.error_goto_if_null_object(self.result(), self.pos))) + if pos_args_cname in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_pos_args) + code.funcstate.release_temp(tmp_pos_args) self.generate_gotref(code) @@ -7430,7 +7441,7 @@ def generate_evaluation_code(self, code): code.putln("} else") code.putln("#endif") code.putln("{") - code.putln("%s = PyDict_Copy(%s); %s" % ( + code.putln("%s = DICT_COPY(%s); %s" % ( self.result(), item.py_result(), code.error_goto_if_null(self.result(), item.pos))) @@ -7479,7 +7490,7 @@ def generate_evaluation_code(self, code): if self.reject_duplicates: # merge mapping into kwdict one by one as we need to check for duplicates helpers.add("MergeKeywords") - code.put_error_if_neg(item.pos, "__Pyx_MergeKeywords(%s, %s)" % ( + code.put_error_if_neg(item.pos, "__Pyx_MergeKeywords(HPY_CONTEXT_FIRST_ARG_CALL %s, %s)" % ( self.result(), item.py_result())) else: # simple case, just add all entries @@ -8360,9 +8371,14 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): tmp_builder = code.funcstate.allocate_temp(tuple_builder_type, manage_ref=False) arg_count = len(self.args) if self.type is tuple_type: + code.putln("#if CYTHON_USING_HPY") + code.putln("%s(%s,%s,%s%s);" % ( + create_func, target, tmp_builder, arg_count, size_factor)) + code.putln("#else") code.putln("%s(%s,%s,%s%s); %s" % ( create_func, target, tmp_builder, arg_count, size_factor, code.error_goto_if_null_object(target, self.pos))) + code.putln("#endif") else: code.putln("%s = %s(%s%s); %s" % ( target, create_func, arg_count, size_factor, diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index bc3ff57a4f9..dffc1ec971d 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -2449,6 +2449,7 @@ def generate_arg_type_test(self, arg, code): code.globalstate.use_utility_code( UtilityCode.load_cached("ArgTypeTest", "FunctionArguments.c")) typeptr_cname = arg.type.typeptr_cname + hpy_typeptr_cname = typeptr_cname.replace("&", "") if typeptr_cname.startswith("(&API") else "HPY_LEGACY_OBJECT_FROM((PyObject *)%s)" %typeptr_cname arg_code = "((PyObject *)%s)" % arg.entry.cname exact = 0 if arg.type.is_builtin_type and arg.type.require_exact: @@ -2457,9 +2458,9 @@ def generate_arg_type_test(self, arg, code): exact = 2 if arg.type_from_annotation else 1 code.putln("#if CYTHON_USING_HPY") code.putln( - 'if (unlikely(!__Pyx_ArgTypeTest(%s, HPY_LEGACY_OBJECT_FROM((PyObject*)%s), %d, %s, %s))) %s' % ( + 'if (unlikely(!__Pyx_ArgTypeTest(%s, %s, %d, %s, %s))) %s' % ( arg.entry.cname, - typeptr_cname, + hpy_typeptr_cname, arg.accept_none, arg.name_cstring, exact, @@ -3866,7 +3867,7 @@ def generate_argument_declarations(self, env, code): # Create nargs, but avoid an "unused" warning in the few cases where we don't need it. if self.signature_has_generic_args(): # error handling for this is checked after the declarations - nargs_code = "CYTHON_UNUSED Py_ssize_t %s;" % Naming.nargs_cname + nargs_code = "CYTHON_UNUSED API_SSIZE_T %s;" % Naming.nargs_cname if self.signature.use_fastcall: code.putln("#if !CYTHON_METH_FASTCALL") code.putln(nargs_code) @@ -3875,7 +3876,7 @@ def generate_argument_declarations(self, env, code): code.putln(nargs_code) # Array containing the values of keyword arguments when using METH_FASTCALL. - code.putln('CYTHON_UNUSED PyObject *const *%s;' % Naming.kwvalues_cname) + code.putln('CYTHON_UNUSED PYOBJECT_TYPE const *%s;' % Naming.kwvalues_cname) def generate_argument_parsing_code(self, env, code, decl_code): # Generate fast equivalent of PyArg_ParseTuple call for diff --git a/Cython/Compiler/Optimize.py b/Cython/Compiler/Optimize.py index b91d3e9497f..ba2bd0f6dc3 100644 --- a/Cython/Compiler/Optimize.py +++ b/Cython/Compiler/Optimize.py @@ -2507,7 +2507,7 @@ def visit_FormattedValueNode(self, node): ]) def _handle_simple_function_dict(self, node, function, pos_args): - """Replace dict(some_dict) by PyDict_Copy(some_dict). + """Replace dict(some_dict) by DICT_COPY(some_dict). """ if len(pos_args) != 1: return node @@ -2515,7 +2515,7 @@ def _handle_simple_function_dict(self, node, function, pos_args): if arg.type is Builtin.dict_type: arg = arg.as_none_safe_node("'NoneType' is not iterable") return ExprNodes.PythonCapiCallNode( - node.pos, "PyDict_Copy", self.PyDict_Copy_func_type, + node.pos, "DICT_COPY", self.PyDict_Copy_func_type, args = [arg], is_temp = node.is_temp ) diff --git a/Cython/Utility/Builtins.c b/Cython/Utility/Builtins.c index 414d9efa363..412000889ea 100644 --- a/Cython/Utility/Builtins.c +++ b/Cython/Utility/Builtins.c @@ -395,7 +395,7 @@ static long __Pyx__PyObject_Ord(PyObject* c) { //////////////////// py_dict_keys.proto //////////////////// -static CYTHON_INLINE PyObject* __Pyx_PyDict_Keys(PyObject* d); /*proto*/ +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyDict_Keys(PYOBJECT_TYPE d); /*proto*/ //////////////////// py_dict_keys //////////////////// @@ -405,7 +405,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_Keys(PyObject* d) { //////////////////// py_dict_values.proto //////////////////// -static CYTHON_INLINE PyObject* __Pyx_PyDict_Values(PyObject* d); /*proto*/ +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyDict_Values(PYOBJECT_TYPE d); /*proto*/ //////////////////// py_dict_values //////////////////// @@ -415,7 +415,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_Values(PyObject* d) { //////////////////// py_dict_items.proto //////////////////// -static CYTHON_INLINE PyObject* __Pyx_PyDict_Items(PyObject* d); /*proto*/ +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyDict_Items(PYOBJECT_TYPE d); /*proto*/ //////////////////// py_dict_items //////////////////// @@ -425,7 +425,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_Items(PyObject* d) { //////////////////// py_dict_iterkeys.proto //////////////////// -static CYTHON_INLINE PyObject* __Pyx_PyDict_IterKeys(PyObject* d); /*proto*/ +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyDict_IterKeys(PYOBJECT_TYPE d); /*proto*/ //////////////////// py_dict_iterkeys //////////////////// @@ -435,7 +435,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_IterKeys(PyObject* d) { //////////////////// py_dict_itervalues.proto //////////////////// -static CYTHON_INLINE PyObject* __Pyx_PyDict_IterValues(PyObject* d); /*proto*/ +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyDict_IterValues(PYOBJECT_TYPE d); /*proto*/ //////////////////// py_dict_itervalues //////////////////// @@ -445,7 +445,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_IterValues(PyObject* d) { //////////////////// py_dict_iteritems.proto //////////////////// -static CYTHON_INLINE PyObject* __Pyx_PyDict_IterItems(PyObject* d); /*proto*/ +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyDict_IterItems(PYOBJECT_TYPE d); /*proto*/ //////////////////// py_dict_iteritems //////////////////// @@ -455,7 +455,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_IterItems(PyObject* d) { //////////////////// py_dict_viewkeys.proto //////////////////// -static CYTHON_INLINE PyObject* __Pyx_PyDict_ViewKeys(PyObject* d); /*proto*/ +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyDict_ViewKeys(PYOBJECT_TYPE d); /*proto*/ //////////////////// py_dict_viewkeys //////////////////// @@ -465,7 +465,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_ViewKeys(PyObject* d) { //////////////////// py_dict_viewvalues.proto //////////////////// -static CYTHON_INLINE PyObject* __Pyx_PyDict_ViewValues(PyObject* d); /*proto*/ +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyDict_ViewValues(PYOBJECT_TYPE d); /*proto*/ //////////////////// py_dict_viewvalues //////////////////// @@ -475,7 +475,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_ViewValues(PyObject* d) { //////////////////// py_dict_viewitems.proto //////////////////// -static CYTHON_INLINE PyObject* __Pyx_PyDict_ViewItems(PyObject* d); /*proto*/ +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyDict_ViewItems(PYOBJECT_TYPE d); /*proto*/ //////////////////// py_dict_viewitems //////////////////// diff --git a/Cython/Utility/FunctionArguments.c b/Cython/Utility/FunctionArguments.c index 22c0c302419..40bfc8a3f32 100644 --- a/Cython/Utility/FunctionArguments.c +++ b/Cython/Utility/FunctionArguments.c @@ -391,55 +391,62 @@ static int __Pyx_ParseOptionalKeywords( //////////////////// MergeKeywords.proto //////////////////// -static int __Pyx_MergeKeywords(PyObject *kwdict, PyObject *source_mapping); /*proto*/ +static int __Pyx_MergeKeywords(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE kwdict, PYOBJECT_TYPE source_mapping); /*proto*/ //////////////////// MergeKeywords //////////////////// //@requires: RaiseDoubleKeywords //@requires: Optimize.c::dict_iter -static int __Pyx_MergeKeywords(PyObject *kwdict, PyObject *source_mapping) { - PyObject *iter, *key = NULL, *value = NULL; +static int __Pyx_MergeKeywords(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE kwdict, PYOBJECT_TYPE source_mapping) { + PYOBJECT_TYPE iter, CAPI_IS_POINTER key = API_NULL_VALUE, CAPI_IS_POINTER value = API_NULL_VALUE; int source_is_dict, result; - Py_ssize_t orig_length, ppos = 0; + API_SSIZE_T orig_length, ppos = 0; + PYOBJECT_TYPE items_loader = HPY_LEGACY_OBJECT_AS(PYIDENT("items")); - iter = __Pyx_dict_iterator(source_mapping, 0, PYIDENT("items"), &orig_length, &source_is_dict); - if (unlikely(!iter)) { + iter = HPY_LEGACY_OBJECT_FROM(__Pyx_dict_iterator(HPY_LEGACY_OBJECT_AS(source_mapping), 0, HPY_LEGACY_OBJECT_AS(items_loader), &orig_length, &source_is_dict)); + if (unlikely(API_IS_NULL(iter))) { // slow fallback: try converting to dict, then iterate - PyObject *args; + PYOBJECT_TYPE args; if (unlikely(!PyErr_ExceptionMatches(PyExc_AttributeError))) goto bad; PyErr_Clear(); - args = PyTuple_Pack(1, source_mapping); - if (likely(args)) { - PyObject *fallback = PyObject_Call((PyObject*)&PyDict_Type, args, NULL); - Py_DECREF(args); - if (likely(fallback)) { - iter = __Pyx_dict_iterator(fallback, 1, PYIDENT("items"), &orig_length, &source_is_dict); - Py_DECREF(fallback); + args = TUPLE_PACK(1, source_mapping); + if (likely(API_IS_NOT_NULL(args))) { +#if CYTHON_USING_HPY + PYOBJECT_TYPE fallback = API_CALL_FUNC(HPY_CONTEXT_CNAME->h_DictType, args, 0, NULL); +#else + PYOBJECT_TYPE fallback = API_CALL_FUNC((PyObject*)&PyDict_Type, args, 0, NULL); +#endif + PYOBJECT_CLOSEREF(args); + if (likely(API_IS_NOT_NULL(fallback))) { + iter = __Pyx_dict_iterator(HPY_LEGACY_OBJECT_AS(fallback), 1, HPY_LEGACY_OBJECT_AS(items_loader), &orig_length, &source_is_dict); + PYOBJECT_CLOSEREF(fallback); } } - if (unlikely(!iter)) goto bad; + if (unlikely(API_IS_NULL(iter))) goto bad; } while (1) { - result = __Pyx_dict_iter_next(iter, orig_length, &ppos, &key, &value, NULL, source_is_dict); + result = __Pyx_dict_iter_next(HPY_LEGACY_OBJECT_AS(iter), orig_length, &ppos, &HPY_LEGACY_OBJECT_AS(key), &HPY_LEGACY_OBJECT_AS(value), API_NULL_VALUE, source_is_dict); if (unlikely(result < 0)) goto bad; if (!result) break; - if (unlikely(PyDict_Contains(kwdict, key))) { - __Pyx_RaiseDoubleKeywordsError("function", key); + if (unlikely(PyDict_Contains(HPY_LEGACY_OBJECT_AS(kwdict), HPY_LEGACY_OBJECT_AS(key)))) { + __Pyx_RaiseDoubleKeywordsError("function", HPY_LEGACY_OBJECT_AS(key)); result = -1; } else { - result = PyDict_SetItem(kwdict, key, value); + result = DICT_SET_ITEM(kwdict, key, value); } - Py_DECREF(key); - Py_DECREF(value); + PYOBJECT_CLOSEREF(key); + PYOBJECT_CLOSEREF(value); if (unlikely(result < 0)) goto bad; } - Py_XDECREF(iter); + PYOBJECT_XCLOSEREF(iter); + PYOBJECT_CLOSEREF(items_loader); return 0; bad: Py_XDECREF(iter); + PYOBJECT_CLOSEREF(items_loader); return -1; } @@ -472,7 +479,7 @@ static int __Pyx_MergeKeywords(PyObject *kwdict, PyObject *source_mapping) { #define __Pyx_NumKwargs_VARARGS(kwds) PyDict_Size(kwds) #define __Pyx_KwValues_VARARGS(args, nargs) NULL #define __Pyx_GetKwValue_VARARGS(kw, kwvalues, s) __Pyx_PyDict_GetItemStrWithError(kw, s) -#define __Pyx_KwargsAsDict_VARARGS(kw, kwvalues) PyDict_Copy(kw) +#define __Pyx_KwargsAsDict_VARARGS(kw, kwvalues) DICT_COPY(kw) #if CYTHON_METH_FASTCALL #define __Pyx_Arg_FASTCALL(args, i) args[i] #define __Pyx_NumKwargs_FASTCALL(kwds) TUPLE_GET_SIZE(kwds) diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 57ba2b116df..3603a9701a8 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -22,7 +22,7 @@ #define PYOBJECT_GLOBAL_LOAD(global) HPyGlobal_Load(HPY_CONTEXT_CNAME, global) #define CAPI_IS_POINTER #define CAPI_NEEDS_DEREFERENCE - + //Create New and Close References #define PYOBJECT_NEWREF(h) HPy_Dup(HPY_CONTEXT_CNAME, h) #define PYOBJECT_XNEWREF(h) HPy_Dup(HPY_CONTEXT_CNAME, h) @@ -62,6 +62,8 @@ #define API_INT_TYPE HPY_CONTEXT_CNAME->h_LongType #define API_LONG_TYPE HPY_CONTEXT_CNAME->h_LongType #define API_SSIZE_T HPy_ssize_t + #define API_STRING_TYPE HPY_CONTEXT_CNAME->h_UnicodeType + #define API_DICT_TYPE HPY_CONTEXT_CNAME->h_DictType //Type Checks #define LONG_CHECK(l) HPyNumber_Check(HPY_CONTEXT_CNAME, l) @@ -105,6 +107,7 @@ //Dict Type #define DICT_NEW() HPyDict_New(HPY_CONTEXT_CNAME) + #define DICT_COPY(o) HPyDict_Copy(HPY_CONTEXT_CNAME, o) #define DICT_GET_ITEM(o, attr_name) HPyDict_GetItem(HPY_CONTEXT_CNAME, o, attr_name) #define DICT_SET_ITEM(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) #define DICT_GET_ITEM_STR(o, attr_name) HPyDict_GetItem_s(HPY_CONTEXT_CNAME, o, attr_name) @@ -175,7 +178,7 @@ #define PYOBJECT_GLOBAL_LOAD(global) global #define CAPI_IS_POINTER * //Some types are sometimes pointers and sometimes not (i.e. PyModuleDef) where the type is always the same in HPy #define CAPI_NEEDS_DEREFERENCE & - + //Create New and Close References #define PYOBJECT_NEWREF(h) Py_NewRef(h) #define PYOBJECT_XNEWREF(h) Py_XNewRef(h) @@ -209,12 +212,14 @@ #define API_RICH_COMPARE_BOOL(h1, h2, op) PyObject_RichCompareBool(h1, h2, op) //API Call Macros - #define API_CALL_FUNC(callable, args, nargs, kwnames) PyObject_Call(HPY_CONTEXT_CNAME, callable, args, kwnames) + #define API_CALL_FUNC(callable, args, nargs, kwnames) PyObject_Call(callable, args, kwnames) //Type Objects #define API_INT_TYPE PyInt_Type #define API_LONG_TYPE PyLong_Type #define API_SSIZE_T Py_ssize_t + #define API_STRING_TYPE PyString_Type + #define API_DICT_TYPE PyDict_Type //Number Type Checks #define LONG_CHECK(l) PyLong_Check(l) @@ -258,6 +263,8 @@ //Dict Type #define DICT_NEW() PyDict_New() + #define DICT_COPY(o) PyDict_Copy(o) + #define DICT_CONTAINS(o, key) PyDict_Contains(o, key) #define DICT_GET_ITEM(o, attr_name) PyDict_GetItem(o, attr_name) #define DICT_SET_ITEM(o, attr_name, attr_val) PyDict_SetItem(o, attr_name, attr_val) #define DICT_GET_ITEM_STR(o, attr_name) PyDict_GetItemString(o, attr_name) diff --git a/Cython/Utility/Optimize.c b/Cython/Utility/Optimize.c index a54e95f889f..0ffb4f0bfde 100644 --- a/Cython/Utility/Optimize.c +++ b/Cython/Utility/Optimize.c @@ -198,20 +198,22 @@ static PyObject* __Pyx__PyList_PopIndex(PyObject* L, PyObject* py_ix, Py_ssize_t /////////////// dict_getitem_default.proto /////////////// -static PyObject* __Pyx_PyDict_GetItemDefault(PyObject* d, PyObject* key, PyObject* default_value); /*proto*/ +static PYOBJECT_TYPE __Pyx_PyDict_GetItemDefault(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE d, PYOBJECT_TYPE key, PYOBJECT_TYPE default_value); /*proto*/ /////////////// dict_getitem_default /////////////// -static PyObject* __Pyx_PyDict_GetItemDefault(PyObject* d, PyObject* key, PyObject* default_value) { - PyObject* value; +static PYOBJECT_TYPE __Pyx_PyDict_GetItemDefault(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE d, PYOBJECT_TYPE key, PYOBJECT_TYPE default_value) { + PYOBJECT_TYPE value; #if !CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07020000 - value = PyDict_GetItemWithError(d, key); - if (unlikely(!value)) { + value = DICT_GET_ITEM_WITH_ERROR(d, key); + if (unlikely(API_IS_NULL(value))) { if (unlikely(PyErr_Occurred())) - return NULL; + return API_NULL_VALUE; value = default_value; } +#if !CYTHON_USING_HPY Py_INCREF(value); +#endif // avoid C compiler warning about unused utility functions if ((1)); #else @@ -225,10 +227,10 @@ static PyObject* __Pyx_PyDict_GetItemDefault(PyObject* d, PyObject* key, PyObjec } #endif else { - if (default_value == Py_None) - value = CALL_UNBOUND_METHOD(PyDict_Type, "get", d, key); + if (API_IS_EQUAL(default_value, API_NONE_VALUE)) + value = CALL_UNBOUND_METHOD(API_DICT_TYPE, "get", d, key); else - value = CALL_UNBOUND_METHOD(PyDict_Type, "get", d, key, default_value); + value = CALL_UNBOUND_METHOD(API_DICT_TYPE, "get", d, key, default_value); } return value; } From 72ae5ae0a0c10b5c5fd5066b9f083756724a4d41 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Tue, 24 Oct 2023 16:32:15 +0200 Subject: [PATCH 209/286] Added HPy dict test, almost running --- Cython/Compiler/ExprNodes.py | 36 ++++-- Cython/Utility/Builtins.c | 2 +- Cython/Utility/HPyUtils.c | 6 + Cython/Utility/ObjectHandling.c | 222 ++++++++++++++++++++++---------- Cython/Utility/TypeConversion.c | 2 +- tests/run/hpy_dict.pyx | 36 ++++++ 6 files changed, 221 insertions(+), 83 deletions(-) create mode 100644 tests/run/hpy_dict.pyx diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 39926e3ea24..ded6edb5513 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -4667,12 +4667,12 @@ def generate_setitem_code(self, value, code): function = "PYOBJECT_SET_ITEM" temp_load_index = code.funcstate.allocate_temp(py_object_type, manage_ref=False) - if hasattr(self.index, "is_global") and self.index.is_global: + if index_code in code.globalstate.const_cname_array: code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_index, index_code)) else: code.putln("%s = %s;" % (temp_load_index, index_code)) temp_load_value = code.funcstate.allocate_temp(py_object_type, manage_ref=False) - if hasattr(value, "is_global") and value.is_global: + if value_code in code.globalstate.const_cname_array: code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_value, value_code)) else: code.putln("%s = %s;" % (temp_load_value, value_code)) @@ -6474,6 +6474,8 @@ def c_call_code(self, code=False): if len(arg_list_code) > 0: arg_string = "HPY_CONTEXT_FIRST_ARG_CALL " + if self.function.result() == 'DICT_COPY': + arg_string = "" for arg in arg_list_code: if code: if arg in code.globalstate.const_cname_array: @@ -6532,10 +6534,10 @@ def generate_evaluation_code(self, code): code.globalstate.use_utility_code(UtilityCode.load_cached( "PyObjectCallNoArg", "ObjectHandling.c")) code.putln( - "%s = __Pyx_PyObject_CallNoArg(%s); %s" % ( + "%s = __Pyx_PyObject_CallNoArg(HPY_CONTEXT_FIRST_ARG_CALL %s); %s" % ( self.result(), function.py_result(), - code.error_goto_if_null(self.result(), self.pos))) + code.error_goto_if_null_object(self.result(), self.pos))) else: code.globalstate.use_utility_code(UtilityCode.load_cached( "PyObjectCallOneArg", "ObjectHandling.c")) @@ -6571,7 +6573,7 @@ def generate_result_code(self, code): else: code.putln("%s = %s;" % (tmp_arg_code, arg_code)) code.putln( - "%s = HPY_LEGACY_OBJECT_FROM(__Pyx_PyObject_Call(%s, %s, API_NULL_VALUE)); %s" % ( + "%s = __Pyx_PyObject_Call_h(%s, %s, API_NULL_VALUE); %s" % ( self.result(), tmp_func_result, tmp_arg_code, @@ -6795,12 +6797,26 @@ def attribute_is_likely_method(attr): # To avoid passing an out-of-bounds argument pointer in the no-args case, # we need at least two entries, so we pad with NULL and point to that. # See https://github.com/cython/cython/issues/5668 - code.putln("PyObject *__pyx_callargs[%d%s] = {%s, %s};" % ( + + loaded_vars = [] + for arg in args: + arg_result = arg.py_result() + load_tmp = code.funcstate.allocate_temp(py_object_type, False) + loaded_vars.append(load_tmp) + if arg_result in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_tmp, arg_result)) + else: + code.putln("%s = %s;" % (load_tmp, arg_result)) + code.putln("PYOBJECT_TYPE __pyx_callargs[%d%s] = {%s, %s};" % ( (len(args) + 1) if args else 2, extra_keyword_args, self_arg, - ', '.join(arg.py_result() for arg in args) if args else "NULL", + ', '.join(tmp for tmp in loaded_vars) if args else "NULL", )) + for tmp in loaded_vars: + if arg_result in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % load_tmp) + code.funcstate.release_temp(tmp) if kwargs_key_value_pairs: for n, keyvalue in enumerate(kwargs_key_value_pairs): key_is_str = ( @@ -6848,7 +6864,7 @@ def attribute_is_likely_method(attr): elif self.kwdict: self.kwdict.generate_disposal_code(code) self.kwdict.free_temps(code) - code.putln(code.error_goto_if_null(self.result(), self.pos)) + code.putln(code.error_goto_if_null_object(self.result(), self.pos)) self.generate_gotref(code) if reuse_function_temp: @@ -7290,7 +7306,7 @@ def generate_result_code(self, code): else: code.putln("%s = %s;" % (tmp_pos_args, pos_args_cname)) code.putln( - "%s = HPY_LEGACY_OBJECT_FROM(__Pyx_PyObject_Call(%s, HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s))); %s" % ( + "%s = __Pyx_PyObject_Call_h(%s, %s, %s); %s" % ( self.result(), self.function.py_result(), tmp_pos_args, @@ -7444,7 +7460,7 @@ def generate_evaluation_code(self, code): code.putln("%s = DICT_COPY(%s); %s" % ( self.result(), item.py_result(), - code.error_goto_if_null(self.result(), item.pos))) + code.error_goto_if_null_object(self.result(), item.pos))) self.generate_gotref(code) item.generate_disposal_code(code) if item.is_temp: diff --git a/Cython/Utility/Builtins.c b/Cython/Utility/Builtins.c index 412000889ea..7513121b22c 100644 --- a/Cython/Utility/Builtins.c +++ b/Cython/Utility/Builtins.c @@ -415,7 +415,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_Values(PyObject* d) { //////////////////// py_dict_items.proto //////////////////// -static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyDict_Items(PYOBJECT_TYPE d); /*proto*/ +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyDict_Items(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE d); /*proto*/ //////////////////// py_dict_items //////////////////// diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 3603a9701a8..e6657cd0922 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -21,7 +21,9 @@ #define PYOBJECT_GLOBAL_STORE(global, h) HPyGlobal_Store(HPY_CONTEXT_CNAME, &global, h) #define PYOBJECT_GLOBAL_LOAD(global) HPyGlobal_Load(HPY_CONTEXT_CNAME, global) #define CAPI_IS_POINTER + #define CAPI_IS_POINTER_IF_TYPEOBJECT #define CAPI_NEEDS_DEREFERENCE + #define CAST_IF_CAPI(cast_type) //Create New and Close References #define PYOBJECT_NEWREF(h) HPy_Dup(HPY_CONTEXT_CNAME, h) @@ -82,6 +84,7 @@ //Long Type - From #define PYOBJECT_LONG_FROM_LONG(i) HPyLong_FromLong(HPY_CONTEXT_CNAME, i) #define PYOBJECT_LONG_FROM_LONGLONG(i) HPyLong_FromLongLong(HPY_CONTEXT_CNAME, i) + #define PYOBJECT_LONG_FROM_UNSIGNED_LONG(i) HPyLong_FromUnsignedLong(HPY_CONTEXT_CNAME, i) #define PYOBJECT_LONG_FROM_UNSIGNED_LONGLONG(i) HPyLong_FromUnsignedLongLong(HPY_CONTEXT_CNAME, i) //Long Type - To @@ -177,7 +180,9 @@ #define PYOBJECT_GLOBAL_STORE(global, h) global = h #define PYOBJECT_GLOBAL_LOAD(global) global #define CAPI_IS_POINTER * //Some types are sometimes pointers and sometimes not (i.e. PyModuleDef) where the type is always the same in HPy + #define CAPI_IS_POINTER_IF_TYPEOBJECT * #define CAPI_NEEDS_DEREFERENCE & + #define CAST_IF_CAPI(cast_type) (cast_type) //Create New and Close References #define PYOBJECT_NEWREF(h) Py_NewRef(h) @@ -238,6 +243,7 @@ //Long Type - From #define PYOBJECT_LONG_FROM_LONG(i) PyLong_FromLong(i) #define PYOBJECT_LONG_FROM_LONGLONG(i) PyLong_FromLongLong(i) + #define PYOBJECT_LONG_FROM_UNSIGNED_LONG(i) PyLong_FromUnsignedLong(i) #define PYOBJECT_LONG_FROM_UNSIGNED_LONGLONG(i) PyLong_FromUnsignedLongLong(i) //Long Type - To diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 6a350c472a7..9b9b051be88 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -283,7 +283,11 @@ static CYTHON_INLINE int __Pyx_IterFinish(void) { #if CYTHON_USE_TYPE_SLOTS static CYTHON_INLINE PyObject *__Pyx_PyObject_GetItem(PyObject *obj, PyObject *key);/*proto*/ #else -#define __Pyx_PyObject_GetItem(obj, key) PyObject_GetItem(obj, key) +#if CYTHON_USING_HPY +#define __Pyx_PyObject_GetItem(ctx, obj, key) PYOBJECT_GET_ITEM(obj, key) +#else +#define __Pyx_PyObject_GetItem(obj, key) PYOBJECT_GET_ITEM(obj, key) +#endif #endif /////////////// ObjectGetItem /////////////// @@ -1729,12 +1733,16 @@ static int __Pyx_PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **me /////////////// UnpackUnboundCMethod.proto /////////////// typedef struct { - PyObject *type; - PyObject **method_name; + PYOBJECT_TYPE type; + PYOBJECT_TYPE *method_name; // "func" is set on first access (direct C function pointer) +#if !CYTHON_USING_HPY PyCFunction func; +#else + HPyDef *func; +#endif // "method" is set on first access (fallback) - PyObject *method; + PYOBJECT_TYPE method; int flag; } __Pyx_CachedCFunction; @@ -1752,21 +1760,25 @@ static PyObject *__Pyx_SelflessCall(PyObject *method, PyObject *args, PyObject * return result; } +#if CYTHON_USING_HPY +HPyDef_METH(__Pyx_UnboundCMethod_Def, "CythonUnboundCMethod", HPyFunc_KEYWORDS, .doc="") +#else static PyMethodDef __Pyx_UnboundCMethod_Def = { /* .ml_name = */ "CythonUnboundCMethod", /* .ml_meth = */ __PYX_REINTERPRET_FUNCION(PyCFunction, __Pyx_SelflessCall), /* .ml_flags = */ METH_VARARGS | METH_KEYWORDS, /* .ml_doc = */ NULL }; +#endif -static int __Pyx_TryUnpackUnboundCMethod(__Pyx_CachedCFunction* target) { - PyObject *method; +static int __Pyx_TryUnpackUnboundCMethod(HPY_CONTEXT_FIRST_ARG_DEF __Pyx_CachedCFunction *target) { + PYOBJECT_TYPE method; method = __Pyx_PyObject_GetAttrStr(target->type, *target->method_name); - if (unlikely(!method)) + if (unlikely(API_IS_NULL(method))) return -1; target->method = method; // FIXME: use functionality from CythonFunction.c/ClassMethod -#if CYTHON_COMPILING_IN_CPYTHON +#if CYTHON_COMPILING_IN_CPYTHON && !CYTHON_USING_HPY if (likely(__Pyx_TypeCheck(method, &PyMethodDescr_Type))) { PyMethodDescrObject *descr = (PyMethodDescrObject*) method; @@ -1775,31 +1787,35 @@ static int __Pyx_TryUnpackUnboundCMethod(__Pyx_CachedCFunction* target) { } else #endif // bound classmethods need special treatment -#if CYTHON_COMPILING_IN_PYPY +#if CYTHON_COMPILING_IN_PYPY || CYTHON_USING_HPY // In PyPy, functions are regular methods, so just do the self check. #else if (PyCFunction_Check(method)) #endif { - PyObject *self; + PYOBJECT_TYPE self; int self_found; #if CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_PYPY - self = PyObject_GetAttrString(method, "__self__"); - if (!self) { + self = PYOBJECT_GET_ATTR_STR(method, "__self__"); + if (API_IS_NULL(self)) { PyErr_Clear(); } #else self = PyCFunction_GET_SELF(method); #endif - self_found = (self && self != Py_None); + self_found = (API_IS_NOT_NULL(self) && API_IS_EQUAL(self, API_NONE_VALUE)); #if CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_PYPY - Py_XDECREF(self); + PYOBJECT_CLOSEREF(self); #endif if (self_found) { - PyObject *unbound_method = PyCFunction_New(&__Pyx_UnboundCMethod_Def, method); - if (unlikely(!unbound_method)) return -1; +#if CYTHON_USING_HPY + PYOBJECT_TYPE unbound_method = method; +#else + PYOBJECT_TYPE unbound_method = PyCFunction_New(&__Pyx_UnboundCMethod_Def, method); +#endif + if (unlikely(API_IS_NULL(unbound_method))) return -1; // New PyCFunction will own method reference, thus decref __Pyx_PyObject_GetAttrStr - Py_DECREF(method); + PYOBJECT_CLOSEREF(method); target->method = unbound_method; } } @@ -1813,16 +1829,16 @@ static int __Pyx_TryUnpackUnboundCMethod(__Pyx_CachedCFunction* target) { CYTHON_UNUSED static PyObject* __Pyx__CallUnboundCMethod0(__Pyx_CachedCFunction* cfunc, PyObject* self); /*proto*/ -#if CYTHON_COMPILING_IN_CPYTHON +#if CYTHON_COMPILING_IN_CPYTHON && !CYTHON_USING_HPY // FASTCALL methods receive "&empty_tuple" as simple "PyObject[0]*" #define __Pyx_CallUnboundCMethod0(cfunc, self) \ (likely((cfunc)->func) ? \ - (likely((cfunc)->flag == METH_NOARGS) ? (*((cfunc)->func))(self, NULL) : \ + (likely((cfunc)->flag == METH_NOARGS) ? (*((cfunc)->func))(self, API_NULL_VALUE) : \ (likely((cfunc)->flag == METH_FASTCALL) ? \ (*(__Pyx_PyCFunctionFast)(void*)(PyCFunction)(cfunc)->func)(self, &$empty_tuple, 0) : \ ((cfunc)->flag == (METH_FASTCALL | METH_KEYWORDS) ? \ - (*(__Pyx_PyCFunctionFastWithKeywords)(void*)(PyCFunction)(cfunc)->func)(self, &$empty_tuple, 0, NULL) : \ - (likely((cfunc)->flag == (METH_VARARGS | METH_KEYWORDS)) ? ((*(PyCFunctionWithKeywords)(void*)(PyCFunction)(cfunc)->func)(self, $empty_tuple, NULL)) : \ + (*(__Pyx_PyCFunctionFastWithKeywords)(void*)(PyCFunction)(cfunc)->func)(self, &$empty_tuple, 0, API_NULL_VALUE) : \ + (likely((cfunc)->flag == (METH_VARARGS | METH_KEYWORDS)) ? ((*(PyCFunctionWithKeywords)(void*)(PyCFunction)(cfunc)->func)(self, $empty_tuple, API_NULL_VALUE)) : \ ((cfunc)->flag == METH_VARARGS ? (*((cfunc)->func))(self, $empty_tuple) : \ __Pyx__CallUnboundCMethod0(cfunc, self)))))) : \ __Pyx__CallUnboundCMethod0(cfunc, self)) @@ -1855,21 +1871,32 @@ static PyObject* __Pyx__CallUnboundCMethod0(__Pyx_CachedCFunction* cfunc, PyObje /////////////// CallUnboundCMethod1.proto /////////////// +#if CYTHON_USING_HPY CYTHON_UNUSED -static PyObject* __Pyx__CallUnboundCMethod1(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg);/*proto*/ +static PYOBJECT_TYPE __Pyx__CallUnboundCMethod1(HPyContext *HPY_CONTEXT_CNAME, __Pyx_CachedCFunction *cfunc, PYOBJECT_TYPE self, PYOBJECT_TYPE arg);/*proto*/ + +#if CYTHON_COMPILING_IN_CPYTHON +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_CallUnboundCMethod1(HPyContext *HPY_CONTEXT_CNAME, __Pyx_CachedCFunction *cfunc, PYOBJECT_TYPE self, PYOBJECT_TYPE arg);/*proto*/ +#else +#define __Pyx_CallUnboundCMethod1(HPY_CONTEXT_CNAME, cfunc, self, arg) __Pyx__CallUnboundCMethod1(HPY_CONTEXT_CNAME, cfunc, self, arg) +#endif +#else +static PYOBJECT_TYPE __Pyx__CallUnboundCMethod1(__Pyx_CachedCFunction *cfunc, PYOBJECT_TYPE self, PYOBJECT_TYPE arg);/*proto*/ #if CYTHON_COMPILING_IN_CPYTHON -static CYTHON_INLINE PyObject* __Pyx_CallUnboundCMethod1(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg);/*proto*/ +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_CallUnboundCMethod1(__Pyx_CachedCFunction *cfunc, PYOBJECT_TYPE self, PYOBJECT_TYPE arg);/*proto*/ #else #define __Pyx_CallUnboundCMethod1(cfunc, self, arg) __Pyx__CallUnboundCMethod1(cfunc, self, arg) #endif +#endif /////////////// CallUnboundCMethod1 /////////////// //@requires: UnpackUnboundCMethod //@requires: PyObjectCall #if CYTHON_COMPILING_IN_CPYTHON -static CYTHON_INLINE PyObject* __Pyx_CallUnboundCMethod1(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg) { +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_CallUnboundCMethod1(HPY_CONTEXT_FIRST_ARG_DEF __Pyx_CachedCFunction *cfunc, PYOBJECT_TYPE self, PYOBJECT_TYPE arg) { +#if !CYTHON_USING_HPY if (likely(cfunc->func)) { int flag = cfunc->flag; if (flag == METH_O) { @@ -1880,39 +1907,57 @@ static CYTHON_INLINE PyObject* __Pyx_CallUnboundCMethod1(__Pyx_CachedCFunction* return (*(__Pyx_PyCFunctionFastWithKeywords)(void*)(PyCFunction)cfunc->func)(self, &arg, 1, NULL); } } - return __Pyx__CallUnboundCMethod1(cfunc, self, arg); +#endif + return __Pyx__CallUnboundCMethod1(HPY_CONTEXT_FIRST_ARG_CALL cfunc, self, arg); } #endif -static PyObject* __Pyx__CallUnboundCMethod1(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg){ - PyObject *args, *result = NULL; - if (unlikely(!cfunc->func && !cfunc->method) && unlikely(__Pyx_TryUnpackUnboundCMethod(cfunc) < 0)) return NULL; -#if CYTHON_COMPILING_IN_CPYTHON +static PYOBJECT_TYPE __Pyx__CallUnboundCMethod1(HPY_CONTEXT_FIRST_ARG_DEF __Pyx_CachedCFunction *cfunc, PYOBJECT_TYPE self, PYOBJECT_TYPE arg) { + PYOBJECT_TYPE args; + PYOBJECT_TYPE result = API_NULL_VALUE; +#if CYTHON_USING_HPY + if (unlikely(!(cfunc->func == HPyDef_Kind_Meth) && API_IS_NULL(cfunc->method)) && unlikely(__Pyx_TryUnpackUnboundCMethod(HPY_CONTEXT_FIRST_ARG_CALL cfunc) < 0)) return API_NULL_VALUE; +#else + if (unlikely(!cfunc->func && API_IS_NULL(cfunc->method)) && unlikely(__Pyx_TryUnpackUnboundCMethod(HPY_CONTEXT_FIRST_ARG_CALL cfunc) < 0)) return API_NULL_VALUE; +#endif +#if CYTHON_COMPILING_IN_CPYTHON && !CYTHON_USING_HPY if (cfunc->func && (cfunc->flag & METH_VARARGS)) { - args = PyTuple_New(1); - if (unlikely(!args)) goto bad; + TUPLE_BUILDER_TYPE builder; + TUPLE_CREATE_START(args, builder, 1); + if (unlikely(API_IS_NULL(args))) goto bad; +#if !CYTHON_USING_HPY Py_INCREF(arg); - PyTuple_SET_ITEM(args, 0, arg); +#endif + TUPLE_CREATE_ASSIGN(args, builder, 0, arg); + TUPLE_CREATE_FINALISE(args, builder); +#if !CYTHON_USING_HPY if (cfunc->flag & METH_KEYWORDS) result = (*(PyCFunctionWithKeywords)(void*)(PyCFunction)cfunc->func)(self, args, NULL); else result = (*cfunc->func)(self, args); +#else + result = __Pyx_PyObject_Call_h(cfunc->method, args, API_NULL_VALUE); +#endif } else { - args = PyTuple_New(2); - if (unlikely(!args)) goto bad; + TUPLE_BUILDER_TYPE builder; + TUPLE_CREATE_START(args, builder, 2); + if (unlikely(API_IS_NULL(args))) goto bad; +#if !CYTHON_USING_HPY Py_INCREF(self); - PyTuple_SET_ITEM(args, 0, self); Py_INCREF(arg); - PyTuple_SET_ITEM(args, 1, arg); - result = __Pyx_PyObject_Call(cfunc->method, args, NULL); +#endif + TUPLE_CREATE_ASSIGN(args, builder, 0, self); + TUPLE_CREATE_ASSIGN(args, builder, 1, arg); + TUPLE_CREATE_FINALISE(args, builder); + result = __Pyx_PyObject_Call_h(cfunc->method, args, API_NULL_VALUE); } #else - args = PyTuple_Pack(2, self, arg); - if (unlikely(!args)) goto bad; - result = __Pyx_PyObject_Call(cfunc->method, args, NULL); + args = TUPLE_PACK(2, self, arg); + if (unlikely(API_IS_NULL(args))) goto bad; + result = __Pyx_PyObject_Call_h(cfunc->method, args, API_NULL_VALUE); #endif bad: - Py_XDECREF(args); + PYOBJECT_XCLOSEREF(args); return result; } @@ -1920,73 +1965,102 @@ static PyObject* __Pyx__CallUnboundCMethod1(__Pyx_CachedCFunction* cfunc, PyObje /////////////// CallUnboundCMethod2.proto /////////////// CYTHON_UNUSED -static PyObject* __Pyx__CallUnboundCMethod2(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg1, PyObject* arg2); /*proto*/ +static PYOBJECT_TYPE __Pyx__CallUnboundCMethod2(HPY_CONTEXT_FIRST_ARG_DEF __Pyx_CachedCFunction *cfunc, PYOBJECT_TYPE self, PYOBJECT_TYPE arg1, PYOBJECT_TYPE arg2); #if CYTHON_COMPILING_IN_CPYTHON -static CYTHON_INLINE PyObject *__Pyx_CallUnboundCMethod2(__Pyx_CachedCFunction *cfunc, PyObject *self, PyObject *arg1, PyObject *arg2); /*proto*/ +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_CallUnboundCMethod2(__Pyx_CachedCFunction *cfunc, PYOBJECT_TYPE self, PYOBJECT_TYPE arg1, PYOBJECT_TYPE arg2); /*proto*/ +#else +#if CYTHON_USING_HPY +#define __Pyx_CallUnboundCMethod2(cfunc, self, arg1, arg2) __Pyx__CallUnboundCMethod2(HPY_CONTEXT_CNAME, cfunc, self, arg1, arg2) #else #define __Pyx_CallUnboundCMethod2(cfunc, self, arg1, arg2) __Pyx__CallUnboundCMethod2(cfunc, self, arg1, arg2) #endif +#endif /////////////// CallUnboundCMethod2 /////////////// //@requires: UnpackUnboundCMethod //@requires: PyObjectCall #if CYTHON_COMPILING_IN_CPYTHON -static CYTHON_INLINE PyObject *__Pyx_CallUnboundCMethod2(__Pyx_CachedCFunction *cfunc, PyObject *self, PyObject *arg1, PyObject *arg2) { +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_CallUnboundCMethod2(HPY_CONTEXT_FIRST_ARG_DEF __Pyx_CachedCFunction *cfunc, PYOBJECT_TYPE self, PYOBJECT_TYPE arg1, PYOBJECT_TYPE arg2) { if (likely(cfunc->func)) { - PyObject *args[2] = {arg1, arg2}; + PYOBJECT_TYPE args[2] = {arg1, arg2}; +#if !CYTHON_USING_HPY if (cfunc->flag == METH_FASTCALL) { return (*(__Pyx_PyCFunctionFast)(void*)(PyCFunction)cfunc->func)(self, args, 2); } if (cfunc->flag == (METH_FASTCALL | METH_KEYWORDS)) return (*(__Pyx_PyCFunctionFastWithKeywords)(void*)(PyCFunction)cfunc->func)(self, args, 2, NULL); +#else + return __Pyx_PyObject_Call_h(cfunc->method, args, API_NULL_VALUE); + +#endif } - return __Pyx__CallUnboundCMethod2(cfunc, self, arg1, arg2); + return __Pyx__CallUnboundCMethod2(HPY_CONTEXT_FIRST_ARG_CALL cfunc, self, arg1, arg2); } #endif -static PyObject* __Pyx__CallUnboundCMethod2(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg1, PyObject* arg2){ - PyObject *args, *result = NULL; - if (unlikely(!cfunc->func && !cfunc->method) && unlikely(__Pyx_TryUnpackUnboundCMethod(cfunc) < 0)) return NULL; -#if CYTHON_COMPILING_IN_CPYTHON - if (cfunc->func && (cfunc->flag & METH_VARARGS)) { - args = PyTuple_New(2); - if (unlikely(!args)) goto bad; +static PYOBJECT_TYPE __Pyx__CallUnboundCMethod2(HPY_CONTEXT_FIRST_ARG_DEF __Pyx_CachedCFunction *cfunc, PYOBJECT_TYPE self, PYOBJECT_TYPE arg1, PYOBJECT_TYPE arg2) { + PYOBJECT_TYPE args, CAPI_IS_POINTER result = API_NULL_VALUE; +#if CYTHON_USING_HPY + if (unlikely(!(cfunc->func == HPyDef_Kind_Meth) && API_IS_NULL(cfunc->method)) && unlikely(__Pyx_TryUnpackUnboundCMethod(HPY_CONTEXT_FIRST_ARG_CALL cfunc) < 0)) return API_NULL_VALUE; +#else + if (unlikely(API_IS_NULL(cfunc->func) && API_IS_NULL(cfunc->method)) && unlikely(__Pyx_TryUnpackUnboundCMethod(HPY_CONTEXT_FIRST_ARG_CALL cfunc) < 0)) return API_NULL_VALUE; +#endif +#if CYTHON_COMPILING_IN_CPYTHON && !CYTHON_USING_HPY + if (API_IS_NOT_NULL(cfunc->func) && (cfunc->flag & METH_VARARGS)) { + TUPLE_BUILDER_TYPE builder; + TUPLE_CREATE_START(args, builder, 2); + if (unlikely(API_IS_NULL(args))) goto bad; +#if !CYTHON_USING_HPY Py_INCREF(arg1); - PyTuple_SET_ITEM(args, 0, arg1); Py_INCREF(arg2); - PyTuple_SET_ITEM(args, 1, arg2); +#endif + TUPLE_CREATE_ASSIGN(args, builder, 0, arg1); + TUPLE_CREATE_ASSIGN(args, builder, 1, arg2); + TUPLE_CREATE_FINALISE(args, builder); +#if !CYTHON_USING_HPY if (cfunc->flag & METH_KEYWORDS) result = (*(PyCFunctionWithKeywords)(void*)(PyCFunction)cfunc->func)(self, args, NULL); else result = (*cfunc->func)(self, args); +#else + result = __Pyx_PyObject_Call_h(cfunc->method, args, API_NULL_VALUE); +#endif } else { - args = PyTuple_New(3); - if (unlikely(!args)) goto bad; + TUPLE_BUILDER_TYPE builder; + TUPLE_CREATE_START(args, builder, 3); + if (unlikely(API_IS_NULL(args))) goto bad; +#if !CYTHON_USING_HPY Py_INCREF(self); - PyTuple_SET_ITEM(args, 0, self); Py_INCREF(arg1); - PyTuple_SET_ITEM(args, 1, arg1); Py_INCREF(arg2); - PyTuple_SET_ITEM(args, 2, arg2); - result = __Pyx_PyObject_Call(cfunc->method, args, NULL); +#endif + TUPLE_CREATE_ASSIGN(args, builder, 0, self); + TUPLE_CREATE_ASSIGN(args, builder, 1, arg1); + TUPLE_CREATE_ASSIGN(args, builder, 2, arg2); + TUPLE_CREATE_FINALISE(args, builder); + result = __Pyx_PyObject_Call_h(cfunc->method, args, API_NULL_VALUE); } #else - args = PyTuple_Pack(3, self, arg1, arg2); - if (unlikely(!args)) goto bad; - result = __Pyx_PyObject_Call(cfunc->method, args, NULL); + args = TUPLE_PACK(3, self, arg1, arg2); + if (unlikely(API_IS_NULL(args))) goto bad; + result = __Pyx_PyObject_Call_h(cfunc->method, args, API_NULL_VALUE); #endif bad: - Py_XDECREF(args); + PYOBJECT_XCLOSEREF(args); return result; } /////////////// PyObjectFastCall.proto /////////////// +#if CYTHON_USING_HPY +#define __Pyx_PyObject_FastCall(func, args, nargs) API_CALL_FUNC(func, args, (size_t)(nargs), API_NULL_VALUE) +#else #define __Pyx_PyObject_FastCall(func, args, nargs) __Pyx_PyObject_FastCallDict(func, args, (size_t)(nargs), NULL) static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObject **args, size_t nargs, PyObject *kwargs); /*proto*/ +#endif /////////////// PyObjectFastCall /////////////// //@requires: PyObjectCall @@ -1994,7 +2068,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObj //@requires: PyObjectCallMethO //@substitute: naming -#if PY_VERSION_HEX < 0x03090000 || CYTHON_COMPILING_IN_LIMITED_API +#if !CYTHON_USING_HPY && PY_VERSION_HEX < 0x03090000 || CYTHON_COMPILING_IN_LIMITED_API static PyObject* __Pyx_PyObject_FastCall_fallback(PyObject *func, PyObject **args, size_t nargs, PyObject *kwargs) { PyObject *argstuple; PyObject *result = 0; @@ -2078,6 +2152,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObj return __Pyx_PyObject_FastCall_fallback(func, args, (size_t)nargs, kwargs); #endif } +#endif /////////////// PyObjectVectorCallKwBuilder.proto //////////////// //@requires: PyObjectFastCall @@ -2219,6 +2294,11 @@ static CYTHON_INLINE PyObject* __Pyx_tp_new_kwargs(PyObject* type_obj, PyObject* /////////////// PyObjectCall.proto /////////////// +#if CYTHON_USING_HPY +#define __Pyx_PyObject_Call_h(func, arg, kw) API_CALL_FUNC(func, &arg, TUPLE_GET_SIZE(arg), kw) +#else +#define __Pyx_PyObject_Call_h(func, arg, kw) __Pyx_PyObject_Call(func, arg, kw) +#endif #if CYTHON_COMPILING_IN_CPYTHON static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw); /*proto*/ #else @@ -2227,7 +2307,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg /////////////// PyObjectCall /////////////// -#if CYTHON_COMPILING_IN_CPYTHON +#if CYTHON_COMPILING_IN_CPYTHON && !CYTHON_USING_HPY static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw) { PyObject *result; ternaryfunc call = Py_TYPE(func)->tp_call; @@ -2498,14 +2578,14 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObjec /////////////// PyObjectCallNoArg.proto /////////////// -static CYTHON_INLINE PyObject* __Pyx_PyObject_CallNoArg(PyObject *func); /*proto*/ +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_CallNoArg(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE func); /*proto*/ /////////////// PyObjectCallNoArg /////////////// //@requires: PyObjectFastCall -static CYTHON_INLINE PyObject* __Pyx_PyObject_CallNoArg(PyObject *func) { - PyObject *arg[2] = {NULL, NULL}; - return __Pyx_PyObject_FastCall(func, arg + 1, 0 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET); +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_CallNoArg(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE func) { + PYOBJECT_TYPE arg = API_NULL_VALUE; + return __Pyx_PyObject_FastCall(func, (&arg)+1, 0 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET); } diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index 477b5cbd7d3..db5a9b771d9 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -697,7 +697,7 @@ static CYTHON_INLINE PYOBJECT_TYPE {{TO_PY_FUNCTION}}(HPY_CONTEXT_FIRST_ARG_DEF if (sizeof({{TYPE}}) < sizeof(long)) { return PYOBJECT_INT_FROM_LONG((long) value); } else if (sizeof({{TYPE}}) <= sizeof(unsigned long)) { - return PYOBJECT_INT_FROM_UNSIGNED_LONG((unsigned long) value); + return PYOBJECT_LONG_FROM_UNSIGNED_LONG((unsigned long) value); #ifdef HAVE_LONG_LONG } else if (sizeof({{TYPE}}) <= sizeof(unsigned PY_LONG_LONG)) { return PYOBJECT_LONG_FROM_UNSIGNED_LONGLONG((unsigned PY_LONG_LONG) value); diff --git a/tests/run/hpy_dict.pyx b/tests/run/hpy_dict.pyx new file mode 100644 index 00000000000..e9deecd0106 --- /dev/null +++ b/tests/run/hpy_dict.pyx @@ -0,0 +1,36 @@ +#mode: run + +def create_dict(): + """ + >>> create_dict() + {'a': 123, 2: 5932} + """ + d = {'a': 123, 2: 5932} + return d + +def dict_pop(d, key): + """ + >>> d = {'a': 123, 2: 5932} + >>> dict_pop(d, 2) + 5932 + """ + return d.pop(key) + +def dict_modify(d, pos, value): + """ + >>> d = test = {'a': 123, 2: 5932} + >>> dict_modify(d, 2, True) + {'a': 123, 2: True} + >>> dict_modify(d, 'abc', 'def') + {'a': 123, 2: True, 'abc': 'def'} + """ + d[pos] = value + return d + +def dict_keys(d): + """ + >>> d = {'a': 123, 2: True, 'abc': 'def'} + >>> dict_keys(d) + ['a', 2, 'abc'] + """ + return d.keys() From 08808224e0d39ca4e8c94a465b99211ac01bb6b8 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Tue, 7 Nov 2023 15:54:00 +0100 Subject: [PATCH 210/286] Got hpy_dict test working and added new test cases to it --- Cython/Compiler/ExprNodes.py | 2 +- Cython/Compiler/PyrexTypes.py | 3 +- Cython/Utility/HPyUtils.c | 2 +- tests/run/hpy_dict.pyx | 79 ++++++++++++++++++++++++++++++++++- 4 files changed, 82 insertions(+), 4 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index ded6edb5513..4261eb10a28 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -8040,7 +8040,7 @@ def generate_result_code(self, code): temp_load_attr, code.error_goto_if_null_object(self.result(), self.pos))) if not attr_str.startswith("__pyx_t_"): - code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s)" % temp_load_attr) + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_attr) code.funcstate.release_temp(temp_load_attr) self.generate_gotref(code) elif self.type.is_memoryviewslice: diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index eeef926163e..37ae6221635 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -1456,7 +1456,8 @@ class BuiltinObjectType(PyObjectType): def __init__(self, name, cname, objstruct_cname=None): self.name = name - self.typeptr_cname = "(%s)" % cname + self.cname = cname + self.typeptr_cname = "(CAPI_NEEDS_DEREFERENCE %s)" % cname self.objstruct_cname = objstruct_cname self.is_gc_simple = name in builtin_types_that_cannot_create_refcycles self.builtin_trashcan = name in builtin_types_with_trashcan diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index e6657cd0922..c53ed85cb86 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -134,7 +134,7 @@ //PyObject/HPy Handle Type #define PYOBJECT_GET_ITEM(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) #define PYOBJECT_SET_ITEM(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) - #define PYOBJECT_GET_ATTR(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) + #define PYOBJECT_GET_ATTR(o, attr_name) HPy_GetAttr(HPY_CONTEXT_CNAME, o, attr_name) #define PYOBJECT_SET_ATTR(o, attr_name, attr_val) HPy_SetAttr(HPY_CONTEXT_CNAME, o, attr_name, attr_val) #define PYOBJECT_GET_ATTR_STR(o, attr_name) HPy_GetAttr_s(HPY_CONTEXT_CNAME, o, attr_name) #define PYOBJECT_SET_ATTR_STR(o1, attr_name, o2) HPy_SetAttr_s(HPY_CONTEXT_CNAME, o1, attr_name, o2) diff --git a/tests/run/hpy_dict.pyx b/tests/run/hpy_dict.pyx index e9deecd0106..5fd45208bf2 100644 --- a/tests/run/hpy_dict.pyx +++ b/tests/run/hpy_dict.pyx @@ -31,6 +31,83 @@ def dict_keys(d): """ >>> d = {'a': 123, 2: True, 'abc': 'def'} >>> dict_keys(d) - ['a', 2, 'abc'] + dict_keys(['a', 2, 'abc']) """ return d.keys() + +def dict_copy(d): + """ + >>> d = {'a': 123, 2: True, 'abc': 'def'} + >>> dict_copy(d) + {'a': 123, 2: True, 'abc': 'def'} + """ + c = d.copy() + return c + +def dict_clear(d): + """ + >>> d = {'a': 123, 2: True, 'abc': 'def'} + >>> dict_clear(d) + + """ + return d.clear() + +def dict_get(d, key): + """ + >>> d = {'a': 123, 2: True, 'abc': 'def'} + >>> dict_get(d, 2) + True + """ + return d.get(2) + +def dict_items(d): + """ + >>> d = {'a': 123, 2: True, 'abc': 'def'} + >>> dict_items(d) + dict_items([('a', 123), (2, True), ('abc', 'def')]) + """ + return d.items() + +def dict_fromkeys(keys, val): + """ + >>> ks = (1, 2, 3) + >>> dict_fromkeys(ks, 0) + {1: 0, 2: 0, 3: 0} + """ + return dict.fromkeys(keys, val) + +def dict_popitem(d): + """ + >>> d = {'a': 123, 2: 5932} + >>> dict_popitem(d) + (2, 5932) + """ + return d.popitem() + +def dict_setdefault(d, key, value): + """ + >>> d = {'a': 123, 2: 5932} + >>> dict_setdefault(d, 2, 1234) + 5932 + >>> dict_setdefault(d, 'cs', 123) + 123 + """ + return d.setdefault(key, value) + +def dict_update(d1, d2): + """ + >>> d1 = {'a': 123, 2: 5932} + >>> d2 = {'hpy': True} + >>> dict_update(d1, d2) + {'a': 123, 2: 5932, 'hpy': True} + """ + d1.update(d2) + return d1 + +def dict_values(d): + """ + >>> d = {'a': 123, 2: True, 'abc': 'def'} + >>> dict_values(d) + dict_values([123, True, 'def']) + """ + return d.values() From b2ca2dd63835f5a13f80602562e3bfe128c0f485 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 8 Nov 2023 16:35:33 +0100 Subject: [PATCH 211/286] Removed some unnecessary added code --- Cython/Compiler/ExprNodes.py | 41 +++++++++++++++++---------------- Cython/Compiler/Nodes.py | 12 ---------- Cython/Utility/ObjectHandling.c | 2 +- 3 files changed, 22 insertions(+), 33 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 4261eb10a28..e01ab9759de 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -1214,7 +1214,6 @@ class PyConstNode(AtomicExprNode): is_literal = 1 type = py_object_type nogil_check = None - is_global = True def is_simple(self): return 1 @@ -1271,8 +1270,6 @@ class ConstNode(AtomicExprNode): is_literal = 1 nogil_check = None - is_global = True - def is_simple(self): return 1 @@ -1608,7 +1605,6 @@ class BytesNode(ConstNode): is_string_literal = True # start off as Python 'bytes' to support len() in O(1) type = bytes_type - is_global = True def calculate_constant_result(self): self.constant_result = self.value @@ -1699,7 +1695,6 @@ class UnicodeNode(ConstNode): is_string_literal = True bytes_value = None type = unicode_type - is_global = True def calculate_constant_result(self): self.constant_result = self.value @@ -4686,10 +4681,10 @@ def generate_setitem_code(self, value, code): self.extra_index_params(code)), self.pos)) - if hasattr(self.index, "is_global") and self.index.is_global: + if index_code in code.globalstate.const_cname_array: code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_index) code.funcstate.release_temp(temp_load_index) - if hasattr(value, "is_global") and value.is_global: + if value_code in code.globalstate.const_cname_array: code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_value) code.funcstate.release_temp(temp_load_value) @@ -6472,14 +6467,17 @@ def c_call_code(self, code=False): for actual_arg in self.args[len(formal_args):]: arg_list_code.append(actual_arg.move_result_rhs()) + global_args = [] + if len(arg_list_code) > 0: arg_string = "HPY_CONTEXT_FIRST_ARG_CALL " - if self.function.result() == 'DICT_COPY': + if self.function.result() in ['DICT_COPY', '__Pyx_PyList_PopIndex', '__Pyx_PyObject_PopIndex']: arg_string = "" for arg in arg_list_code: if code: if arg in code.globalstate.const_cname_array: - arg_string = arg_string + "PYOBJECT_GLOBAL_LOAD(" + arg + ")" #temp fix for globals and non-globals being handled by the same code + arg_string = arg_string + "PYOBJECT_GLOBAL_LOAD(%s)" % arg + global_args.append(arg) else: arg_string = arg_string + "%s" % arg else: @@ -6490,7 +6488,10 @@ def c_call_code(self, code=False): arg_string = "HPY_CONTEXT_ONLY_ARG_CALL" result = "%s(%s)" % (self.function.result(), arg_string) - return result + if code: + return result, global_args + else: + return result def is_c_result_required(self): func_type = self.function_type() @@ -6629,7 +6630,7 @@ def generate_result_code(self, code): else: exc_checks.append("PyErr_Occurred()") if self.is_temp or exc_checks: - rhs = self.c_call_code(code=code) + rhs, globals_to_close = self.c_call_code(code=code) if self.result(): lhs = "%s = " % self.result() if self.is_temp and self.type.is_pyobject: @@ -6649,6 +6650,8 @@ def generate_result_code(self, code): else: goto_error = "" code.putln("%s%s; %s" % (lhs, rhs, goto_error)) + for arg in globals_to_close: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % arg) if self.type.is_pyobject and self.result(): self.generate_gotref(code) if self.has_optional_args: @@ -8028,7 +8031,7 @@ def generate_result_code(self, code): lookup_func_name = '__Pyx_PyObject_GetAttrStr' temp_load_attr = code.funcstate.allocate_temp(py_object_type, manage_ref=False) attr_str = code.intern_identifier(self.attribute) - if attr_str.startswith("__pyx_int_") or attr_str.startswith("__pyx_k_") or attr_str.startswith("__pyx_n_s_"): + if attr_str in code.globalstate.const_cname_array: code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_attr, attr_str)) else: code.putln("%s = %s;" % (temp_load_attr, attr_str)) @@ -8039,7 +8042,7 @@ def generate_result_code(self, code): self.obj.py_result(), temp_load_attr, code.error_goto_if_null_object(self.result(), self.pos))) - if not attr_str.startswith("__pyx_t_"): + if attr_str in code.globalstate.const_cname_array: code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_attr) code.funcstate.release_temp(temp_load_attr) self.generate_gotref(code) @@ -8427,7 +8430,7 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): arg.generate_giveref(code) code.putln("#endif") if self.type is tuple_type: - code.putln("%s(%s, %s, %s, %s);" % ( + code.putln("%s(%s, %s, %s, %s);" % ( #I had to remove the error checking condition, as HPyTuple/ListBuilder_Set doesn't have a return value set_item_func, target, tmp_builder, @@ -9647,15 +9650,13 @@ def generate_evaluation_code(self, code): code.putln("} else {") temp_load_key = code.funcstate.allocate_temp(py_object_type, manage_ref=False) - code.putln("//%s" % item.key) - code.putln("//%s" % item.key.type) - if hasattr(item.key, "is_global") and item.key.is_global: + if item.key.py_result() in code.globalstate.const_cname_array: code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_key, item.key.py_result())) else: code.putln("%s = %s;" % (temp_load_key, item.key.py_result())) temp_load_value = code.funcstate.allocate_temp(py_object_type, manage_ref=False) - if hasattr(item.value, "is_global") and item.value.is_global: + if item.value.py_result() in code.globalstate.const_cname_array: code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_value, item.value.py_result())) else: code.putln("%s = %s;" % (temp_load_value, item.value.py_result())) @@ -9664,11 +9665,11 @@ def generate_evaluation_code(self, code): temp_load_key, temp_load_value)) - if hasattr(item.key, "is_global") and item.key.is_global: + if item.key.py_result() in code.globalstate.const_cname_array: code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_key) code.funcstate.release_temp(temp_load_key) - if hasattr(item.value, "is_global") and item.value.is_global: + if item.value.py_result() in code.globalstate.const_cname_array: code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_value) code.funcstate.release_temp(temp_load_value) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index dffc1ec971d..f07581c1df9 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -2449,23 +2449,12 @@ def generate_arg_type_test(self, arg, code): code.globalstate.use_utility_code( UtilityCode.load_cached("ArgTypeTest", "FunctionArguments.c")) typeptr_cname = arg.type.typeptr_cname - hpy_typeptr_cname = typeptr_cname.replace("&", "") if typeptr_cname.startswith("(&API") else "HPY_LEGACY_OBJECT_FROM((PyObject *)%s)" %typeptr_cname arg_code = "((PyObject *)%s)" % arg.entry.cname exact = 0 if arg.type.is_builtin_type and arg.type.require_exact: # 2 is used to indicate that the type is from the annotation # and provide a little extra info on failure. exact = 2 if arg.type_from_annotation else 1 - code.putln("#if CYTHON_USING_HPY") - code.putln( - 'if (unlikely(!__Pyx_ArgTypeTest(%s, %s, %d, %s, %s))) %s' % ( - arg.entry.cname, - hpy_typeptr_cname, - arg.accept_none, - arg.name_cstring, - exact, - code.error_goto(arg.pos))) - code.putln("#else") code.putln( 'if (unlikely(!__Pyx_ArgTypeTest(%s, %s, %d, %s, %s))) %s' % ( arg_code, @@ -2474,7 +2463,6 @@ def generate_arg_type_test(self, arg, code): arg.name_cstring, exact, code.error_goto(arg.pos))) - code.putln("#endif") else: error(arg.pos, "Cannot test type of extern C class without type object name specification") diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 9b9b051be88..f5c1b8985e3 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -2295,7 +2295,7 @@ static CYTHON_INLINE PyObject* __Pyx_tp_new_kwargs(PyObject* type_obj, PyObject* /////////////// PyObjectCall.proto /////////////// #if CYTHON_USING_HPY -#define __Pyx_PyObject_Call_h(func, arg, kw) API_CALL_FUNC(func, &arg, TUPLE_GET_SIZE(arg), kw) +#define __Pyx_PyObject_Call_h(func, arg, kw) API_CALL_FUNC(func, &arg, TUPLE_GET_SIZE(arg), kw) // Will be removed when no longer needed #else #define __Pyx_PyObject_Call_h(func, arg, kw) __Pyx_PyObject_Call(func, arg, kw) #endif From b8bc2e9f2269be1b80ae53f729bc87eda173732b Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 9 Nov 2023 14:13:47 +0100 Subject: [PATCH 212/286] Removed unnecessary changes and made manually compiled tests run --- Cython/Compiler/ExprNodes.py | 14 +++++--- Cython/Compiler/Nodes.py | 2 +- Cython/Utility/HPyUtils.c | 10 +++--- Cython/Utility/ModuleSetupCode.c | 4 ++- Cython/Utility/ObjectHandling.c | 57 ++++++++++++++++++-------------- Cython/Utility/Optimize.c | 33 ++++++++++++++---- 6 files changed, 75 insertions(+), 45 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index e01ab9759de..29c913dc6e5 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -6476,8 +6476,10 @@ def c_call_code(self, code=False): for arg in arg_list_code: if code: if arg in code.globalstate.const_cname_array: - arg_string = arg_string + "PYOBJECT_GLOBAL_LOAD(%s)" % arg - global_args.append(arg) + temp = code.funcstate.allocate_temp(py_object_type, False) + arg_string = arg_string + "%s" % temp + global_args.append([temp, arg]) + code.funcstate.release_temp(temp) else: arg_string = arg_string + "%s" % arg else: @@ -6630,7 +6632,7 @@ def generate_result_code(self, code): else: exc_checks.append("PyErr_Occurred()") if self.is_temp or exc_checks: - rhs, globals_to_close = self.c_call_code(code=code) + rhs, globals_to_load = self.c_call_code(code=code) if self.result(): lhs = "%s = " % self.result() if self.is_temp and self.type.is_pyobject: @@ -6649,9 +6651,11 @@ def generate_result_code(self, code): goto_error = code.error_goto_if(" && ".join(exc_checks), self.pos) else: goto_error = "" + for arg in globals_to_load: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (arg[0], arg[1])) code.putln("%s%s; %s" % (lhs, rhs, goto_error)) - for arg in globals_to_close: - code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % arg) + for arg in globals_to_load: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % arg[0]) if self.type.is_pyobject and self.result(): self.generate_gotref(code) if self.has_optional_args: diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index f07581c1df9..784ca38cde7 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -4081,7 +4081,7 @@ def generate_tuple_and_keyword_parsing_code(self, args, code, decl_code): loaded_args_arr = [] for arg in non_posonly_args: arg_str = code.intern_identifier(arg.entry.name) - if not arg_str.startswith("__pyx_t_"): + if arg_str in code.globalstate.const_cname_array: temp_load_arg = code.funcstate.allocate_temp(py_object_type, manage_ref=False) loaded_args_arr.append(temp_load_arg) code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_arg, arg_str)) diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index c53ed85cb86..80aa77759d9 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -21,10 +21,8 @@ #define PYOBJECT_GLOBAL_STORE(global, h) HPyGlobal_Store(HPY_CONTEXT_CNAME, &global, h) #define PYOBJECT_GLOBAL_LOAD(global) HPyGlobal_Load(HPY_CONTEXT_CNAME, global) #define CAPI_IS_POINTER - #define CAPI_IS_POINTER_IF_TYPEOBJECT #define CAPI_NEEDS_DEREFERENCE - #define CAST_IF_CAPI(cast_type) - + //Create New and Close References #define PYOBJECT_NEWREF(h) HPy_Dup(HPY_CONTEXT_CNAME, h) #define PYOBJECT_XNEWREF(h) HPy_Dup(HPY_CONTEXT_CNAME, h) @@ -86,6 +84,7 @@ #define PYOBJECT_LONG_FROM_LONGLONG(i) HPyLong_FromLongLong(HPY_CONTEXT_CNAME, i) #define PYOBJECT_LONG_FROM_UNSIGNED_LONG(i) HPyLong_FromUnsignedLong(HPY_CONTEXT_CNAME, i) #define PYOBJECT_LONG_FROM_UNSIGNED_LONGLONG(i) HPyLong_FromUnsignedLongLong(HPY_CONTEXT_CNAME, i) + #define PYOBJECT_LONG_FROM_SSIZE_T(i) HPyLong_FromSsize_t(HPY_CONTEXT_CNAME, i) //Long Type - To #define PYOBJECT_LONG_AS_SSIZE(l) HPyLong_AsSsize_t(HPY_CONTEXT_CNAME, l) @@ -180,10 +179,8 @@ #define PYOBJECT_GLOBAL_STORE(global, h) global = h #define PYOBJECT_GLOBAL_LOAD(global) global #define CAPI_IS_POINTER * //Some types are sometimes pointers and sometimes not (i.e. PyModuleDef) where the type is always the same in HPy - #define CAPI_IS_POINTER_IF_TYPEOBJECT * #define CAPI_NEEDS_DEREFERENCE & - #define CAST_IF_CAPI(cast_type) (cast_type) - + //Create New and Close References #define PYOBJECT_NEWREF(h) Py_NewRef(h) #define PYOBJECT_XNEWREF(h) Py_XNewRef(h) @@ -245,6 +242,7 @@ #define PYOBJECT_LONG_FROM_LONGLONG(i) PyLong_FromLongLong(i) #define PYOBJECT_LONG_FROM_UNSIGNED_LONG(i) PyLong_FromUnsignedLong(i) #define PYOBJECT_LONG_FROM_UNSIGNED_LONGLONG(i) PyLong_FromUnsignedLongLong(i) + #define PYOBJECT_LONG_FROM_SSIZE_T(i) PyLong_FromSsize_t(i) //Long Type - To #define PYOBJECT_LONG_AS_SSIZE(l) PyLong_AsSsize_t(l) diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index df2dfb40944..a1247910841 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -441,6 +441,8 @@ #define CYTHON_USE_PYLONG_INTERNALS 0 #undef CYTHON_USE_UNICODE_INTERNALS #define CYTHON_USE_UNICODE_INTERNALS 0 + #undef CYTHON_USE_PYLIST_INTERNALS + #define CYTHON_USE_PYLIST_INTERNALS 0 #undef CYTHON_USE_DICT_VERSIONS #define CYTHON_USE_DICT_VERSIONS 0 #undef CYTHON_FAST_THREAD_STATE @@ -1301,7 +1303,7 @@ static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStrWithError(PyObject *dict, #define PyInt_FromUnicode PyLong_FromUnicode #define PyInt_FromLong PyLong_FromLong #define PyInt_FromSize_t PyLong_FromSize_t -#define PyInt_FromSsize_t PyLong_FromSsize_t +#define PyInt_FromSsize_t PYOBJECT_LONG_FROM_SSIZE_T #define PyInt_AsLong PyLong_AsLong #define PyInt_AS_LONG PyLong_AS_LONG #define PyInt_AsSsize_t PyLong_AsSsize_t diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index f5c1b8985e3..4cf303c4977 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1587,11 +1587,7 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStr(HPY_CONTEXT_FIRST_A return tp->tp_getattro(obj, attr_name); #endif PYOBJECT_TYPE item = DICT_GET_ITEM(obj, attr_name); -#if CYTHON_USING_HPY - if (HPy_Is(item, HPy_NULL)) { - HPyErr_Clear(HPY_CONTEXT_CNAME); - } -#endif + return item; } #endif @@ -1621,13 +1617,13 @@ static CYTHON_INLINE int __Pyx_PyObject_SetAttrStr(PyObject* obj, PyObject* attr /////////////// PyObjectGetMethod.proto /////////////// -static int __Pyx_PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method);/*proto*/ +static int __Pyx_PyObject_GetMethod(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, PYOBJECT_TYPE name, PYOBJECT_TYPE *method);/*proto*/ /////////////// PyObjectGetMethod /////////////// //@requires: PyObjectGetAttrStr -static int __Pyx_PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method) { - PyObject *attr; +static int __Pyx_PyObject_GetMethod(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, PYOBJECT_TYPE name, PYOBJECT_TYPE *method) { + PYOBJECT_TYPE attr; #if CYTHON_UNPACK_METHODS && CYTHON_COMPILING_IN_CPYTHON && CYTHON_USE_PYTYPE_LOOKUP __Pyx_TypeName type_name; // Copied from _PyObject_GetMethod() in CPython 3.7 @@ -1709,8 +1705,12 @@ static int __Pyx_PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **me return 0; // Generic fallback implementation using normal attribute lookup. +#else +#if CYTHON_USING_HPY + attr = HPy_GetAttr(HPY_CONTEXT_CNAME, obj, name); #else attr = __Pyx_PyObject_GetAttrStr(obj, name); +#endif goto try_unpack; #endif @@ -2246,7 +2246,7 @@ static PyObject* __Pyx_PyObject_CallMethod0(PyObject* obj, PyObject* method_name /////////////// PyObjectCallMethod1.proto /////////////// -static PyObject* __Pyx_PyObject_CallMethod1(PyObject* obj, PyObject* method_name, PyObject* arg); /*proto*/ +static PYOBJECT_TYPE __Pyx_PyObject_CallMethod1(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, PYOBJECT_TYPE method_name, PYOBJECT_TYPE arg); /*proto*/ /////////////// PyObjectCallMethod1 /////////////// //@requires: PyObjectGetMethod @@ -2254,16 +2254,18 @@ static PyObject* __Pyx_PyObject_CallMethod1(PyObject* obj, PyObject* method_name //@requires: PyObjectCall2Args #if !(CYTHON_VECTORCALL && __PYX_LIMITED_VERSION_HEX >= 0x030C00A2) -static PyObject* __Pyx__PyObject_CallMethod1(PyObject* method, PyObject* arg) { +static PYOBJECT_TYPE __Pyx__PyObject_CallMethod1(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE method, PYOBJECT_TYPE arg) { // Separate function to avoid excessive inlining. - PyObject *result = __Pyx_PyObject_CallOneArg(method, arg); + PYOBJECT_TYPE result = __Pyx_PyObject_CallOneArg(HPY_CONTEXT_FIRST_ARG_CALL method, arg); +#if !CYTHON_USING_HPY Py_DECREF(method); +#endif return result; } #endif -static PyObject* __Pyx_PyObject_CallMethod1(PyObject* obj, PyObject* method_name, PyObject* arg) { -#if CYTHON_VECTORCALL && __PYX_LIMITED_VERSION_HEX >= 0x030C00A2 +static PYOBJECT_TYPE __Pyx_PyObject_CallMethod1(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, PYOBJECT_TYPE method_name, PYOBJECT_TYPE arg) { +#if CYTHON_VECTORCALL && __PYX_LIMITED_VERSION_HEX >= 0x030C00A2 && !CYTHON_USING_HPY PyObject *args[2] = {obj, arg}; // avoid unused functions (void) __Pyx_PyObject_GetMethod; @@ -2271,15 +2273,16 @@ static PyObject* __Pyx_PyObject_CallMethod1(PyObject* obj, PyObject* method_name (void) __Pyx_PyObject_Call2Args; return PyObject_VectorcallMethod(method_name, args, 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); #else - PyObject *method = NULL, *result; - int is_method = __Pyx_PyObject_GetMethod(obj, method_name, &method); + PYOBJECT_TYPE method = API_NULL_VALUE; + PYOBJECT_TYPE result; + int is_method = __Pyx_PyObject_GetMethod(HPY_CONTEXT_FIRST_ARG_CALL obj, method_name, &method); if (likely(is_method)) { - result = __Pyx_PyObject_Call2Args(method, obj, arg); - Py_DECREF(method); + result = __Pyx_PyObject_Call2Args(HPY_CONTEXT_FIRST_ARG_CALL method, obj, arg); + PYOBJECT_CLOSEREF(method); return result; } - if (unlikely(!method)) return NULL; - return __Pyx__PyObject_CallMethod1(method, arg); + if (unlikely(API_IS_NULL(method))) return API_NULL_VALUE; + return __Pyx__PyObject_CallMethod1(HPY_CONTEXT_FIRST_ARG_CALL method, arg); #endif } @@ -2552,27 +2555,31 @@ static PyObject *__Pyx_PyFunction_FastCallDict(PyObject *func, PyObject **args, /////////////// PyObjectCall2Args.proto /////////////// -static CYTHON_INLINE PyObject* __Pyx_PyObject_Call2Args(PyObject* function, PyObject* arg1, PyObject* arg2); /*proto*/ +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_Call2Args(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE function, PYOBJECT_TYPE arg1, PYOBJECT_TYPE arg2); /*proto*/ /////////////// PyObjectCall2Args /////////////// //@requires: PyObjectFastCall -static CYTHON_INLINE PyObject* __Pyx_PyObject_Call2Args(PyObject* function, PyObject* arg1, PyObject* arg2) { - PyObject *args[3] = {NULL, arg1, arg2}; +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_Call2Args(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE function, PYOBJECT_TYPE arg1, PYOBJECT_TYPE arg2) { + PYOBJECT_TYPE args[3] = {API_NULL_VALUE, arg1, arg2}; return __Pyx_PyObject_FastCall(function, args+1, 2 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET); } /////////////// PyObjectCallOneArg.proto /////////////// -static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObject *arg); /*proto*/ +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_CallOneArg(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE func, PYOBJECT_TYPE arg); /*proto*/ /////////////// PyObjectCallOneArg /////////////// //@requires: PyObjectFastCall -static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObject *arg) { - PyObject *args[2] = {NULL, arg}; +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_CallOneArg(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE func, PYOBJECT_TYPE arg) { + PYOBJECT_TYPE args[2] = {API_NULL_VALUE, arg}; +#if CYTHON_USING_HPY + return __Pyx_PyObject_FastCall(func, args+1, 1); +#else return __Pyx_PyObject_FastCall(func, args+1, 1 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET); +#endif } diff --git a/Cython/Utility/Optimize.c b/Cython/Utility/Optimize.c index 0ffb4f0bfde..d3c7604dac3 100644 --- a/Cython/Utility/Optimize.c +++ b/Cython/Utility/Optimize.c @@ -128,6 +128,19 @@ static CYTHON_INLINE PyObject* __Pyx_PyList_Pop(PyObject* L) { /////////////// pop_index.proto /////////////// +#if CYTHON_USING_HPY + +static PYOBJECT_TYPE __Pyx__PyObject_PopNewIndex(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE L, PYOBJECT_TYPE py_ix); /*proto*/ +static PYOBJECT_TYPE __Pyx__PyObject_PopIndex(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE L, PYOBJECT_TYPE py_ix); /*proto*/ + +#define __Pyx_PyList_PopIndex(L, py_ix, ix, is_signed, type, to_py_func) \ + __Pyx_PyObject_PopIndex(L, py_ix, ix, is_signed, type, to_py_func) + +#define __Pyx_PyObject_PopIndex(L, py_ix, ix, is_signed, type, to_py_func) ( \ + (unlikely(API_IS_EQUAL(py_ix, API_NONE_VALUE))) ? __Pyx__PyObject_PopNewIndex(HPY_CONTEXT_CNAME, L, to_py_func(ix)) : \ + __Pyx__PyObject_PopIndex(HPY_CONTEXT_CNAME, L, py_ix)) +#else + static PyObject* __Pyx__PyObject_PopNewIndex(PyObject* L, PyObject* py_ix); /*proto*/ static PyObject* __Pyx__PyObject_PopIndex(PyObject* L, PyObject* py_ix); /*proto*/ @@ -155,20 +168,26 @@ static PyObject* __Pyx__PyList_PopIndex(PyObject* L, PyObject* py_ix, Py_ssize_t (unlikely((py_ix) == Py_None)) ? __Pyx__PyObject_PopNewIndex(L, to_py_func(ix)) : \ __Pyx__PyObject_PopIndex(L, py_ix)) #endif +#endif /////////////// pop_index /////////////// //@requires: ObjectHandling.c::PyObjectCallMethod1 -static PyObject* __Pyx__PyObject_PopNewIndex(PyObject* L, PyObject* py_ix) { - PyObject *r; - if (unlikely(!py_ix)) return NULL; - r = __Pyx__PyObject_PopIndex(L, py_ix); - Py_DECREF(py_ix); +static PYOBJECT_TYPE __Pyx__PyObject_PopNewIndex(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE L, PYOBJECT_TYPE py_ix) { + PYOBJECT_TYPE r; + if (unlikely(API_IS_NULL(py_ix))) return API_NULL_VALUE; + r = __Pyx__PyObject_PopIndex(HPY_CONTEXT_FIRST_ARG_CALL L, py_ix); +#if !CYTHON_USING_HPY + Py_DECREF(py_ix); //Can't close arg in HPy +#endif return r; } -static PyObject* __Pyx__PyObject_PopIndex(PyObject* L, PyObject* py_ix) { - return __Pyx_PyObject_CallMethod1(L, PYIDENT("pop"), py_ix); +static PYOBJECT_TYPE __Pyx__PyObject_PopIndex(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE L, PYOBJECT_TYPE py_ix) { + PYOBJECT_TYPE temp_popindex = PYOBJECT_GLOBAL_LOAD(PYIDENT("pop")); + PYOBJECT_TYPE retval = __Pyx_PyObject_CallMethod1(HPY_CONTEXT_FIRST_ARG_CALL L, temp_popindex, py_ix); + PYOBJECT_GLOBAL_CLOSEREF(temp_popindex); + return retval; } #if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE From 8cac3e8941ada163c60010a99c189ec5959eb121 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 20 Nov 2023 16:08:08 +0100 Subject: [PATCH 213/286] Ported __Pyx_TypeCheck --- Cython/Utility/FunctionArguments.c | 2 +- Cython/Utility/HPyUtils.c | 2 ++ Cython/Utility/ModuleSetupCode.c | 8 ++++---- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Cython/Utility/FunctionArguments.c b/Cython/Utility/FunctionArguments.c index 40bfc8a3f32..6ac82c2ea06 100644 --- a/Cython/Utility/FunctionArguments.c +++ b/Cython/Utility/FunctionArguments.c @@ -23,7 +23,7 @@ static int __Pyx__ArgTypeTest(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, PYTYP return 0; } else if (!exact) { - if (likely(__Pyx_TypeCheck(HPY_LEGACY_OBJECT_AS(obj), HPY_LEGACY_OBJECT_AS(type)))) return 1; + if (likely(__Pyx_TypeCheck(obj, type))) return 1; } else if (exact == 2) { // type from annotation if (__Pyx_TypeCheck(obj, type)) { diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 80aa77759d9..a7b711349a0 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -71,6 +71,7 @@ #define DICT_CHECK(o) HPyDict_Check(HPY_CONTEXT_CNAME, o) #define DICT_CHECK_EXACT(o) HPyDict_Check(HPY_CONTEXT_CNAME, o) #define TUPLE_CHECK(o) HPyTuple_Check(HPY_CONTEXT_CNAME, o) + #define PYOBJECT_TYPE_CHECK(o, t) HPy_TypeCheck(HPY_CONTEXT_CNAME, o, t) //Integer Type - From #define PYOBJECT_INT_FROM_LONG(i) HPyLong_FromLong(HPY_CONTEXT_CNAME, i) @@ -229,6 +230,7 @@ #define DICT_CHECK(o) PyDict_Check(o) #define DICT_CHECK_EXACT(o) PyDict_CheckExact(o) #define TUPLE_CHECK(o) PyTuple_Check(o) + #define PYOBJECT_TYPE_CHECK(o, t) PyObject_TypeCheck(o, t) //Integer Type - From #define PYOBJECT_INT_FROM_LONG(i) PyInt_FromLong(i) diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index a1247910841..82fb3653da0 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -1369,7 +1369,7 @@ static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStrWithError(PyObject *dict, /////////////// FastTypeChecks.proto /////////////// -#if CYTHON_COMPILING_IN_CPYTHON +#if CYTHON_COMPILING_IN_CPYTHON && !CYTHON_USING_HPY #define __Pyx_TypeCheck(obj, type) __Pyx_IsSubtype(Py_TYPE(obj), (PyTypeObject *)type) #define __Pyx_TypeCheck2(obj, type1, type2) __Pyx_IsAnySubtype2(Py_TYPE(obj), (PyTypeObject *)type1, (PyTypeObject *)type2) static CYTHON_INLINE int __Pyx_IsSubtype(PyTypeObject *a, PyTypeObject *b);/*proto*/ @@ -1377,8 +1377,8 @@ static CYTHON_INLINE int __Pyx_IsAnySubtype2(PyTypeObject *cls, PyTypeObject *a, static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches(PyObject *err, PyObject *type);/*proto*/ static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches2(PyObject *err, PyObject *type1, PyObject *type2);/*proto*/ #else -#define __Pyx_TypeCheck(obj, type) PyObject_TypeCheck(obj, (PyTypeObject *)type) -#define __Pyx_TypeCheck2(obj, type1, type2) (PyObject_TypeCheck(obj, (PyTypeObject *)type1) || PyObject_TypeCheck(obj, (PyTypeObject *)type2)) +#define __Pyx_TypeCheck(obj, type) PYOBJECT_TYPE_CHECK(obj, CAST_IF_CAPI(PyTypeObject *)type) +#define __Pyx_TypeCheck2(obj, type1, type2) (PYOBJECT_TYPE_CHECK(obj, CAST_IF_CAPI(PyTypeObject *)type1) || PYOBJECT_TYPE_CHECK(obj, CAST_IF_CAPI(PyTypeObject *)type2)) #define __Pyx_PyErr_GivenExceptionMatches(err, type) PyErr_GivenExceptionMatches(err, type) #define __Pyx_PyErr_GivenExceptionMatches2(err, type1, type2) (PyErr_GivenExceptionMatches(err, type1) || PyErr_GivenExceptionMatches(err, type2)) #endif @@ -1390,7 +1390,7 @@ static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches2(PyObject *err, PyObj //@requires: Exceptions.c::PyThreadStateGet //@requires: Exceptions.c::PyErrFetchRestore -#if CYTHON_COMPILING_IN_CPYTHON +#if CYTHON_COMPILING_IN_CPYTHON && !CYTHON_USING_HPY static int __Pyx_InBases(PyTypeObject *a, PyTypeObject *b) { while (a) { a = __Pyx_PyType_GetSlot(a, tp_base, PyTypeObject*); From 4e4e148ac87c2cde116ba7746cac73ec0302f57c Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 20 Nov 2023 16:14:40 +0100 Subject: [PATCH 214/286] Removed changes to Builtins.c --- Cython/Utility/Builtins.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cython/Utility/Builtins.c b/Cython/Utility/Builtins.c index 7513121b22c..7f613a788be 100644 --- a/Cython/Utility/Builtins.c +++ b/Cython/Utility/Builtins.c @@ -395,7 +395,7 @@ static long __Pyx__PyObject_Ord(PyObject* c) { //////////////////// py_dict_keys.proto //////////////////// -static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyDict_Keys(PYOBJECT_TYPE d); /*proto*/ +static CYTHON_INLINE PyObject* __Pyx_PyDict_Keys(PyObject* d); /*proto*/ //////////////////// py_dict_keys //////////////////// @@ -405,7 +405,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_Keys(PyObject* d) { //////////////////// py_dict_values.proto //////////////////// -static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyDict_Values(PYOBJECT_TYPE d); /*proto*/ +static CYTHON_INLINE PyObject* __Pyx_PyDict_Values(PyObject* d); /*proto*/ //////////////////// py_dict_values //////////////////// @@ -415,7 +415,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_Values(PyObject* d) { //////////////////// py_dict_items.proto //////////////////// -static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyDict_Items(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE d); /*proto*/ +static CYTHON_INLINE PyObject* __Pyx_PyDict_Items(PyObject* d); /*proto*/ //////////////////// py_dict_items //////////////////// @@ -425,7 +425,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_Items(PyObject* d) { //////////////////// py_dict_iterkeys.proto //////////////////// -static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyDict_IterKeys(PYOBJECT_TYPE d); /*proto*/ +static CYTHON_INLINE PyObject* __Pyx_PyDict_IterKeys(PyObject* d); /*proto*/ //////////////////// py_dict_iterkeys //////////////////// @@ -435,7 +435,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_IterKeys(PyObject* d) { //////////////////// py_dict_itervalues.proto //////////////////// -static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyDict_IterValues(PYOBJECT_TYPE d); /*proto*/ +static CYTHON_INLINE PyObject* __Pyx_PyDict_IterValues(PyObject* d); /*proto*/ //////////////////// py_dict_itervalues //////////////////// @@ -445,7 +445,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_IterValues(PyObject* d) { //////////////////// py_dict_iteritems.proto //////////////////// -static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyDict_IterItems(PYOBJECT_TYPE d); /*proto*/ +static CYTHON_INLINE PyObject* __Pyx_PyDict_IterItems(PyObject* d); /*proto*/ //////////////////// py_dict_iteritems //////////////////// @@ -455,7 +455,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_IterItems(PyObject* d) { //////////////////// py_dict_viewkeys.proto //////////////////// -static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyDict_ViewKeys(PYOBJECT_TYPE d); /*proto*/ +static CYTHON_INLINE PyObject* __Pyx_PyDict_ViewKeys(PyObject* d); /*proto*/ //////////////////// py_dict_viewkeys //////////////////// @@ -465,7 +465,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_ViewKeys(PyObject* d) { //////////////////// py_dict_viewvalues.proto //////////////////// -static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyDict_ViewValues(PYOBJECT_TYPE d); /*proto*/ +static CYTHON_INLINE PyObject* __Pyx_PyDict_ViewValues(PyObject* d); /*proto*/ //////////////////// py_dict_viewvalues //////////////////// @@ -475,7 +475,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_ViewValues(PyObject* d) { //////////////////// py_dict_viewitems.proto //////////////////// -static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyDict_ViewItems(PYOBJECT_TYPE d); /*proto*/ +static CYTHON_INLINE PyObject* __Pyx_PyDict_ViewItems(PyObject* d); /*proto*/ //////////////////// py_dict_viewitems //////////////////// @@ -620,4 +620,4 @@ static {{out_type}} __Pyx_PyMemoryView_Get_{{name}}(PyObject *obj) { Py_XDECREF(attr); return -1; } -#endif +#endif \ No newline at end of file From 499d22b48003e856eb809a430bef43a85189cca6 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 20 Nov 2023 16:16:16 +0100 Subject: [PATCH 215/286] Added DICT_CONTAINS macro --- Cython/Compiler/ExprNodes.py | 4 ++-- Cython/Utility/FunctionArguments.c | 2 +- Cython/Utility/HPyUtils.c | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 29c913dc6e5..a8cb221459d 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -7493,7 +7493,7 @@ def generate_evaluation_code(self, code): for arg in item.key_value_pairs: arg.generate_evaluation_code(code) if self.reject_duplicates: - code.putln("if (unlikely(PyDict_Contains(%s, %s))) {" % ( + code.putln("if (unlikely(DICT_CONTAINS(%s, %s))) {" % ( self.result(), arg.key.py_result())) helpers.add("RaiseDoubleKeywords") @@ -9644,7 +9644,7 @@ def generate_evaluation_code(self, code): keys_seen.add(key.value) if keys_seen is None: - code.putln('if (unlikely(PyDict_Contains(%s, %s))) {' % ( + code.putln('if (unlikely(DICT_CONTAINS(%s, %s))) {' % ( self.result(), key.py_result())) # currently only used in function calls needs_error_helper = True diff --git a/Cython/Utility/FunctionArguments.c b/Cython/Utility/FunctionArguments.c index 6ac82c2ea06..c5a6b3fc425 100644 --- a/Cython/Utility/FunctionArguments.c +++ b/Cython/Utility/FunctionArguments.c @@ -430,7 +430,7 @@ static int __Pyx_MergeKeywords(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE kwdict, P if (unlikely(result < 0)) goto bad; if (!result) break; - if (unlikely(PyDict_Contains(HPY_LEGACY_OBJECT_AS(kwdict), HPY_LEGACY_OBJECT_AS(key)))) { + if (unlikely(DICT_CONTAINS(kwdict, key))) { __Pyx_RaiseDoubleKeywordsError("function", HPY_LEGACY_OBJECT_AS(key)); result = -1; } else { diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index a7b711349a0..46b97cea853 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -111,6 +111,7 @@ //Dict Type #define DICT_NEW() HPyDict_New(HPY_CONTEXT_CNAME) #define DICT_COPY(o) HPyDict_Copy(HPY_CONTEXT_CNAME, o) + #define DICT_CONTAINS(dict, key) HPy_Contains(HPY_CONTEXT_CNAME, dict, key) #define DICT_GET_ITEM(o, attr_name) HPyDict_GetItem(HPY_CONTEXT_CNAME, o, attr_name) #define DICT_SET_ITEM(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) #define DICT_GET_ITEM_STR(o, attr_name) HPyDict_GetItem_s(HPY_CONTEXT_CNAME, o, attr_name) From 7c759976dc54ddbc1f46c8832bd430e8ce238a09 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 20 Nov 2023 16:28:59 +0100 Subject: [PATCH 216/286] Removed unnecessary node attribute --- Cython/Compiler/ExprNodes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index a8cb221459d..247751371fa 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -1361,7 +1361,6 @@ class IntNode(ConstNode): unsigned = "" longness = "" is_c_literal = None # unknown - is_global = True # hex_value and base_10_value are designed only to simplify # writing tests to get a consistent representation of value From b921cae2e9e914927633dcc6077df224408d8d8d Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Tue, 21 Nov 2023 13:26:32 +0100 Subject: [PATCH 217/286] Removed unnecessary error clear; fixed error message --- Cython/Compiler/ModuleNode.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index f5880b09e86..e76cf989c7f 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -3422,9 +3422,6 @@ def generate_module_import_setup(self, env, code): code.putln("PYOBJECT_TYPE modules = HPY_LEGACY_OBJECT_FROM(PyImport_GetModuleDict()); %s" % ##TODO: Figure out how to get module dict code.error_goto_if_null_object("modules", self.pos)) code.putln('if (API_IS_NULL(DICT_GET_ITEM_STR(modules, %s))) {' % fq_module_name_cstring) - code.putln('#if CYTHON_USING_HPY') - code.putln('HPyErr_Clear(HPY_CONTEXT_CNAME);') - code.putln('#endif') code.putln(code.error_goto_if_neg('DICT_SET_ITEM_STR(modules, %s, %s)' % ( fq_module_name_cstring, Naming.pymodinit_module_arg), self.pos)) code.putln("}") From 9e06578c06d79e9cdcd6272bcd501aae800e3882 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 20 Sep 2023 11:41:45 +0200 Subject: [PATCH 218/286] HPy Lists can now be created, but not yet changed --- Cython/Compiler/ExprNodes.py | 59 ++++++++++++++------------------- Cython/Compiler/PyrexTypes.py | 41 +++++++++++++++++++++++ Cython/Utility/HPyUtils.c | 18 ++++++++++ Cython/Utility/ObjectHandling.c | 2 +- 4 files changed, 85 insertions(+), 35 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 247751371fa..63d4c4f62d1 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -31,7 +31,7 @@ from .Nodes import Node, SingleAssignmentNode from . import PyrexTypes from .PyrexTypes import py_object_type, typecast, error_type, \ - unspecified_type, tuple_builder_type + unspecified_type, tuple_builder_type, list_builder_type from . import TypeSlots from .Builtin import ( list_type, tuple_type, set_type, dict_type, type_type, @@ -8382,29 +8382,20 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): else: # build the tuple/list step by step, potentially multiplying it as we go if self.type is list_type: - create_func, set_item_func = 'PyList_New', '__Pyx_PyList_SET_ITEM' + builder_type = list_builder_type + create_func, set_item_func = 'LIST_CREATE_START', 'LIST_CREATE_ASSIGN' + build_func = 'LIST_CREATE_FINALISE' elif self.type is tuple_type: builder_type = tuple_builder_type create_func, set_item_func = 'TUPLE_CREATE_START', 'TUPLE_CREATE_ASSIGN' build_func = 'TUPLE_CREATE_FINALISE' else: raise InternalError("sequence packing for unexpected type %s" % self.type) - if self.type is tuple_type: - tmp_builder = code.funcstate.allocate_temp(tuple_builder_type, manage_ref=False) + tmp_builder = code.funcstate.allocate_temp(builder_type, manage_ref=False) arg_count = len(self.args) - if self.type is tuple_type: - code.putln("#if CYTHON_USING_HPY") - code.putln("%s(%s,%s,%s%s);" % ( - create_func, target, tmp_builder, arg_count, size_factor)) - code.putln("#else") - code.putln("%s(%s,%s,%s%s); %s" % ( - create_func, target, tmp_builder, arg_count, size_factor, - code.error_goto_if_null_object(target, self.pos))) - code.putln("#endif") - else: - code.putln("%s = %s(%s%s); %s" % ( - target, create_func, arg_count, size_factor, - code.error_goto_if_null(target, self.pos))) + code.putln("%s(%s,%s,%s%s); %s" % ( + create_func, target, tmp_builder, arg_count, size_factor, + code.error_goto_if_null_object(target, self.pos))) code.put_gotref(target, py_object_type) if c_mult: @@ -8431,25 +8422,25 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): if c_mult or not arg.result_in_temp(): code.put_incref(arg.result(), arg.ctype()) arg.generate_giveref(code) - code.putln("#endif") - if self.type is tuple_type: - code.putln("%s(%s, %s, %s, %s);" % ( #I had to remove the error checking condition, as HPyTuple/ListBuilder_Set doesn't have a return value - set_item_func, - target, - tmp_builder, - (offset and i) and ('%s + %s' % (offset, i)) or (offset or i), - arg.py_result())) + tmp_load_arg = code.funcstate.allocate_temp(py_object_type, manage_ref=False) + if hasattr(arg, "is_global") and arg.is_global: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_arg, arg.py_result())) else: - code.putln("if (%s(%s, %s, %s)) %s;" % ( - set_item_func, - target, - (offset and i) and ('%s + %s' % (offset, i)) or (offset or i), - arg.py_result(), - code.error_goto(self.pos))) + code.putln("%s = %s;" % (tmp_load_arg, arg.py_result())) + code.putln("#endif") + code.putln("%s(%s, %s, %s, %s);" % ( # %s;" % ( + set_item_func, + target, + tmp_builder, + (offset and i) and ('%s + %s' % (offset, i)) or (offset or i), + tmp_load_arg))#, + #code.error_goto(self.pos))) + if arg in code.globalstate.const_cname_array: + code.putln("PYOBJECT_CLOSEREF(%s);" % tmp_load_arg) + code.funcstate.release_temp(tmp_load_arg) - if self.type is tuple_type: - code.putln("%s(%s, %s);" % (build_func, target, tmp_builder)) - code.funcstate.release_temp(tmp_builder) + code.putln("%s(%s, %s);" % (build_func, target, tmp_builder)) + code.funcstate.release_temp(tmp_builder) if c_mult: code.putln('}') diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 37ae6221635..96fa664f8ab 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -1436,6 +1436,46 @@ def __lt__(self, other): ('object') is always true """ return False + +class ListBuilderType(PyrexType): + # + # Class for a C tuple builder + # + + name = "list_builder" + buffer_defaults = None + is_extern = False + is_subclassed = False + is_gc_simple = False + builtin_trashcan = False # builtin type using trashcan + + def __str__(self): + return "list builder" + + def __repr__(self): + return "" + + def default_coerced_ctype(self): + """The default C type that this Python type coerces to, or None.""" + return None + + def assignable_from(self, src_type): + # except for pointers, conversion will be attempted + return False + + def declaration_code(self, entity_code, + for_display = 0, dll_linkage = None, pyrex = 0): + return self.base_declaration_code("LIST_BUILDER_TYPE", entity_code) + + def py_type_name(self): + return "object" + + def __lt__(self, other): + """ + Make sure we sort highest, as instance checking on py_type_name + ('object') is always true + """ + return False class BuiltinObjectType(PyObjectType): # objstruct_cname string Name of PyObject struct @@ -4805,6 +4845,7 @@ def specialize_here(self, pos, env, template_values=None): py_object_type = PyObjectType() tuple_builder_type = TupleBuilderType() +list_builder_type = ListBuilderType() c_void_type = CVoidType() diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 46b97cea853..caeb639e559 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -124,6 +124,7 @@ #define TUPLE_CREATE_EMPTY() HPyTuple_FromArray(HPY_CONTEXT_CNAME, NULL, 0) #define TUPLE_GET_ITEM(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, PYOBJECT_LONG_FROM_LONG(pos)) #define TUPLE_GET_SIZE(h) HPy_Length(HPY_CONTEXT_CNAME, (h)) + #define TUPLE_CHECK(h) HPyTuple_Check(h) #define TUPLE_BUILDER_TYPE HPyTupleBuilder #define TUPLE_CREATE_START(target, builder, size) builder = HPyTupleBuilder_New(HPY_CONTEXT_CNAME, size) #define TUPLE_CREATE_ASSIGN(tuple, builder, index, item) HPyTupleBuilder_Set(HPY_CONTEXT_CNAME, builder, index, item) @@ -131,6 +132,14 @@ #define TUPLE_PACK(num_args, ...) HPyTuple_Pack(HPY_CONTEXT_CNAME, num_args, __VA_ARGS__) //List Type + #define LIST_CREATE_EMPTY() HPyList_New(HPY_CONTEXT_CNAME, 0) + #define LIST_GET_ITEM(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, PYOBJECT_FROM_LONG(pos)) + #define LIST_GET_SIZE(h) HPy_Length(HPY_CONTEXT_CNAME, (h)) + #define LIST_CHECK(h) HPyList_Check(h) + #define LIST_BUILDER_TYPE HPyListBuilder + #define LIST_CREATE_START(target, builder, size) builder = HPyListBuilder_New(HPY_CONTEXT_CNAME, size) + #define LIST_CREATE_ASSIGN(tuple, builder, index, item) HPyListBuilder_Set(HPY_CONTEXT_CNAME, builder, index, item) + #define LIST_CREATE_FINALISE(target, builder) target = HPyListBuilder_Build(HPY_CONTEXT_CNAME, builder); //PyObject/HPy Handle Type #define PYOBJECT_GET_ITEM(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) @@ -284,6 +293,7 @@ #define TUPLE_CREATE_EMPTY() PyTuple_New(0) #define TUPLE_GET_ITEM(h, pos) __Pyx_PySequence_ITEM(h, pos) #define TUPLE_GET_SIZE(h) PyTuple_GET_SIZE(h) + #define TUPLE_CHECK(h) PyTuple_Check(h) #define TUPLE_BUILDER_TYPE PyObject * //Not used, just needed to prevent errors #define TUPLE_CREATE_START(target, builder, size) target=PyTuple_New(size) #define TUPLE_CREATE_ASSIGN(tuple, builder, index, item) __Pyx_PyTuple_SET_ITEM(tuple, index, item) @@ -291,6 +301,14 @@ #define TUPLE_PACK(num_args, ...) PyTuple_Pack(num_args, __VA_ARGS__) //List Type + #define LIST_CREATE_EMPTY() PyList_New(0) + #define LIST_GET_ITEM(h, pos) __Pyx_PySequence_ITEM(HPY_CONTEXT_CNAME, h, pos) + #define LIST_GET_SIZE(h) PyList_GET_SIZE(h) + #define LIST_CHECK(h) PyList_Check(h) + #define LIST_BUILDER_TYPE PyObject * //Not used, just needed to prevent errors + #define LIST_CREATE_START(target, builder, size) target=PyList_New(size) + #define LIST_CREATE_ASSIGN(tuple, builder, index, item) __Pyx_PyList_SET_ITEM(tuple, index, item) + #define LIST_CREATE_FINALISE(target, null) //PyObject/HPy Handle Type #define PYOBJECT_GET_ITEM(o, attr_name) PyObject_GetItem(o, attr_name) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 4cf303c4977..9b6921da920 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -539,7 +539,7 @@ static int __Pyx_SetItemInt_Generic(PyObject *o, PyObject *j, PyObject *v) { return r; } -static CYTHON_INLINE int __Pyx_SetItemInt_Fast(PyObject *o, Py_ssize_t i, PyObject *v, int is_list, +static CYTHON_INLINE int __Pyx_SetItemInt_Fast(PYOBJECT_TYPE o, API_SSIZR_T i, PYOBJECT_TYPE v, int is_list, CYTHON_NCP_UNUSED int wraparound, CYTHON_NCP_UNUSED int boundscheck) { #if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE && !CYTHON_AVOID_BORROWED_REFS && CYTHON_USE_TYPE_SLOTS if (is_list || PyList_CheckExact(o)) { From aed2b367a8d4d9e78922109520c9540c477c1134 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 20 Sep 2023 14:10:52 +0200 Subject: [PATCH 219/286] Working on allowing changing list elements --- Cython/Utility/CythonFunction.c | 4 ++-- Cython/Utility/HPyUtils.c | 8 ++++++-- Cython/Utility/ObjectHandling.c | 36 +++++++++++++++++++++++---------- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index bcdfdee619c..ed9f01aa3e5 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -452,11 +452,11 @@ __Pyx_CyFunction_init_defaults(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_ op->defaults_kwdict = PyTuple_GET_ITEM(res, 1); Py_INCREF(op->defaults_kwdict); #else - PYOBJECT_FIELD_STORE(op, struct_op->defaults_tuple, TUPLE_GET_ITEM(res, 0)); + PYOBJECT_FIELD_STORE(op, struct_op->defaults_tuple, SEQUENCE_GET_ITEM(res, 0)); PYOBJECT_TYPE load_default_tuple_temp = PYOBJECT_FIELD_LOAD(op, struct_op->defaults_tuple); if (unlikely(API_IS_NULL(load_default_tuple_temp))) result = -1; else { - PYOBJECT_FIELD_STORE(op, struct_op->defaults_kwdict, TUPLE_GET_ITEM(res, 1)); + PYOBJECT_FIELD_STORE(op, struct_op->defaults_kwdict, SEQUENCE_GET_ITEM(res, 1)); PYOBJECT_TYPE load_default_kwdict_temp = PYOBJECT_FIELD_LOAD(op, struct_op->defaults_kwdict); if (unlikely(API_IS_NULL(load_default_kwdict_temp))) result = -1; } diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index caeb639e559..a0bcc8d2502 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -119,12 +119,14 @@ #define DICT_GET_ITEM_WITH_ERROR(o, attr_name) HPyDict_GetItem(HPY_CONTEXT_CNAME, o, attr_name) //Sequence Type + #define SEQUENCE_GET_ITEM(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, pos) + #define SEQUENCE_SET_ITEM(h, pos, o) HPy_SetItem_i(HPY_CONTEXT_CNAME, h, pos, o) //Tuple Type #define TUPLE_CREATE_EMPTY() HPyTuple_FromArray(HPY_CONTEXT_CNAME, NULL, 0) #define TUPLE_GET_ITEM(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, PYOBJECT_LONG_FROM_LONG(pos)) #define TUPLE_GET_SIZE(h) HPy_Length(HPY_CONTEXT_CNAME, (h)) - #define TUPLE_CHECK(h) HPyTuple_Check(h) + #define TUPLE_CHECK(h) HPyTuple_Check(HPY_CONTEXT_CNAME, h) #define TUPLE_BUILDER_TYPE HPyTupleBuilder #define TUPLE_CREATE_START(target, builder, size) builder = HPyTupleBuilder_New(HPY_CONTEXT_CNAME, size) #define TUPLE_CREATE_ASSIGN(tuple, builder, index, item) HPyTupleBuilder_Set(HPY_CONTEXT_CNAME, builder, index, item) @@ -135,7 +137,7 @@ #define LIST_CREATE_EMPTY() HPyList_New(HPY_CONTEXT_CNAME, 0) #define LIST_GET_ITEM(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, PYOBJECT_FROM_LONG(pos)) #define LIST_GET_SIZE(h) HPy_Length(HPY_CONTEXT_CNAME, (h)) - #define LIST_CHECK(h) HPyList_Check(h) + #define LIST_CHECK(h) HPyList_Check(HPY_CONTEXT_CNAME, h) #define LIST_BUILDER_TYPE HPyListBuilder #define LIST_CREATE_START(target, builder, size) builder = HPyListBuilder_New(HPY_CONTEXT_CNAME, size) #define LIST_CREATE_ASSIGN(tuple, builder, index, item) HPyListBuilder_Set(HPY_CONTEXT_CNAME, builder, index, item) @@ -288,6 +290,8 @@ #define DICT_GET_ITEM_WITH_ERROR(o, attr_name) PyDict_GetItemWithError(o, attr_name) //Sequence Type + #define SEQUENCE_GET_ITEM(h, pos) __Pyx_PySequence_ITEM(h, pos) + #define SEQUENCE_SET_ITEM(h, pos, o) PySequence_SetItem(h, pos, o) //Tuple Type #define TUPLE_CREATE_EMPTY() PyTuple_New(0) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 9b6921da920..ceed5f442ed 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -521,25 +521,25 @@ static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i, #define __Pyx_SetItemInt(o, i, v, type, is_signed, to_py_func, is_list, wraparound, boundscheck) \ (__Pyx_fits_Py_ssize_t(i, type, is_signed) ? \ - __Pyx_SetItemInt_Fast(o, (Py_ssize_t)i, v, is_list, wraparound, boundscheck) : \ + __Pyx_SetItemInt_Fast(HPY_CONTEXT_FIRST_ARG_CALL o, (API_SSIZE_T)i, v, is_list, wraparound, boundscheck) : \ (is_list ? (PyErr_SetString(PyExc_IndexError, "list assignment index out of range"), -1) : \ - __Pyx_SetItemInt_Generic(o, to_py_func(i), v))) + __Pyx_SetItemInt_Generic(HPY_CONTEXT_FIRST_ARG_CALL o, to_py_func(i), v))) -static int __Pyx_SetItemInt_Generic(PyObject *o, PyObject *j, PyObject *v); -static CYTHON_INLINE int __Pyx_SetItemInt_Fast(PyObject *o, Py_ssize_t i, PyObject *v, +static int __Pyx_SetItemInt_Generic(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE o, PYOBJECT_TYPE j, PYOBJECT_TYPE v); +static CYTHON_INLINE int __Pyx_SetItemInt_Fast(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE o, API_SSIZE_T i, PYOBJECT_GLOBAL_TYPE v, int is_list, int wraparound, int boundscheck); /////////////// SetItemInt /////////////// -static int __Pyx_SetItemInt_Generic(PyObject *o, PyObject *j, PyObject *v) { +static int __Pyx_SetItemInt_Generic(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE o, PYOBJECT_TYPE j, PYOBJECT_TYPE v) { int r; - if (unlikely(!j)) return -1; - r = PyObject_SetItem(o, j, v); - Py_DECREF(j); + if (unlikely(API_IS_NULL(j))) return -1; + r = PYOBJECT_SET_ITEM(o, j, v); + PYOBJECT_CLOSEREF(j); return r; } -static CYTHON_INLINE int __Pyx_SetItemInt_Fast(PYOBJECT_TYPE o, API_SSIZR_T i, PYOBJECT_TYPE v, int is_list, +static CYTHON_INLINE int __Pyx_SetItemInt_Fast(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE o, API_SSIZE_T i, PYOBJECT_GLOBAL_TYPE v, int is_list, CYTHON_NCP_UNUSED int wraparound, CYTHON_NCP_UNUSED int boundscheck) { #if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE && !CYTHON_AVOID_BORROWED_REFS && CYTHON_USE_TYPE_SLOTS if (is_list || PyList_CheckExact(o)) { @@ -578,15 +578,29 @@ static CYTHON_INLINE int __Pyx_SetItemInt_Fast(PYOBJECT_TYPE o, API_SSIZR_T i, P return sm->sq_ass_item(o, i, v); } } +#else +#if CYTHON_COMPILING_IN_PYPY + if (is_list || (PySequence_Check(o) && !DICT_CHECK(o))) +#else +#if CYTHON_USING_HPY + if (is_list || TUPLE_CHECK(o) || LIST_CHECK(o)) #else // PySequence_SetItem behaves differently to PyObject_SetItem for i<0 // and possibly some other cases so can't generally be substituted if (is_list || !PyMapping_Check(o)) +#endif +#endif { - return PySequence_SetItem(o, i, v); + PYOBJECT_TYPE tmp_load_v = PYOBJECT_GLOBAL_LOAD(v); + int retval = SEQUENCE_SET_ITEM(o, i, tmp_load_v); + PYOBJECT_CLOSEREF(tmp_load_v); + return retval; } #endif - return __Pyx_SetItemInt_Generic(o, PyInt_FromSsize_t(i), v); + PYOBJECT_TYPE tmp_load_v = PYOBJECT_GLOBAL_LOAD(v); + int retval = __Pyx_SetItemInt_Generic(HPY_CONTEXT_FIRST_ARG_CALL o, PYOBJECT_LONG_FROM_SSIZE(i), tmp_load_v); + PYOBJECT_CLOSEREF(tmp_load_v); + return retval; } From 2b448b136f9521372b0dbc12967bda5797f8bc7b Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 20 Sep 2023 15:14:29 +0200 Subject: [PATCH 220/286] Possibly made array assign work when using changes from binops_on_rebase branch --- Cython/Compiler/ExprNodes.py | 9 ++++++++- Cython/Utility/ObjectHandling.c | 9 +++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 63d4c4f62d1..dd6a7a218d9 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -4687,6 +4687,13 @@ def generate_setitem_code(self, value, code): code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_value) code.funcstate.release_temp(temp_load_value) + if index_code in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_index) + code.funcstate.release_temp(temp_load_index) + if value_code in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_value) + code.funcstate.release_temp(temp_load_value) + def generate_assignment_code(self, rhs, code, overloaded_assignment=False, exception_check=None, exception_value=None): self.generate_subexpr_evaluation_code(code) @@ -8422,12 +8429,12 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): if c_mult or not arg.result_in_temp(): code.put_incref(arg.result(), arg.ctype()) arg.generate_giveref(code) + code.putln("#endif") tmp_load_arg = code.funcstate.allocate_temp(py_object_type, manage_ref=False) if hasattr(arg, "is_global") and arg.is_global: code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_arg, arg.py_result())) else: code.putln("%s = %s;" % (tmp_load_arg, arg.py_result())) - code.putln("#endif") code.putln("%s(%s, %s, %s, %s);" % ( # %s;" % ( set_item_func, target, diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index ceed5f442ed..ed3429d85bd 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -526,7 +526,7 @@ static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i, __Pyx_SetItemInt_Generic(HPY_CONTEXT_FIRST_ARG_CALL o, to_py_func(i), v))) static int __Pyx_SetItemInt_Generic(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE o, PYOBJECT_TYPE j, PYOBJECT_TYPE v); -static CYTHON_INLINE int __Pyx_SetItemInt_Fast(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE o, API_SSIZE_T i, PYOBJECT_GLOBAL_TYPE v, +static CYTHON_INLINE int __Pyx_SetItemInt_Fast(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE o, API_SSIZE_T i, PYOBJECT_TYPE v, int is_list, int wraparound, int boundscheck); /////////////// SetItemInt /////////////// @@ -539,7 +539,7 @@ static int __Pyx_SetItemInt_Generic(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE o, P return r; } -static CYTHON_INLINE int __Pyx_SetItemInt_Fast(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE o, API_SSIZE_T i, PYOBJECT_GLOBAL_TYPE v, int is_list, +static CYTHON_INLINE int __Pyx_SetItemInt_Fast(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE o, API_SSIZE_T i, PYOBJECT_TYPE v, int is_list, CYTHON_NCP_UNUSED int wraparound, CYTHON_NCP_UNUSED int boundscheck) { #if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE && !CYTHON_AVOID_BORROWED_REFS && CYTHON_USE_TYPE_SLOTS if (is_list || PyList_CheckExact(o)) { @@ -591,10 +591,7 @@ static CYTHON_INLINE int __Pyx_SetItemInt_Fast(HPY_CONTEXT_FIRST_ARG_DEF PYOBJEC #endif #endif { - PYOBJECT_TYPE tmp_load_v = PYOBJECT_GLOBAL_LOAD(v); - int retval = SEQUENCE_SET_ITEM(o, i, tmp_load_v); - PYOBJECT_CLOSEREF(tmp_load_v); - return retval; + return SEQUENCE_SET_ITEM(o, i, v); } #endif PYOBJECT_TYPE tmp_load_v = PYOBJECT_GLOBAL_LOAD(v); From 447e0705d47812f284c5011d445081294c13557c Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 20 Sep 2023 16:09:55 +0200 Subject: [PATCH 221/286] Fixed issues occuring on HPy with arrays and tuples --- Cython/Compiler/ExprNodes.py | 5 ++--- Cython/Utility/ObjectHandling.c | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index dd6a7a218d9..d464a853a90 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -8400,9 +8400,8 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): raise InternalError("sequence packing for unexpected type %s" % self.type) tmp_builder = code.funcstate.allocate_temp(builder_type, manage_ref=False) arg_count = len(self.args) - code.putln("%s(%s,%s,%s%s); %s" % ( - create_func, target, tmp_builder, arg_count, size_factor, - code.error_goto_if_null_object(target, self.pos))) + code.putln("%s(%s,%s,%s%s);" % ( + create_func, target, tmp_builder, arg_count, size_factor)) code.put_gotref(target, py_object_type) if c_mult: diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index ed3429d85bd..efeaea29ae0 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -523,7 +523,7 @@ static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i, (__Pyx_fits_Py_ssize_t(i, type, is_signed) ? \ __Pyx_SetItemInt_Fast(HPY_CONTEXT_FIRST_ARG_CALL o, (API_SSIZE_T)i, v, is_list, wraparound, boundscheck) : \ (is_list ? (PyErr_SetString(PyExc_IndexError, "list assignment index out of range"), -1) : \ - __Pyx_SetItemInt_Generic(HPY_CONTEXT_FIRST_ARG_CALL o, to_py_func(i), v))) + __Pyx_SetItemInt_Generic(HPY_CONTEXT_FIRST_ARG_CALL o, to_py_func(HPY_CONTEXT_FIRST_ARG_CALL i), v))) static int __Pyx_SetItemInt_Generic(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE o, PYOBJECT_TYPE j, PYOBJECT_TYPE v); static CYTHON_INLINE int __Pyx_SetItemInt_Fast(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE o, API_SSIZE_T i, PYOBJECT_TYPE v, From f0bedab8292cdc85b1bc80e071ca59aa834ec351 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Fri, 22 Sep 2023 15:19:23 +0200 Subject: [PATCH 222/286] List.append now works on HPy --- Cython/Utility/HPyUtils.c | 13 +++++++------ Cython/Utility/ObjectHandling.c | 6 +++--- Cython/Utility/Optimize.c | 18 +++++++++--------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index a0bcc8d2502..c89c79b9cd3 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -72,6 +72,8 @@ #define DICT_CHECK_EXACT(o) HPyDict_Check(HPY_CONTEXT_CNAME, o) #define TUPLE_CHECK(o) HPyTuple_Check(HPY_CONTEXT_CNAME, o) #define PYOBJECT_TYPE_CHECK(o, t) HPy_TypeCheck(HPY_CONTEXT_CNAME, o, t) + #define LIST_CHECK(h) HPyList_Check(HPY_CONTEXT_CNAME, h) + #define LIST_CHECK_EXACT(h) PyList_CheckExact(h) //Integer Type - From #define PYOBJECT_INT_FROM_LONG(i) HPyLong_FromLong(HPY_CONTEXT_CNAME, i) @@ -126,7 +128,6 @@ #define TUPLE_CREATE_EMPTY() HPyTuple_FromArray(HPY_CONTEXT_CNAME, NULL, 0) #define TUPLE_GET_ITEM(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, PYOBJECT_LONG_FROM_LONG(pos)) #define TUPLE_GET_SIZE(h) HPy_Length(HPY_CONTEXT_CNAME, (h)) - #define TUPLE_CHECK(h) HPyTuple_Check(HPY_CONTEXT_CNAME, h) #define TUPLE_BUILDER_TYPE HPyTupleBuilder #define TUPLE_CREATE_START(target, builder, size) builder = HPyTupleBuilder_New(HPY_CONTEXT_CNAME, size) #define TUPLE_CREATE_ASSIGN(tuple, builder, index, item) HPyTupleBuilder_Set(HPY_CONTEXT_CNAME, builder, index, item) @@ -136,8 +137,7 @@ //List Type #define LIST_CREATE_EMPTY() HPyList_New(HPY_CONTEXT_CNAME, 0) #define LIST_GET_ITEM(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, PYOBJECT_FROM_LONG(pos)) - #define LIST_GET_SIZE(h) HPy_Length(HPY_CONTEXT_CNAME, (h)) - #define LIST_CHECK(h) HPyList_Check(HPY_CONTEXT_CNAME, h) + #define LIST_APPEND(list, h) PyList_Append(list, h) #define LIST_BUILDER_TYPE HPyListBuilder #define LIST_CREATE_START(target, builder, size) builder = HPyListBuilder_New(HPY_CONTEXT_CNAME, size) #define LIST_CREATE_ASSIGN(tuple, builder, index, item) HPyListBuilder_Set(HPY_CONTEXT_CNAME, builder, index, item) @@ -243,6 +243,9 @@ #define DICT_CHECK_EXACT(o) PyDict_CheckExact(o) #define TUPLE_CHECK(o) PyTuple_Check(o) #define PYOBJECT_TYPE_CHECK(o, t) PyObject_TypeCheck(o, t) + #define LIST_CHECK(h) PyList_Check(h) + #define LIST_CHECK_EXACT(h) PyList_CheckExact(h) + //Integer Type - From #define PYOBJECT_INT_FROM_LONG(i) PyInt_FromLong(i) @@ -297,7 +300,6 @@ #define TUPLE_CREATE_EMPTY() PyTuple_New(0) #define TUPLE_GET_ITEM(h, pos) __Pyx_PySequence_ITEM(h, pos) #define TUPLE_GET_SIZE(h) PyTuple_GET_SIZE(h) - #define TUPLE_CHECK(h) PyTuple_Check(h) #define TUPLE_BUILDER_TYPE PyObject * //Not used, just needed to prevent errors #define TUPLE_CREATE_START(target, builder, size) target=PyTuple_New(size) #define TUPLE_CREATE_ASSIGN(tuple, builder, index, item) __Pyx_PyTuple_SET_ITEM(tuple, index, item) @@ -307,8 +309,7 @@ //List Type #define LIST_CREATE_EMPTY() PyList_New(0) #define LIST_GET_ITEM(h, pos) __Pyx_PySequence_ITEM(HPY_CONTEXT_CNAME, h, pos) - #define LIST_GET_SIZE(h) PyList_GET_SIZE(h) - #define LIST_CHECK(h) PyList_Check(h) + #define LIST_APPEND(list, h) PyList_Append(list, h) #define LIST_BUILDER_TYPE PyObject * //Not used, just needed to prevent errors #define LIST_CREATE_START(target, builder, size) target=PyList_New(size) #define LIST_CREATE_ASSIGN(tuple, builder, index, item) __Pyx_PyList_SET_ITEM(tuple, index, item) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index efeaea29ae0..c1c800f87b8 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1582,7 +1582,7 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStrNoError(HPY_CONTEXT_ /////////////// PyObjectGetAttrStr.proto /////////////// #if CYTHON_USE_TYPE_SLOTS -static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStr(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, PYOBJECT_TYPE attr_name);/*proto*/ +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStr(PYOBJECT_TYPE obj, PYOBJECT_TYPE attr_name);/*proto*/ #else #define __Pyx_PyObject_GetAttrStr(o,n) PYOBJECT_GET_ATTR(o,n) //Needed to turn this into a function macro to be able to pass the context properly #define __Pyx_PyObject_GetAttrStr_legacy(o,n) PyObject_GetAttr(o,n) //Used for functions where the context isn't reachable yet @@ -1647,7 +1647,7 @@ static int __Pyx_PyObject_GetMethod(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, assert (*method == NULL); if (unlikely(tp->tp_getattro != PyObject_GenericGetAttr)) { - attr = __Pyx_PyObject_GetAttrStr(obj, name); + attr = PyObject_GetAttrString(obj, name); //Must be changed back to __Pyx_PyObject_GetAttrString when context is available goto try_unpack; } if (unlikely(tp->tp_dict == NULL) && unlikely(PyType_Ready(tp) < 0)) { @@ -2155,7 +2155,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObj } if (nargs == 0) { - return __Pyx_PyObject_Call(func, $empty_tuple, kwargs); + return __Pyx_PyObject_Call(func, PyTuple_New(0), kwargs); //Must be changed back to $empty_tuple when context is available } #if PY_VERSION_HEX >= 0x03090000 && !CYTHON_COMPILING_IN_LIMITED_API return PyObject_VectorcallDict(func, args, (size_t)nargs, kwargs); diff --git a/Cython/Utility/Optimize.c b/Cython/Utility/Optimize.c index d3c7604dac3..a8f9792a4f8 100644 --- a/Cython/Utility/Optimize.c +++ b/Cython/Utility/Optimize.c @@ -8,20 +8,20 @@ /////////////// append.proto /////////////// -static CYTHON_INLINE int __Pyx_PyObject_Append(PyObject* L, PyObject* x); /*proto*/ +static CYTHON_INLINE int __Pyx_PyObject_Append(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE L, PYOBJECT_TYPE x); /*proto*/ /////////////// append /////////////// //@requires: ListAppend //@requires: ObjectHandling.c::PyObjectCallMethod1 -static CYTHON_INLINE int __Pyx_PyObject_Append(PyObject* L, PyObject* x) { - if (likely(PyList_CheckExact(L))) { +static CYTHON_INLINE int __Pyx_PyObject_Append(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE L, PYOBJECT_TYPE x) { + if (likely(LIST_CHECK_EXACT(L))) { if (unlikely(__Pyx_PyList_Append(L, x) < 0)) return -1; } else { - PyObject* retval = __Pyx_PyObject_CallMethod1(L, PYIDENT("append"), x); - if (unlikely(!retval)) + PYOBJECT_TYPE retval = HPY_LEGACY_OBJECT_FROM(__Pyx_PyObject_CallMethod1(HPY_LEGACY_OBJECT_AS(L), HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(PYIDENT("append"))), HPY_LEGACY_OBJECT_AS(x))); + if (unlikely(API_IS_NULL(retval))) return -1; - Py_DECREF(retval); + PYOBJECT_CLOSEREF(retval); } return 0; } @@ -48,7 +48,7 @@ static CYTHON_INLINE int __Pyx_PyList_Append(PyObject* list, PyObject* x) { return PyList_Append(list, x); } #else -#define __Pyx_PyList_Append(L,x) PyList_Append(L,x) +#define __Pyx_PyList_Append(L,x) LIST_APPEND(L,x) #endif /////////////// ListCompAppend.proto /////////////// @@ -141,8 +141,8 @@ static PYOBJECT_TYPE __Pyx__PyObject_PopIndex(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT __Pyx__PyObject_PopIndex(HPY_CONTEXT_CNAME, L, py_ix)) #else -static PyObject* __Pyx__PyObject_PopNewIndex(PyObject* L, PyObject* py_ix); /*proto*/ -static PyObject* __Pyx__PyObject_PopIndex(PyObject* L, PyObject* py_ix); /*proto*/ +static PYOBJECT_TYPE __Pyx__PyObject_PopNewIndex(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE L, PYOBJECT_TYPE py_ix); /*proto*/ +static PYOBJECT_TYPE __Pyx__PyObject_PopIndex(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE L, PYOBJECT_TYPE py_ix); /*proto*/ #if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE static PyObject* __Pyx__PyList_PopIndex(PyObject* L, PyObject* py_ix, Py_ssize_t ix); /*proto*/ From db5ee06bdd4912fc63253bc52bae8454a4166d06 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 25 Sep 2023 15:02:33 +0200 Subject: [PATCH 223/286] Ported list pop() method --- Cython/Utility/ObjectHandling.c | 3 ++- Cython/Utility/Optimize.c | 20 +++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index c1c800f87b8..6a566ea3f02 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1583,6 +1583,7 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStrNoError(HPY_CONTEXT_ #if CYTHON_USE_TYPE_SLOTS static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStr(PYOBJECT_TYPE obj, PYOBJECT_TYPE attr_name);/*proto*/ +#define __Pyx_PyObject_GetAttrStr_legacy(o,n) PyObject_GetAttr(o,n) #else #define __Pyx_PyObject_GetAttrStr(o,n) PYOBJECT_GET_ATTR(o,n) //Needed to turn this into a function macro to be able to pass the context properly #define __Pyx_PyObject_GetAttrStr_legacy(o,n) PyObject_GetAttr(o,n) //Used for functions where the context isn't reachable yet @@ -1647,7 +1648,7 @@ static int __Pyx_PyObject_GetMethod(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, assert (*method == NULL); if (unlikely(tp->tp_getattro != PyObject_GenericGetAttr)) { - attr = PyObject_GetAttrString(obj, name); //Must be changed back to __Pyx_PyObject_GetAttrString when context is available + attr = __Pyx_PyObject_GetAttrStr_legacy(obj, name); //Must be changed back to __Pyx_PyObject_GetAttrString when context is available goto try_unpack; } if (unlikely(tp->tp_dict == NULL) && unlikely(PyType_Ready(tp) < 0)) { diff --git a/Cython/Utility/Optimize.c b/Cython/Utility/Optimize.c index a8f9792a4f8..84fa3997e10 100644 --- a/Cython/Utility/Optimize.c +++ b/Cython/Utility/Optimize.c @@ -92,7 +92,7 @@ static CYTHON_INLINE int __Pyx_PyList_Extend(PyObject* L, PyObject* v) { /////////////// pop.proto /////////////// -static CYTHON_INLINE PyObject* __Pyx__PyObject_Pop(PyObject* L); /*proto*/ +static CYTHON_INLINE PYOBJECT_TYPE __Pyx__PyObject_Pop(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE L); /*proto*/ #if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE static CYTHON_INLINE PyObject* __Pyx_PyList_Pop(PyObject* L); /*proto*/ @@ -100,18 +100,20 @@ static CYTHON_INLINE PyObject* __Pyx_PyList_Pop(PyObject* L); /*proto*/ __Pyx_PyList_Pop(L) : __Pyx__PyObject_Pop(L)) #else -#define __Pyx_PyList_Pop(L) __Pyx__PyObject_Pop(L) -#define __Pyx_PyObject_Pop(L) __Pyx__PyObject_Pop(L) +#define __Pyx_PyList_Pop(L) __Pyx__PyObject_Pop(HPY_CONTEXT_FIRST_ARG_CALL L) +#define __Pyx_PyObject_Pop(L) __Pyx__PyObject_Pop(HPY_CONTEXT_FIRST_ARG_CALL L) #endif /////////////// pop /////////////// //@requires: ObjectHandling.c::PyObjectCallMethod0 -static CYTHON_INLINE PyObject* __Pyx__PyObject_Pop(PyObject* L) { +static CYTHON_INLINE PYOBJECT_TYPE __Pyx__PyObject_Pop(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE L) { +#if !CYTHON_USING_HPY if (__Pyx_IS_TYPE(L, &PySet_Type)) { return PySet_Pop(L); } - return __Pyx_PyObject_CallMethod0(L, PYIDENT("pop")); +#endif + return HPY_LEGACY_OBJECT_FROM(__Pyx_PyObject_CallMethod0(HPY_LEGACY_OBJECT_AS(L), HPY_LEGACY_OBJECT_AS(PYIDENT("pop")))); } #if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE @@ -141,7 +143,7 @@ static PYOBJECT_TYPE __Pyx__PyObject_PopIndex(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT __Pyx__PyObject_PopIndex(HPY_CONTEXT_CNAME, L, py_ix)) #else -static PYOBJECT_TYPE __Pyx__PyObject_PopNewIndex(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE L, PYOBJECT_TYPE py_ix); /*proto*/ +static PYOBJECT_TYPE __Pyx__PyObject_PopNewIndex(PYOBJECT_TYPE L, PYOBJECT_TYPE py_ix); /*proto*/ static PYOBJECT_TYPE __Pyx__PyObject_PopIndex(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE L, PYOBJECT_TYPE py_ix); /*proto*/ #if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE @@ -173,7 +175,11 @@ static PyObject* __Pyx__PyList_PopIndex(PyObject* L, PyObject* py_ix, Py_ssize_t /////////////// pop_index /////////////// //@requires: ObjectHandling.c::PyObjectCallMethod1 -static PYOBJECT_TYPE __Pyx__PyObject_PopNewIndex(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE L, PYOBJECT_TYPE py_ix) { +#if CYTHON_USING_HPY +static PYOBJECT_TYPE __Pyx__PyObject_PopNewIndex(HPyContext *HPY_CONTEXT_CNAME, PYOBJECT_TYPE L, PYOBJECT_TYPE py_ix) { +#else +static PYOBJECT_TYPE __Pyx__PyObject_PopNewIndex(PYOBJECT_TYPE L, PYOBJECT_TYPE py_ix) { +#endif PYOBJECT_TYPE r; if (unlikely(API_IS_NULL(py_ix))) return API_NULL_VALUE; r = __Pyx__PyObject_PopIndex(HPY_CONTEXT_FIRST_ARG_CALL L, py_ix); From edfc497e2ae8ed8cb295a38f79d29249d2d56bda Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Tue, 26 Sep 2023 10:11:18 +0200 Subject: [PATCH 224/286] Array functions except for sort() work now --- Cython/Utility/ObjectHandling.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 6a566ea3f02..1ce005e9a88 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -2604,7 +2604,12 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_CallNoArg(HPY_CONTEXT_FIRST_AR static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_CallNoArg(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE func) { PYOBJECT_TYPE arg = API_NULL_VALUE; +#if CYTHON_USING_HPY + arg = LIST_CREATE_EMPTY(); + return API_CALL_FUNC(func, &arg, 0, API_NULL_VALUE); +#else return __Pyx_PyObject_FastCall(func, (&arg)+1, 0 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET); +#endif } From 20bc77682e206b6ccc83e0d7bbf6197178fe63af Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Tue, 21 Nov 2023 14:40:09 +0100 Subject: [PATCH 225/286] Removed duplicate temp free from rebase --- Cython/Compiler/ExprNodes.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index d464a853a90..227f5c160bd 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -4687,13 +4687,6 @@ def generate_setitem_code(self, value, code): code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_value) code.funcstate.release_temp(temp_load_value) - if index_code in code.globalstate.const_cname_array: - code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_index) - code.funcstate.release_temp(temp_load_index) - if value_code in code.globalstate.const_cname_array: - code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_value) - code.funcstate.release_temp(temp_load_value) - def generate_assignment_code(self, rhs, code, overloaded_assignment=False, exception_check=None, exception_value=None): self.generate_subexpr_evaluation_code(code) From cdde3982b471958ca8627f12e6ec05f01869752e Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Fri, 10 Nov 2023 09:42:00 +0100 Subject: [PATCH 226/286] Added array test and fixes to make it run --- Cython/Compiler/ExprNodes.py | 4 +- Cython/Utility/CythonFunction.c | 26 +++++--- Cython/Utility/HPyUtils.c | 5 +- Cython/Utility/ModuleSetupCode.c | 2 + Cython/Utility/Optimize.c | 2 +- tests/run/hpy_array.pyx | 108 +++++++++++++++++++++++++++++++ 6 files changed, 133 insertions(+), 14 deletions(-) create mode 100644 tests/run/hpy_array.pyx diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 227f5c160bd..c71850b407c 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -2585,7 +2585,7 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, code.putln("PYOBJECT_CLOSEREF(%s);" % load_cname_temp) code.funcstate.release_temp(load_namespace_temp) code.funcstate.release_temp(load_cname_temp) - if rhs_result in code.globalstate.const_cname_array and b: + if rhs_result in code.globalstate.const_cname_array and entry.scope.is_module_scope: code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % load_result_temp) code.funcstate.release_temp(load_result_temp) code.putln("#else") @@ -8423,7 +8423,7 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): arg.generate_giveref(code) code.putln("#endif") tmp_load_arg = code.funcstate.allocate_temp(py_object_type, manage_ref=False) - if hasattr(arg, "is_global") and arg.is_global: + if arg.py_result() in code.globalstate.const_cname_array: code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_arg, arg.py_result())) else: code.putln("%s = %s;" % (tmp_load_arg, arg.py_result())) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index ed9f01aa3e5..21accd6f9fc 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -617,9 +617,13 @@ __Pyx_CyFunction_get_is_coroutine(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObje is_coroutine = PYOBJECT_FIELD_LOAD(op, struct_op->flags) & __Pyx_CYFUNCTION_COROUTINE; if (is_coroutine) { PYOBJECT_TYPE load_marker_temp = PYOBJECT_GLOBAL_LOAD(PYIDENT("_is_coroutine")); - PyObject *module, *fromlist, *marker = HPY_LEGACY_OBJECT_AS(load_marker_temp); + PYOBJECT_TYPE module; + PYOBJECT_TYPE fromlist; + LIST_BUILDER_TYPE fromlist_builder; + PYOBJECT_TYPE marker = load_marker_temp; PYOBJECT_GLOBAL_CLOSEREF(load_marker_temp); - fromlist = PyList_New(1); + LIST_CREATE_START(fromlist, fromlist_builder, 1); +#if !CYTHON_USING_HPY if (unlikely(!fromlist)) return API_NULL_VALUE; Py_INCREF(marker); #if CYTHON_ASSUME_SAFE_MACROS @@ -628,17 +632,21 @@ __Pyx_CyFunction_get_is_coroutine(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObje if (unlikely(PyList_SetItem(fromlist, 0, marker) < 0)) { Py_DECREF(marker); Py_DECREF(fromlist); - return NULL; + return API_NULL_VALUE; } #endif +#else + LIST_BUILDER_ASSIGN(fromlist, fromlist_builder, 0, marker); +#endif + LIST_BUILDER_FINALISE(fromlist, fromlist_builder); PYOBJECT_TYPE load_asyncio_temp = PYOBJECT_GLOBAL_LOAD(PYIDENT("asyncio.coroutines")); - module = PyImport_ImportModuleLevelObject(HPY_LEGACY_OBJECT_AS(load_asyncio_temp), NULL, NULL, fromlist, 0); + module = HPY_LEGACY_OBJECT_FROM(PyImport_ImportModuleLevelObject(HPY_LEGACY_OBJECT_AS(load_asyncio_temp), NULL, NULL, HPY_LEGACY_OBJECT_AS(fromlist), 0)); PYOBJECT_GLOBAL_CLOSEREF(load_asyncio_temp); - Py_DECREF(fromlist); - if (unlikely(!module)) goto ignore; - PYOBJECT_FIELD_STORE(op, struct_op->func_is_coroutine, __Pyx_PyObject_GetAttrStr(HPY_LEGACY_OBJECT_FROM(module), HPY_LEGACY_OBJECT_FROM(marker))); - Py_DECREF(module); - if (likely(!API_IS_NULL(load_is_coroutine_temp))) { + PYOBJECT_CLOSEREF(fromlist); + if (unlikely(API_IS_NULL(module))) goto ignore; + PYOBJECT_FIELD_STORE(op, struct_op->func_is_coroutine, __Pyx_PyObject_GetAttrStr(module, marker)); + PYOBJECT_CLOSEREF(module); + if (likely(API_IS_NOY_NULL(load_is_coroutine_temp))) { #if CYTHON_USING_HPY return load_is_coroutine_temp; #else diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index c89c79b9cd3..9a8023765cf 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -71,6 +71,7 @@ #define DICT_CHECK(o) HPyDict_Check(HPY_CONTEXT_CNAME, o) #define DICT_CHECK_EXACT(o) HPyDict_Check(HPY_CONTEXT_CNAME, o) #define TUPLE_CHECK(o) HPyTuple_Check(HPY_CONTEXT_CNAME, o) + #define LIST_CHECK(h) HPyList_Check(HPY_CONTEXT_CNAME, h) #define PYOBJECT_TYPE_CHECK(o, t) HPy_TypeCheck(HPY_CONTEXT_CNAME, o, t) #define LIST_CHECK(h) HPyList_Check(HPY_CONTEXT_CNAME, h) #define LIST_CHECK_EXACT(h) PyList_CheckExact(h) @@ -136,8 +137,8 @@ //List Type #define LIST_CREATE_EMPTY() HPyList_New(HPY_CONTEXT_CNAME, 0) - #define LIST_GET_ITEM(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, PYOBJECT_FROM_LONG(pos)) - #define LIST_APPEND(list, h) PyList_Append(list, h) + #define LIST_GET_ITEM(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, PYOBJECT_LONG_FROM_LONG(pos)) + #define LIST_APPEND(list, h) HPyList_Append(HPY_CONTEXT_CNAME, list, h) #define LIST_BUILDER_TYPE HPyListBuilder #define LIST_CREATE_START(target, builder, size) builder = HPyListBuilder_New(HPY_CONTEXT_CNAME, size) #define LIST_CREATE_ASSIGN(tuple, builder, index, item) HPyListBuilder_Set(HPY_CONTEXT_CNAME, builder, index, item) diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 82fb3653da0..d4955f5eaff 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -437,6 +437,8 @@ /* Module state is not yet supported in HPy */ #define CYTHON_USE_MODULE_STATE 0 /* Any Python objects are generally opaque in HPy */ + #undef CYTHON_ASSUME_SAFE_MACROS + #define CYTHON_ASSUME_SAFE_MACROS 0 #undef CYTHON_USE_PYLONG_INTERNALS #define CYTHON_USE_PYLONG_INTERNALS 0 #undef CYTHON_USE_UNICODE_INTERNALS diff --git a/Cython/Utility/Optimize.c b/Cython/Utility/Optimize.c index 84fa3997e10..2fd36d62f88 100644 --- a/Cython/Utility/Optimize.c +++ b/Cython/Utility/Optimize.c @@ -18,7 +18,7 @@ static CYTHON_INLINE int __Pyx_PyObject_Append(HPY_CONTEXT_FIRST_ARG_DEF PYOBJEC if (likely(LIST_CHECK_EXACT(L))) { if (unlikely(__Pyx_PyList_Append(L, x) < 0)) return -1; } else { - PYOBJECT_TYPE retval = HPY_LEGACY_OBJECT_FROM(__Pyx_PyObject_CallMethod1(HPY_LEGACY_OBJECT_AS(L), HPY_LEGACY_OBJECT_AS(PYOBJECT_GLOBAL_LOAD(PYIDENT("append"))), HPY_LEGACY_OBJECT_AS(x))); + PYOBJECT_TYPE retval = __Pyx_PyObject_CallMethod1(HPY_CONTEXT_FIRST_ARG_CALL L, PYOBJECT_GLOBAL_LOAD(PYIDENT("append")), x); if (unlikely(API_IS_NULL(retval))) return -1; PYOBJECT_CLOSEREF(retval); diff --git a/tests/run/hpy_array.pyx b/tests/run/hpy_array.pyx new file mode 100644 index 00000000000..925753d9bbc --- /dev/null +++ b/tests/run/hpy_array.pyx @@ -0,0 +1,108 @@ +#mode: run + +def create_array(): + """ + >>> create_array() + [1, 2, 'a', 'b', True] + """ + arr = [1, 2, 'a', 'b', True] + return arr + +def array_pop(arr, index): + """ + >>> arr = [1, 2, 'a', 'b', True] + >>> array_pop(arr, 2) + [1, 2, 'b', True] + """ + arr.pop(index) + return arr + +def array_append(arr, value): + """ + >>> arr = [1, 2, 'a', 'b', True] + >>> array_append(arr, 3) + [1, 2, 'a', 'b', True, 3] + """ + arr.append(value) + return arr + +def array_extend(arr, new_arr): + """ + >>> arr = [1, 2, 'a', 'b', True] + >>> new_arr = [3, 4, '#'] + >>> array_extend(arr, new_arr) + [1, 2, 'a', 'b', True, 3, 4, '#'] + """ + arr.extend(new_arr) + return arr + +def array_clear(arr): + """ + >>> arr = [1, 2, 'a', 'b', True] + >>> array_clear(arr) + + """ + return arr.clear() + +def array_copy(arr): + """ + >>> arr = [1, 2, 'a', 'b', True] + >>> array_copy(arr) + [1, 2, 'a', 'b', True] + """ + arr2 = arr.copy() + return arr2 + +def array_count(arr): + """ + >>> arr = [1, 2, 'a', 'b', False, 1] + >>> array_count(arr) + 2 + """ + return arr.count(1) + +def array_index(arr, val): + """ + >>> arr = [1, 2, 'a', 'b', False, 1] + >>> array_index(arr, 1) + 0 + >>> array_index(arr, 2) + 1 + """ + return arr.index(val) + +def array_insert(arr, index, val): + """ + >>> arr = [1, 2, 'a', 'b', False, 1] + >>> array_insert(arr, 3, 4) + [1, 2, 'a', 4, 'b', False, 1] + """ + arr.insert(index, val) + return arr + +def array_remove(arr, value): + """ + >>> arr = [1, 2, 'a', 'b', False, 1] + >>> array_remove(arr, 1) + [2, 'a', 'b', False, 1] + """ + arr.remove(value) + return arr + +def array_reverse(arr): + """ + >>> arr = [1, 2, 'a', 'b', False] + >>> array_reverse(arr) + [False, 'b', 'a', 2, 1] + """ + arr.reverse() + return arr + +def array_sort(arr): + """ + >>> arr = [1, 2, 10, 3, 11, -5, 6] + >>> array_sort(arr) + [-5, 1, 2, 3, 6, 10, 11] + """ + arr.sort() + return arr From 3b308d97100df14a42f0c242aae036c832767c46 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 13 Nov 2023 14:55:01 +0100 Subject: [PATCH 227/286] Fixed issues arising in C++ tests --- Cython/Compiler/Code.py | 1 - Cython/Compiler/PyrexTypes.py | 4 ++-- Cython/Utility/CythonFunction.c | 12 ++++++------ Cython/Utility/Exceptions.c | 4 +--- Cython/Utility/HPyUtils.c | 3 +++ Cython/Utility/TypeConversion.c | 6 +++++- tests/run/hpy_forloop.pyx | 4 ++-- 7 files changed, 19 insertions(+), 15 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 1aa5d8bd94e..28ce2d77050 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1539,7 +1539,6 @@ def generate_object_constant_decls(self): for c in self.py_constants] consts.sort() for _, cname, c in consts: - c.type.is_global = True self.parts['module_state'].putln("PYOBJECT_GLOBAL_TYPE %s;" % cname) self.parts['module_state_defines'].putln( "#define %s %s->%s" % (cname, Naming.modulestateglobal_cname, cname)) diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 96fa664f8ab..ade647ecf50 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -1492,7 +1492,7 @@ class BuiltinObjectType(PyObjectType): vtabptr_cname = None typedef_flag = True is_external = True - decl_type = 'PYOBJECT_TYPE' + decl_type = 'PYOBJECT_TYPE_NO_POINTER' def __init__(self, name, cname, objstruct_cname=None): self.name = name @@ -1601,7 +1601,7 @@ def declaration_code(self, entity_code, return self.base_declaration_code(base_code, entity_code) def as_pyobject(self, cname): - if self.decl_type == 'PYOBJECT_TYPE': + if self.decl_type == 'PYOBJECT_TYPE_NO_POINTER': return cname else: return "(PYOBJECT_TYPE)" + cname diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 21accd6f9fc..b510ece9908 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -472,7 +472,7 @@ __Pyx_CyFunction_set_defaults(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_F if (API_IS_NULL(value)) { // del => explicit None to prevent rebuilding value = API_ASSIGN_NONE; - } else if (unlikely(!API_IS_EQUAL(value, API_NONE_VALUE) && !DICT_CHECK(value))) { + } else if (unlikely(API_IS_NOT_EQUAL(value, API_NONE_VALUE) && !TUPLE_CHECK(value))) { PyErr_SetString(PyExc_TypeError, "__defaults__ must be set to a tuple object"); return -1; @@ -519,7 +519,7 @@ __Pyx_CyFunction_set_kwdefaults(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject if (API_IS_NULL(value)) { // del => explicit None to prevent rebuilding value = API_ASSIGN_NONE; - } else if (unlikely(API_IS_EQUAL(value, API_NONE_VALUE) && !DICT_CHECK(value))) { + } else if (unlikely(API_IS_NOT_EQUAL(value, API_NONE_VALUE) && !DICT_CHECK(value))) { PyErr_SetString(PyExc_TypeError, "__kwdefaults__ must be set to a dict object"); return -1; @@ -636,9 +636,9 @@ __Pyx_CyFunction_get_is_coroutine(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObje } #endif #else - LIST_BUILDER_ASSIGN(fromlist, fromlist_builder, 0, marker); + LIST_CREATE_ASSIGN(fromlist, fromlist_builder, 0, marker); #endif - LIST_BUILDER_FINALISE(fromlist, fromlist_builder); + LIST_CREATE_FINALISE(fromlist, fromlist_builder); PYOBJECT_TYPE load_asyncio_temp = PYOBJECT_GLOBAL_LOAD(PYIDENT("asyncio.coroutines")); module = HPY_LEGACY_OBJECT_FROM(PyImport_ImportModuleLevelObject(HPY_LEGACY_OBJECT_AS(load_asyncio_temp), NULL, NULL, HPY_LEGACY_OBJECT_AS(fromlist), 0)); PYOBJECT_GLOBAL_CLOSEREF(load_asyncio_temp); @@ -646,7 +646,7 @@ __Pyx_CyFunction_get_is_coroutine(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObje if (unlikely(API_IS_NULL(module))) goto ignore; PYOBJECT_FIELD_STORE(op, struct_op->func_is_coroutine, __Pyx_PyObject_GetAttrStr(module, marker)); PYOBJECT_CLOSEREF(module); - if (likely(API_IS_NOY_NULL(load_is_coroutine_temp))) { + if (likely(API_IS_NOT_NULL(load_is_coroutine_temp))) { #if CYTHON_USING_HPY return load_is_coroutine_temp; #else @@ -842,7 +842,7 @@ static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFun if (unlikely(API_IS_NULL(op))) return API_NULL_VALUE; struct_op->flags = flags; - PYOBJECT_FIELD_STORE(op, struct_op->func_self, op); + PYOBJECT_FIELD_STORE(op, struct_op->func_self, (PYOBJECT_TYPE) op); PYOBJECT_FIELD_STORE(op, struct_op->func_module, module); PYOBJECT_FIELD_STORE(op, struct_op->func_closure, closure); PYOBJECT_FIELD_STORE(op, struct_op->func_dict, API_NULL_VALUE); diff --git a/Cython/Utility/Exceptions.c b/Cython/Utility/Exceptions.c index 95b0f6ced58..91054df8b71 100644 --- a/Cython/Utility/Exceptions.c +++ b/Cython/Utility/Exceptions.c @@ -1008,14 +1008,12 @@ static void __Pyx_AddTraceback(HPY_CONTEXT_FIRST_ARG_DEF const char *funcname, i __Pyx_ErrRestoreInState(tstate, ptype, pvalue, ptraceback); $global_code_object_cache_insert(c_line ? -c_line : py_line, py_code); } - PYOBJECT_TYPE load_moddict_temp = PYOBJECT_GLOBAL_LOAD($moddict_cname); py_frame = PyFrame_New( tstate, /*PyThreadState *tstate,*/ py_code, /*PyCodeObject *code,*/ - HPY_LEGACY_OBJECT_AS(load_moddict_temp), /*PyObject *globals,*/ + $moddict_cname, /*PyObject *globals,*/ 0 /*PyObject *locals*/ ); - PYOBJECT_GLOBAL_CLOSEREF(load_moddict_temp); if (!py_frame) goto bad; __Pyx_PyFrame_SetLineNumber(py_frame, py_line); PyTraceBack_Here(py_frame); diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 9a8023765cf..f8653120d16 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -12,6 +12,7 @@ //Handle/PyObject Macros #define PYOBJECT_TYPE HPy + #define PYOBJECT_TYPE_NO_POINTER HPy #define PYTYPEOBJECT_TYPE HPy #define PYOBJECT_FIELD_TYPE HPyField #define PYOBJECT_FIELD_STORE(owner, field, h) HPyField_Store(HPY_CONTEXT_CNAME, owner, &field, h) @@ -184,6 +185,7 @@ //Handle/PyObject Macros #define PYOBJECT_TYPE PyObject * + #define PYOBJECT_TYPE_NO_POINTER PyObject #define PYTYPEOBJECT_TYPE PyTypeObject * #define PYOBJECT_FIELD_TYPE PyObject * #define PYOBJECT_FIELD_STORE(owner, field, h) field = h @@ -224,6 +226,7 @@ //General Methods #define API_IS_EQUAL(a, b) a==b + #define API_IS_NOT_EQUAL(a, b) a!=b #define API_RICH_COMPARE(h1, h2, op) PyObject_RichCompare(h1, h2, op) #define API_RICH_COMPARE_BOOL(h1, h2, op) PyObject_RichCompareBool(h1, h2, op) diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index db5a9b771d9..6cb5274eed4 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -108,8 +108,12 @@ static CYTHON_INLINE size_t __Pyx_Py_UNICODE_strlen(const Py_UNICODE *u) #define __Pyx_PyUnicode_FromUnicodeAndLength PyUnicode_FromUnicode #define __Pyx_PyUnicode_AsUnicode PyUnicode_AsUnicode +#if CYTHON_USING_HPY #define __Pyx_hNewRef(obj) PYOBJECT_NEWREF(obj) //This will be merged into Pyx_NewRef eventually, but #if CYTHON_USING_HPY will make all calls to this macro use HPy, even if it hasn't been ported yet -#define __Pyx_NewRef(obj) Py_NewRef(obj) +#else +#define __Pyx_hNewRef(obj) (Py_INCREF(obj), obj) +#endif +#define __Pyx_NewRef(obj) (Py_INCREF(obj), obj) #define __Pyx_Owned_Py_None(b) __Pyx_hNewRef(API_NONE_VALUE) static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyBool_FromLong(HPY_CONTEXT_FIRST_ARG_DEF long b); static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*); diff --git a/tests/run/hpy_forloop.pyx b/tests/run/hpy_forloop.pyx index 862830d7a03..af589313cf2 100644 --- a/tests/run/hpy_forloop.pyx +++ b/tests/run/hpy_forloop.pyx @@ -5,9 +5,9 @@ import cython def add(): """ >>> add() - 49999995000000 + 4999999950000000 """ a = 0 - for i in range(10000000): + for i in range(100000000): a = a + i return a From 093956547fb7accafebd0cec7e8bf5bbd86279a9 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Tue, 21 Nov 2023 15:25:19 +0100 Subject: [PATCH 228/286] Fixed remaining C++ test issues --- Cython/Compiler/Code.py | 12 +++++-- Cython/Compiler/ExprNodes.py | 63 +++++++++++++++++++-------------- Cython/Utility/HPyUtils.c | 4 +-- Cython/Utility/MemoryView_C.c | 2 +- Cython/Utility/ObjectHandling.c | 2 +- 5 files changed, 50 insertions(+), 33 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 28ce2d77050..1be98acf95b 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1492,10 +1492,16 @@ def cached_unbound_method_call_code(self, obj_cname, type_cname, method_name, ar self.use_utility_code(UtilityCode.load_cached(utility_code_name, "ObjectHandling.c")) cache_cname = self.get_cached_unbound_method(type_cname, method_name) args = [obj_cname] + arg_cnames + args_with_load = [] + for arg in args: + if arg in self.const_cname_array: + args_with_load.append("PYOBJECT_GLOBAL_LOAD(%s)" % arg) + else: + args_with_load.append(arg) return "__Pyx_%s(HPY_CONTEXT_FIRST_ARG_CALL &%s, %s)" % ( utility_code_name, cache_cname, - ', '.join(args), + ', '.join(args_with_load), ) def add_cached_builtin_decl(self, entry): @@ -2332,9 +2338,9 @@ def put_init_to_py_none(self, cname, type, nanny=True): from .PyrexTypes import py_object_type, typecast py_none = typecast(type, py_object_type, "API_NONE_VALUE") if nanny: - self.putln("%s = __Pyx_hNewRef(API_NONE_VALUE);" % (cname)) + self.putln("%s = __Pyx_hNewRef(%s);" % (cname, py_none)) else: - self.putln("%s = PYOBJECT_NEWREF(API_NONE_VALUE);" % (cname)) + self.putln("%s = PYOBJECT_NEWREF(%s);" % (cname, py_none)) def put_init_var_to_py_none(self, entry, template = "%s", nanny=True): code = template % entry.cname diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index c71850b407c..678b1ff743c 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -4646,6 +4646,15 @@ def generate_setitem_code(self, value, code): UtilityCode.load_cached("SetItemInt", "ObjectHandling.c")) function = "__Pyx_SetItemInt" index_code = self.index.result() + code.putln(code.error_goto_if_neg( + "%s(%s, %s, %s%s)" % ( + function, + self.base.py_result(), + index_code, + value_code, + self.extra_index_params(code)), + self.pos)) + else: index_code = self.index.py_result() if self.base.type is dict_type: @@ -4660,32 +4669,32 @@ def generate_setitem_code(self, value, code): else: function = "PYOBJECT_SET_ITEM" - temp_load_index = code.funcstate.allocate_temp(py_object_type, manage_ref=False) - if index_code in code.globalstate.const_cname_array: - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_index, index_code)) - else: - code.putln("%s = %s;" % (temp_load_index, index_code)) - temp_load_value = code.funcstate.allocate_temp(py_object_type, manage_ref=False) - if value_code in code.globalstate.const_cname_array: - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_value, value_code)) - else: - code.putln("%s = %s;" % (temp_load_value, value_code)) + temp_load_index = code.funcstate.allocate_temp(py_object_type, manage_ref=False) + if index_code in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_index, index_code)) + else: + code.putln("%s = %s;" % (temp_load_index, index_code)) + temp_load_value = code.funcstate.allocate_temp(py_object_type, manage_ref=False) + if value_code in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_value, value_code)) + else: + code.putln("%s = %s;" % (temp_load_value, value_code)) - code.putln(code.error_goto_if_neg( - "%s(%s, %s, %s%s)" % ( - function, - self.base.py_result(), - temp_load_index, - temp_load_value, - self.extra_index_params(code)), - self.pos)) + code.putln(code.error_goto_if_neg( + "%s(%s, %s, %s%s)" % ( + function, + self.base.py_result(), + index_code, + value_code, + self.extra_index_params(code)), + self.pos)) - if index_code in code.globalstate.const_cname_array: - code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_index) - code.funcstate.release_temp(temp_load_index) - if value_code in code.globalstate.const_cname_array: - code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_value) - code.funcstate.release_temp(temp_load_value) + if index_code in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_index) + code.funcstate.release_temp(temp_load_index) + if value_code in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_value) + code.funcstate.release_temp(temp_load_value) def generate_assignment_code(self, rhs, code, overloaded_assignment=False, exception_check=None, exception_value=None): @@ -8422,9 +8431,10 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): code.put_incref(arg.result(), arg.ctype()) arg.generate_giveref(code) code.putln("#endif") + code.putln("{") tmp_load_arg = code.funcstate.allocate_temp(py_object_type, manage_ref=False) if arg.py_result() in code.globalstate.const_cname_array: - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_arg, arg.py_result())) + code.putln("PYOBJECT_TYPE %s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_arg, arg.py_result())) else: code.putln("%s = %s;" % (tmp_load_arg, arg.py_result())) code.putln("%s(%s, %s, %s, %s);" % ( # %s;" % ( @@ -8437,7 +8447,8 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): if arg in code.globalstate.const_cname_array: code.putln("PYOBJECT_CLOSEREF(%s);" % tmp_load_arg) code.funcstate.release_temp(tmp_load_arg) - + code.putln("}") + code.putln("%s(%s, %s);" % (build_func, target, tmp_builder)) code.funcstate.release_temp(tmp_builder) diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index f8653120d16..1fc2585e6eb 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -198,8 +198,8 @@ #define CAPI_NEEDS_DEREFERENCE & //Create New and Close References - #define PYOBJECT_NEWREF(h) Py_NewRef(h) - #define PYOBJECT_XNEWREF(h) Py_XNewRef(h) + #define PYOBJECT_NEWREF(h) (Py_INCREF(h), h) + #define PYOBJECT_XNEWREF(h) (Py_XINCREF(h), h) #define PYOBJECT_CLOSEREF(h) Py_DECREF(h) #define PYOBJECT_XCLOSEREF(h) Py_XDECREF(h) #define PYOBJECT_GLOBAL_CLOSEREF(ref) /* nop */ diff --git a/Cython/Utility/MemoryView_C.c b/Cython/Utility/MemoryView_C.c index 1307e6e0d60..7cfb01bbd28 100644 --- a/Cython/Utility/MemoryView_C.c +++ b/Cython/Utility/MemoryView_C.c @@ -26,7 +26,7 @@ typedef struct { #endif // using CYTHON_ATOMICS as a cdef extern bint in the Cython memoryview code // interacts badly with "import *". Therefore, define a helper function-like macro -#define __PYX_CYTHON_ATOMICS_ENABLED() CYTHON_ATOMICS +#define __PYX_CYTHON_ATOMICS_ENABLED(HPY_CONTEXT_FIRST_ARG_CALL) CYTHON_ATOMICS #define __pyx_atomic_int_type int #define __pyx_nonatomic_int_type int diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 1ce005e9a88..668163db2ed 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -595,7 +595,7 @@ static CYTHON_INLINE int __Pyx_SetItemInt_Fast(HPY_CONTEXT_FIRST_ARG_DEF PYOBJEC } #endif PYOBJECT_TYPE tmp_load_v = PYOBJECT_GLOBAL_LOAD(v); - int retval = __Pyx_SetItemInt_Generic(HPY_CONTEXT_FIRST_ARG_CALL o, PYOBJECT_LONG_FROM_SSIZE(i), tmp_load_v); + int retval = __Pyx_SetItemInt_Generic(HPY_CONTEXT_FIRST_ARG_CALL o, PYOBJECT_LONG_FROM_SSIZE_T(i), tmp_load_v); PYOBJECT_CLOSEREF(tmp_load_v); return retval; } From cfe0f2c4df233e22d38191f1ab0480c4758cfd54 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 16 Nov 2023 14:34:07 +0100 Subject: [PATCH 229/286] Made HPy tests work again --- Cython/Compiler/Code.py | 1 + Cython/Compiler/Nodes.py | 12 ++++++++++-- Cython/Utility/ObjectHandling.c | 4 ++++ Cython/Utility/Optimize.c | 8 ++++---- Cython/Utility/TypeConversion.c | 20 ++++++++++---------- tests/run/hpy_array.pyx | 2 +- tests/run/hpy_basic.pyx | 16 ++-------------- tests/run/hpy_basic_noargs.pyx | 4 ++++ 8 files changed, 36 insertions(+), 31 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 1be98acf95b..fb17e649137 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1334,6 +1334,7 @@ def close_global_decls(self): def put_pyobject_decl(self, entry): self['global_var'].putln("static PYOBJECT_GLOBAL_TYPE %s;" % entry.cname) + self.const_cname_array.append(entry.cname) # constant handling at code generation time diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 784ca38cde7..2ee769a51fd 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -7042,12 +7042,20 @@ def generate_execution_code(self, code): else: cause_code = "0" code.globalstate.use_utility_code(raise_utility_code) + tmp_load_type = code.funcstate.allocate_temp(py_object_type, False) + if type_code in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_type, type_code)) + else: + code.putln("%s = %s;" % (tmp_load_type, type_code)) code.putln( - "__Pyx_Raise(%s, %s, %s, %s);" % ( - type_code, + "__Pyx_Raise(HPY_LEGACY_OBJECT_AS(%s), %s, %s, %s);" % ( + tmp_load_type, value_code, tb_code, cause_code)) + if type_code in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_load_type) + code.funcstate.release_temp(tmp_load_type) for obj in (self.exc_type, self.exc_value, self.exc_tb, self.cause): if obj: obj.generate_disposal_code(code) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 668163db2ed..482deeae5fe 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1570,7 +1570,11 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStrNoError(HPY_CONTEXT_ return _PyObject_GenericGetAttrWithDict(obj, attr_name, NULL, 1); } #endif +#if !CYTHON_USING_HPY result = __Pyx_PyObject_GetAttrStr(obj, attr_name); +#else + result = PYOBJECT_GET_ITEM(obj, attr_name); +#endif if (unlikely(API_IS_NULL(result))) { __Pyx_PyObject_GetAttrStr_ClearAttributeError(); } diff --git a/Cython/Utility/Optimize.c b/Cython/Utility/Optimize.c index 2fd36d62f88..b438f2e0f81 100644 --- a/Cython/Utility/Optimize.c +++ b/Cython/Utility/Optimize.c @@ -1131,7 +1131,7 @@ return_compare = ( static CYTHON_INLINE {{c_ret_type}} __Pyx_PyInt_{{'' if ret_type.is_pyobject else 'Bool'}}{{op}}{{order}}(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE op1, PYOBJECT_TYPE op2, long intval, long inplace) { CYTHON_MAYBE_UNUSED_VAR(intval); CYTHON_UNUSED_VAR(inplace); - if (op1 == op2) { + if (API_IS_EQUAL(op1, op2)) { {{return_true if op == 'Eq' else return_false}}; } @@ -1171,14 +1171,14 @@ static CYTHON_INLINE {{c_ret_type}} __Pyx_PyInt_{{'' if ret_type.is_pyobject els } #endif - if (PyFloat_CheckExact({{pyval}})) { + if (FLOAT_CHECK_EXACT({{pyval}})) { const long {{'a' if order == 'CObj' else 'b'}} = intval; double {{ival}} = __Pyx_PyFloat_AS_DOUBLE({{pyval}}); {{return_compare('(double)a', '(double)b', c_op)}} } return {{'' if ret_type.is_pyobject else '__Pyx_PyObject_IsTrueAndDecref'}}( - PyObject_RichCompare(op1, op2, Py_{{op.upper()}})); + HPY_CONTEXT_FIRST_ARG_CALL API_RICH_COMPARE(op1, op2, Py_{{op.upper()}})); } @@ -1413,7 +1413,7 @@ static {{c_ret_type}} {{cfunc_name}}(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE op1 {{if op in ('Eq', 'Ne')}} return {{'' if ret_type.is_pyobject else '__Pyx_PyObject_IsTrueAndDecref'}}( - API_RICH_COMPARE(op1, op2, Py_{{op.upper()}})); + HPY_CONTEXT_FIRST_ARG_CALL API_RICH_COMPARE(op1, op2, Py_{{op.upper()}})); {{else}} #if CYTHON_USING_HPY return HPy_{{op}}(HPY_CONTEXT_CNAME, op1, op2); diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index 6cb5274eed4..d357946aa87 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -116,8 +116,8 @@ static CYTHON_INLINE size_t __Pyx_Py_UNICODE_strlen(const Py_UNICODE *u) #define __Pyx_NewRef(obj) (Py_INCREF(obj), obj) #define __Pyx_Owned_Py_None(b) __Pyx_hNewRef(API_NONE_VALUE) static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyBool_FromLong(HPY_CONTEXT_FIRST_ARG_DEF long b); -static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*); -static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject*); +static CYTHON_INLINE int __Pyx_PyObject_IsTrue(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE); +static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE); static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(HPY_CONTEXT_FIRST_ARG_DEF PyObject* x); #define __Pyx_PySequence_Tuple(obj) \ @@ -308,17 +308,17 @@ static CYTHON_INLINE const char* __Pyx_PyObject_AsStringAndSize(PyObject* o, Py_ } /* Note: __Pyx_PyObject_IsTrue is written to minimize branching. */ -static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject* x) { - int is_true = x == Py_True; - if (is_true | (x == Py_False) | (x == Py_None)) return is_true; - else return PyObject_IsTrue(x); +static CYTHON_INLINE int __Pyx_PyObject_IsTrue(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE x) { + int is_true = API_IS_EQUAL(x, API_TRUE); + if (is_true | API_IS_EQUAL(x, API_FALSE) | API_IS_EQUAL(x, API_NULL_VALUE)) return is_true; + else return API_IS_TRUE(x); } -static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject* x) { +static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE x) { int retval; - if (unlikely(!x)) return -1; - retval = __Pyx_PyObject_IsTrue(x); - Py_DECREF(x); + if (unlikely(API_IS_NULL(x))) return -1; + retval = __Pyx_PyObject_IsTrue(HPY_CONTEXT_FIRST_ARG_CALL x); + PYOBJECT_CLOSEREF(x); return retval; } diff --git a/tests/run/hpy_array.pyx b/tests/run/hpy_array.pyx index 925753d9bbc..7950787b493 100644 --- a/tests/run/hpy_array.pyx +++ b/tests/run/hpy_array.pyx @@ -105,4 +105,4 @@ def array_sort(arr): [-5, 1, 2, 3, 6, 10, 11] """ arr.sort() - return arr + return arr \ No newline at end of file diff --git a/tests/run/hpy_basic.pyx b/tests/run/hpy_basic.pyx index f41bb98be30..542ebfea416 100644 --- a/tests/run/hpy_basic.pyx +++ b/tests/run/hpy_basic.pyx @@ -1,22 +1,10 @@ #mode: run -#tag: hpy import cython -@cython.hpy -def add_int_hpy(a:int, b:int): +def add_int(a:int, b:int): """ - >>> add_int_hpy(1, 2) + >>> add_int(1, 2) 3 """ return a + b - -def add_int_capi(a:int, b:int): - """ - >>> add_int_capi(1, 2) - 3 - """ - return a + b - -assert add_int_hpy(22, 33) == 55 -assert add_int_capi(44, 55) == 99 \ No newline at end of file diff --git a/tests/run/hpy_basic_noargs.pyx b/tests/run/hpy_basic_noargs.pyx index 7864919af17..3e310d144e9 100644 --- a/tests/run/hpy_basic_noargs.pyx +++ b/tests/run/hpy_basic_noargs.pyx @@ -5,4 +5,8 @@ import cython @cython.hpy def add(): + """ + >>> add() + 3 + """ return 1+2 \ No newline at end of file From 4aa418ae5e56389790c92e7361471455409becae Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Tue, 21 Nov 2023 15:31:50 +0100 Subject: [PATCH 230/286] FIxed oversights in last commit --- Cython/Utility/HPyUtils.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 1fc2585e6eb..ef44fbc95f2 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -75,7 +75,7 @@ #define LIST_CHECK(h) HPyList_Check(HPY_CONTEXT_CNAME, h) #define PYOBJECT_TYPE_CHECK(o, t) HPy_TypeCheck(HPY_CONTEXT_CNAME, o, t) #define LIST_CHECK(h) HPyList_Check(HPY_CONTEXT_CNAME, h) - #define LIST_CHECK_EXACT(h) PyList_CheckExact(h) + #define LIST_CHECK_EXACT(h) HPyList_Check(HPY_CONTEXT_CNAME, h) //Integer Type - From #define PYOBJECT_INT_FROM_LONG(i) HPyLong_FromLong(HPY_CONTEXT_CNAME, i) @@ -123,7 +123,7 @@ #define DICT_GET_ITEM_WITH_ERROR(o, attr_name) HPyDict_GetItem(HPY_CONTEXT_CNAME, o, attr_name) //Sequence Type - #define SEQUENCE_GET_ITEM(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, pos) + #define SEQUENCE_GET_ITEM(h, pos) HPy_GetItem_i(HPY_CONTEXT_CNAME, h, pos) #define SEQUENCE_SET_ITEM(h, pos, o) HPy_SetItem_i(HPY_CONTEXT_CNAME, h, pos, o) //Tuple Type From 11dca3036e627d3e37945c201e28d1d491ee4af5 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Tue, 21 Nov 2023 15:48:45 +0100 Subject: [PATCH 231/286] Removed few unnecessary changes --- Cython/Compiler/Code.py | 2 -- Cython/Compiler/ExprNodes.py | 42 +++++++----------------------------- 2 files changed, 8 insertions(+), 36 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index fb17e649137..95867e0b576 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -2168,7 +2168,6 @@ def put_var_declaration(self, entry, storage_class="", self.put(entry.type.cpp_optional_declaration_code( entry.cname, dll_linkage=dll_linkage)) else: - entry.type.is_global = False self.put(entry.type.declaration_code( entry.cname, dll_linkage=dll_linkage)) if entry.type.is_pyobject: @@ -2185,7 +2184,6 @@ def put_temp_declarations(self, func_context): else: decl = type.declaration_code(name) if type.is_pyobject: - type.is_global=False self.putln("%s = API_NULL_VALUE;" % decl) elif type.is_memoryviewslice: self.putln("%s = %s;" % (decl, type.literal_code(type.default_value))) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 678b1ff743c..6cff5363905 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -4646,14 +4646,6 @@ def generate_setitem_code(self, value, code): UtilityCode.load_cached("SetItemInt", "ObjectHandling.c")) function = "__Pyx_SetItemInt" index_code = self.index.result() - code.putln(code.error_goto_if_neg( - "%s(%s, %s, %s%s)" % ( - function, - self.base.py_result(), - index_code, - value_code, - self.extra_index_params(code)), - self.pos)) else: index_code = self.index.py_result() @@ -4668,33 +4660,15 @@ def generate_setitem_code(self, value, code): # (PyTuple_SetItem() is for creating new tuples from scratch). else: function = "PYOBJECT_SET_ITEM" - - temp_load_index = code.funcstate.allocate_temp(py_object_type, manage_ref=False) - if index_code in code.globalstate.const_cname_array: - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_index, index_code)) - else: - code.putln("%s = %s;" % (temp_load_index, index_code)) - temp_load_value = code.funcstate.allocate_temp(py_object_type, manage_ref=False) - if value_code in code.globalstate.const_cname_array: - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_value, value_code)) - else: - code.putln("%s = %s;" % (temp_load_value, value_code)) - code.putln(code.error_goto_if_neg( - "%s(%s, %s, %s%s)" % ( - function, - self.base.py_result(), - index_code, - value_code, - self.extra_index_params(code)), - self.pos)) - - if index_code in code.globalstate.const_cname_array: - code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_index) - code.funcstate.release_temp(temp_load_index) - if value_code in code.globalstate.const_cname_array: - code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_value) - code.funcstate.release_temp(temp_load_value) + code.putln(code.error_goto_if_neg( + "%s(%s, %s, %s%s)" % ( + function, + self.base.py_result(), + index_code, + value_code, + self.extra_index_params(code)), + self.pos)) def generate_assignment_code(self, rhs, code, overloaded_assignment=False, exception_check=None, exception_value=None): From 1916537b6ca5871e83ba42eeab1aa203cc9a40a2 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 22 Nov 2023 11:30:55 +0100 Subject: [PATCH 232/286] Undo stolen reference --- Cython/Utility/ObjectHandling.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 482deeae5fe..7511c991ac7 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -535,7 +535,9 @@ static int __Pyx_SetItemInt_Generic(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE o, P int r; if (unlikely(API_IS_NULL(j))) return -1; r = PYOBJECT_SET_ITEM(o, j, v); +#if !CYTHON_USING_HPY PYOBJECT_CLOSEREF(j); +#endif return r; } @@ -595,8 +597,12 @@ static CYTHON_INLINE int __Pyx_SetItemInt_Fast(HPY_CONTEXT_FIRST_ARG_DEF PYOBJEC } #endif PYOBJECT_TYPE tmp_load_v = PYOBJECT_GLOBAL_LOAD(v); - int retval = __Pyx_SetItemInt_Generic(HPY_CONTEXT_FIRST_ARG_CALL o, PYOBJECT_LONG_FROM_SSIZE_T(i), tmp_load_v); + PYOBJECT_TYPE i_obj = PYOBJECT_LONG_FROM_SSIZE_T(i) + int retval = __Pyx_SetItemInt_Generic(HPY_CONTEXT_FIRST_ARG_CALL o, i_obj, tmp_load_v); PYOBJECT_CLOSEREF(tmp_load_v); +#if CYTHON_USING_HPY + PYOBJECT_CLOSEREF(i_obj); +#endif return retval; } From c36890ac440e92db6a3a26c59e29cd9b859bbe04 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 22 Nov 2023 11:50:49 +0100 Subject: [PATCH 233/286] Fixed comments for PR --- Cython/Compiler/PyrexTypes.py | 2 +- Cython/Utility/ObjectHandling.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index ade647ecf50..37ad466c519 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -1439,7 +1439,7 @@ def __lt__(self, other): class ListBuilderType(PyrexType): # - # Class for a C tuple builder + # Class for a C list builder # name = "list_builder" diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 7511c991ac7..ab55095e165 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -2166,7 +2166,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObj } if (nargs == 0) { - return __Pyx_PyObject_Call(func, PyTuple_New(0), kwargs); //Must be changed back to $empty_tuple when context is available + return __Pyx_PyObject_Call(func, PyTuple_New(0), kwargs); //TODO(HPy): Must be changed back to $empty_tuple when context is available } #if PY_VERSION_HEX >= 0x03090000 && !CYTHON_COMPILING_IN_LIMITED_API return PyObject_VectorcallDict(func, args, (size_t)nargs, kwargs); From 40898d41d00d5ade3d4224a3aaf99906d445553b Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Fri, 24 Nov 2023 13:07:02 +0100 Subject: [PATCH 234/286] Fixed issues from rebasing onto upstream --- Cython/Compiler/Builtin.py | 4 ++-- Cython/Compiler/Code.py | 2 +- Cython/Compiler/PyrexTypes.py | 18 ++++++++++----- Cython/Utility/CythonFunction.c | 40 ++++++++++++++++----------------- Cython/Utility/Exceptions.c | 2 +- Cython/Utility/HPyUtils.c | 13 ++++++++--- Cython/Utility/ObjectHandling.c | 19 +++++++++------- Cython/Utility/StringTools.c | 12 ++++++---- Cython/Utility/TypeConversion.c | 2 +- 9 files changed, 65 insertions(+), 47 deletions(-) diff --git a/Cython/Compiler/Builtin.py b/Cython/Compiler/Builtin.py index dac1fce8658..892770cfd34 100644 --- a/Cython/Compiler/Builtin.py +++ b/Cython/Compiler/Builtin.py @@ -302,7 +302,7 @@ def declare_in_type(self, self_type): BuiltinMethod("__mul__", "Tz", "T", "__Pyx_PySequence_Multiply", utility_code=UtilityCode.load("PySequenceMultiply", "ObjectHandling.c")), ]), - ("str", "API_STRING_TYPE", [BuiltinMethod("join", "TO", "T", "__Pyx_PyString_Join", + ("str", "API_STRING_TYPE_DEREF", [BuiltinMethod("join", "TO", "T", "__Pyx_PyString_Join", utility_code=UtilityCode.load("StringJoin", "StringTools.c")), BuiltinMethod("__mul__", "Tz", "T", "__Pyx_PySequence_Multiply", utility_code=UtilityCode.load("PySequenceMultiply", "ObjectHandling.c")), @@ -327,7 +327,7 @@ def declare_in_type(self, self_type): utility_code=UtilityCode.load("PySequenceMultiply", "ObjectHandling.c")), ]), - ("dict", "API_DICT_TYPE", [BuiltinMethod("__contains__", "TO", "b", "PyDict_Contains"), + ("dict", "API_DICT_TYPE_DEREF", [BuiltinMethod("__contains__", "TO", "b", "PyDict_Contains"), BuiltinMethod("has_key", "TO", "b", "PyDict_Contains"), BuiltinMethod("items", "T", "O", "__Pyx_PyDict_Items", utility_code=UtilityCode.load("py_dict_items", "Builtins.c")), diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 95867e0b576..1631643bd00 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1582,7 +1582,7 @@ def generate_cached_methods_decls(self): cname, type_cname)) init.putln('PYOBJECT_CLOSEREF(temp_meth_name);') init.putln('#else') - init.putln('%s.type = (PyObject*)&%s;' % ( + init.putln('%s.type = (PyObject*)%s;' % ( cname, type_cname)) init.putln('%s.method_name = &%s;' % ( cname, method_name_cname)) diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 37ad466c519..47abac9da5a 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -1436,7 +1436,7 @@ def __lt__(self, other): ('object') is always true """ return False - + class ListBuilderType(PyrexType): # # Class for a C list builder @@ -1497,7 +1497,7 @@ class BuiltinObjectType(PyObjectType): def __init__(self, name, cname, objstruct_cname=None): self.name = name self.cname = cname - self.typeptr_cname = "(CAPI_NEEDS_DEREFERENCE %s)" % cname + self.typeptr_cname = "(%s)" % cname self.objstruct_cname = objstruct_cname self.is_gc_simple = name in builtin_types_that_cannot_create_refcycles self.builtin_trashcan = name in builtin_types_with_trashcan @@ -1800,10 +1800,16 @@ def to_py_call_code(self, source_code, result_code, result_type, to_py_function= func = func.replace("Object", result_type_name.title(), 1) elif result_type_name == 'bytearray': func = func.replace("Object", "ByteArray", 1) - return '%s = %s(HPY_CONTEXT_FIRST_ARG_CALL %s)' % ( - result_code, - func, - source_code or 'NULL') + if func == 'PyInt_FromSsize_t': + return '%s = %s(%s)' % ( + result_code, + func, + source_code or 'NULL') + else: + return '%s = %s(HPY_CONTEXT_FIRST_ARG_CALL %s)' % ( + result_code, + func, + source_code or 'NULL') def from_py_call_code(self, source_code, result_code, error_pos, code, from_py_function=None, error_condition=None, diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index b510ece9908..99ec2ffe5f3 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -90,12 +90,16 @@ HPyType_HELPERS(__pyx_CyFunctionObject) #endif #undef __Pyx_CyOrPyCFunction_Check -#define __Pyx_CyFunction_Check(obj) __Pyx_TypeCheck(obj, __pyx_CyFunctionType) +#define __Pyx_CyFunction_Check(obj) __Pyx_TypeCheck(obj, PYOBJECT_GLOBAL_LOAD(__pyx_CyFunctionType)) #define __Pyx_CyOrPyCFunction_Check(obj) __Pyx_TypeCheck2(obj, __pyx_CyFunctionType, &PyCFunction_Type) +#if !CYTHON_USING_HPY #define __Pyx_CyFunction_CheckExact(obj) __Pyx_IS_TYPE(obj, __pyx_CyFunctionType) -static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void *cfunc);/*proto*/ +#else +#define __Pyx_CyFunction_CheckExact(obj) __Pyx_TypeCheck(obj, __pyx_CyFunctionType) +#endif +static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE func, void *cfunc);/*proto*/ #undef __Pyx_IsSameCFunction -#define __Pyx_IsSameCFunction(func, cfunc) __Pyx__IsSameCyOrCFunction(func, cfunc) +#define __Pyx_IsSameCFunction(func, cfunc) __Pyx__IsSameCyOrCFunction(HPY_CONTEXT_FIRST_ARG_CALL func, cfunc) static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, PYMETHODDEF_TYPE *ml, @@ -149,27 +153,21 @@ static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD(PyObject //@requires: ObjectHandling.c::PyObjectGetAttrStr #if CYTHON_COMPILING_IN_LIMITED_API -static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void *cfunc) { - if (__Pyx_CyFunction_Check(func)) { - return PyCFunction_GetFunction(((__pyx_CyFunctionObject*)func)->func) == (PyCFunction) cfunc; - } else if (PyCFunction_Check(func)) { - return PyCFunction_GetFunction(func) == (PyCFunction) cfunc; - } - return 0; -} +static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE func, void *cfunc) { +#if CYTHON_USING_HPY + __pyx_CyFunctionObject *struct_func = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, func); #else -static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void *cfunc) { - return __Pyx_CyOrPyCFunction_Check(func) && __Pyx_CyOrPyCFunction_GET_FUNCTION(func) == (PyCFunction) cfunc; -} + __pyx_CyFunctionObject *struct_func = (__pyx_CyFunctionObject*) func; #endif - -#if CYTHON_COMPILING_IN_LIMITED_API -static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void *cfunc) { if (__Pyx_CyFunction_Check(func)) { - return PyCFunction_GetFunction(((__pyx_CyFunctionObject*)func)->func) == (PyCFunction) cfunc; + return PyCFunction_GetFunction(struct_func->func) == CAST_IF_CAPI(PyCFunction) cfunc; +#if CYTHON_USING_HPY + } +#else } else if (PyCFunction_Check(func)) { return PyCFunction_GetFunction(func) == (PyCFunction) cfunc; } +#endif return 0; } #else @@ -329,7 +327,7 @@ static int __Pyx_CyFunction_set_qualname(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObject_FuncDef op, PYOBJECT_TYPE value, void *context) { CYTHON_UNUSED_VAR(context); - if (unlikely(value == NULL || !PyUnicode_Check(HPY_LEGACY_OBJECT_AS((value)))) { + if (unlikely(API_IS_NULL(value) || !UNICODE_CHECK(value))) { PyErr_SetString(PyExc_TypeError, "__qualname__ must be set to a string object"); return -1; @@ -614,7 +612,7 @@ __Pyx_CyFunction_get_is_coroutine(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObje return __Pyx_hNewRef(load_is_coroutine_temp); } - is_coroutine = PYOBJECT_FIELD_LOAD(op, struct_op->flags) & __Pyx_CYFUNCTION_COROUTINE; + is_coroutine = struct_op->flags & __Pyx_CYFUNCTION_COROUTINE; if (is_coroutine) { PYOBJECT_TYPE load_marker_temp = PYOBJECT_GLOBAL_LOAD(PYIDENT("_is_coroutine")); PYOBJECT_TYPE module; @@ -728,7 +726,6 @@ static PyGetSetDef __pyx_CyFunction_getsets[] = { #endif {0, 0, 0, 0, 0} }; -#endif /* CYTHON_USING_HPY */ #if CYTHON_USING_HPY HPyDef_MEMBER(__pyx_CyFunction_member_module, "__module__", HPyMember_OBJECT, offsetof(__pyx_CyFunctionObject, func_module)) @@ -1446,6 +1443,7 @@ static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD(PyObject return ((__Pyx_PyCMethod)(void(*)(void))def->ml_meth)(self, cls, args, (size_t)nargs, kwnames); } #endif /* !CYTHON_USING_HPY */ +#endif #if CYTHON_USING_HPY HPyDef_SLOT(__Pyx_CyFunction_call, HPy_tp_call) diff --git a/Cython/Utility/Exceptions.c b/Cython/Utility/Exceptions.c index 91054df8b71..889c13c8294 100644 --- a/Cython/Utility/Exceptions.c +++ b/Cython/Utility/Exceptions.c @@ -883,7 +883,7 @@ static PYOBJECT_TYPE __Pyx_PyCode_Replace_For_AddTraceback(HPY_CONTEXT_FIRST_ARG return result; } #else - return NULL; + return API_NULL_VALUE; #endif } diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index ef44fbc95f2..360b4d2863a 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -23,6 +23,7 @@ #define PYOBJECT_GLOBAL_LOAD(global) HPyGlobal_Load(HPY_CONTEXT_CNAME, global) #define CAPI_IS_POINTER #define CAPI_NEEDS_DEREFERENCE + #define CAST_IF_CAPI(type) //Create New and Close References #define PYOBJECT_NEWREF(h) HPy_Dup(HPY_CONTEXT_CNAME, h) @@ -60,19 +61,20 @@ #define API_CALL_FUNC(callable, args, nargs, kwnames) HPy_Call(HPY_CONTEXT_CNAME, callable, args, nargs, kwnames) //Type Objects - #define API_INT_TYPE HPY_CONTEXT_CNAME->h_LongType #define API_LONG_TYPE HPY_CONTEXT_CNAME->h_LongType #define API_SSIZE_T HPy_ssize_t #define API_STRING_TYPE HPY_CONTEXT_CNAME->h_UnicodeType + #define API_STRING_TYPE_DEREF API_STRING_TYPE #define API_DICT_TYPE HPY_CONTEXT_CNAME->h_DictType + #define API_DICT_TYPE_DEREF API_DICT_TYPE //Type Checks #define LONG_CHECK(l) HPyNumber_Check(HPY_CONTEXT_CNAME, l) #define FLOAT_CHECK_EXACT(f) HPyNumber_Check(HPY_CONTEXT_CNAME, f) + #define UNICODE_CHECK(u) HPyUnicode_Check(HPY_CONTEXT_CNAME, u) #define DICT_CHECK(o) HPyDict_Check(HPY_CONTEXT_CNAME, o) #define DICT_CHECK_EXACT(o) HPyDict_Check(HPY_CONTEXT_CNAME, o) #define TUPLE_CHECK(o) HPyTuple_Check(HPY_CONTEXT_CNAME, o) - #define LIST_CHECK(h) HPyList_Check(HPY_CONTEXT_CNAME, h) #define PYOBJECT_TYPE_CHECK(o, t) HPy_TypeCheck(HPY_CONTEXT_CNAME, o, t) #define LIST_CHECK(h) HPyList_Check(HPY_CONTEXT_CNAME, h) #define LIST_CHECK_EXACT(h) HPyList_Check(HPY_CONTEXT_CNAME, h) @@ -152,6 +154,7 @@ #define PYOBJECT_SET_ATTR(o, attr_name, attr_val) HPy_SetAttr(HPY_CONTEXT_CNAME, o, attr_name, attr_val) #define PYOBJECT_GET_ATTR_STR(o, attr_name) HPy_GetAttr_s(HPY_CONTEXT_CNAME, o, attr_name) #define PYOBJECT_SET_ATTR_STR(o1, attr_name, o2) HPy_SetAttr_s(HPY_CONTEXT_CNAME, o1, attr_name, o2) + #define PYOBJECT_HASH(o) HPy_Hash(HPY_CONTEXT_CNAME, o) //Type Type #define TYPESPEC_TYPE HPyType_Spec @@ -196,6 +199,7 @@ #define PYOBJECT_GLOBAL_LOAD(global) global #define CAPI_IS_POINTER * //Some types are sometimes pointers and sometimes not (i.e. PyModuleDef) where the type is always the same in HPy #define CAPI_NEEDS_DEREFERENCE & + #define CAST_IF_CAPI(type) (type) //Create New and Close References #define PYOBJECT_NEWREF(h) (Py_INCREF(h), h) @@ -234,15 +238,17 @@ #define API_CALL_FUNC(callable, args, nargs, kwnames) PyObject_Call(callable, args, kwnames) //Type Objects - #define API_INT_TYPE PyInt_Type #define API_LONG_TYPE PyLong_Type #define API_SSIZE_T Py_ssize_t #define API_STRING_TYPE PyString_Type + #define API_STRING_TYPE_DEREF &PyString_Type #define API_DICT_TYPE PyDict_Type + #define API_DICT_TYPE_DEREF &PyDict_Type //Number Type Checks #define LONG_CHECK(l) PyLong_Check(l) #define FLOAT_CHECK_EXACT(f) PyFloat_CheckExact(f) + #define UNICODE_CHECK(u) PyUnicode_Check(u) #define DICT_CHECK(o) PyDict_Check(o) #define DICT_CHECK_EXACT(o) PyDict_CheckExact(o) #define TUPLE_CHECK(o) PyTuple_Check(o) @@ -326,6 +332,7 @@ #define PYOBJECT_SET_ATTR(o, attr_name, attr_val) PyObject_SetAttr(o, attr_name, attr_val) #define PYOBJECT_GET_ATTR_STR(o, attr_name) PyObject_GetAttrString(o, attr_name) #define PYOBJECT_SET_ATTR_STR(o1, attr_name, o2) PyObject_SetAttrString(o1, attr_name, o2) + #define PYOBJECT_HASH(o) PyObject_Hash(o) //Type Type #define TYPESPEC_TYPE PyType_Spec diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index ab55095e165..a918d39fe7f 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -597,7 +597,7 @@ static CYTHON_INLINE int __Pyx_SetItemInt_Fast(HPY_CONTEXT_FIRST_ARG_DEF PYOBJEC } #endif PYOBJECT_TYPE tmp_load_v = PYOBJECT_GLOBAL_LOAD(v); - PYOBJECT_TYPE i_obj = PYOBJECT_LONG_FROM_SSIZE_T(i) + PYOBJECT_TYPE i_obj = PYOBJECT_LONG_FROM_SSIZE_T(i); int retval = __Pyx_SetItemInt_Generic(HPY_CONTEXT_FIRST_ARG_CALL o, i_obj, tmp_load_v); PYOBJECT_CLOSEREF(tmp_load_v); #if CYTHON_USING_HPY @@ -2080,8 +2080,8 @@ static PYOBJECT_TYPE __Pyx__CallUnboundCMethod2(HPY_CONTEXT_FIRST_ARG_DEF __Pyx_ #if CYTHON_USING_HPY #define __Pyx_PyObject_FastCall(func, args, nargs) API_CALL_FUNC(func, args, (size_t)(nargs), API_NULL_VALUE) #else -#define __Pyx_PyObject_FastCall(func, args, nargs) __Pyx_PyObject_FastCallDict(func, args, (size_t)(nargs), NULL) -static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObject **args, size_t nargs, PyObject *kwargs); /*proto*/ +#define __Pyx_PyObject_FastCall(func, args, nargs) __Pyx_PyObject_FastCallDict(func, args, (size_t)(nargs), API_NULL_VALUE) +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_FastCallDict(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE func, PYOBJECT_TYPE *args, size_t nargs, PYOBJECT_TYPE kwargs); /*proto*/ #endif /////////////// PyObjectFastCall /////////////// @@ -2109,18 +2109,22 @@ static PyObject* __Pyx_PyObject_FastCall_fallback(PyObject *func, PyObject **arg } #endif -static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObject **args, size_t _nargs, PyObject *kwargs) { +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_FastCallDict(HPY_CONTEXT_FIRST_ARG_CALL PYOBJECT_TYPE func, PYOBJECT_TYPE *args, size_t _nargs, PYOBJECT_TYPE kwargs) { // Special fast paths for 0 and 1 arguments // NOTE: in many cases, this is called with a constant value for nargs // which is known at compile-time. So the branches below will typically // be optimized away. - Py_ssize_t nargs = __Pyx_PyVectorcall_NARGS(_nargs); +#if CYTHON_USING_HPY + API_SSIZE_T nargs = _nargs +#else + API_SSIZE_T nargs = __Pyx_PyVectorcall_NARGS(_nargs); +#endif #if CYTHON_COMPILING_IN_CPYTHON - if (nargs == 0 && kwargs == NULL) { + if (nargs == 0 && API_IS_NULL(kwargs)) { if (__Pyx_CyOrPyCFunction_Check(func) && likely( __Pyx_CyOrPyCFunction_GET_FLAGS(func) & METH_NOARGS)) return __Pyx_PyObject_CallMethO(func, NULL); } - else if (nargs == 1 && kwargs == NULL) { + else if (nargs == 1 && API_IS_NULL(kwargs)) { if (__Pyx_CyOrPyCFunction_Check(func) && likely( __Pyx_CyOrPyCFunction_GET_FLAGS(func) & METH_O)) return __Pyx_PyObject_CallMethO(func, args[0]); } @@ -2174,7 +2178,6 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObj return __Pyx_PyObject_FastCall_fallback(func, args, (size_t)nargs, kwargs); #endif } -#endif /////////////// PyObjectVectorCallKwBuilder.proto //////////////// //@requires: PyObjectFastCall diff --git a/Cython/Utility/StringTools.c b/Cython/Utility/StringTools.c index 8df65b6c3ec..cdb04b51798 100644 --- a/Cython/Utility/StringTools.c +++ b/Cython/Utility/StringTools.c @@ -26,23 +26,26 @@ static CYTHON_INLINE Py_ssize_t __Pyx_Py_UNICODE_ssize_strlen(const Py_UNICODE * //////////////////// InitStrings.proto //////////////////// +#if !CYTHON_USING_HPY static int __Pyx_InitStrings(HPY_CONTEXT_FIRST_ARG_DEF __Pyx_StringTabEntry *t); /*proto*/ +#endif //////////////////// InitStrings //////////////////// +#if !CYTHON_USING_HPY static int __Pyx_InitStrings(HPY_CONTEXT_FIRST_ARG_DEF __Pyx_StringTabEntry *t) { while (t->p) { PyObject *str; if (t->is_unicode | t->is_str) { if (t->intern) { - PYOBJECT_GLOBAL_STORE(CAPI_IS_POINTER str, HPY_LEGACY_OBJECT_FROM(PyUnicode_InternFromString(t->s))); + str = HPY_LEGACY_OBJECT_FROM(PyUnicode_InternFromString(t->s)); } else if (t->encoding) { - PYOBJECT_GLOBAL_STORE(CAPI_IS_POINTER str, HPY_LEGACY_OBJECT_FROM(PyUnicode_Decode(t->s, t->n - 1, t.encoding, NULL))); + str = HPY_LEGACY_OBJECT_FROM(PyUnicode_Decode(t->s, t->n - 1, t->encoding, NULL)); } else { - PYOBJECT_GLOBAL_STORE(CAPI_IS_POINTER str, HPY_LEGACY_OBJECT_FROM(PyUnicode_FromStringAndSize(t->s, t->n - 1))); + str = HPY_LEGACY_OBJECT_FROM(PyUnicode_FromStringAndSize(t->s, t->n - 1)); } } else { - PYOBJECT_GLOBAL_STORE(CAPI_IS_POINTER str, BYTES_FROM_STR_AND_SIZE(t->s, t->n - 1)); + str = BYTES_FROM_STR_AND_SIZE(t->s, t->n - 1); } if (!str) return -1; @@ -54,6 +57,7 @@ static int __Pyx_InitStrings(HPY_CONTEXT_FIRST_ARG_DEF __Pyx_StringTabEntry *t) } return 0; } +#endif //////////////////// BytesContains.proto //////////////////// diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index d357946aa87..aa578a75c6e 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -745,7 +745,7 @@ static CYTHON_INLINE PYOBJECT_TYPE {{TO_PY_FUNCTION}}(HPY_CONTEXT_FIRST_ARG_DEF // default is signed=False kwds = DICT_NEW(); if (API_IS_NULL(kwds)) goto limited_bad; - if (DICT_SET_ITEM_STR(kwds, "signed", __Pyx_hNewRef(Py_True))) goto limited_bad; + if (DICT_SET_ITEM_STR(kwds, "signed", __Pyx_hNewRef(API_TRUE))) goto limited_bad; } result = API_CALL_FUNC(from_bytes, &arg_tuple, 2, kwds); #else From 46f0225b144edd048b382fb651e7bd200f1f8164 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 27 Nov 2023 13:40:42 +0100 Subject: [PATCH 235/286] Fixed segfault on HPy when functions don't have a docstring --- Cython/Utility/CythonFunction.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 99ec2ffe5f3..4d45333faff 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -834,7 +834,9 @@ static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFun assert(ml->kind == HPyDef_Kind_Meth); struct_op->func = ml; PYOBJECT_FIELD_STORE(op, struct_op->func_qualname, qualname); - PYOBJECT_FIELD_STORE(op, struct_op->func_doc, HPyUnicode_FromString(HPY_CONTEXT_CNAME, ml->meth.doc)); + if (ml->meth.doc != NULL) { + PYOBJECT_FIELD_STORE(op, struct_op->func_doc, HPyUnicode_FromString(HPY_CONTEXT_CNAME, ml->meth.doc)); + } #endif /* !CYTHON_USING_HPY */ if (unlikely(API_IS_NULL(op))) return API_NULL_VALUE; From ae4ecfc13fa88429a6dbb6f36a9bb092e66a1726 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 27 Nov 2023 14:58:50 +0100 Subject: [PATCH 236/286] Add initialisation for func_doc if it is empty --- Cython/Utility/CythonFunction.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 4d45333faff..db3f86809d1 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -836,7 +836,9 @@ static PYOBJECT_TYPE __Pyx_CyFunction_Init(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFun PYOBJECT_FIELD_STORE(op, struct_op->func_qualname, qualname); if (ml->meth.doc != NULL) { PYOBJECT_FIELD_STORE(op, struct_op->func_doc, HPyUnicode_FromString(HPY_CONTEXT_CNAME, ml->meth.doc)); - } + } else { + struct_op->func_doc = HPyField_NULL; + } #endif /* !CYTHON_USING_HPY */ if (unlikely(API_IS_NULL(op))) return API_NULL_VALUE; From 25df331145ea8a61a163c7e7de5e7b360409b71f Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 22 Nov 2023 16:42:51 +0100 Subject: [PATCH 237/286] Added global loading temp node --- Cython/Compiler/ExprNodes.py | 387 ++++++++++++++--------------------- Cython/Compiler/Nodes.py | 41 ++-- 2 files changed, 173 insertions(+), 255 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 6cff5363905..0997aecf047 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -2439,21 +2439,13 @@ def generate_result_code(self, code): code.putln('PyErr_Clear();') code.globalstate.use_utility_code( UtilityCode.load_cached("GetModuleGlobalName", "ObjectHandling.c")) - load_cname_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=False) - code.putln("#if CYTHON_USING_HPY") - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_cname_temp, interned_cname)) - code.putln( - '__Pyx_GetModuleGlobalName(%s, %s);' % ( - self.result(), - load_cname_temp)) - code.putln("PYOBJECT_CLOSEREF(%s);" % load_cname_temp) - code.putln("#else") + load_cname_temp = HPyGlobalTempNode(interned_cname) + load_cname_temp.load_global(code) code.putln( '__Pyx_GetModuleGlobalName(%s, %s);' % ( self.result(), - interned_cname)) - code.putln("#endif") - code.funcstate.release_temp(load_cname_temp) + load_cname_temp.temp_var)) + load_cname_temp.release_global(code) if not self.cf_is_null: code.putln("}") code.putln(code.error_goto_if_null(self.result(), self.pos)) @@ -2465,11 +2457,14 @@ def generate_result_code(self, code): interned_cname = code.intern_identifier(self.entry.name) code.globalstate.use_utility_code( UtilityCode.load_cached("GetBuiltinName", "ObjectHandling.c")) + load_cname_temp = HPyGlobalTempNode(interned_cname) + load_cname_temp.load_global(code) code.putln( - '%s = __Pyx_GetBuiltinName(HPY_CONTEXT_FIRST_ARG_CALL PYOBJECT_GLOBAL_LOAD(%s)); %s' % ( + '%s = __Pyx_GetBuiltinName(HPY_CONTEXT_FIRST_ARG_CALL %s); %s' % ( self.result(), - interned_cname, + load_cname_temp.temp_var, code.error_goto_if_null_object(self.result(), self.pos))) + load_cname_temp.release_global(code) self.generate_gotref(code) elif entry.is_pyglobal or (entry.is_builtin and entry.scope.is_module_scope): @@ -2479,23 +2474,14 @@ def generate_result_code(self, code): if entry.scope.is_module_scope: code.globalstate.use_utility_code( UtilityCode.load_cached("GetModuleGlobalName", "ObjectHandling.c")) - load_cname_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=False) - code.putln("#if CYTHON_USING_HPY") - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_cname_temp, interned_cname)) + load_cname_temp = HPyGlobalTempNode(interned_cname) + load_cname_temp.load_global(code) code.putln( '__Pyx_GetModuleGlobalName(%s, %s); %s' % ( self.result(), - load_cname_temp, + load_cname_temp.temp_var, code.error_goto_if_null_object(self.result(), self.pos))) - code.putln("PYOBJECT_CLOSEREF(%s);" % load_cname_temp) - code.putln("#else") - code.putln( - '__Pyx_GetModuleGlobalName(%s, %s); %s' % ( - self.result(), - interned_cname, - code.error_goto_if_null_object(self.result(), self.pos))) - code.putln("#endif") - code.funcstate.release_temp(load_cname_temp) + load_cname_temp.release_global(code) else: # FIXME: is_pyglobal is also used for class namespace code.globalstate.use_utility_code( @@ -2546,8 +2532,7 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, interned_cname = code.intern_identifier(self.entry.name) namespace = self.entry.scope.namespace_cname rhs_result = rhs.py_result() - load_result_temp = rhs_result - b = False + load_result_value = rhs_result if entry.is_member: # if the entry is a member we have to cheat: SetAttr does not work # on types, so we create a descriptor which is then added to tp_dict. @@ -2556,12 +2541,9 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, setter = 'DICT_SET_ITEM' namespace = Naming.moddict_cname interned_cname = interned_cname - if rhs_result in code.globalstate.const_cname_array: - b = True - code.putln("#if CYTHON_USING_HPY") - load_result_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=False) - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_result_temp, rhs_result)) - code.putln("#endif") + load_result_temp = HPyGlobalTempNode(rhs_result) + load_result_temp.load_global(code) + load_result_value = load_result_temp.temp_var elif entry.is_pyclass_attr: # Special-case setting __new__ n = "SetNewInClass" if self.name == "__new__" else "SetNameInClass" @@ -2569,25 +2551,24 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, setter = '__Pyx_' + n else: assert False, repr(entry) - code.putln("#if CYTHON_USING_HPY") - load_namespace_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=False) - load_cname_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=False) - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_namespace_temp, namespace)) - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_cname_temp, interned_cname)) + code.putln("#if CYTHON_USING_HPY") + # Wrapping namespace and interned_cname in PyObjects breaks the C API + # version when not using the Limited API + load_namespace_temp = HPyGlobalTempNode(namespace) + load_namespace_temp.load_global(code) + load_cname_temp = HPyGlobalTempNode(interned_cname) + load_cname_temp.load_global(code) code.put_error_if_neg( self.pos, '%s(%s, %s, %s)' % ( setter, - load_namespace_temp, - load_cname_temp, - load_result_temp)) - code.putln("PYOBJECT_CLOSEREF(%s);" % load_namespace_temp) - code.putln("PYOBJECT_CLOSEREF(%s);" % load_cname_temp) - code.funcstate.release_temp(load_namespace_temp) - code.funcstate.release_temp(load_cname_temp) - if rhs_result in code.globalstate.const_cname_array and entry.scope.is_module_scope: - code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % load_result_temp) - code.funcstate.release_temp(load_result_temp) + load_namespace_temp.temp_var, + load_cname_temp.temp_var, + load_result_value)) + load_namespace_temp.release_global(code) + load_cname_temp.release_global(code) + if entry.scope.is_module_scope: + load_result_temp.release_global(code) code.putln("#else") code.put_error_if_neg( self.pos, @@ -3621,6 +3602,25 @@ class PyTempNode(TempNode): def __init__(self, pos, env): TempNode.__init__(self, pos, PyrexTypes.py_object_type, env) +class HPyGlobalTempNode(ExprNode): + # TempNode for potentially loading a HPy Global + def __init__(self, var_name): + self.var_name = var_name + + def load_global(self, code, needs_type=False): + self.temp_var = code.funcstate.allocate_temp(py_object_type, manage_ref=False) + if needs_type: + code.putln("PYOBJECT_TYPE %s;" % self.temp_var) + if self.var_name in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (self.temp_var, self.var_name)) + else: + code.putln("%s = %s;" % (self.temp_var, self.var_name)) + + def release_global(self, code): + if self.var_name in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % self.temp_var) + code.funcstate.release_temp(self.temp_var) + class RawCNameExprNode(ExprNode): subexprs = [] @@ -4661,15 +4661,24 @@ def generate_setitem_code(self, value, code): else: function = "PYOBJECT_SET_ITEM" + temp_load_index = HPyGlobalTempNode(index_code) + temp_load_index.load_global(code) + + temp_load_value = HPyGlobalTempNode(value_code) + temp_load_value.load_global(code) + code.putln(code.error_goto_if_neg( "%s(%s, %s, %s%s)" % ( function, self.base.py_result(), - index_code, - value_code, + temp_load_index.temp_var, + temp_load_value.temp_var, self.extra_index_params(code)), self.pos)) + temp_load_index.release_global(code) + temp_load_value.release_global(code) + def generate_assignment_code(self, rhs, code, overloaded_assignment=False, exception_check=None, exception_value=None): self.generate_subexpr_evaluation_code(code) @@ -6453,7 +6462,7 @@ def c_call_code(self, code=False): if len(arg_list_code) > 0: arg_string = "HPY_CONTEXT_FIRST_ARG_CALL " - if self.function.result() in ['DICT_COPY', '__Pyx_PyList_PopIndex', '__Pyx_PyObject_PopIndex']: + if self.function.result() in ['DICT_COPY', '__Pyx_PyList_PopIndex', '__Pyx_PyObject_PopIndex']: arg_string = "" for arg in arg_list_code: if code: @@ -6547,33 +6556,23 @@ def generate_result_code(self, code): code.globalstate.use_utility_code(UtilityCode.load_cached( "PyObjectCall", "ObjectHandling.c")) func_result = self.function.py_result() - tmp_func_result = code.funcstate.allocate_temp(py_object_type, False) - if func_result in code.globalstate.const_cname_array: - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_func_result, func_result)) - else: - code.putln("%s = %s;" % (tmp_func_result, func_result)) - tmp_arg_code = code.funcstate.allocate_temp(py_object_type, False) - if arg_code in code.globalstate.const_cname_array: - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_arg_code, arg_code)) - else: - code.putln("%s = %s;" % (tmp_arg_code, arg_code)) + tmp_func_result = HPyGlobalTempNode(func_result) + tmp_func_result.load_global(code) + func_result = self.function.py_result() + tmp_arg_code = HPyGlobalTempNode(arg_code) + tmp_arg_code.load_global(code) code.putln( "%s = __Pyx_PyObject_Call_h(%s, %s, API_NULL_VALUE); %s" % ( self.result(), - tmp_func_result, - tmp_arg_code, + tmp_func_result.temp_var, + tmp_arg_code.temp_var, code.error_goto_if_null_object(self.result(), self.pos))) - if func_result in code.globalstate.const_cname_array: - code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_func_result) - if arg_code in code.globalstate.const_cname_array: - code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_arg_code) - code.funcstate.release_temp(tmp_func_result) - code.funcstate.release_temp(tmp_arg_code) + tmp_func_result.release_global(code) + tmp_arg_code.release_global(code) self.generate_gotref(code) elif func_type.is_cfunction: nogil = not code.funcstate.gil_owned if self.has_optional_args: - code.putln("//Has Optional Args") actual_nargs = len(self.args) expected_nargs = len(func_type.args) - func_type.optional_arg_count self.opt_arg_struct = code.funcstate.allocate_temp( @@ -6790,22 +6789,17 @@ def attribute_is_likely_method(attr): loaded_vars = [] for arg in args: arg_result = arg.py_result() - load_tmp = code.funcstate.allocate_temp(py_object_type, False) + load_tmp = HPyGlobalTempNode(arg_result) loaded_vars.append(load_tmp) - if arg_result in code.globalstate.const_cname_array: - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_tmp, arg_result)) - else: - code.putln("%s = %s;" % (load_tmp, arg_result)) + load_tmp.load_global(code) code.putln("PYOBJECT_TYPE __pyx_callargs[%d%s] = {%s, %s};" % ( (len(args) + 1) if args else 2, extra_keyword_args, self_arg, - ', '.join(tmp for tmp in loaded_vars) if args else "NULL", + ', '.join(tmp.temp_var for tmp in loaded_vars) if args else "NULL", )) for tmp in loaded_vars: - if arg_result in code.globalstate.const_cname_array: - code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % load_tmp) - code.funcstate.release_temp(tmp) + tmp.release_global(code) if kwargs_key_value_pairs: for n, keyvalue in enumerate(kwargs_key_value_pairs): key_is_str = ( @@ -7289,21 +7283,16 @@ def generate_result_code(self, code): code.globalstate.use_utility_code(UtilityCode.load_cached( "PyObjectCall", "ObjectHandling.c")) pos_args_cname = self.positional_args.py_result() - tmp_pos_args = code.funcstate.allocate_temp(py_object_type, False) - if pos_args_cname in code.globalstate.const_cname_array: - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_pos_args, pos_args_cname)) - else: - code.putln("%s = %s;" % (tmp_pos_args, pos_args_cname)) + tmp_pos_args = HPyGlobalTempNode(pos_args_cname) + tmp_pos_args.load_global(code) code.putln( "%s = __Pyx_PyObject_Call_h(%s, %s, %s); %s" % ( self.result(), self.function.py_result(), - tmp_pos_args, + tmp_pos_args.temp_var, kwargs, code.error_goto_if_null_object(self.result(), self.pos))) - if pos_args_cname in code.globalstate.const_cname_array: - code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_pos_args) - code.funcstate.release_temp(tmp_pos_args) + tmp_pos_args.release_global(code) self.generate_gotref(code) @@ -8015,22 +8004,17 @@ def generate_result_code(self, code): code.globalstate.use_utility_code( UtilityCode.load_cached("PyObjectGetAttrStr", "ObjectHandling.c")) lookup_func_name = '__Pyx_PyObject_GetAttrStr' - temp_load_attr = code.funcstate.allocate_temp(py_object_type, manage_ref=False) attr_str = code.intern_identifier(self.attribute) - if attr_str in code.globalstate.const_cname_array: - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_attr, attr_str)) - else: - code.putln("%s = %s;" % (temp_load_attr, attr_str)) + temp_load_attr = HPyGlobalTempNode(attr_str) + temp_load_attr.load_global(code) code.putln( '%s = %s(%s, %s); %s' % ( self.result(), lookup_func_name, self.obj.py_result(), - temp_load_attr, + temp_load_attr.temp_var, code.error_goto_if_null_object(self.result(), self.pos))) - if attr_str in code.globalstate.const_cname_array: - code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_attr) - code.funcstate.release_temp(temp_load_attr) + temp_load_attr.release_global(code) self.generate_gotref(code) elif self.type.is_memoryviewslice: if self.is_memslice_transpose: @@ -8352,12 +8336,21 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): if self.type is tuple_type and (self.is_literal or self.slow) and not c_mult: # use PyTuple_Pack() to avoid generating huge amounts of one-time code - code.putln('PYOBJECT_GLOBAL_STORE(%s, TUPLE_PACK(%d, PYOBJECT_GLOBAL_LOAD(%s))); %s' % ( + loaded_args = [] + code.putln("{") + for arg in self.args: + temp_load_arg = HPyGlobalTempNode(arg.py_result()) + temp_load_arg.load_global(code, needs_type=True) + loaded_args.append(temp_load_arg) + code.putln('PYOBJECT_GLOBAL_STORE(%s, TUPLE_PACK(%d, %s)); %s' % ( target, len(self.args), - '), PYOBJECT_GLOBAL_LOAD('.join(arg.py_result() for arg in self.args), + ', '.join(arg.temp_var for arg in loaded_args), code.error_goto_if_null_object(target, self.pos))) + for arg in loaded_args: + arg.release_global(code) code.put_gotref(target, py_object_type) + code.putln("}") elif self.type.is_ctuple: for i, arg in enumerate(self.args): code.putln("%s.f%s = %s;" % ( @@ -8406,21 +8399,16 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): arg.generate_giveref(code) code.putln("#endif") code.putln("{") - tmp_load_arg = code.funcstate.allocate_temp(py_object_type, manage_ref=False) - if arg.py_result() in code.globalstate.const_cname_array: - code.putln("PYOBJECT_TYPE %s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_arg, arg.py_result())) - else: - code.putln("%s = %s;" % (tmp_load_arg, arg.py_result())) + tmp_load_arg = HPyGlobalTempNode(arg.py_result()) + tmp_load_arg.load_global(code, needs_type=True) code.putln("%s(%s, %s, %s, %s);" % ( # %s;" % ( set_item_func, target, tmp_builder, (offset and i) and ('%s + %s' % (offset, i)) or (offset or i), - tmp_load_arg))#, + tmp_load_arg.temp_var))#, #code.error_goto(self.pos))) - if arg in code.globalstate.const_cname_array: - code.putln("PYOBJECT_CLOSEREF(%s);" % tmp_load_arg) - code.funcstate.release_temp(tmp_load_arg) + tmp_load_arg.release_global(code) code.putln("}") code.putln("%s(%s, %s);" % (build_func, target, tmp_builder)) @@ -9627,29 +9615,18 @@ def generate_evaluation_code(self, code): code.error_goto(item.pos))) code.putln("} else {") - temp_load_key = code.funcstate.allocate_temp(py_object_type, manage_ref=False) - if item.key.py_result() in code.globalstate.const_cname_array: - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_key, item.key.py_result())) - else: - code.putln("%s = %s;" % (temp_load_key, item.key.py_result())) + temp_load_key = HPyGlobalTempNode(item.key.py_result()) + temp_load_key.load_global(code) - temp_load_value = code.funcstate.allocate_temp(py_object_type, manage_ref=False) - if item.value.py_result() in code.globalstate.const_cname_array: - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (temp_load_value, item.value.py_result())) - else: - code.putln("%s = %s;" % (temp_load_value, item.value.py_result())) + temp_load_value = HPyGlobalTempNode(item.value.py_result()) + temp_load_value.load_global(code) code.put_error_if_neg(self.pos, "DICT_SET_ITEM(%s, %s, %s)" % ( self.result(), - temp_load_key, - temp_load_value)) + temp_load_key.temp_var, + temp_load_value.temp_var)) - if item.key.py_result() in code.globalstate.const_cname_array: - code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_key) - code.funcstate.release_temp(temp_load_key) - - if item.value.py_result() in code.globalstate.const_cname_array: - code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % temp_load_value) - code.funcstate.release_temp(temp_load_value) + temp_load_key.release_global(code) + temp_load_value.release_global(code) if self.reject_duplicates and keys_seen is None: code.putln('}') @@ -10274,17 +10251,16 @@ def generate_cyfunction_code(self, code): else: flags = '0' - load_py_qual_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True) - load_py_mod_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True) - load_moddict_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True) - load_code_obj_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True) + load_py_qual_temp = HPyGlobalTempNode(self.get_py_qualified_name(code)) + load_py_mod_temp = HPyGlobalTempNode(self.get_py_mod_name(code)) + load_moddict_temp = HPyGlobalTempNode(Naming.moddict_cname) + load_code_obj_temp = HPyGlobalTempNode(code_object_result) - code.putln("#if CYTHON_USING_HPY") - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_py_qual_temp, self.get_py_qualified_name(code))) - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_py_mod_temp, self.get_py_mod_name(code))) - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_moddict_temp, Naming.moddict_cname)) - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_code_obj_temp, code_object_result)) + load_py_qual_temp.load_global(code) + load_py_mod_temp.load_global(code) + load_moddict_temp.load_global(code) + load_code_obj_temp.load_global(code) code.putln( '%s = %s(HPY_CONTEXT_FIRST_ARG_CALL &%s, %s, %s, %s, %s, %s, %s); %s' % ( @@ -10292,40 +10268,19 @@ def generate_cyfunction_code(self, code): constructor, self.pymethdef_cname, flags, - load_py_qual_temp, + load_py_qual_temp.temp_var, self.closure_result_code(), - load_py_mod_temp, - load_moddict_temp, - load_code_obj_temp, + load_py_mod_temp.temp_var, + load_moddict_temp.temp_var, + load_code_obj_temp.temp_var, code.error_goto_if_null_object(self.result(), self.pos))) - - code.putln("PYOBJECT_CLOSEREF(%s);" % load_py_qual_temp) - code.putln("PYOBJECT_CLOSEREF(%s);" % load_py_mod_temp) - code.putln("PYOBJECT_CLOSEREF(%s);" % load_moddict_temp) - code.putln("PYOBJECT_CLOSEREF(%s);" % load_code_obj_temp) - - code.putln("#else") - code.putln( - '%s = %s(HPY_CONTEXT_FIRST_ARG_CALL &%s, %s, %s, %s, %s, %s, %s); %s' % ( - self.result(), - constructor, - self.pymethdef_cname, - flags, - self.get_py_qualified_name(code), - self.closure_result_code(), - self.get_py_mod_name(code), - Naming.moddict_cname, - code_object_result, - code.error_goto_if_null_object(self.result(), self.pos))) + load_py_qual_temp.release_global(code) + load_py_mod_temp.release_global(code) + load_moddict_temp.release_global(code) + load_code_obj_temp.release_global(code) self.generate_gotref(code) - code.putln("#endif") - - code.funcstate.release_temp(load_py_qual_temp) - code.funcstate.release_temp(load_py_mod_temp) - code.funcstate.release_temp(load_moddict_temp) - code.funcstate.release_temp(load_code_obj_temp) if def_node.requires_classobj: assert code.pyclass_stack, "pyclass_stack is empty" @@ -10437,21 +10392,19 @@ def generate_result_code(self, code): elif self.def_node.is_generator: flags.append('CO_GENERATOR') - load_empty_bytes_temp = code.funcstate.allocate_temp(py_object_type, True) - load_empty_tuple_temp = code.funcstate.allocate_temp(py_object_type, True) - load_varnames_temp = code.funcstate.allocate_temp(py_object_type, True) - load_filepath_temp = code.funcstate.allocate_temp(py_object_type, True) - load_funcname_temp = code.funcstate.allocate_temp(py_object_type, True) + code.putln("{") - code.putln("#if CYTHON_USING_HPY") + load_empty_bytes_temp = HPyGlobalTempNode(Naming.empty_bytes) + load_empty_tuple_temp = HPyGlobalTempNode(Naming.empty_tuple) + load_varnames_temp = HPyGlobalTempNode(self.varnames.result()) + load_filepath_temp = HPyGlobalTempNode(file_path_const) + load_funcname_temp = HPyGlobalTempNode(func_name) - #Type decls are temporary, still need to figure out how to get them to be declared automatically - code.putln("{") - code.putln("PYOBJECT_TYPE %s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_empty_bytes_temp, Naming.empty_bytes)) - code.putln("PYOBJECT_TYPE %s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_empty_tuple_temp, Naming.empty_tuple)) - code.putln("PYOBJECT_TYPE %s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_varnames_temp, self.varnames.result())) - code.putln("PYOBJECT_TYPE %s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_filepath_temp, file_path_const)) - code.putln("PYOBJECT_TYPE %s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_funcname_temp, func_name)) + load_empty_bytes_temp.load_global(code, needs_type=True) + load_empty_tuple_temp.load_global(code, needs_type=True) + load_varnames_temp.load_global(code, needs_type=True) + load_filepath_temp.load_global(code, needs_type=True) + load_funcname_temp.load_global(code, needs_type=True) code.putln("PYOBJECT_GLOBAL_STORE(%s, HPY_LEGACY_OBJECT_FROM((PyObject*)__Pyx_PyCode_New(%d, %d, %d, %d, 0, %s, HPY_LEGACY_OBJECT_AS(%s), \ HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), \ @@ -10460,60 +10413,30 @@ def generate_result_code(self, code): HPY_LEGACY_OBJECT_AS(%s), %d, HPY_LEGACY_OBJECT_AS(%s)))); %s" % ( self.result_code, len(func.args) - func.num_kwonly_args, # argcount - func.num_posonly_args, # posonlyargcount (Py3.8+ only) - func.num_kwonly_args, # kwonlyargcount (Py3 only) - len(self.varnames.args), # nlocals - '|'.join(flags) or '0', # flags - load_empty_bytes_temp, # code - load_empty_tuple_temp, # consts - load_empty_tuple_temp, # names (FIXME) - load_varnames_temp, # varnames - load_empty_tuple_temp, # freevars (FIXME) - load_empty_tuple_temp, # cellvars (FIXME) - load_filepath_temp, # filename - load_funcname_temp, # name - self.pos[1], # firstlineno - load_empty_bytes_temp, # lnotab - code.error_goto_if_null_object(self.result_code, self.pos), - )) - - code.putln("PYOBJECT_CLOSEREF(%s);" % load_empty_bytes_temp) - code.putln("PYOBJECT_CLOSEREF(%s);" % load_empty_tuple_temp) - code.putln("PYOBJECT_CLOSEREF(%s);" % load_varnames_temp) - code.putln("PYOBJECT_CLOSEREF(%s);" % load_filepath_temp) - code.putln("PYOBJECT_CLOSEREF(%s);" % load_funcname_temp) - - code.putln("}") - - code.putln("#else") - - code.putln("%s = (PyObject*)__Pyx_PyCode_New(%d, %d, %d, %d, 0, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d, %s); %s" % ( - self.result_code, - len(func.args) - func.num_kwonly_args, # argcount - func.num_posonly_args, # posonlyargcount (Py3.8+ only) - func.num_kwonly_args, # kwonlyargcount (Py3 only) - len(self.varnames.args), # nlocals - '|'.join(flags) or '0', # flags - Naming.empty_bytes, # code - Naming.empty_tuple, # consts - Naming.empty_tuple, # names (FIXME) - self.varnames.result(), # varnames - Naming.empty_tuple, # freevars (FIXME) - Naming.empty_tuple, # cellvars (FIXME) - file_path_const, # filename - func_name, # name - self.pos[1], # firstlineno - Naming.empty_bytes, # lnotab + func.num_posonly_args, # posonlyargcount (Py3.8+ only) + func.num_kwonly_args, # kwonlyargcount (Py3 only) + len(self.varnames.args), # nlocals + '|'.join(flags) or '0', # flags + load_empty_bytes_temp.temp_var, # code + load_empty_tuple_temp.temp_var, # consts + load_empty_tuple_temp.temp_var, # names (FIXME) + load_varnames_temp.temp_var, # varnames + load_empty_tuple_temp.temp_var, # freevars (FIXME) + load_empty_tuple_temp.temp_var, # cellvars (FIXME) + load_filepath_temp.temp_var, # filename + load_funcname_temp.temp_var, # name + self.pos[1], # firstlineno + load_empty_bytes_temp.temp_var, # lnotab code.error_goto_if_null_object(self.result_code, self.pos), )) - code.putln("#endif") + load_empty_bytes_temp.release_global(code) + load_empty_tuple_temp.release_global(code) + load_varnames_temp.release_global(code) + load_filepath_temp.release_global(code) + load_funcname_temp.release_global(code) - code.funcstate.release_temp(load_empty_bytes_temp) - code.funcstate.release_temp(load_empty_tuple_temp) - code.funcstate.release_temp(load_varnames_temp) - code.funcstate.release_temp(load_filepath_temp) - code.funcstate.release_temp(load_funcname_temp) + code.putln("}") class DefaultLiteralArgNode(ExprNode): @@ -12389,7 +12312,7 @@ def py_operation_function(self, code): if self.inplace: function_name = function_name.replace('PyNumber_', 'PyNumber_InPlace') return function_name - + def hpy_operation_function(self, code): function_name = self.hpy_functions[self.operator] if self.inplace: @@ -14816,7 +14739,7 @@ def generate_result_code(self, code): code.put_incref_memoryviewslice(self.result(), self.type, have_gil=not self.in_nogil_context) else: - code.putln("%s = %s;" % ( + code.putln("%s = %s;" % ( self.result(), self.arg.result_as(self.ctype()))) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 2ee769a51fd..a8f384ed0ef 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -1728,8 +1728,9 @@ def generate_execution_code(self, code): return # nothing to do here for C++ enums if self.visibility == 'public' or self.api: code.mark_pos(self.pos) - load_moddict_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True) - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_moddict_temp, Naming.moddict_cname)) + from . import ExprNodes + load_moddict_temp = ExprNodes.HPyGlobalTempNode(Naming.moddict_cname) + load_moddict_temp.load_global(code) temp = code.funcstate.allocate_temp(PyrexTypes.py_object_type, manage_ref=True) for item in self.entry.enum_values: code.putln("%s = PyInt_FromLong(%s); %s" % ( @@ -1737,14 +1738,13 @@ def generate_execution_code(self, code): item.cname, code.error_goto_if_null(temp, item.pos))) code.put_gotref(temp, PyrexTypes.py_object_type) - code.putln('if (DICT_SET_ITEM_STR(load_moddict_temp, "%s", %s) < 0) %s' % ( + code.putln('if (DICT_SET_ITEM_STR(load_moddict_temp.temp_var, "%s", %s) < 0) %s' % ( Naming.moddict_cname, item.name, temp, code.error_goto(item.pos))) code.put_decref_clear(temp, PyrexTypes.py_object_type) - code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % load_moddict_temp) - code.funcstate.release_temp(load_moddict_temp) + load_moddict_temp.release_global(code) code.funcstate.release_temp(temp) @@ -4417,16 +4417,13 @@ def generate_keyword_unpacking_code(self, min_positional_args, max_positional_ar code.putln('else if (unlikely(PyErr_Occurred())) %s' % code.error_goto(self.pos)) code.putln('}') else: - tmp_load_pystr = code.funcstate.allocate_temp(py_object_type, False) - if pystring_cname in code.globalstate.const_cname_array: - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_pystr, pystring_cname)) - else: - code.putln("%s = %s;" % (tmp_load_pystr, pystring_cname)) + from . import ExprNodes + + tmp_load_pystr = ExprNodes.HPyGlobalTempNode(pystring_cname) + tmp_load_pystr.load_global(code) code.putln('if (likely(API_IS_NOT_NULL(values[%d] = __Pyx_GetKwValue_%s(HPY_CONTEXT_FIRST_ARG_CALL %s, %s, %s)))) {' % ( - i, self.signature.fastvar, Naming.kwds_cname, Naming.kwvalues_cname, tmp_load_pystr)) - if pystring_cname in code.globalstate.const_cname_array: - code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_load_pystr) - code.funcstate.release_temp(tmp_load_pystr) + i, self.signature.fastvar, Naming.kwds_cname, Naming.kwvalues_cname, tmp_load_pystr.temp_var)) + tmp_load_pystr.release_global(code) code.putln('(void)__Pyx_Arg_NewRef_%s(values[%d]);' % (self.signature.fastvar, i)) code.putln('kw_args--;') code.putln('}') @@ -7041,21 +7038,19 @@ def generate_execution_code(self, code): cause_code = self.cause.py_result() else: cause_code = "0" + + from . import ExprNodes + code.globalstate.use_utility_code(raise_utility_code) - tmp_load_type = code.funcstate.allocate_temp(py_object_type, False) - if type_code in code.globalstate.const_cname_array: - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_type, type_code)) - else: - code.putln("%s = %s;" % (tmp_load_type, type_code)) + tmp_load_type = ExprNodes.HPyGlobalTempNode(type_code) + tmp_load_type.load_global(code) code.putln( "__Pyx_Raise(HPY_LEGACY_OBJECT_AS(%s), %s, %s, %s);" % ( - tmp_load_type, + tmp_load_type.temp_var, value_code, tb_code, cause_code)) - if type_code in code.globalstate.const_cname_array: - code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_load_type) - code.funcstate.release_temp(tmp_load_type) + tmp_load_type.release_global(code) for obj in (self.exc_type, self.exc_value, self.exc_tb, self.cause): if obj: obj.generate_disposal_code(code) From 9c86ae99fba75a5e985ef7ec63323a5878fffba3 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Tue, 5 Dec 2023 11:16:12 +0100 Subject: [PATCH 238/286] Make LoadGlobalNode inherit from TempNode --- Cython/Compiler/ExprNodes.py | 237 ++++++++++++++++++----------------- Cython/Compiler/Nodes.py | 24 ++-- 2 files changed, 131 insertions(+), 130 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 0997aecf047..139a3a768bc 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -2439,13 +2439,13 @@ def generate_result_code(self, code): code.putln('PyErr_Clear();') code.globalstate.use_utility_code( UtilityCode.load_cached("GetModuleGlobalName", "ObjectHandling.c")) - load_cname_temp = HPyGlobalTempNode(interned_cname) - load_cname_temp.load_global(code) + load_cname_temp = LoadGlobalNode(self.pos, interned_cname) + load_cname_temp.allocate(code) code.putln( '__Pyx_GetModuleGlobalName(%s, %s);' % ( self.result(), - load_cname_temp.temp_var)) - load_cname_temp.release_global(code) + load_cname_temp.temp_cname)) + load_cname_temp.release(code) if not self.cf_is_null: code.putln("}") code.putln(code.error_goto_if_null(self.result(), self.pos)) @@ -2457,14 +2457,14 @@ def generate_result_code(self, code): interned_cname = code.intern_identifier(self.entry.name) code.globalstate.use_utility_code( UtilityCode.load_cached("GetBuiltinName", "ObjectHandling.c")) - load_cname_temp = HPyGlobalTempNode(interned_cname) - load_cname_temp.load_global(code) + load_cname_temp = LoadGlobalNode(self.pos, interned_cname) + load_cname_temp.allocate(code) code.putln( '%s = __Pyx_GetBuiltinName(HPY_CONTEXT_FIRST_ARG_CALL %s); %s' % ( self.result(), - load_cname_temp.temp_var, + load_cname_temp.temp_cname, code.error_goto_if_null_object(self.result(), self.pos))) - load_cname_temp.release_global(code) + load_cname_temp.release(code) self.generate_gotref(code) elif entry.is_pyglobal or (entry.is_builtin and entry.scope.is_module_scope): @@ -2474,14 +2474,14 @@ def generate_result_code(self, code): if entry.scope.is_module_scope: code.globalstate.use_utility_code( UtilityCode.load_cached("GetModuleGlobalName", "ObjectHandling.c")) - load_cname_temp = HPyGlobalTempNode(interned_cname) - load_cname_temp.load_global(code) + load_cname_temp = LoadGlobalNode(self.pos, interned_cname) + load_cname_temp.allocate(code) code.putln( '__Pyx_GetModuleGlobalName(%s, %s); %s' % ( self.result(), - load_cname_temp.temp_var, + load_cname_temp.temp_cname, code.error_goto_if_null_object(self.result(), self.pos))) - load_cname_temp.release_global(code) + load_cname_temp.release(code) else: # FIXME: is_pyglobal is also used for class namespace code.globalstate.use_utility_code( @@ -2541,9 +2541,9 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, setter = 'DICT_SET_ITEM' namespace = Naming.moddict_cname interned_cname = interned_cname - load_result_temp = HPyGlobalTempNode(rhs_result) - load_result_temp.load_global(code) - load_result_value = load_result_temp.temp_var + load_result_temp = LoadGlobalNode(self.pos, rhs_result) + load_result_temp.allocate(code) + load_result_value = load_result_temp.temp_cname elif entry.is_pyclass_attr: # Special-case setting __new__ n = "SetNewInClass" if self.name == "__new__" else "SetNameInClass" @@ -2554,21 +2554,21 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, code.putln("#if CYTHON_USING_HPY") # Wrapping namespace and interned_cname in PyObjects breaks the C API # version when not using the Limited API - load_namespace_temp = HPyGlobalTempNode(namespace) - load_namespace_temp.load_global(code) - load_cname_temp = HPyGlobalTempNode(interned_cname) - load_cname_temp.load_global(code) + load_namespace_temp = LoadGlobalNode(self.pos, namespace) + load_namespace_temp.allocate(code) + load_cname_temp = LoadGlobalNode(self.pos, interned_cname) + load_cname_temp.allocate(code) code.put_error_if_neg( self.pos, '%s(%s, %s, %s)' % ( setter, - load_namespace_temp.temp_var, - load_cname_temp.temp_var, + load_namespace_temp.temp_cname, + load_cname_temp.temp_cname, load_result_value)) - load_namespace_temp.release_global(code) - load_cname_temp.release_global(code) + load_namespace_temp.release(code) + load_cname_temp.release(code) if entry.scope.is_module_scope: - load_result_temp.release_global(code) + load_result_temp.release(code) code.putln("#else") code.put_error_if_neg( self.pos, @@ -3602,24 +3602,25 @@ class PyTempNode(TempNode): def __init__(self, pos, env): TempNode.__init__(self, pos, PyrexTypes.py_object_type, env) -class HPyGlobalTempNode(ExprNode): +class LoadGlobalNode(TempNode): # TempNode for potentially loading a HPy Global - def __init__(self, var_name): + def __init__(self, pos, var_name): + TempNode.__init__(self, pos, py_object_type) self.var_name = var_name - def load_global(self, code, needs_type=False): - self.temp_var = code.funcstate.allocate_temp(py_object_type, manage_ref=False) - if needs_type: - code.putln("PYOBJECT_TYPE %s;" % self.temp_var) + def allocate(self, code, needs_decl=False): + super(self.__class__, self).allocate(code) + if needs_decl: + code.putln("PYOBJECT_TYPE %s;" % self.temp_cname) if self.var_name in code.globalstate.const_cname_array: - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (self.temp_var, self.var_name)) + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (self.temp_cname, self.var_name)) else: - code.putln("%s = %s;" % (self.temp_var, self.var_name)) + code.putln("%s = %s;" % (self.temp_cname, self.var_name)) def release_global(self, code): if self.var_name in code.globalstate.const_cname_array: - code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % self.temp_var) - code.funcstate.release_temp(self.temp_var) + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % self.temp_cname) + super(self.__class__, self).release(code) class RawCNameExprNode(ExprNode): subexprs = [] @@ -4661,23 +4662,23 @@ def generate_setitem_code(self, value, code): else: function = "PYOBJECT_SET_ITEM" - temp_load_index = HPyGlobalTempNode(index_code) - temp_load_index.load_global(code) + temp_load_index = LoadGlobalNode(self.pos, index_code) + temp_load_index.allocate(code) - temp_load_value = HPyGlobalTempNode(value_code) - temp_load_value.load_global(code) + temp_load_value = LoadGlobalNode(self.pos, value_code) + temp_load_value.allocate(code) code.putln(code.error_goto_if_neg( "%s(%s, %s, %s%s)" % ( function, self.base.py_result(), - temp_load_index.temp_var, - temp_load_value.temp_var, + temp_load_index.temp_cname, + temp_load_value.temp_cname, self.extra_index_params(code)), self.pos)) - temp_load_index.release_global(code) - temp_load_value.release_global(code) + temp_load_index.release(code) + temp_load_value.release(code) def generate_assignment_code(self, rhs, code, overloaded_assignment=False, exception_check=None, exception_value=None): @@ -6556,19 +6557,19 @@ def generate_result_code(self, code): code.globalstate.use_utility_code(UtilityCode.load_cached( "PyObjectCall", "ObjectHandling.c")) func_result = self.function.py_result() - tmp_func_result = HPyGlobalTempNode(func_result) - tmp_func_result.load_global(code) + tmp_func_result = LoadGlobalNode(self.pos, func_result) + tmp_func_result.allocate(code) func_result = self.function.py_result() - tmp_arg_code = HPyGlobalTempNode(arg_code) - tmp_arg_code.load_global(code) + tmp_arg_code = LoadGlobalNode(self.pos, arg_code) + tmp_arg_code.allocate(code) code.putln( "%s = __Pyx_PyObject_Call_h(%s, %s, API_NULL_VALUE); %s" % ( self.result(), - tmp_func_result.temp_var, - tmp_arg_code.temp_var, + tmp_func_result.temp_cname, + tmp_arg_code.temp_cname, code.error_goto_if_null_object(self.result(), self.pos))) - tmp_func_result.release_global(code) - tmp_arg_code.release_global(code) + tmp_func_result.release(code) + tmp_arg_code.release(code) self.generate_gotref(code) elif func_type.is_cfunction: nogil = not code.funcstate.gil_owned @@ -6789,17 +6790,17 @@ def attribute_is_likely_method(attr): loaded_vars = [] for arg in args: arg_result = arg.py_result() - load_tmp = HPyGlobalTempNode(arg_result) + load_tmp = LoadGlobalNode(self.pos, arg_result) loaded_vars.append(load_tmp) - load_tmp.load_global(code) + load_tmp.allocate(code) code.putln("PYOBJECT_TYPE __pyx_callargs[%d%s] = {%s, %s};" % ( (len(args) + 1) if args else 2, extra_keyword_args, self_arg, - ', '.join(tmp.temp_var for tmp in loaded_vars) if args else "NULL", + ', '.join(tmp.temp_cname for tmp in loaded_vars) if args else "NULL", )) for tmp in loaded_vars: - tmp.release_global(code) + tmp.release(code) if kwargs_key_value_pairs: for n, keyvalue in enumerate(kwargs_key_value_pairs): key_is_str = ( @@ -7283,16 +7284,16 @@ def generate_result_code(self, code): code.globalstate.use_utility_code(UtilityCode.load_cached( "PyObjectCall", "ObjectHandling.c")) pos_args_cname = self.positional_args.py_result() - tmp_pos_args = HPyGlobalTempNode(pos_args_cname) - tmp_pos_args.load_global(code) + tmp_pos_args = LoadGlobalNode(self.pos, pos_args_cname) + tmp_pos_args.allocate(code) code.putln( "%s = __Pyx_PyObject_Call_h(%s, %s, %s); %s" % ( self.result(), self.function.py_result(), - tmp_pos_args.temp_var, + tmp_pos_args.temp_cname, kwargs, code.error_goto_if_null_object(self.result(), self.pos))) - tmp_pos_args.release_global(code) + tmp_pos_args.release(code) self.generate_gotref(code) @@ -8005,16 +8006,16 @@ def generate_result_code(self, code): UtilityCode.load_cached("PyObjectGetAttrStr", "ObjectHandling.c")) lookup_func_name = '__Pyx_PyObject_GetAttrStr' attr_str = code.intern_identifier(self.attribute) - temp_load_attr = HPyGlobalTempNode(attr_str) - temp_load_attr.load_global(code) + temp_load_attr = LoadGlobalNode(self.pos, attr_str) + temp_load_attr.allocate(code) code.putln( '%s = %s(%s, %s); %s' % ( self.result(), lookup_func_name, self.obj.py_result(), - temp_load_attr.temp_var, + temp_load_attr.temp_cname, code.error_goto_if_null_object(self.result(), self.pos))) - temp_load_attr.release_global(code) + temp_load_attr.release(code) self.generate_gotref(code) elif self.type.is_memoryviewslice: if self.is_memslice_transpose: @@ -8339,16 +8340,16 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): loaded_args = [] code.putln("{") for arg in self.args: - temp_load_arg = HPyGlobalTempNode(arg.py_result()) - temp_load_arg.load_global(code, needs_type=True) + temp_load_arg = LoadGlobalNode(self.pos, arg.py_result()) + temp_load_arg.allocate(code, needs_decl=True) loaded_args.append(temp_load_arg) code.putln('PYOBJECT_GLOBAL_STORE(%s, TUPLE_PACK(%d, %s)); %s' % ( target, len(self.args), - ', '.join(arg.temp_var for arg in loaded_args), + ', '.join(arg.temp_cname for arg in loaded_args), code.error_goto_if_null_object(target, self.pos))) for arg in loaded_args: - arg.release_global(code) + arg.release(code) code.put_gotref(target, py_object_type) code.putln("}") elif self.type.is_ctuple: @@ -8399,16 +8400,16 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): arg.generate_giveref(code) code.putln("#endif") code.putln("{") - tmp_load_arg = HPyGlobalTempNode(arg.py_result()) - tmp_load_arg.load_global(code, needs_type=True) + tmp_load_arg = LoadGlobalNode(self.pos, arg.py_result()) + tmp_load_arg.allocate(code, needs_decl=True) code.putln("%s(%s, %s, %s, %s);" % ( # %s;" % ( set_item_func, target, tmp_builder, (offset and i) and ('%s + %s' % (offset, i)) or (offset or i), - tmp_load_arg.temp_var))#, + tmp_load_arg.temp_cname))#, #code.error_goto(self.pos))) - tmp_load_arg.release_global(code) + tmp_load_arg.release(code) code.putln("}") code.putln("%s(%s, %s);" % (build_func, target, tmp_builder)) @@ -9615,18 +9616,18 @@ def generate_evaluation_code(self, code): code.error_goto(item.pos))) code.putln("} else {") - temp_load_key = HPyGlobalTempNode(item.key.py_result()) - temp_load_key.load_global(code) + temp_load_key = LoadGlobalNode(self.pos, item.key.py_result()) + temp_load_key.allocate(code) - temp_load_value = HPyGlobalTempNode(item.value.py_result()) - temp_load_value.load_global(code) + temp_load_value = LoadGlobalNode(self.pos, item.value.py_result()) + temp_load_value.allocate(code) code.put_error_if_neg(self.pos, "DICT_SET_ITEM(%s, %s, %s)" % ( self.result(), - temp_load_key.temp_var, - temp_load_value.temp_var)) + temp_load_key.temp_cname, + temp_load_value.temp_cname)) - temp_load_key.release_global(code) - temp_load_value.release_global(code) + temp_load_key.release(code) + temp_load_value.release(code) if self.reject_duplicates and keys_seen is None: code.putln('}') @@ -10251,16 +10252,16 @@ def generate_cyfunction_code(self, code): else: flags = '0' - load_py_qual_temp = HPyGlobalTempNode(self.get_py_qualified_name(code)) - load_py_mod_temp = HPyGlobalTempNode(self.get_py_mod_name(code)) - load_moddict_temp = HPyGlobalTempNode(Naming.moddict_cname) - load_code_obj_temp = HPyGlobalTempNode(code_object_result) + load_py_qual_temp = LoadGlobalNode(self.pos, self.get_py_qualified_name(code)) + load_py_mod_temp = LoadGlobalNode(self.pos, self.get_py_mod_name(code)) + load_moddict_temp = LoadGlobalNode(self.pos, Naming.moddict_cname) + load_code_obj_temp = LoadGlobalNode(self.pos, code_object_result) - load_py_qual_temp.load_global(code) - load_py_mod_temp.load_global(code) - load_moddict_temp.load_global(code) - load_code_obj_temp.load_global(code) + load_py_qual_temp.allocate(code) + load_py_mod_temp.allocate(code) + load_moddict_temp.allocate(code) + load_code_obj_temp.allocate(code) code.putln( '%s = %s(HPY_CONTEXT_FIRST_ARG_CALL &%s, %s, %s, %s, %s, %s, %s); %s' % ( @@ -10268,17 +10269,17 @@ def generate_cyfunction_code(self, code): constructor, self.pymethdef_cname, flags, - load_py_qual_temp.temp_var, + load_py_qual_temp.temp_cname, self.closure_result_code(), - load_py_mod_temp.temp_var, - load_moddict_temp.temp_var, - load_code_obj_temp.temp_var, + load_py_mod_temp.temp_cname, + load_moddict_temp.temp_cname, + load_code_obj_temp.temp_cname, code.error_goto_if_null_object(self.result(), self.pos))) - load_py_qual_temp.release_global(code) - load_py_mod_temp.release_global(code) - load_moddict_temp.release_global(code) - load_code_obj_temp.release_global(code) + load_py_qual_temp.release(code) + load_py_mod_temp.release(code) + load_moddict_temp.release(code) + load_code_obj_temp.release(code) self.generate_gotref(code) @@ -10394,17 +10395,17 @@ def generate_result_code(self, code): code.putln("{") - load_empty_bytes_temp = HPyGlobalTempNode(Naming.empty_bytes) - load_empty_tuple_temp = HPyGlobalTempNode(Naming.empty_tuple) - load_varnames_temp = HPyGlobalTempNode(self.varnames.result()) - load_filepath_temp = HPyGlobalTempNode(file_path_const) - load_funcname_temp = HPyGlobalTempNode(func_name) + load_empty_bytes_temp = LoadGlobalNode(self.pos, Naming.empty_bytes) + load_empty_tuple_temp = LoadGlobalNode(self.pos, Naming.empty_tuple) + load_varnames_temp = LoadGlobalNode(self.pos, self.varnames.result()) + load_filepath_temp = LoadGlobalNode(self.pos, file_path_const) + load_funcname_temp = LoadGlobalNode(self.pos, func_name) - load_empty_bytes_temp.load_global(code, needs_type=True) - load_empty_tuple_temp.load_global(code, needs_type=True) - load_varnames_temp.load_global(code, needs_type=True) - load_filepath_temp.load_global(code, needs_type=True) - load_funcname_temp.load_global(code, needs_type=True) + load_empty_bytes_temp.allocate(code, needs_decl=True) + load_empty_tuple_temp.allocate(code, needs_decl=True) + load_varnames_temp.allocate(code, needs_decl=True) + load_filepath_temp.allocate(code, needs_decl=True) + load_funcname_temp.allocate(code, needs_decl=True) code.putln("PYOBJECT_GLOBAL_STORE(%s, HPY_LEGACY_OBJECT_FROM((PyObject*)__Pyx_PyCode_New(%d, %d, %d, %d, 0, %s, HPY_LEGACY_OBJECT_AS(%s), \ HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), \ @@ -10417,24 +10418,24 @@ def generate_result_code(self, code): func.num_kwonly_args, # kwonlyargcount (Py3 only) len(self.varnames.args), # nlocals '|'.join(flags) or '0', # flags - load_empty_bytes_temp.temp_var, # code - load_empty_tuple_temp.temp_var, # consts - load_empty_tuple_temp.temp_var, # names (FIXME) - load_varnames_temp.temp_var, # varnames - load_empty_tuple_temp.temp_var, # freevars (FIXME) - load_empty_tuple_temp.temp_var, # cellvars (FIXME) - load_filepath_temp.temp_var, # filename - load_funcname_temp.temp_var, # name + load_empty_bytes_temp.temp_cname, # code + load_empty_tuple_temp.temp_cname, # consts + load_empty_tuple_temp.temp_cname, # names (FIXME) + load_varnames_temp.temp_cname, # varnames + load_empty_tuple_temp.temp_cname, # freevars (FIXME) + load_empty_tuple_temp.temp_cname, # cellvars (FIXME) + load_filepath_temp.temp_cname, # filename + load_funcname_temp.temp_cname, # name self.pos[1], # firstlineno - load_empty_bytes_temp.temp_var, # lnotab + load_empty_bytes_temp.temp_cname, # lnotab code.error_goto_if_null_object(self.result_code, self.pos), )) - load_empty_bytes_temp.release_global(code) - load_empty_tuple_temp.release_global(code) - load_varnames_temp.release_global(code) - load_filepath_temp.release_global(code) - load_funcname_temp.release_global(code) + load_empty_bytes_temp.release(code) + load_empty_tuple_temp.release(code) + load_varnames_temp.release(code) + load_filepath_temp.release(code) + load_funcname_temp.release(code) code.putln("}") diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index a8f384ed0ef..f1e9133f797 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -1729,8 +1729,8 @@ def generate_execution_code(self, code): if self.visibility == 'public' or self.api: code.mark_pos(self.pos) from . import ExprNodes - load_moddict_temp = ExprNodes.HPyGlobalTempNode(Naming.moddict_cname) - load_moddict_temp.load_global(code) + load_moddict_temp = ExprNodes.LoadGlobalNode(self.pos, Naming.moddict_cname) + load_moddict_temp.allocate(code) temp = code.funcstate.allocate_temp(PyrexTypes.py_object_type, manage_ref=True) for item in self.entry.enum_values: code.putln("%s = PyInt_FromLong(%s); %s" % ( @@ -1738,13 +1738,13 @@ def generate_execution_code(self, code): item.cname, code.error_goto_if_null(temp, item.pos))) code.put_gotref(temp, PyrexTypes.py_object_type) - code.putln('if (DICT_SET_ITEM_STR(load_moddict_temp.temp_var, "%s", %s) < 0) %s' % ( + code.putln('if (DICT_SET_ITEM_STR(load_moddict_temp.temp_cname, "%s", %s) < 0) %s' % ( Naming.moddict_cname, item.name, temp, code.error_goto(item.pos))) code.put_decref_clear(temp, PyrexTypes.py_object_type) - load_moddict_temp.release_global(code) + load_moddict_temp.release(code) code.funcstate.release_temp(temp) @@ -4419,11 +4419,11 @@ def generate_keyword_unpacking_code(self, min_positional_args, max_positional_ar else: from . import ExprNodes - tmp_load_pystr = ExprNodes.HPyGlobalTempNode(pystring_cname) - tmp_load_pystr.load_global(code) + tmp_load_pystr = ExprNodes.LoadGlobalNode(self.pos, pystring_cname) + tmp_load_pystr.allocate(code) code.putln('if (likely(API_IS_NOT_NULL(values[%d] = __Pyx_GetKwValue_%s(HPY_CONTEXT_FIRST_ARG_CALL %s, %s, %s)))) {' % ( - i, self.signature.fastvar, Naming.kwds_cname, Naming.kwvalues_cname, tmp_load_pystr.temp_var)) - tmp_load_pystr.release_global(code) + i, self.signature.fastvar, Naming.kwds_cname, Naming.kwvalues_cname, tmp_load_pystr.temp_cname)) + tmp_load_pystr.release(code) code.putln('(void)__Pyx_Arg_NewRef_%s(values[%d]);' % (self.signature.fastvar, i)) code.putln('kw_args--;') code.putln('}') @@ -7042,15 +7042,15 @@ def generate_execution_code(self, code): from . import ExprNodes code.globalstate.use_utility_code(raise_utility_code) - tmp_load_type = ExprNodes.HPyGlobalTempNode(type_code) - tmp_load_type.load_global(code) + tmp_load_type = ExprNodes.LoadGlobalNode(self.pos, type_code) + tmp_load_type.allocate(code) code.putln( "__Pyx_Raise(HPY_LEGACY_OBJECT_AS(%s), %s, %s, %s);" % ( - tmp_load_type.temp_var, + tmp_load_type.temp_cname, value_code, tb_code, cause_code)) - tmp_load_type.release_global(code) + tmp_load_type.release(code) for obj in (self.exc_type, self.exc_value, self.exc_tb, self.cause): if obj: obj.generate_disposal_code(code) From c717eb3d4f2982980bcefc949e336de3dbcb2087 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 27 Nov 2023 16:49:08 +0100 Subject: [PATCH 239/286] Make basic types work --- Cython/Compiler/ExprNodes.py | 141 ++++++++++++++++++++++++---- Cython/Utility/HPyUtils.c | 2 + Cython/Utility/ModuleSetupCode.c | 2 +- Cython/Utility/ObjectHandling.c | 153 ++++++++++++++++++++----------- 4 files changed, 223 insertions(+), 75 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 139a3a768bc..d47e1452eeb 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -2549,6 +2549,11 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, n = "SetNewInClass" if self.name == "__new__" else "SetNameInClass" code.globalstate.use_utility_code(UtilityCode.load_cached(n, "ObjectHandling.c")) setter = '__Pyx_' + n + if rhs_result in code.globalstate.const_cname_array: + code.putln("#if CYTHON_USING_HPY") + load_result_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=False) + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_result_temp, rhs_result)) + code.putln("#endif") else: assert False, repr(entry) code.putln("#if CYTHON_USING_HPY") @@ -9864,26 +9869,68 @@ def analyse_annotations(self, env): def generate_result_code(self, code): code.globalstate.use_utility_code(UtilityCode.load_cached("Py3ClassCreate", "ObjectHandling.c")) cname = code.intern_identifier(self.name) + tmp_load_cname = code.funcstate.allocate_temp(py_object_type, False) + if cname in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_cname, cname)) + else: + code.putln("%s = %s;" % (tmp_load_cname, cname)) class_def_node = self.class_def_node + tmp_load_bases = code.funcstate.allocate_temp(py_object_type, False) + if class_def_node.bases.py_result() in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_bases, class_def_node.bases.py_result())) + else: + code.putln("%s = %s;" % (tmp_load_bases, class_def_node.bases.py_result())) + tmp_load_dict = code.funcstate.allocate_temp(py_object_type, False) + if class_def_node.dict.py_result() in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_dict, class_def_node.dict.py_result())) + else: + code.putln("%s = %s;" % (tmp_load_dict, class_def_node.dict.py_result())) mkw = class_def_node.mkw.py_result() if class_def_node.mkw else 'NULL' + tmp_load_mkw = code.funcstate.allocate_temp(py_object_type, False) + if mkw in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_mkw, mkw)) + else: + print(mkw) + if mkw == 'NULL': + mkw = 'API_NULL_VALUE' + code.putln("%s = %s;" % (tmp_load_mkw, mkw)) + tmp_load_metaclass = code.funcstate.allocate_temp(py_object_type, False) if class_def_node.metaclass: metaclass = class_def_node.metaclass.py_result() + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_metaclass, metaclass)) elif self.force_type: - metaclass = "((PyObject*)&PyType_Type)" + metaclass = "(CAST_IF_CAPI(PYOBJECT_TYPE)CAPI_NEEDS_DEREFERENCE __Pyx_DefaultClassType)" + code.putln("%s = %s;" % (tmp_load_metaclass, metaclass)) else: - metaclass = "((PyObject*)&__Pyx_DefaultClassType)" + metaclass = "(CAST_IF_CAPI(PYOBJECT_TYPE)CAPI_NEEDS_DEREFERENCE __Pyx_DefaultClassType)" + code.putln("%s = %s;" % (tmp_load_metaclass, metaclass)) code.putln( - '%s = __Pyx_Py3ClassCreate(%s, %s, %s, %s, %s, %d, %d); %s' % ( + '%s = __Pyx_Py3ClassCreate(HPY_CONTEXT_FIRST_ARG_CALL %s, %s, %s, %s, %s, %d, %d); %s' % ( self.result(), - metaclass, - cname, - class_def_node.bases.py_result(), - class_def_node.dict.py_result(), - mkw, + tmp_load_metaclass, + tmp_load_cname, + tmp_load_bases, + tmp_load_dict, + tmp_load_mkw, self.calculate_metaclass, self.allow_py2_metaclass, - code.error_goto_if_null(self.result(), self.pos))) + code.error_goto_if_null_object(self.result(), self.pos))) self.generate_gotref(code) + if cname in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_load_cname) + code.funcstate.release_temp(tmp_load_cname) + if class_def_node.bases.py_result() in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_load_bases) + code.funcstate.release_temp(tmp_load_bases) + if class_def_node.dict.py_result() in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_load_dict) + code.funcstate.release_temp(tmp_load_dict) + if mkw in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_load_mkw) + code.funcstate.release_temp(tmp_load_mkw) + if metaclass in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_load_metaclass) + code.funcstate.release_temp(tmp_load_metaclass) class PyClassMetaclassNode(ExprNode): @@ -9943,24 +9990,80 @@ def may_be_none(self): def generate_result_code(self, code): cname = code.intern_identifier(self.name) + tmp_load_cname = code.funcstate.allocate_temp(py_object_type, False) + if cname in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_cname, cname)) + else: + code.putln("%s = %s;" % (tmp_load_cname, cname)) py_mod_name = self.get_py_mod_name(code) + tmp_load_modname = code.funcstate.allocate_temp(py_object_type, False) + if py_mod_name in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_modname, py_mod_name)) + else: + code.putln("%s = %s;" % (tmp_load_modname, py_mod_name)) qualname = self.get_py_qualified_name(code) + tmp_load_qualname = code.funcstate.allocate_temp(py_object_type, False) + if qualname in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_qualname, qualname)) + else: + code.putln("%s = %s;" % (tmp_load_qualname, qualname)) class_def_node = self.class_def_node - null = "(PyObject *) NULL" + tmp_load_defnode = code.funcstate.allocate_temp(py_object_type, False) + if class_def_node.bases.result() in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_defnode, class_def_node.bases.result())) + else: + code.putln("%s = %s;" % (tmp_load_defnode, class_def_node.bases.result())) + null = "(PYOBJECT_TYPE) API_NULL_VALUE" doc_code = self.doc.result() if self.doc else null + tmp_load_doccode = code.funcstate.allocate_temp(py_object_type, False) + if doc_code in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_doccode, doc_code)) + else: + code.putln("%s = %s;" % (tmp_load_doccode, doc_code)) mkw = class_def_node.mkw.py_result() if class_def_node.mkw else null + tmp_load_mkw = code.funcstate.allocate_temp(py_object_type, False) + if mkw in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_mkw, mkw)) + else: + code.putln("%s = %s;" % (tmp_load_mkw, mkw)) metaclass = class_def_node.metaclass.py_result() if class_def_node.metaclass else null + tmp_load_metaclass = code.funcstate.allocate_temp(py_object_type, False) + if metaclass in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_metaclass, metaclass)) + else: + code.putln("%s = %s;" % (tmp_load_metaclass, metaclass)) code.putln( - "%s = __Pyx_Py3MetaclassPrepare(%s, %s, %s, %s, %s, %s, %s); %s" % ( + "%s = __Pyx_Py3MetaclassPrepare(HPY_CONTEXT_FIRST_ARG_CALL %s, %s, %s, %s, %s, %s, %s); %s" % ( self.result(), - metaclass, - class_def_node.bases.result(), - cname, - qualname, - mkw, - py_mod_name, - doc_code, - code.error_goto_if_null(self.result(), self.pos))) + tmp_load_metaclass, + tmp_load_defnode, + tmp_load_cname, + tmp_load_qualname, + tmp_load_mkw, + tmp_load_modname, + tmp_load_doccode, + code.error_goto_if_null_object(self.result(), self.pos))) + if cname in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_load_cname) + code.funcstate.release_temp(tmp_load_cname) + if py_mod_name in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_load_modname) + code.funcstate.release_temp(tmp_load_modname) + if qualname in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_load_qualname) + code.funcstate.release_temp(tmp_load_qualname) + if class_def_node in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_load_defnode) + code.funcstate.release_temp(tmp_load_defnode) + if doc_code in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_load_doccode) + code.funcstate.release_temp(tmp_load_doccode) + if mkw in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_load_mkw) + code.funcstate.release_temp(tmp_load_mkw) + if metaclass in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_load_metaclass) + code.funcstate.release_temp(tmp_load_metaclass) self.generate_gotref(code) diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 360b4d2863a..53c03c4be37 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -164,6 +164,7 @@ #define TYPE_AS_PYOBJECT(t) t #define GET_TYPE(o) HPy_Type(HPY_CONTEXT_CNAME, o) #define OBJ_IS_TYPE(o, t) HPy_TypeCheck(HPY_CONTEXT_CNAME, o, t) + #define TYPE_IS_SUBTYPE(sub, t) HPyType_IsSubtype(HPY_CONTEXT_CNAME, sub, t) //Error & Exception Macros #define PYERR_OCCURRED() HPyErr_Occurred(HPY_CONTEXT_CNAME) @@ -342,6 +343,7 @@ #define TYPE_AS_PYOBJECT(t) (PyObject*)&##t #define GET_TYPE(o) Py_TYPE(o) #define OBJ_IS_TYPE(o, t) Py_IS_TYPE(o, t) + #define TYPE_IS_SUBTYPE(sub, t) PyType_IsSubtype(sub, t) //Error & Exception Macros #define PYERR_OCCURRED() (!!PyErr_Occurred()) diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index d4955f5eaff..28b44d0eaee 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -735,7 +735,7 @@ class __Pyx_FakeReference { // TODO: remove this block #if CYTHON_USING_HPY #define __Pyx_BUILTIN_MODULE_NAME "HPY_CONTEXT_CNAME->h_Builtins" - #define __Pyx_DefaultClassType HPyType_Type + #define __Pyx_DefaultClassType HPY_CONTEXT_CNAME->h_TypeType #else #define __Pyx_BUILTIN_MODULE_NAME "builtins" #define __Pyx_DefaultClassType PyType_Type diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index a918d39fe7f..081c26358ba 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -873,34 +873,38 @@ static CYTHON_INLINE PyObject* __Pyx_Py{{type}}_GetSlice( /////////////// CalculateMetaclass.proto /////////////// -static PyObject *__Pyx_CalculateMetaclass(PyTypeObject *metaclass, PyObject *bases); +static PYOBJECT_TYPE __Pyx_CalculateMetaclass(HPY_CONTEXT_FIRST_ARG_DEF PYTYPEOBJECT_TYPE metaclass, PYOBJECT_TYPE bases); /////////////// CalculateMetaclass /////////////// -static PyObject *__Pyx_CalculateMetaclass(PyTypeObject *metaclass, PyObject *bases) { - Py_ssize_t i, nbases; +static PYOBJECT_TYPE __Pyx_CalculateMetaclass(HPY_CONTEXT_FIRST_ARG_DEF PYTYPEOBJECT_TYPE metaclass, PYOBJECT_TYPE bases) { + API_SSIZE_T i, nbases; #if CYTHON_ASSUME_SAFE_SIZE nbases = PyTuple_GET_SIZE(bases); #else - nbases = PyTuple_Size(bases); - if (nbases < 0) return NULL; + nbases = TUPLE_GET_SIZE(bases); + if (nbases < 0) return API_NULL_VALUE; #endif for (i=0; i < nbases; i++) { - PyTypeObject *tmptype; + PYTYPEOBJECT_TYPE tmptype; #if CYTHON_ASSUME_SAFE_MACROS PyObject *tmp = PyTuple_GET_ITEM(bases, i); #else - PyObject *tmp = PyTuple_GetItem(bases, i); - if (!tmp) return NULL; + PYOBJECT_TYPE tmp = TUPLE_GET_ITEM(bases, i); + if (API_IS_NULL(tmp)) return API_NULL_VALUE; #endif - tmptype = Py_TYPE(tmp); - if (!metaclass) { + tmptype = GET_TYPE(tmp); +#if PY_MAJOR_VERSION < 3 + if (tmptype == &PyClass_Type) + continue; +#endif + if (API_IS_NULL(metaclass)) { metaclass = tmptype; continue; } - if (PyType_IsSubtype(metaclass, tmptype)) + if (TYPE_IS_SUBTYPE(metaclass, tmptype)) continue; - if (PyType_IsSubtype(tmptype, metaclass)) { + if (TYPE_IS_SUBTYPE(tmptype, metaclass)) { metaclass = tmptype; continue; } @@ -910,14 +914,18 @@ static PyObject *__Pyx_CalculateMetaclass(PyTypeObject *metaclass, PyObject *bas "the metaclass of a derived class " "must be a (non-strict) subclass " "of the metaclasses of all its bases"); - return NULL; + return API_NULL_VALUE; } - if (!metaclass) { - metaclass = &PyType_Type; + if (API_IS_NULL(metaclass)) { + metaclass = CAPI_NEEDS_DEREFERENCE __Pyx_DefaultClassType; } // make owned reference +#if !CYTHON_USING_HPY Py_INCREF((PyObject*) metaclass); return (PyObject*) metaclass; +#else + return metaclass; +#endif } @@ -1148,10 +1156,12 @@ __Pyx_PEP560_update_bases(PyObject *bases) /////////////// Py3ClassCreate.proto /////////////// -static PyObject *__Pyx_Py3MetaclassPrepare(PyObject *metaclass, PyObject *bases, PyObject *name, PyObject *qualname, - PyObject *mkw, PyObject *modname, PyObject *doc); /*proto*/ -static PyObject *__Pyx_Py3ClassCreate(PyObject *metaclass, PyObject *name, PyObject *bases, PyObject *dict, - PyObject *mkw, int calculate_metaclass, int allow_py2_metaclass); /*proto*/ +static PYOBJECT_TYPE __Pyx_Py3MetaclassPrepare(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE metaclass, PYOBJECT_TYPE bases, + PYOBJECT_TYPE name, PYOBJECT_TYPE qualname, PYOBJECT_TYPE mkw, + PYOBJECT_TYPE modname, PYOBJECT_TYPE doc); /*proto*/ +static PYOBJECT_TYPE __Pyx_Py3ClassCreate(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE metaclass, PYOBJECT_TYPE name, + PYOBJECT_TYPE bases, PYOBJECT_TYPE dict, PYOBJECT_TYPE mkw, + int calculate_metaclass, int allow_py2_metaclass); /*proto*/ /////////////// Py3ClassCreate /////////////// //@substitute: naming @@ -1162,63 +1172,95 @@ static PyObject *__Pyx_Py3ClassCreate(PyObject *metaclass, PyObject *name, PyObj //@requires: PyObjectLookupSpecial // only in fallback code: -static PyObject *__Pyx_Py3MetaclassPrepare(PyObject *metaclass, PyObject *bases, PyObject *name, - PyObject *qualname, PyObject *mkw, PyObject *modname, PyObject *doc) { - PyObject *ns; - if (metaclass) { - PyObject *prep = __Pyx_PyObject_GetAttrStrNoError(metaclass, PYIDENT("__prepare__")); - if (prep) { +static PYOBJECT_TYPE __Pyx_Py3MetaclassPrepare(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE metaclass, PYOBJECT_TYPE bases, + PYOBJECT_TYPE name, PYOBJECT_TYPE qualname, PYOBJECT_TYPE mkw, + PYOBJECT_TYPE modname, PYOBJECT_TYPE doc) { + PYOBJECT_TYPE ns; + if (API_IS_NOT_NULL(metaclass)) { + PYOBJECT_TYPE loaded_prepare = PYOBJECT_GLOBAL_LOAD(PYIDENT("__prepare__")); + PYOBJECT_TYPE prep = __Pyx_PyObject_GetAttrStrNoError(HPY_CONTEXT_FIRST_ARG_CALL metaclass, loaded_prepare); + PYOBJECT_GLOBAL_CLOSEREF(loaded_prepare); + if (API_IS_NOT_NULL(prep)) { +#if CYTHON_USING_HPY + HPyListBuilder builder = HPyListBuilder_New(HPY_CONTEXT_CNAME, 3); + HPyListBuilder_Set(HPY_CONTEXT_CNAME, builder, 0, HPy_NULL); + HPyListBuilder_Set(HPY_CONTEXT_CNAME, builder, 1, name); + HPyListBuilder_Set(HPY_CONTEXT_CNAME, builder, 2, bases); + PYOBJECT_TYPE pargs = HPyListBuilder_Build(HPY_CONTEXT_CNAME, builder); + ns = HPy_Call(HPY_CONTEXT_CNAME, prep, &pargs, HPy_Length(HPY_CONTEXT_CNAME, pargs), mkw); + HPy_Close(HPY_CONTEXT_CNAME, pargs); +#else PyObject *pargs[3] = {NULL, name, bases}; ns = __Pyx_PyObject_FastCallDict(prep, pargs+1, 2 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET, mkw); Py_DECREF(prep); +#endif } else { if (unlikely(PyErr_Occurred())) - return NULL; - ns = PyDict_New(); + return API_NULL_VALUE; + ns = DICT_NEW(); } } else { - ns = PyDict_New(); + ns = DICT_NEW(); } - if (unlikely(!ns)) - return NULL; + if (unlikely(API_IS_NULL(ns))) + return API_NULL_VALUE; /* Required here to emulate assignment order */ - if (unlikely(PyObject_SetItem(ns, PYIDENT("__module__"), modname) < 0)) goto bad; - if (unlikely(PyObject_SetItem(ns, PYIDENT("__qualname__"), qualname) < 0)) goto bad; - if (unlikely(doc && PyObject_SetItem(ns, PYIDENT("__doc__"), doc) < 0)) goto bad; + PYOBJECT_TYPE loaded_module = PYOBJECT_GLOBAL_LOAD(PYIDENT("__module__")); + if (unlikely(PYOBJECT_SET_ITEM(ns, loaded_module, modname) < 0)) goto bad; + PYOBJECT_GLOBAL_CLOSEREF(loaded_module); +#if PY_VERSION_HEX >= 0x03030000 + PYOBJECT_TYPE loaded_qualname = PYOBJECT_GLOBAL_LOAD(PYIDENT("__qualname__")); + if (unlikely(PYOBJECT_SET_ITEM(ns, loaded_qualname, qualname) < 0)) goto bad; + PYOBJECT_GLOBAL_CLOSEREF(loaded_qualname); +#else + CYTHON_MAYBE_UNUSED_VAR(qualname); +#endif + PYOBJECT_TYPE loaded_doc = PYOBJECT_GLOBAL_LOAD(PYIDENT("__doc__")); + if (unlikely(API_IS_NOT_NULL(doc) && PYOBJECT_SET_ITEM(ns, loaded_doc, doc) < 0)) goto bad; + PYOBJECT_GLOBAL_CLOSEREF(loaded_doc); return ns; bad: - Py_DECREF(ns); - return NULL; + PYOBJECT_CLOSEREF(ns); + return API_NULL_VALUE; } -static PyObject *__Pyx_Py3ClassCreate(PyObject *metaclass, PyObject *name, PyObject *bases, - PyObject *dict, PyObject *mkw, +static PYOBJECT_TYPE __Pyx_Py3ClassCreate(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE metaclass, PYOBJECT_TYPE name, + PYOBJECT_TYPE bases, PYOBJECT_TYPE dict, PYOBJECT_TYPE mkw, int calculate_metaclass, int allow_py2_metaclass) { - PyObject *result; - PyObject *owned_metaclass = NULL; - PyObject *margs[4] = {NULL, name, bases, dict}; + PYOBJECT_TYPE result; + PYOBJECT_TYPE owned_metaclass = API_NULL_VALUE; + PYOBJECT_TYPE margs[4] = {API_NULL_VALUE, name, bases, dict}; if (allow_py2_metaclass) { - /* honour Python2 __metaclass__ for backward compatibility */ - owned_metaclass = PyObject_GetItem(dict, PYIDENT("__metaclass__")); - if (owned_metaclass) { + PYOBJECT_TYPE loaded_metaclass = PYOBJECT_GLOBAL_LOAD(PYIDENT("__metaclass__")); + owned_metaclass = PYOBJECT_GET_ITEM(dict, loaded_metaclass); + PYOBJECT_GLOBAL_CLOSEREF(loaded_metaclass); + if (API_IS_NOT_NULL(owned_metaclass)) { metaclass = owned_metaclass; } else if (likely(PyErr_ExceptionMatches(PyExc_KeyError))) { PyErr_Clear(); } else { - return NULL; + return API_NULL_VALUE; } } - if (calculate_metaclass && (!metaclass || PyType_Check(metaclass))) { + if (calculate_metaclass && (API_IS_NULL(metaclass) || TYPE_CHECK(metaclass))) { +#if CYTHON_USING_HPY + metaclass = __Pyx_CalculateMetaclass(HPY_CONTEXT_FIRST_ARG_CALL metaclass, bases); +#else metaclass = __Pyx_CalculateMetaclass((PyTypeObject*) metaclass, bases); Py_XDECREF(owned_metaclass); - if (unlikely(!metaclass)) - return NULL; +#endif + if (unlikely(API_IS_NULL(metaclass))) + return API_NULL_VALUE; owned_metaclass = metaclass; } +#if !CYTHON_USING_HPY result = __Pyx_PyObject_FastCallDict(metaclass, margs+1, 3 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET, mkw); - Py_XDECREF(owned_metaclass); +#else + result = API_CALL_FUNC(metaclass, margs+1, 3, mkw); +#endif + PYOBJECT_XCLOSEREF(owned_metaclass); return result; } @@ -1356,15 +1398,15 @@ static PyObject *__Pyx__GetNameInClass(PyObject *nmspace, PyObject *name) { /////////////// SetNameInClass.proto /////////////// -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030d0000 +#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030d0000 && !CYTHON_USING_HPY // Identifier names are always interned and have a pre-calculated hash value. #define __Pyx_SetNameInClass(ns, name, value) \ (likely(PyDict_CheckExact(ns)) ? _PyDict_SetItem_KnownHash(ns, name, value, ((PyASCIIObject *) name)->hash) : PyObject_SetItem(ns, name, value)) -#elif CYTHON_COMPILING_IN_CPYTHON +#elif CYTHON_COMPILING_IN_CPYTHON && !CYTHON_USING_HPY #define __Pyx_SetNameInClass(ns, name, value) \ (likely(PyDict_CheckExact(ns)) ? PyDict_SetItem(ns, name, value) : PyObject_SetItem(ns, name, value)) #else -#define __Pyx_SetNameInClass(ns, name, value) PyObject_SetItem(ns, name, value) +#define __Pyx_SetNameInClass(ns, name, value) PYOBJECT_SET_ITEM(ns, name, value) #endif /////////////// SetNewInClass.proto /////////////// @@ -2080,7 +2122,7 @@ static PYOBJECT_TYPE __Pyx__CallUnboundCMethod2(HPY_CONTEXT_FIRST_ARG_DEF __Pyx_ #if CYTHON_USING_HPY #define __Pyx_PyObject_FastCall(func, args, nargs) API_CALL_FUNC(func, args, (size_t)(nargs), API_NULL_VALUE) #else -#define __Pyx_PyObject_FastCall(func, args, nargs) __Pyx_PyObject_FastCallDict(func, args, (size_t)(nargs), API_NULL_VALUE) +#define __Pyx_PyObject_FastCall(func, args, nargs) __Pyx_PyObject_FastCallDict(HPY_CONTEXT_FIRST_ARG_CALL func, args, (size_t)(nargs), API_NULL_VALUE) static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_FastCallDict(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE func, PYOBJECT_TYPE *args, size_t nargs, PYOBJECT_TYPE kwargs); /*proto*/ #endif @@ -2109,13 +2151,14 @@ static PyObject* __Pyx_PyObject_FastCall_fallback(PyObject *func, PyObject **arg } #endif -static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_FastCallDict(HPY_CONTEXT_FIRST_ARG_CALL PYOBJECT_TYPE func, PYOBJECT_TYPE *args, size_t _nargs, PYOBJECT_TYPE kwargs) { +#if !CYTHON_USING_HPY +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_FastCallDict(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE func, PYOBJECT_TYPE *args, size_t _nargs, PYOBJECT_TYPE kwargs) { // Special fast paths for 0 and 1 arguments // NOTE: in many cases, this is called with a constant value for nargs // which is known at compile-time. So the branches below will typically // be optimized away. #if CYTHON_USING_HPY - API_SSIZE_T nargs = _nargs + API_SSIZE_T nargs = _nargs; #else API_SSIZE_T nargs = __Pyx_PyVectorcall_NARGS(_nargs); #endif @@ -2178,7 +2221,7 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_FastCallDict(HPY_CONTEXT_FIRST return __Pyx_PyObject_FastCall_fallback(func, args, (size_t)nargs, kwargs); #endif } - +#endif /////////////// PyObjectVectorCallKwBuilder.proto //////////////// //@requires: PyObjectFastCall // For versions that define PyObject_Vectorcall, use PyObject_Vectorcall and define functions to build a kwnames tuple and add arguments to args. From f2ebf37cd946f7b8c70349af9112362bf258c499 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 27 Nov 2023 16:53:16 +0100 Subject: [PATCH 240/286] Remove print statement --- Cython/Compiler/ExprNodes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index d47e1452eeb..39dd82a1373 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -9890,7 +9890,6 @@ def generate_result_code(self, code): if mkw in code.globalstate.const_cname_array: code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_mkw, mkw)) else: - print(mkw) if mkw == 'NULL': mkw = 'API_NULL_VALUE' code.putln("%s = %s;" % (tmp_load_mkw, mkw)) From c54633f9a79c98da4864c85306fc04032afc03d4 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Tue, 28 Nov 2023 14:16:49 +0100 Subject: [PATCH 241/286] Working on making type methods work --- Cython/Compiler/ExprNodes.py | 33 ++++++++++++++++++++++++++++++--- Cython/Utility/ObjectHandling.c | 2 +- tests/run/hpy_types.pyx | 24 ++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 tests/run/hpy_types.pyx diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 39dd82a1373..e89454fd427 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -8074,11 +8074,38 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, if self.is_py_attr: code.globalstate.use_utility_code( UtilityCode.load_cached("PyObjectSetAttrStr", "ObjectHandling.c")) + self_result = self.obj.py_result() + tmp_load_self = code.funcstate.allocate_temp(py_object_type, False) + if self_result in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_self, self_result)) + else: + code.putln("%s = %s;" % (tmp_load_self, self_result)) + attr_ident = code.intern_identifier(self.attribute) + tmp_load_attr = code.funcstate.allocate_temp(py_object_type, False) + if attr_ident in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_attr, attr_ident)) + else: + code.putln("%s = %s;" % (tmp_load_attr, attr_ident)) + rhs_result = rhs.py_result() + tmp_load_rhs = code.funcstate.allocate_temp(py_object_type, False) + if rhs_result in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_rhs, rhs_result)) + else: + code.putln("%s = %s;" % (tmp_load_rhs, rhs_result)) code.put_error_if_neg(self.pos, '__Pyx_PyObject_SetAttrStr(%s, %s, %s)' % ( - self.obj.py_result(), - code.intern_identifier(self.attribute), - rhs.py_result())) + tmp_load_self, + tmp_load_attr, + tmp_load_rhs)) + if self_result in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_load_self) + code.funcstate.release_temp(tmp_load_self) + if attr_ident in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_load_attr) + code.funcstate.release_temp(tmp_load_attr) + if rhs_result in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % tmp_load_rhs) + code.funcstate.release_temp(tmp_load_rhs) rhs.generate_disposal_code(code) rhs.free_temps(code) elif self.obj.type.is_complex: diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 081c26358ba..45d01cb3330 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1664,7 +1664,7 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStr(HPY_CONTEXT_FIRST_A static CYTHON_INLINE int __Pyx_PyObject_SetAttrStr(PyObject* obj, PyObject* attr_name, PyObject* value);/*proto*/ #else #define __Pyx_PyObject_DelAttrStr(o,n) PyObject_DelAttr(o,n) -#define __Pyx_PyObject_SetAttrStr(o,n,v) PyObject_SetAttr(o,n,v) +#define __Pyx_PyObject_SetAttrStr(o,n,v) PYOBJECT_SET_ATTR(o,n,v) #endif /////////////// PyObjectSetAttrStr /////////////// diff --git a/tests/run/hpy_types.pyx b/tests/run/hpy_types.pyx new file mode 100644 index 00000000000..9bd67a42581 --- /dev/null +++ b/tests/run/hpy_types.pyx @@ -0,0 +1,24 @@ +import cython + +class Circle: + def __init__(self): + self.radius = 10 + + def radius_getter(self): + return self.radius + +def circle_init(): + """ + >>> circle_init() + 10 + """ + loop = Circle() + return loop.radius + +def get_circle_radius(circle): + """ + >>> loop = Circle() + >>> get_circle_radius(loop) + 10 + """ + return circle.radius \ No newline at end of file From bd1ccf752fce0a55e5216b32bbaa2aeeb37d874d Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 29 Nov 2023 15:00:46 +0100 Subject: [PATCH 242/286] Type methods now work on HPy on O0 --- Cython/Utility/HPyUtils.c | 1 + Cython/Utility/ObjectHandling.c | 15 +++++++-------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 53c03c4be37..9f573cb4dad 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -54,6 +54,7 @@ //General Methods #define API_IS_EQUAL(a, b) HPy_Is(HPY_CONTEXT_CNAME, a, b) + #define API_IS_NOT_EQUAL(a, b) !HPy_Is(HPY_CONTEXT_CNAME, a, b) #define API_RICH_COMPARE(h1, h2, op) HPy_RichCompare(HPY_CONTEXT_CNAME, h1, h2, op) #define API_RICH_COMPARE_BOOL(h1, h2, op) HPy_RichCompareBool(HPY_CONTEXT_CNAME, h1, h2, op) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 45d01cb3330..11c642cf30d 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1182,13 +1182,12 @@ static PYOBJECT_TYPE __Pyx_Py3MetaclassPrepare(HPY_CONTEXT_FIRST_ARG_DEF PYOBJEC PYOBJECT_GLOBAL_CLOSEREF(loaded_prepare); if (API_IS_NOT_NULL(prep)) { #if CYTHON_USING_HPY - HPyListBuilder builder = HPyListBuilder_New(HPY_CONTEXT_CNAME, 3); - HPyListBuilder_Set(HPY_CONTEXT_CNAME, builder, 0, HPy_NULL); - HPyListBuilder_Set(HPY_CONTEXT_CNAME, builder, 1, name); - HPyListBuilder_Set(HPY_CONTEXT_CNAME, builder, 2, bases); + HPyListBuilder builder = HPyListBuilder_New(HPY_CONTEXT_CNAME, 2); + HPyListBuilder_Set(HPY_CONTEXT_CNAME, builder, 0, name); + HPyListBuilder_Set(HPY_CONTEXT_CNAME, builder, 1, bases); PYOBJECT_TYPE pargs = HPyListBuilder_Build(HPY_CONTEXT_CNAME, builder); ns = HPy_Call(HPY_CONTEXT_CNAME, prep, &pargs, HPy_Length(HPY_CONTEXT_CNAME, pargs), mkw); - HPy_Close(HPY_CONTEXT_CNAME, pargs); + HPy_Close(HPY_CONTEXT_CNAME, prep); #else PyObject *pargs[3] = {NULL, name, bases}; ns = __Pyx_PyObject_FastCallDict(prep, pargs+1, 2 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET, mkw); @@ -2213,7 +2212,7 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_FastCallDict(HPY_CONTEXT_FIRST } if (nargs == 0) { - return __Pyx_PyObject_Call(func, PyTuple_New(0), kwargs); //TODO(HPy): Must be changed back to $empty_tuple when context is available + return __Pyx_PyObject_Call(func, $empty_tuple, kwargs); } #if PY_VERSION_HEX >= 0x03090000 && !CYTHON_COMPILING_IN_LIMITED_API return PyObject_VectorcallDict(func, args, (size_t)nargs, kwargs); @@ -2370,10 +2369,10 @@ static CYTHON_INLINE PyObject* __Pyx_tp_new_kwargs(PyObject* type_obj, PyObject* #else #define __Pyx_PyObject_Call_h(func, arg, kw) __Pyx_PyObject_Call(func, arg, kw) #endif -#if CYTHON_COMPILING_IN_CPYTHON +#if CYTHON_COMPILING_IN_CPYTHON && !CYTHON_USING_HPY static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw); /*proto*/ #else -#define __Pyx_PyObject_Call(func, arg, kw) PyObject_Call(func, arg, kw) +#define __Pyx_PyObject_Call(func, arg, kw) API_CALL_FUNC(func, arg, 0, kw) #endif /////////////// PyObjectCall /////////////// From ce58385431a91b7bf66d17dce076c5584b957d1c Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Tue, 5 Dec 2023 10:37:13 +0100 Subject: [PATCH 243/286] Class inheritance works sometimes now --- Cython/Compiler/ExprNodes.py | 9 ++- Cython/Compiler/Nodes.py | 4 +- Cython/Utility/HPyUtils.c | 14 +++- Cython/Utility/ObjectHandling.c | 134 ++++++++++++++++---------------- tests/run/hpy_types.pyx | 34 ++++---- 5 files changed, 110 insertions(+), 85 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index e89454fd427..7772906a946 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -9923,7 +9923,10 @@ def generate_result_code(self, code): tmp_load_metaclass = code.funcstate.allocate_temp(py_object_type, False) if class_def_node.metaclass: metaclass = class_def_node.metaclass.py_result() - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_metaclass, metaclass)) + if metaclass in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (tmp_load_metaclass, metaclass)) + else: + code.putln("%s = %s;" % (tmp_load_metaclass, metaclass)) elif self.force_type: metaclass = "(CAST_IF_CAPI(PYOBJECT_TYPE)CAPI_NEEDS_DEREFERENCE __Pyx_DefaultClassType)" code.putln("%s = %s;" % (tmp_load_metaclass, metaclass)) @@ -9986,12 +9989,12 @@ def generate_result_code(self, code): else: code.globalstate.use_utility_code( UtilityCode.load_cached("CalculateMetaclass", "ObjectHandling.c")) - call = "__Pyx_CalculateMetaclass(NULL, %s)" % ( + call = "__Pyx_CalculateMetaclass(HPY_CONTEXT_FIRST_ARG_CALL API_NULL_VALUE, %s)" % ( bases.result()) code.putln( "%s = %s; %s" % ( self.result(), call, - code.error_goto_if_null(self.result(), self.pos))) + code.error_goto_if_null_object(self.result(), self.pos))) self.generate_gotref(code) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index f1e9133f797..53898c8c08b 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -5239,9 +5239,9 @@ def generate_execution_code(self, code): self.dict.generate_evaluation_code(code) if self.orig_bases: # update __orig_bases__ if needed - code.putln("if (%s != %s) {" % (self.bases.result(), self.orig_bases.result())) + code.putln("if (API_IS_NOT_EQUAL(%s, %s)) {" % (self.bases.result(), self.orig_bases.result())) code.putln( - code.error_goto_if_neg('PyDict_SetItemString(%s, "__orig_bases__", %s)' % ( + code.error_goto_if_neg('DICT_SET_ITEM_STR(%s, "__orig_bases__", %s)' % ( self.dict.result(), self.orig_bases.result()), self.pos )) diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 9f573cb4dad..e37391c2fd7 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -30,6 +30,7 @@ #define PYOBJECT_XNEWREF(h) HPy_Dup(HPY_CONTEXT_CNAME, h) #define PYOBJECT_CLOSEREF(h) HPy_Close(HPY_CONTEXT_CNAME, h) #define PYOBJECT_XCLOSEREF(h) HPy_Close(HPY_CONTEXT_CNAME, h) + #define PYOBJECT_CLEAR(h) HPy_Close(HPY_CONTEXT_CNAME, h) #define PYOBJECT_GLOBAL_CLOSEREF(ref) HPy_Close(HPY_CONTEXT_CNAME, ref) #define REFNANNY_CLOSEREF(func, h) PYOBJECT_CLOSEREF(h) @@ -132,7 +133,9 @@ //Tuple Type #define TUPLE_CREATE_EMPTY() HPyTuple_FromArray(HPY_CONTEXT_CNAME, NULL, 0) #define TUPLE_GET_ITEM(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, PYOBJECT_LONG_FROM_LONG(pos)) - #define TUPLE_GET_SIZE(h) HPy_Length(HPY_CONTEXT_CNAME, (h)) + #define TUPLE_GET_ITEM_SAFE(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, PYOBJECT_LONG_FROM_LONG(pos)) + #define TUPLE_GET_SIZE(h) HPy_Length(HPY_CONTEXT_CNAME, h) + #define TUPLE_GET_SIZE_SAFE(h) HPy_Length(HPY_CONTEXT_CNAME, h) #define TUPLE_BUILDER_TYPE HPyTupleBuilder #define TUPLE_CREATE_START(target, builder, size) builder = HPyTupleBuilder_New(HPY_CONTEXT_CNAME, size) #define TUPLE_CREATE_ASSIGN(tuple, builder, index, item) HPyTupleBuilder_Set(HPY_CONTEXT_CNAME, builder, index, item) @@ -141,7 +144,10 @@ //List Type #define LIST_CREATE_EMPTY() HPyList_New(HPY_CONTEXT_CNAME, 0) + #define LIST_NEW(i) HPyList_New(HPY_CONTEXT_CNAME, i) #define LIST_GET_ITEM(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, PYOBJECT_LONG_FROM_LONG(pos)) + #define LIST_GET_SIZE(h) HPy_Length(HPY_CONTEXT_CNAME, h) + #define LIST_GET_SIZE_SAFE(h) HPy_Length(HPY_CONTEXT_CNAME, h) #define LIST_APPEND(list, h) HPyList_Append(HPY_CONTEXT_CNAME, list, h) #define LIST_BUILDER_TYPE HPyListBuilder #define LIST_CREATE_START(target, builder, size) builder = HPyListBuilder_New(HPY_CONTEXT_CNAME, size) @@ -209,6 +215,7 @@ #define PYOBJECT_CLOSEREF(h) Py_DECREF(h) #define PYOBJECT_XCLOSEREF(h) Py_XDECREF(h) #define PYOBJECT_GLOBAL_CLOSEREF(ref) /* nop */ + #define PYOBJECT_CLEAR(h) Py_CLEAR(h) #define REFNANNY_CLOSEREF(func, h) func(h) //HPy to/from PyObject Functions @@ -311,7 +318,9 @@ //Tuple Type #define TUPLE_CREATE_EMPTY() PyTuple_New(0) #define TUPLE_GET_ITEM(h, pos) __Pyx_PySequence_ITEM(h, pos) + #define TUPLE_GET_ITEM_SAFE(h, pos) PyTuple_GetItem(h, pos) #define TUPLE_GET_SIZE(h) PyTuple_GET_SIZE(h) + #define TUPLE_GET_SIZE_SAFE(h) PyTuple_Size(h) #define TUPLE_BUILDER_TYPE PyObject * //Not used, just needed to prevent errors #define TUPLE_CREATE_START(target, builder, size) target=PyTuple_New(size) #define TUPLE_CREATE_ASSIGN(tuple, builder, index, item) __Pyx_PyTuple_SET_ITEM(tuple, index, item) @@ -320,8 +329,11 @@ //List Type #define LIST_CREATE_EMPTY() PyList_New(0) + #define LIST_NEW(i) PyList_New(i) #define LIST_GET_ITEM(h, pos) __Pyx_PySequence_ITEM(HPY_CONTEXT_CNAME, h, pos) #define LIST_APPEND(list, h) PyList_Append(list, h) + #define LIST_GET_SIZE(h) PyList_GET_SIZE(h) + #define LIST_GET_SIZE_SAFE(h) PyList_Size(h) #define LIST_BUILDER_TYPE PyObject * //Not used, just needed to prevent errors #define LIST_CREATE_START(target, builder, size) target=PyList_New(size) #define LIST_CREATE_ASSIGN(tuple, builder, index, item) __Pyx_PyList_SET_ITEM(tuple, index, item) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 11c642cf30d..a467095a40e 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1032,45 +1032,46 @@ static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *na /////////////// Py3UpdateBases.proto /////////////// -static PyObject* __Pyx_PEP560_update_bases(PyObject *bases); /* proto */ +static PYOBJECT_TYPE __Pyx_PEP560_update_bases(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE bases); /* proto */ /////////////// Py3UpdateBases ///////////////////// //@requires: PyObjectCallOneArg //@requires: PyObjectGetAttrStrNoError /* Shamelessly adapted from cpython/bltinmodule.c update_bases */ -static PyObject* -__Pyx_PEP560_update_bases(PyObject *bases) +static PYOBJECT_TYPE +__Pyx_PEP560_update_bases(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE bases) { - Py_ssize_t i, j, size_bases; - PyObject *base = NULL, *meth, *new_base, *result, *new_bases = NULL; + API_SSIZE_T i, j, size_bases; + PYOBJECT_TYPE base = API_NULL_VALUE, CAPI_IS_POINTER meth, CAPI_IS_POINTER new_base; + PYOBJECT_TYPE result, CAPI_IS_POINTER new_bases = API_NULL_VALUE; /*assert(PyTuple_Check(bases));*/ #if CYTHON_ASSUME_SAFE_SIZE size_bases = PyTuple_GET_SIZE(bases); #else - size_bases = PyTuple_Size(bases); - if (size_bases < 0) return NULL; + size_bases = TUPLE_GET_SIZE_SAFE(bases); + if (size_bases < 0) return API_NULL_VALUE; #endif for (i = 0; i < size_bases; i++) { // original code in CPython: base = args[i]; #if CYTHON_AVOID_BORROWED_REFS - Py_CLEAR(base); + PYOBJECT_CLEAR(base); #endif #if CYTHON_ASSUME_SAFE_MACROS base = PyTuple_GET_ITEM(bases, i); #else - base = PyTuple_GetItem(bases, i); - if (!base) goto error; + base = TUPLE_GET_ITEM(bases, i); + if (API_IS_NULL(base)) goto error; #endif -#if CYTHON_AVOID_BORROWED_REFS +#if CYTHON_AVOID_BORROWED_REFS && !CYTHON_USING_HPY Py_INCREF(base); #endif - if (PyType_Check(base)) { - if (new_bases) { + if (TYPE_CHECK(base)) { + if (API_IS_NOT_NULL(new_bases)) { // If we already have made a replacement, then we append every normal base, // otherwise just skip it. - if (PyList_Append(new_bases, base) < 0) { + if (LIST_APPEND(new_bases, base) < 0) { goto error; } } @@ -1078,80 +1079,89 @@ __Pyx_PEP560_update_bases(PyObject *bases) } // original code in CPython: // if (_PyObject_LookupAttrId(base, &PyId___mro_entries__, &meth) < 0) { - meth = __Pyx_PyObject_GetAttrStrNoError(base, PYIDENT("__mro_entries__")); - if (!meth && PyErr_Occurred()) { + PYOBJECT_TYPE loaded_mro_entries = PYOBJECT_GLOBAL_LOAD(PYIDENT("__mro_entries__")); + meth = __Pyx_PyObject_GetAttrStrNoError(HPY_CONTEXT_FIRST_ARG_CALL base, loaded_mro_entries); + PYOBJECT_GLOBAL_CLOSEREF(loaded_mro_entries); + if (API_IS_NULL(meth) && PyErr_Occurred()) { goto error; } - if (!meth) { - if (new_bases) { - if (PyList_Append(new_bases, base) < 0) { + if (API_IS_NULL(meth)) { + if (API_IS_NOT_NULL(new_bases)) { + if (LIST_APPEND(new_bases, base) < 0) { goto error; } } continue; } - new_base = __Pyx_PyObject_CallOneArg(meth, bases); - Py_DECREF(meth); - if (!new_base) { + new_base = __Pyx_PyObject_CallOneArg(HPY_CONTEXT_FIRST_ARG_CALL meth, bases); + PYOBJECT_CLOSEREF(meth); + if (API_IS_NULL(new_base)) { goto error; } - if (!PyTuple_Check(new_base)) { + if (!TUPLE_CHECK(new_base)) { PyErr_SetString(PyExc_TypeError, "__mro_entries__ must return a tuple"); - Py_DECREF(new_base); + PYOBJECT_CLOSEREF(new_base); goto error; } - if (!new_bases) { + if (API_IS_NULL(new_bases)) { // If this is a first successful replacement, create new_bases list and // copy previously encountered bases. - if (!(new_bases = PyList_New(i))) { + new_bases = LIST_NEW(i); + if (API_IS_NULL(new_bases)) { goto error; } for (j = 0; j < i; j++) { // original code in CPython: base = args[j]; // We use a different name here to keep refcounting separate from base - PyObject *base_from_list; + PYOBJECT_TYPE base_from_list; #if CYTHON_ASSUME_SAFE_MACROS base_from_list = PyTuple_GET_ITEM(bases, j); PyList_SET_ITEM(new_bases, j, base_from_list); Py_INCREF(base_from_list); #else - base_from_list = PyTuple_GetItem(bases, j); - if (!base_from_list) goto error; + base_from_list = TUPLE_GET_ITEM_SAFE(bases, j); + if (API_IS_NULL(base_from_list)) goto error; +#if !CYTHON_USING_HPY Py_INCREF(base_from_list); - if (PyList_SetItem(new_bases, j, base_from_list) < 0) goto error; +#endif + if (SEQUENCE_SET_ITEM(new_bases, j, base_from_list) < 0) goto error; #endif } } #if CYTHON_ASSUME_SAFE_SIZE j = PyList_GET_SIZE(new_bases); #else - j = PyList_Size(new_bases); + j = LIST_GET_SIZE_SAFE(new_bases); if (j < 0) goto error; #endif - if (PyList_SetSlice(new_bases, j, j, new_base) < 0) { + if (PyList_SetSlice(HPY_LEGACY_OBJECT_AS(new_bases), j, j, HPY_LEGACY_OBJECT_AS(new_base)) < 0) { goto error; } - Py_DECREF(new_base); + PYOBJECT_CLOSEREF(new_base); } - if (!new_bases) { + if (API_IS_NULL(new_bases)) { // unlike the CPython implementation, always return a new reference +#if CYTHON_USING_HPY + return PYOBJECT_NEWREF(bases); +#else Py_INCREF(bases); return bases; +#endif } - result = PyList_AsTuple(new_bases); - Py_DECREF(new_bases); + result = HPY_LEGACY_OBJECT_FROM(PyList_AsTuple(HPY_LEGACY_OBJECT_AS(new_bases))); + PYOBJECT_CLOSEREF(new_bases); #if CYTHON_AVOID_BORROWED_REFS - Py_XDECREF(base); + PYOBJECT_XCLOSEREF(base); #endif return result; error: - Py_XDECREF(new_bases); + PYOBJECT_XCLOSEREF(new_bases); #if CYTHON_AVOID_BORROWED_REFS - Py_XDECREF(base); + PYOBJECT_XCLOSEREF(base); #endif - return NULL; + return API_NULL_VALUE; } /////////////// Py3ClassCreate.proto /////////////// @@ -1182,17 +1192,13 @@ static PYOBJECT_TYPE __Pyx_Py3MetaclassPrepare(HPY_CONTEXT_FIRST_ARG_DEF PYOBJEC PYOBJECT_GLOBAL_CLOSEREF(loaded_prepare); if (API_IS_NOT_NULL(prep)) { #if CYTHON_USING_HPY - HPyListBuilder builder = HPyListBuilder_New(HPY_CONTEXT_CNAME, 2); - HPyListBuilder_Set(HPY_CONTEXT_CNAME, builder, 0, name); - HPyListBuilder_Set(HPY_CONTEXT_CNAME, builder, 1, bases); - PYOBJECT_TYPE pargs = HPyListBuilder_Build(HPY_CONTEXT_CNAME, builder); - ns = HPy_Call(HPY_CONTEXT_CNAME, prep, &pargs, HPy_Length(HPY_CONTEXT_CNAME, pargs), mkw); - HPy_Close(HPY_CONTEXT_CNAME, prep); + HPy pargs[2] = {name, bases}; + ns = HPy_Call(HPY_CONTEXT_CNAME, prep, pargs, 2, mkw); #else PyObject *pargs[3] = {NULL, name, bases}; - ns = __Pyx_PyObject_FastCallDict(prep, pargs+1, 2 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET, mkw); - Py_DECREF(prep); + ns = __Pyx_PyObject_FastCallDict(HPY_CONTEXT_FIRST_ARG_CALL prep, pargs+1, 2 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET, mkw); #endif + PYOBJECT_CLOSEREF(prep); } else { if (unlikely(PyErr_Occurred())) return API_NULL_VALUE; @@ -1209,13 +1215,9 @@ static PYOBJECT_TYPE __Pyx_Py3MetaclassPrepare(HPY_CONTEXT_FIRST_ARG_DEF PYOBJEC PYOBJECT_TYPE loaded_module = PYOBJECT_GLOBAL_LOAD(PYIDENT("__module__")); if (unlikely(PYOBJECT_SET_ITEM(ns, loaded_module, modname) < 0)) goto bad; PYOBJECT_GLOBAL_CLOSEREF(loaded_module); -#if PY_VERSION_HEX >= 0x03030000 PYOBJECT_TYPE loaded_qualname = PYOBJECT_GLOBAL_LOAD(PYIDENT("__qualname__")); if (unlikely(PYOBJECT_SET_ITEM(ns, loaded_qualname, qualname) < 0)) goto bad; PYOBJECT_GLOBAL_CLOSEREF(loaded_qualname); -#else - CYTHON_MAYBE_UNUSED_VAR(qualname); -#endif PYOBJECT_TYPE loaded_doc = PYOBJECT_GLOBAL_LOAD(PYIDENT("__doc__")); if (unlikely(API_IS_NOT_NULL(doc) && PYOBJECT_SET_ITEM(ns, loaded_doc, doc) < 0)) goto bad; PYOBJECT_GLOBAL_CLOSEREF(loaded_doc); @@ -1620,7 +1622,7 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStrNoError(HPY_CONTEXT_ #if !CYTHON_USING_HPY result = __Pyx_PyObject_GetAttrStr(obj, attr_name); #else - result = PYOBJECT_GET_ITEM(obj, attr_name); + result = PYOBJECT_GET_ATTR(obj, attr_name); #endif if (unlikely(API_IS_NULL(result))) { __Pyx_PyObject_GetAttrStr_ClearAttributeError(); @@ -2122,7 +2124,7 @@ static PYOBJECT_TYPE __Pyx__CallUnboundCMethod2(HPY_CONTEXT_FIRST_ARG_DEF __Pyx_ #define __Pyx_PyObject_FastCall(func, args, nargs) API_CALL_FUNC(func, args, (size_t)(nargs), API_NULL_VALUE) #else #define __Pyx_PyObject_FastCall(func, args, nargs) __Pyx_PyObject_FastCallDict(HPY_CONTEXT_FIRST_ARG_CALL func, args, (size_t)(nargs), API_NULL_VALUE) -static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_FastCallDict(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE func, PYOBJECT_TYPE *args, size_t nargs, PYOBJECT_TYPE kwargs); /*proto*/ +static CYTHON_INLINE PyObject *__Pyx_PyObject_FastCallDict(HPY_CONTEXT_FIRST_ARG_DEF PyObject *func, PyObject **args, size_t nargs, PyObject *kwargs); /*proto*/ #endif /////////////// PyObjectFastCall /////////////// @@ -2131,7 +2133,7 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_FastCallDict(HPY_CONTEXT_FIRST //@requires: PyObjectCallMethO //@substitute: naming -#if !CYTHON_USING_HPY && PY_VERSION_HEX < 0x03090000 || CYTHON_COMPILING_IN_LIMITED_API +#if !CYTHON_USING_HPY && (PY_VERSION_HEX < 0x03090000 || CYTHON_COMPILING_IN_LIMITED_API) static PyObject* __Pyx_PyObject_FastCall_fallback(PyObject *func, PyObject **args, size_t nargs, PyObject *kwargs) { PyObject *argstuple; PyObject *result = 0; @@ -2156,17 +2158,13 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_FastCallDict(HPY_CONTEXT_FIRST // NOTE: in many cases, this is called with a constant value for nargs // which is known at compile-time. So the branches below will typically // be optimized away. -#if CYTHON_USING_HPY - API_SSIZE_T nargs = _nargs; -#else API_SSIZE_T nargs = __Pyx_PyVectorcall_NARGS(_nargs); -#endif #if CYTHON_COMPILING_IN_CPYTHON if (nargs == 0 && API_IS_NULL(kwargs)) { if (__Pyx_CyOrPyCFunction_Check(func) && likely( __Pyx_CyOrPyCFunction_GET_FLAGS(func) & METH_NOARGS)) return __Pyx_PyObject_CallMethO(func, NULL); } - else if (nargs == 1 && API_IS_NULL(kwargs)) { + else if (nargs == 1 && !kwargs) { if (__Pyx_CyOrPyCFunction_Check(func) && likely( __Pyx_CyOrPyCFunction_GET_FLAGS(func) & METH_O)) return __Pyx_PyObject_CallMethO(func, args[0]); } @@ -2192,8 +2190,8 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_FastCallDict(HPY_CONTEXT_FIRST #endif #endif - if (kwargs == NULL) { - #if CYTHON_VECTORCALL && !CYTHON_COMPILING_IN_LIMITED_API + if (!kwargs) { + #if CYTHON_VECTORCALL && !CYTHON_COMPILING_IN_LIMITED_API #if PY_VERSION_HEX < 0x03090000 vectorcallfunc f = _PyVectorcall_Function(func); #else @@ -2628,8 +2626,13 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_Call2Args(HPY_CONTEXT_FIRST_AR //@requires: PyObjectFastCall static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_Call2Args(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE function, PYOBJECT_TYPE arg1, PYOBJECT_TYPE arg2) { - PYOBJECT_TYPE args[3] = {API_NULL_VALUE, arg1, arg2}; +#if CYTHON_USING_HPY + HPy args[2] = {arg1, arg2}; + return __Pyx_PyObject_FastCall(function, args, 2); +#else + PyObject *args[3] = {NULL, arg1, arg2}; return __Pyx_PyObject_FastCall(function, args+1, 2 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET); +#endif } @@ -2641,10 +2644,11 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_CallOneArg(HPY_CONTEXT_FIRST_A //@requires: PyObjectFastCall static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_CallOneArg(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE func, PYOBJECT_TYPE arg) { - PYOBJECT_TYPE args[2] = {API_NULL_VALUE, arg}; #if CYTHON_USING_HPY - return __Pyx_PyObject_FastCall(func, args+1, 1); + PYOBJECT_TYPE args[1] = {arg}; + return __Pyx_PyObject_FastCall(func, args, 1); #else + PYOBJECT_TYPE args[2] = {API_NULL_VALUE, arg}; return __Pyx_PyObject_FastCall(func, args+1, 1 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET); #endif } diff --git a/tests/run/hpy_types.pyx b/tests/run/hpy_types.pyx index 9bd67a42581..cfa9acf3cd7 100644 --- a/tests/run/hpy_types.pyx +++ b/tests/run/hpy_types.pyx @@ -1,24 +1,30 @@ import cython -class Circle: - def __init__(self): - self.radius = 10 +class Shape: + def __init__(self, colour): + self.colour = colour - def radius_getter(self): + def get_colour(self): + return self.colour + + def set_colour(self, colour): + self.colour = colour + +class Circle(Shape): + def __init__(self, colour, radius): + Shape.__init__(self, colour) + self.radius = radius + + def get_radius(self): return self.radius -def circle_init(): - """ - >>> circle_init() - 10 - """ - loop = Circle() - return loop.radius + def set_radius(self, radius): + self.radius = radius def get_circle_radius(circle): """ - >>> loop = Circle() + >>> loop = Circle("red", 11) >>> get_circle_radius(loop) - 10 + 11 """ - return circle.radius \ No newline at end of file + return circle.get_radius() From 7bb4f7e5b71c9f79172fb72879994db01b77e9d7 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Tue, 5 Dec 2023 21:13:55 +0100 Subject: [PATCH 244/286] Fix: use PYOBJECT_GET_ATTR_STR in __Pyx_PyObject_GetAttrStr --- Cython/Utility/ObjectHandling.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index a467095a40e..41fbaa410f6 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1651,9 +1651,7 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStr(HPY_CONTEXT_FIRST_A if (likely(tp->tp_getattro)) return tp->tp_getattro(obj, attr_name); #endif - PYOBJECT_TYPE item = DICT_GET_ITEM(obj, attr_name); - - return item; + return PYOBJECT_GET_ATTR_STR(obj, attr_name); } #endif From 83905fcc5484b9fb104edbf7f90151b7025f7f2f Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Tue, 5 Dec 2023 21:14:33 +0100 Subject: [PATCH 245/286] Add missing API_IS_NOT_EQUAL in HPy case --- Cython/Utility/HPyUtils.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index e37391c2fd7..85b963a9c77 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -23,7 +23,7 @@ #define PYOBJECT_GLOBAL_LOAD(global) HPyGlobal_Load(HPY_CONTEXT_CNAME, global) #define CAPI_IS_POINTER #define CAPI_NEEDS_DEREFERENCE - #define CAST_IF_CAPI(type) + #define CAST_IF_CAPI(type) //Create New and Close References #define PYOBJECT_NEWREF(h) HPy_Dup(HPY_CONTEXT_CNAME, h) @@ -66,9 +66,9 @@ #define API_LONG_TYPE HPY_CONTEXT_CNAME->h_LongType #define API_SSIZE_T HPy_ssize_t #define API_STRING_TYPE HPY_CONTEXT_CNAME->h_UnicodeType - #define API_STRING_TYPE_DEREF API_STRING_TYPE + #define API_STRING_TYPE_DEREF API_STRING_TYPE #define API_DICT_TYPE HPY_CONTEXT_CNAME->h_DictType - #define API_DICT_TYPE_DEREF API_DICT_TYPE + #define API_DICT_TYPE_DEREF API_DICT_TYPE //Type Checks #define LONG_CHECK(l) HPyNumber_Check(HPY_CONTEXT_CNAME, l) From daff3880c8fd689ca81861be494c279328b1edc6 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Tue, 5 Dec 2023 21:15:06 +0100 Subject: [PATCH 246/286] Avoid infinite recursion in cython_func_or_meth.repr --- Cython/Utility/CythonFunction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index db3f86809d1..0a90428e9df 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -1046,7 +1046,7 @@ __Pyx_CyFunction_repr_impl(HPyContext *HPY_CONTEXT_CNAME, HPy op) { __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); HPy qualname_field = HPyField_Load(HPY_CONTEXT_CNAME, op, struct_op->func_qualname); - HPy result = HPyUnicode_FromFormat(HPY_CONTEXT_CNAME, "", qualname_field, op); + HPy result = HPyUnicode_FromFormat(HPY_CONTEXT_CNAME, "", qualname_field, struct_op); HPy_Close(HPY_CONTEXT_CNAME, qualname_field); return result; } From 78019ab9dc31ff9f52cd3079e857fb62ffb4d627 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Tue, 5 Dec 2023 21:15:22 +0100 Subject: [PATCH 247/286] Add missing return statement in __pyx_CyFunction_descr_get_impl --- Cython/Utility/CythonFunction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 0a90428e9df..3346d7fa2a6 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -1463,7 +1463,7 @@ static HPy __Pyx_CyFunction_call_impl(HPyContext *HPY_CONTEXT_CNAME, HPy func, c HPyDef_SLOT(__pyx_CyFunction_descr_get, HPy_tp_descr_get) static HPy __pyx_CyFunction_descr_get_impl(HPyContext *ctx, HPy func, HPy self, HPy typ) { - __Pyx_PyMethod_New(ctx, func, self, typ); + return __Pyx_PyMethod_New(ctx, func, self, typ); } static HPyDef *__pyx_CyFunctionType_HPyDefines[] = { From 4a50a6532c1542f48ae5ea116da1db680b70183b Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Wed, 6 Dec 2023 10:20:10 +0100 Subject: [PATCH 248/286] Fix segfault in __Pyx_CyFunction_doc_get --- Cython/Utility/CythonFunction.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 3346d7fa2a6..66c659c3a8b 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -198,8 +198,13 @@ __Pyx_CyFunction_doc_get(HPyContext *HPY_CONTEXT_CNAME, __pyx_CyFunctionObject_F { CYTHON_UNUSED_VAR(closure); __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); - HPy h_doc = HPyField_Load(HPY_CONTEXT_CNAME, op, struct_op->func_doc); - if (HPy_IsNull(h_doc)) { + HPy h_doc; + if (!HPyField_IsNull(struct_op->func_doc)) { + h_doc = HPyField_Load(HPY_CONTEXT_CNAME, op, struct_op->func_doc); + } else { + h_doc = HPy_NULL; + } + if (HPy_IsNull(h_doc) && struct_op->func->meth.doc != NULL) { h_doc = HPyUnicode_FromString(HPY_CONTEXT_CNAME, struct_op->func->meth.doc); } if (HPy_IsNull(h_doc)) { From 229d30f45e65f167057683a6ce487092d2c95f6c Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Fri, 15 Dec 2023 08:45:35 +0100 Subject: [PATCH 249/286] Fixed segfaults from rebase --- Cython/Compiler/ExprNodes.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 7772906a946..32c7adc13b5 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -2549,11 +2549,9 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, n = "SetNewInClass" if self.name == "__new__" else "SetNameInClass" code.globalstate.use_utility_code(UtilityCode.load_cached(n, "ObjectHandling.c")) setter = '__Pyx_' + n - if rhs_result in code.globalstate.const_cname_array: - code.putln("#if CYTHON_USING_HPY") - load_result_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=False) - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_result_temp, rhs_result)) - code.putln("#endif") + load_result_temp = LoadGlobalNode(self.pos, rhs_result) + load_result_temp.allocate(code) + load_result_value = load_result_temp.temp_cname else: assert False, repr(entry) code.putln("#if CYTHON_USING_HPY") @@ -2572,7 +2570,7 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, load_result_value)) load_namespace_temp.release(code) load_cname_temp.release(code) - if entry.scope.is_module_scope: + if not entry.is_member and (entry.scope.is_module_scope or entry.is_pyclass_attr): load_result_temp.release(code) code.putln("#else") code.put_error_if_neg( @@ -6795,17 +6793,22 @@ def attribute_is_likely_method(attr): loaded_vars = [] for arg in args: arg_result = arg.py_result() - load_tmp = LoadGlobalNode(self.pos, arg_result) + load_tmp = code.funcstate.allocate_temp(py_object_type, False) loaded_vars.append(load_tmp) - load_tmp.allocate(code) + if arg_result in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_tmp, arg_result)) + else: + code.putln("%s = %s;" % (load_tmp, arg_result)) code.putln("PYOBJECT_TYPE __pyx_callargs[%d%s] = {%s, %s};" % ( (len(args) + 1) if args else 2, extra_keyword_args, self_arg, - ', '.join(tmp.temp_cname for tmp in loaded_vars) if args else "NULL", + ', '.join(tmp for tmp in loaded_vars) if args else "NULL", )) for tmp in loaded_vars: - tmp.release(code) + if arg_result in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % load_tmp) + code.funcstate.release_temp(tmp) if kwargs_key_value_pairs: for n, keyvalue in enumerate(kwargs_key_value_pairs): key_is_str = ( From f66f1c29c9d2c58e595b7b9e1c0308c07f1dd882 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 18 Jan 2024 16:50:24 +0100 Subject: [PATCH 250/286] Fix most of type port PR issues --- Cython/Compiler/ExprNodes.py | 21 ++++++++------------- Cython/Utility/CythonFunction.c | 6 +----- Cython/Utility/HPyUtils.c | 9 +++++++++ Cython/Utility/ModuleSetupCode.c | 2 +- Cython/Utility/ObjectHandling.c | 9 ++------- 5 files changed, 21 insertions(+), 26 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 32c7adc13b5..573cacff0c4 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -3575,11 +3575,11 @@ def analyse_types(self, env): def analyse_target_declaration(self, env): self.is_target = True - def generate_result_code(self, code): + def generate_result_code(self, code, ref_managed=True): pass def allocate(self, code): - self.temp_cname = code.funcstate.allocate_temp(self.type, manage_ref=True) + self.temp_cname = code.funcstate.allocate_temp(self.type, manage_ref=ref_managed) def release(self, code): code.funcstate.release_temp(self.temp_cname) @@ -3612,7 +3612,7 @@ def __init__(self, pos, var_name): self.var_name = var_name def allocate(self, code, needs_decl=False): - super(self.__class__, self).allocate(code) + super(self.__class__, self).allocate(code, False) if needs_decl: code.putln("PYOBJECT_TYPE %s;" % self.temp_cname) if self.var_name in code.globalstate.const_cname_array: @@ -3620,7 +3620,7 @@ def allocate(self, code, needs_decl=False): else: code.putln("%s = %s;" % (self.temp_cname, self.var_name)) - def release_global(self, code): + def release(self, code): if self.var_name in code.globalstate.const_cname_array: code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % self.temp_cname) super(self.__class__, self).release(code) @@ -6793,22 +6793,17 @@ def attribute_is_likely_method(attr): loaded_vars = [] for arg in args: arg_result = arg.py_result() - load_tmp = code.funcstate.allocate_temp(py_object_type, False) + load_tmp = LoadGlobalNode(self.pos, arg_result) + load_tmp.allocate(code) loaded_vars.append(load_tmp) - if arg_result in code.globalstate.const_cname_array: - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_tmp, arg_result)) - else: - code.putln("%s = %s;" % (load_tmp, arg_result)) code.putln("PYOBJECT_TYPE __pyx_callargs[%d%s] = {%s, %s};" % ( (len(args) + 1) if args else 2, extra_keyword_args, self_arg, - ', '.join(tmp for tmp in loaded_vars) if args else "NULL", + ', '.join(load_tmp.temp_cname for load_tmp in loaded_vars) if args else "NULL", )) for tmp in loaded_vars: - if arg_result in code.globalstate.const_cname_array: - code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % load_tmp) - code.funcstate.release_temp(tmp) + tmp.release(code) if kwargs_key_value_pairs: for n, keyvalue in enumerate(kwargs_key_value_pairs): key_is_str = ( diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 66c659c3a8b..cb5ab5feb16 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -199,11 +199,7 @@ __Pyx_CyFunction_doc_get(HPyContext *HPY_CONTEXT_CNAME, __pyx_CyFunctionObject_F CYTHON_UNUSED_VAR(closure); __pyx_CyFunctionObject *struct_op = __pyx_CyFunctionObject_AsStruct(HPY_CONTEXT_CNAME, op); HPy h_doc; - if (!HPyField_IsNull(struct_op->func_doc)) { - h_doc = HPyField_Load(HPY_CONTEXT_CNAME, op, struct_op->func_doc); - } else { - h_doc = HPy_NULL; - } + HPyField_XLoad(HPY_CONTEXT_CNAME, h_doc, struct_op->func_doc, op); if (HPy_IsNull(h_doc) && struct_op->func->meth.doc != NULL) { h_doc = HPyUnicode_FromString(HPY_CONTEXT_CNAME, struct_op->func->meth.doc); } diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 85b963a9c77..4798931b732 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -437,4 +437,13 @@ static CYTHON_INLINE HPy HPyDict_GetItem_s(HPyContext *ctx, HPy mp, const char * return res; } +static CYTHON_INLINE HPy HPyField_XLoad(HPyContext *ctx, HPy h_item, HPyField field, HPy owner) +{ + if (!HPyField_IsNull(field)) { + h_item = HPyField_Load(HPY_CONTEXT_CNAME, owner, field); + } else { + h_item = HPy_NULL; + } +} + #endif diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 28b44d0eaee..cc8da2eec3b 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -735,7 +735,7 @@ class __Pyx_FakeReference { // TODO: remove this block #if CYTHON_USING_HPY #define __Pyx_BUILTIN_MODULE_NAME "HPY_CONTEXT_CNAME->h_Builtins" - #define __Pyx_DefaultClassType HPY_CONTEXT_CNAME->h_TypeType + #define __Pyx_DefaultClassType (HPY_CONTEXT_CNAME->h_TypeType) #else #define __Pyx_BUILTIN_MODULE_NAME "builtins" #define __Pyx_DefaultClassType PyType_Type diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 41fbaa410f6..04fb430e4cc 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -920,12 +920,7 @@ static PYOBJECT_TYPE __Pyx_CalculateMetaclass(HPY_CONTEXT_FIRST_ARG_DEF PYTYPEOB metaclass = CAPI_NEEDS_DEREFERENCE __Pyx_DefaultClassType; } // make owned reference -#if !CYTHON_USING_HPY - Py_INCREF((PyObject*) metaclass); - return (PyObject*) metaclass; -#else - return metaclass; -#endif + return PYOBJECT_NEWREF(CAST_IF_CAPI(PyObject*) metaclass); } @@ -1193,7 +1188,7 @@ static PYOBJECT_TYPE __Pyx_Py3MetaclassPrepare(HPY_CONTEXT_FIRST_ARG_DEF PYOBJEC if (API_IS_NOT_NULL(prep)) { #if CYTHON_USING_HPY HPy pargs[2] = {name, bases}; - ns = HPy_Call(HPY_CONTEXT_CNAME, prep, pargs, 2, mkw); + ns = HPy_CallTupleDict(HPY_CONTEXT_CNAME, prep, pargs, mkw); #else PyObject *pargs[3] = {NULL, name, bases}; ns = __Pyx_PyObject_FastCallDict(HPY_CONTEXT_FIRST_ARG_CALL prep, pargs+1, 2 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET, mkw); From 8a86ddb8cb0ecad384068178eaec362112a26177 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 18 Jan 2024 16:51:19 +0100 Subject: [PATCH 251/286] Small fix to PR for type_class_port --- Cython/Compiler/ExprNodes.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 573cacff0c4..43707c370a7 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -3575,11 +3575,11 @@ def analyse_types(self, env): def analyse_target_declaration(self, env): self.is_target = True - def generate_result_code(self, code, ref_managed=True): + def generate_result_code(self, code): pass def allocate(self, code): - self.temp_cname = code.funcstate.allocate_temp(self.type, manage_ref=ref_managed) + self.temp_cname = code.funcstate.allocate_temp(self.type, manage_ref=True) def release(self, code): code.funcstate.release_temp(self.temp_cname) @@ -3612,7 +3612,7 @@ def __init__(self, pos, var_name): self.var_name = var_name def allocate(self, code, needs_decl=False): - super(self.__class__, self).allocate(code, False) + super(self.__class__, self).allocate(code) if needs_decl: code.putln("PYOBJECT_TYPE %s;" % self.temp_cname) if self.var_name in code.globalstate.const_cname_array: From 723f284f40a966869024dc309f0f0998b88a8fc3 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 22 Jan 2024 11:20:21 +0100 Subject: [PATCH 252/286] Fixed issue with wrong context in HPyField_XLoad --- Cython/Utility/HPyUtils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 4798931b732..d4e8cd0db40 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -440,7 +440,7 @@ static CYTHON_INLINE HPy HPyDict_GetItem_s(HPyContext *ctx, HPy mp, const char * static CYTHON_INLINE HPy HPyField_XLoad(HPyContext *ctx, HPy h_item, HPyField field, HPy owner) { if (!HPyField_IsNull(field)) { - h_item = HPyField_Load(HPY_CONTEXT_CNAME, owner, field); + h_item = HPyField_Load(ctx, owner, field); } else { h_item = HPy_NULL; } From 79603c2fdb4b7a4d56cfa254525280a9af7420e3 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 22 Jan 2024 12:37:27 +0100 Subject: [PATCH 253/286] Fixed segfault in temp node allocation --- Cython/Compiler/ExprNodes.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 43707c370a7..c51883a5476 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -3578,8 +3578,8 @@ def analyse_target_declaration(self, env): def generate_result_code(self, code): pass - def allocate(self, code): - self.temp_cname = code.funcstate.allocate_temp(self.type, manage_ref=True) + def allocate(self, code, ref_managed=True): + self.temp_cname = code.funcstate.allocate_temp(self.type, manage_ref=ref_managed) def release(self, code): code.funcstate.release_temp(self.temp_cname) @@ -3612,7 +3612,7 @@ def __init__(self, pos, var_name): self.var_name = var_name def allocate(self, code, needs_decl=False): - super(self.__class__, self).allocate(code) + super(self.__class__, self).allocate(code, False) if needs_decl: code.putln("PYOBJECT_TYPE %s;" % self.temp_cname) if self.var_name in code.globalstate.const_cname_array: From 54c2861317e1d4979c974656de1208e5ec1297e0 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 22 Jan 2024 12:50:42 +0100 Subject: [PATCH 254/286] Fixed call signature issue on HPy for MetaclassPrepare --- Cython/Utility/ObjectHandling.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 04fb430e4cc..0cc25ed324f 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1187,7 +1187,10 @@ static PYOBJECT_TYPE __Pyx_Py3MetaclassPrepare(HPY_CONTEXT_FIRST_ARG_DEF PYOBJEC PYOBJECT_GLOBAL_CLOSEREF(loaded_prepare); if (API_IS_NOT_NULL(prep)) { #if CYTHON_USING_HPY - HPy pargs[2] = {name, bases}; + HPyTupleBuilder pargs_builder = HPyTupleBuilder_New(HPY_CONTEXT_CNAME, 2); + HPyTupleBuilder_Set(HPY_CONTEXT_CNAME, pargs_builder, 0, name); + HPyTupleBuilder_Set(HPY_CONTEXT_CNAME, pargs_builder, 1, bases); + HPy pargs = HPyTupleBuilder_Build(HPY_CONTEXT_CNAME, pargs_builder); ns = HPy_CallTupleDict(HPY_CONTEXT_CNAME, prep, pargs, mkw); #else PyObject *pargs[3] = {NULL, name, bases}; From 8b72665b69d0b6d9a2497fba581ddfaf604ebcf2 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 14 Dec 2023 14:47:11 +0100 Subject: [PATCH 255/286] Fixed most errors for fannkuch benchmark --- Cython/Compiler/Builtin.py | 2 +- Cython/Compiler/ExprNodes.py | 123 +++++++++++++------- Cython/Compiler/ModuleNode.py | 6 +- Cython/Utility/CommonStructures.c | 2 +- Cython/Utility/CythonFunction.c | 3 +- Cython/Utility/FunctionArguments.c | 2 +- Cython/Utility/HPyUtils.c | 10 +- Cython/Utility/ImportExport.c | 163 ++++++++++++++------------- Cython/Utility/ModuleSetupCode.c | 6 +- Cython/Utility/ObjectHandling.c | 145 +++++++++++------------- Cython/Utility/StringTools.c | 6 +- Cython/Utility/TypeConversion.c | 32 +++--- tests/run/hpy_fannkuch_benchmark.pyx | 54 +++++++++ 13 files changed, 328 insertions(+), 226 deletions(-) create mode 100644 tests/run/hpy_fannkuch_benchmark.pyx diff --git a/Cython/Compiler/Builtin.py b/Cython/Compiler/Builtin.py index 892770cfd34..14c270b8daa 100644 --- a/Cython/Compiler/Builtin.py +++ b/Cython/Compiler/Builtin.py @@ -317,7 +317,7 @@ def declare_in_type(self, self_type): utility_code=UtilityCode.load("PySequenceMultiply", "ObjectHandling.c")), ]), - ("list", "&PyList_Type", [BuiltinMethod("insert", "TzO", "r", "PyList_Insert"), + ("list", "&PyList_Type", [BuiltinMethod("insert", "TzO", "r", "LIST_INSERT"), BuiltinMethod("reverse", "T", "r", "PyList_Reverse"), BuiltinMethod("append", "TO", "r", "__Pyx_PyList_Append", utility_code=UtilityCode.load("ListAppend", "Optimize.c")), diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index c51883a5476..c1b9adcbee7 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -2554,8 +2554,8 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, load_result_value = load_result_temp.temp_cname else: assert False, repr(entry) - code.putln("#if CYTHON_USING_HPY") - # Wrapping namespace and interned_cname in PyObjects breaks the C API + code.putln("#if CYTHON_USING_HPY") + # Wrapping namespace and interned_cname in PyObjects breaks the C API # version when not using the Limited API load_namespace_temp = LoadGlobalNode(self.pos, namespace) load_namespace_temp.allocate(code) @@ -2851,14 +2851,17 @@ def generate_result_code(self, code): utility_code = UtilityCode.load_cached("ImportDottedModuleRelFirst", "ImportExport.c") helper_func = "__Pyx_ImportDottedModuleRelFirst" code.globalstate.use_utility_code(utility_code) - import_code = "%s(%s, %s)" % ( + load_result = LoadGlobalNode(self.pos, self.module_name.py_result()) + load_result.allocate(code) + import_code = "%s(HPY_CONTEXT_FIRST_ARG_CALL %s, %s)" % ( helper_func, - self.module_name.py_result(), - self.module_names.py_result() if self.module_names else 'NULL', + load_result.temp_cname, + self.module_names.py_result() if self.module_names else 'API_NULL_VALUE', ) + load_result.release(code) else: code.globalstate.use_utility_code(UtilityCode.load_cached("Import", "ImportExport.c")) - import_code = "__Pyx_Import(%s, %s, %d)" % ( + import_code = "__Pyx_Import(HPY_CONTEXT_FIRST_ARG_CALL %s, %s, %d)" % ( self.module_name.py_result(), self.name_list.py_result() if self.name_list else '0', self.level) @@ -2866,7 +2869,7 @@ def generate_result_code(self, code): code.putln("%s = %s; %s" % ( self.result(), import_code, - code.error_goto_if_null(self.result(), self.pos))) + code.error_goto_if_null_object(self.result(), self.pos))) self.generate_gotref(code) def get_known_standard_library_import(self): @@ -3519,7 +3522,7 @@ def generate_evaluation_code(self, code): if self.result_is_used: self.allocate_temp_result(code) - code.putln("%s = __Pyx_PyObject_IsTrue(%s);" % (self.result(), result_var)) + code.putln("%s = __Pyx_PyObject_IsTrue(HPY_CONTEXT_FIRST_ARG_CALL %s);" % (self.result(), result_var)) code.put_decref_clear(result_var, type=py_object_type) if self.result_is_used: code.put_error_if_neg(self.pos, self.result()) @@ -5632,7 +5635,7 @@ def generate_result_code(self, code): (has_c_start, has_c_stop, c_start, c_stop, py_start, py_stop, py_slice) = self.get_slice_config() code.putln( - "%s = __Pyx_PyObject_GetSlice(%s, %s, %s, %s, %s, %s, %d, %d, %d); %s" % ( + "%s = HPY_LEGACY_OBJECT_FROM(__Pyx_PyObject_GetSlice(HPY_LEGACY_OBJECT_AS(%s), %s, %s, HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), %d, %d, %d)); %s" % ( result, self.base.py_result(), c_start, c_stop, @@ -5652,13 +5655,13 @@ def generate_result_code(self, code): else: cfunc = 'PySequence_GetSlice' code.putln( - "%s = %s(%s, %s, %s); %s" % ( + "%s = HPY_LEGACY_OBJECT_FROM(%s(HPY_LEGACY_OBJECT_AS(%s), %s, %s)); %s" % ( result, cfunc, self.base.py_result(), start_code, stop_code, - code.error_goto_if_null(result, self.pos))) + code.error_goto_if_null_object(result, self.pos))) self.generate_gotref(code) def generate_assignment_code(self, rhs, code, overloaded_assignment=False, @@ -5668,7 +5671,7 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, code.globalstate.use_utility_code(self.set_slice_utility_code) has_c_start, has_c_stop, c_start, c_stop, py_start, py_stop, py_slice = self.get_slice_config() code.put_error_if_neg(self.pos, - "__Pyx_PyObject_SetSlice(%s, %s, %s, %s, %s, %s, %s, %d, %d, %d)" % ( + "__Pyx_PyObject_SetSlice(HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), %s, %s, HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), %d, %d, %d)" % ( self.base.py_result(), rhs.py_result(), c_start, c_stop, @@ -5715,21 +5718,21 @@ def generate_deletion_code(self, code, ignore_nonexisting=False): self.free_subexpr_temps(code) def get_slice_config(self): - has_c_start, c_start, py_start = False, '0', 'NULL' + has_c_start, c_start, py_start = False, '0', 'API_NULL_VALUE' if self.start: has_c_start = not self.start.type.is_pyobject if has_c_start: c_start = self.start.result() else: py_start = '&%s' % self.start.py_result() - has_c_stop, c_stop, py_stop = False, '0', 'NULL' + has_c_stop, c_stop, py_stop = False, '0', 'API_NULL_VALUE' if self.stop: has_c_stop = not self.stop.type.is_pyobject if has_c_stop: c_stop = self.stop.result() else: py_stop = '&%s' % self.stop.py_result() - py_slice = self.slice and '&%s' % self.slice.py_result() or 'NULL' + py_slice = self.slice and '&%s' % self.slice.py_result() or 'API_NULL_VALUE' return (has_c_start, has_c_stop, c_start, c_stop, py_start, py_stop, py_slice) @@ -5890,16 +5893,27 @@ def generate_result_code(self, code): return # already initialised code.mark_pos(self.pos) + code.putln("{") + load_start = LoadGlobalNode(self.pos, self.start.py_result()) + load_stop = LoadGlobalNode(self.pos, self.stop.py_result()) + load_step = LoadGlobalNode(self.pos, self.step.py_result()) + load_start.allocate(code, needs_decl=True) + load_stop.allocate(code, needs_decl=True) + load_step.allocate(code, needs_decl=True) code.putln( - "%s = PySlice_New(%s, %s, %s); %s" % ( + "%s = HPY_LEGACY_OBJECT_FROM(PySlice_New(HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s))); %s" % ( self.result(), - self.start.py_result(), - self.stop.py_result(), - self.step.py_result(), - code.error_goto_if_null(self.result(), self.pos))) + load_start.temp_cname, + load_stop.temp_cname, + load_step.temp_cname, + code.error_goto_if_null_object(self.result(), self.pos))) + load_start.release(code) + load_stop.release(code) + load_step.release(code) self.generate_gotref(code) if self.is_literal: self.generate_giveref(code) + code.putln("}") class SliceIntNode(SliceNode): # start:stop:step in subscript list @@ -6466,7 +6480,7 @@ def c_call_code(self, code=False): if len(arg_list_code) > 0: arg_string = "HPY_CONTEXT_FIRST_ARG_CALL " - if self.function.result() in ['DICT_COPY', '__Pyx_PyList_PopIndex', '__Pyx_PyObject_PopIndex']: + if self.function.result() in ['DICT_COPY', '__Pyx_PyList_PopIndex', '__Pyx_PyObject_PopIndex', '__Pyx_PySequence_ListKeepNew', 'LIST_INSERT']: arg_string = "" for arg in arg_list_code: if code: @@ -6539,12 +6553,15 @@ def generate_evaluation_code(self, code): else: code.globalstate.use_utility_code(UtilityCode.load_cached( "PyObjectCallOneArg", "ObjectHandling.c")) + load_result_temp = LoadGlobalNode(self.pos, function.py_result()) + load_result_temp.allocate(code) code.putln( - "%s = __Pyx_PyObject_CallOneArg(%s, %s); %s" % ( + "%s = __Pyx_PyObject_CallOneArg(HPY_CONTEXT_FIRST_ARG_CALL %s, %s); %s" % ( self.result(), - function.py_result(), + load_result_temp.temp_cname, arg.py_result(), - code.error_goto_if_null(self.result(), self.pos))) + code.error_goto_if_null_object(self.result(), self.pos))) + load_result_temp.release(code) self.generate_gotref(code) @@ -6562,15 +6579,23 @@ def generate_result_code(self, code): func_result = self.function.py_result() tmp_func_result = LoadGlobalNode(self.pos, func_result) tmp_func_result.allocate(code) - func_result = self.function.py_result() tmp_arg_code = LoadGlobalNode(self.pos, arg_code) tmp_arg_code.allocate(code) + code.putln("#if CYTHON_USING_HPY") code.putln( - "%s = __Pyx_PyObject_Call_h(%s, %s, API_NULL_VALUE); %s" % ( + "%s = HPy_CallTupleDict(HPY_CONTEXT_CNAME, %s, %s, API_NULL_VALUE); %s" % ( self.result(), tmp_func_result.temp_cname, tmp_arg_code.temp_cname, code.error_goto_if_null_object(self.result(), self.pos))) + code.putln("#else") + code.putln( + "%s = __Pyx_PyObject_Call(%s, %s, API_NULL_VALUE); %s" % ( + self.result(), + tmp_func_result.temp_cname, + tmp_arg_code.temp_cname, + code.error_goto_if_null_object(self.result(), self.pos))) + code.putln("#endif") tmp_func_result.release(code) tmp_arg_code.release(code) self.generate_gotref(code) @@ -7283,19 +7308,29 @@ def generate_result_code(self, code): if self.keyword_args: kwargs = self.keyword_args.py_result() else: - kwargs = 'NULL' + kwargs = 'API_NULL_VALUE' code.globalstate.use_utility_code(UtilityCode.load_cached( "PyObjectCall", "ObjectHandling.c")) pos_args_cname = self.positional_args.py_result() tmp_pos_args = LoadGlobalNode(self.pos, pos_args_cname) tmp_pos_args.allocate(code) + code.putln("#if CYTHON_USING_HPY") code.putln( - "%s = __Pyx_PyObject_Call_h(%s, %s, %s); %s" % ( + "%s = HPy_CallTupleDict(HPY_CONTEXT_CNAME, %s, %s, %s); %s" % ( self.result(), self.function.py_result(), tmp_pos_args.temp_cname, kwargs, code.error_goto_if_null_object(self.result(), self.pos))) + code.putln("#else") + code.putln( + "%s = __Pyx_PyObject_Call(%s, %s, %s); %s" % ( + self.result(), + self.function.py_result(), + tmp_pos_args.temp_cname, + kwargs, + code.error_goto_if_null_object(self.result(), self.pos))) + code.putln("#endif") tmp_pos_args.release(code) self.generate_gotref(code) @@ -7452,10 +7487,10 @@ def generate_evaluation_code(self, code): code.putln('} else {') code.globalstate.use_utility_code(UtilityCode.load_cached( "PyObjectCallOneArg", "ObjectHandling.c")) - code.putln("%s = __Pyx_PyObject_CallOneArg((PyObject*)&PyDict_Type, %s); %s" % ( + code.putln("%s = __Pyx_PyObject_CallOneArg(HPY_CONTEXT_FIRST_ARG_CALL (PyObject*)&PyDict_Type, %s); %s" % ( self.result(), item.py_result(), - code.error_goto_if_null(self.result(), self.pos))) + code.error_goto_if_null_object(self.result(), self.pos))) self.generate_gotref(code) item.generate_disposal_code(code) code.putln('}') @@ -13238,7 +13273,7 @@ def generate_operand1_test(self, code): test_result = code.funcstate.allocate_temp( PyrexTypes.c_bint_type, manage_ref=False) code.putln( - "%s = __Pyx_PyObject_IsTrue(%s); %s" % ( + "%s = __Pyx_PyObject_IsTrue(HPY_CONTEXT_FIRST_ARG_CALL %s); %s" % ( test_result, self.operand1.py_result(), code.error_goto_if_neg(test_result, self.pos))) @@ -13295,7 +13330,7 @@ def generate_operand_test(self, code): test_result = code.funcstate.allocate_temp( PyrexTypes.c_bint_type, manage_ref=False) code.putln( - "%s = __Pyx_PyObject_IsTrue(%s); %s" % ( + "%s = __Pyx_PyObject_IsTrue(HPY_CONTEXT_FIRST_ARG_CALL %s); %s" % ( test_result, self.arg.py_result(), code.error_goto_if_neg(test_result, self.pos))) @@ -13802,27 +13837,39 @@ def generate_operation_code(self, code, result_code, ]) if self.special_bool_cmp_utility_code: code.globalstate.use_utility_code(self.special_bool_cmp_utility_code) + load_result1 = LoadGlobalNode(self.pos, result1) + load_result2 = LoadGlobalNode(self.pos, result2) + load_result1.allocate(code) + load_result2.allocate(code) code.putln( - "%s = %s(%s(%s, %s, %s)); %s%s" % ( + "%s = %s(%s(HPY_CONTEXT_FIRST_ARG_CALL %s, %s, %s)); %s%s" % ( result_code, coerce_result, self.special_bool_cmp_function, - result1, result2, + load_result1.temp_cname, load_result2.temp_cname, special_bool_extra_args_result if self.special_bool_extra_args else richcmp_constants[op], got_ref, error_clause(result_code, self.pos))) + load_result1.release(code) + load_result2.release(code) elif operand1.type.is_pyobject and op not in ('is', 'is_not'): assert op not in ('in', 'not_in'), op assert self.type.is_pyobject or self.type is PyrexTypes.c_bint_type + load_op1 = LoadGlobalNode(self.pos, operand1.py_result()) + load_op2 = LoadGlobalNode(self.pos, operand2.py_result()) + load_op1.allocate(code) + load_op2.allocate(code) code.putln("%s = API_RICH_COMPARE%s(%s, %s, %s); %s%s" % ( result_code, "" if self.type.is_pyobject else "_BOOL", - operand1.py_result(), - operand2.py_result(), + load_op1.temp_cname, + load_op2.temp_cname, richcmp_constants[op], got_ref, error_clause(result_code, self.pos))) + load_op1.release(code) + load_op2.release(code) elif operand1.type.is_complex: code.putln("%s = %s(%s%s(%s, %s));" % ( @@ -14226,7 +14273,7 @@ def coerce_cascaded_operands_to_temp(self, env): def generate_evaluation_code(self, code, result, operand1, needs_evaluation=False): if self.type.is_pyobject: - code.putln("if (__Pyx_PyObject_IsTrue(%s)) {" % result) + code.putln("if (__Pyx_PyObject_IsTrue(HPY_CONTEXT_FIRST_ARG_CALL %s)) {" % result) code.put_decref(result, self.type) else: code.putln("if (%s) {" % result) @@ -14763,7 +14810,7 @@ def generate_result_code(self, code): "((!CYTHON_ASSUME_SAFE_MACROS) && %s < 0)" % self.result(), self.pos)) else: code.putln( - "%s = __Pyx_PyObject_IsTrue(%s); %s" % ( + "%s = __Pyx_PyObject_IsTrue(HPY_CONTEXT_FIRST_ARG_CALL %s); %s" % ( self.result(), self.arg.py_result(), code.error_goto_if_neg(self.result(), self.pos))) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index e76cf989c7f..962308e96a0 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -2252,7 +2252,7 @@ def generate_richcmp_function(self, scope, code): code.putln("PyObject *ret;") code.putln("ret = %s(o1, o2);" % comp_entry[ordering_source].func_cname) code.putln("if (likely(ret && ret != Py_NotImplemented)) {") - code.putln("int order_res = __Pyx_PyObject_IsTrue(ret);") + code.putln("int order_res = __Pyx_PyObject_IsTrue(HPY_CONTEXT_FIRST_ARG_CALL ret);") code.putln("Py_DECREF(ret);") code.putln("if (unlikely(order_res < 0)) return NULL;") # We may need to check equality too. For some combos it's never required. @@ -2277,7 +2277,7 @@ def generate_richcmp_function(self, scope, code): code.putln("ret = %s(o1, o2);" % comp_entry[eq_func].func_cname) code.putln("if (likely(ret && ret != Py_NotImplemented)) {") - code.putln("int eq_res = __Pyx_PyObject_IsTrue(ret);") + code.putln("int eq_res = __Pyx_PyObject_IsTrue(HPY_CONTEXT_FIRST_ARG_CALL ret);") code.putln("Py_DECREF(ret);") code.putln("if (unlikely(eq_res < 0)) return NULL;") if invert_equals: @@ -2307,7 +2307,7 @@ def generate_richcmp_function(self, scope, code): #code.putln("if (o1 == o2) return __Pyx_NewRef(Py_False);") code.putln("ret = %s(o1, o2);" % comp_entry['__eq__'].func_cname) code.putln("if (likely(ret && ret != Py_NotImplemented)) {") - code.putln("int b = __Pyx_PyObject_IsTrue(ret);") + code.putln("int b = __Pyx_PyObject_IsTrue(HPY_CONTEXT_FIRST_ARG_CALL ret);") code.putln("Py_DECREF(ret);") code.putln("if (unlikely(b < 0)) return NULL;") code.putln("ret = (b) ? API_FALSE : API_TRUE;") diff --git a/Cython/Utility/CommonStructures.c b/Cython/Utility/CommonStructures.c index 0ce2b8045fc..5e2af152754 100644 --- a/Cython/Utility/CommonStructures.c +++ b/Cython/Utility/CommonStructures.c @@ -99,7 +99,7 @@ static PYTYPEOBJECT_TYPE __Pyx_FetchCommonTypeFromSpec(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE py_basicsize; py_basicsize = PYOBJECT_GET_ATTR_STR(cached_type, "__basicsize__"); if (unlikely(API_IS_NULL(py_basicsize))) goto bad; - basicsize = PYOBJECT_LONG_AS_SSIZE(py_basicsize); + basicsize = PYOBJECT_LONG_AS_SSIZE_T(py_basicsize); PYOBJECT_CLOSEREF(py_basicsize); py_basicsize = API_DEFAULT_VALUE; if (unlikely(basicsize == (API_SSIZE_T)-1) && PYERR_OCCURRED()) goto bad; diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index cb5ab5feb16..f961f80aad7 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -91,10 +91,11 @@ HPyType_HELPERS(__pyx_CyFunctionObject) #undef __Pyx_CyOrPyCFunction_Check #define __Pyx_CyFunction_Check(obj) __Pyx_TypeCheck(obj, PYOBJECT_GLOBAL_LOAD(__pyx_CyFunctionType)) -#define __Pyx_CyOrPyCFunction_Check(obj) __Pyx_TypeCheck2(obj, __pyx_CyFunctionType, &PyCFunction_Type) #if !CYTHON_USING_HPY +#define __Pyx_CyOrPyCFunction_Check(obj) __Pyx_TypeCheck2(obj, __pyx_CyFunctionType, &PyCFunction_Type) #define __Pyx_CyFunction_CheckExact(obj) __Pyx_IS_TYPE(obj, __pyx_CyFunctionType) #else +#define __Pyx_CyOrPyCFunction_Check(obj) __Pyx_TypeCheck(obj, PYOBJECT_GLOBAL_LOAD(__pyx_CyFunctionType)) //TODO(HPy): Loading here is a memory leak #define __Pyx_CyFunction_CheckExact(obj) __Pyx_TypeCheck(obj, __pyx_CyFunctionType) #endif static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE func, void *cfunc);/*proto*/ diff --git a/Cython/Utility/FunctionArguments.c b/Cython/Utility/FunctionArguments.c index c5a6b3fc425..ac7efe9fd7d 100644 --- a/Cython/Utility/FunctionArguments.c +++ b/Cython/Utility/FunctionArguments.c @@ -534,7 +534,7 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_GetKwValue_FASTCALL(HPY_CONTEXT_FIRST_A } for (i = 0; i < n; i++) { - int eq = __Pyx_PyUnicode_Equals(HPY_LEGACY_OBJECT_AS(s), HPY_LEGACY_OBJECT_AS(TUPLE_GET_ITEM(kwnames, i)), Py_EQ); + int eq = __Pyx_PyUnicode_Equals(HPY_CONTEXT_FIRST_ARG_CALL s, TUPLE_GET_ITEM(kwnames, i), Py_EQ); if (unlikely(eq != 0)) { if (unlikely(eq < 0)) return API_NULL_VALUE; /* error */ return kwvalues[i]; diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index d4e8cd0db40..7f836296afe 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -72,6 +72,7 @@ //Type Checks #define LONG_CHECK(l) HPyNumber_Check(HPY_CONTEXT_CNAME, l) + #define LONG_CHECK_EXACT(l) HPyNumber_Check(HPY_CONTEXT_CNAME, l) #define FLOAT_CHECK_EXACT(f) HPyNumber_Check(HPY_CONTEXT_CNAME, f) #define UNICODE_CHECK(u) HPyUnicode_Check(HPY_CONTEXT_CNAME, u) #define DICT_CHECK(o) HPyDict_Check(HPY_CONTEXT_CNAME, o) @@ -96,7 +97,7 @@ #define PYOBJECT_LONG_FROM_SSIZE_T(i) HPyLong_FromSsize_t(HPY_CONTEXT_CNAME, i) //Long Type - To - #define PYOBJECT_LONG_AS_SSIZE(l) HPyLong_AsSsize_t(HPY_CONTEXT_CNAME, l) + #define PYOBJECT_LONG_AS_SSIZE_T(l) HPyLong_AsSsize_t(HPY_CONTEXT_CNAME, l) #define PYOBJECT_LONG_AS_UNSIGNED_LONG(l) HPyLong_AsUnsignedLong(HPY_CONTEXT_CNAME, l) //Float Type - From @@ -149,6 +150,7 @@ #define LIST_GET_SIZE(h) HPy_Length(HPY_CONTEXT_CNAME, h) #define LIST_GET_SIZE_SAFE(h) HPy_Length(HPY_CONTEXT_CNAME, h) #define LIST_APPEND(list, h) HPyList_Append(HPY_CONTEXT_CNAME, list, h) + #define LIST_INSERT(list, index, h) PyList_Insert(HPY_LEGACY_OBJECT_AS(list), index, HPY_LEGACY_OBJECT_AS(h)) #define LIST_BUILDER_TYPE HPyListBuilder #define LIST_CREATE_START(target, builder, size) builder = HPyListBuilder_New(HPY_CONTEXT_CNAME, size) #define LIST_CREATE_ASSIGN(tuple, builder, index, item) HPyListBuilder_Set(HPY_CONTEXT_CNAME, builder, index, item) @@ -157,6 +159,7 @@ //PyObject/HPy Handle Type #define PYOBJECT_GET_ITEM(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) #define PYOBJECT_SET_ITEM(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) + #define PYOBJECT_DEL_ITEM(o, attr_name) HPy_DelItem(HPY_CONTEXT_CNAME, o, attr_name) #define PYOBJECT_GET_ATTR(o, attr_name) HPy_GetAttr(HPY_CONTEXT_CNAME, o, attr_name) #define PYOBJECT_SET_ATTR(o, attr_name, attr_val) HPy_SetAttr(HPY_CONTEXT_CNAME, o, attr_name, attr_val) #define PYOBJECT_GET_ATTR_STR(o, attr_name) HPy_GetAttr_s(HPY_CONTEXT_CNAME, o, attr_name) @@ -256,6 +259,7 @@ //Number Type Checks #define LONG_CHECK(l) PyLong_Check(l) + #define LONG_CHECK_EXACT(l) PyLong_CheckExact(l) #define FLOAT_CHECK_EXACT(f) PyFloat_CheckExact(f) #define UNICODE_CHECK(u) PyUnicode_Check(u) #define DICT_CHECK(o) PyDict_Check(o) @@ -281,7 +285,7 @@ #define PYOBJECT_LONG_FROM_SSIZE_T(i) PyLong_FromSsize_t(i) //Long Type - To - #define PYOBJECT_LONG_AS_SSIZE(l) PyLong_AsSsize_t(l) + #define PYOBJECT_LONG_AS_SSIZE_T(l) PyLong_AsSsize_t(l) #define PYOBJECT_LONG_AS_UNSIGNED_LONG(l) PyLong_AsUnsignedLong(l) //Float Type - From @@ -332,6 +336,7 @@ #define LIST_NEW(i) PyList_New(i) #define LIST_GET_ITEM(h, pos) __Pyx_PySequence_ITEM(HPY_CONTEXT_CNAME, h, pos) #define LIST_APPEND(list, h) PyList_Append(list, h) + #define LIST_INSERT(list, index, h) PyList_Insert(list, index, h) #define LIST_GET_SIZE(h) PyList_GET_SIZE(h) #define LIST_GET_SIZE_SAFE(h) PyList_Size(h) #define LIST_BUILDER_TYPE PyObject * //Not used, just needed to prevent errors @@ -342,6 +347,7 @@ //PyObject/HPy Handle Type #define PYOBJECT_GET_ITEM(o, attr_name) PyObject_GetItem(o, attr_name) #define PYOBJECT_SET_ITEM(o, attr_name, attr_val) PyObject_SetItem(o, attr_name, attr_val) + #define PYOBJECT_DEL_ITEM(o, attr_name) PyObject_DelItem(o, attr_name) #define PYOBJECT_GET_ATTR(o, attr_name) PyObject_GetAttr(o, attr_name) #define PYOBJECT_SET_ATTR(o, attr_name, attr_val) PyObject_SetAttr(o, attr_name, attr_val) #define PYOBJECT_GET_ATTR_STR(o, attr_name) PyObject_GetAttrString(o, attr_name) diff --git a/Cython/Utility/ImportExport.c b/Cython/Utility/ImportExport.c index df8b3b07f18..2f22d8ea6ed 100644 --- a/Cython/Utility/ImportExport.c +++ b/Cython/Utility/ImportExport.c @@ -1,33 +1,33 @@ /////////////// ImportDottedModule.proto /////////////// -static PyObject *__Pyx_ImportDottedModule(PyObject *name, PyObject *parts_tuple); /*proto*/ -static PyObject *__Pyx_ImportDottedModule_WalkParts(PyObject *module, PyObject *name, PyObject *parts_tuple); /*proto*/ +static PYOBJECT_TYPE __Pyx_ImportDottedModule(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE name, PYOBJECT_TYPE parts_tuple); /*proto*/ +static PYOBJECT_TYPE __Pyx_ImportDottedModule_WalkParts(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE module, PYOBJECT_TYPE name, PYOBJECT_TYPE parts_tuple); /*proto*/ /////////////// ImportDottedModule /////////////// //@requires: Import -static PyObject *__Pyx__ImportDottedModule_Error(PyObject *name, PyObject *parts_tuple, Py_ssize_t count) { - PyObject *partial_name = NULL, *slice = NULL, *sep = NULL; - Py_ssize_t size; +static PYOBJECT_TYPE __Pyx__ImportDottedModule_Error(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE name, PYOBJECT_TYPE parts_tuple, API_SSIZE_T count) { + PYOBJECT_TYPE partial_name = API_NULL_VALUE, CAPI_IS_POINTER slice = API_NULL_VALUE, CAPI_IS_POINTER sep = API_NULL_VALUE; + API_SSIZE_T size; if (unlikely(PyErr_Occurred())) { PyErr_Clear(); } #if CYTHON_ASSUME_SAFE_SIZE size = PyTuple_GET_SIZE(parts_tuple); #else - size = PyTuple_Size(parts_tuple); + size = TUPLE_GET_SIZE_SAFE(parts_tuple); if (size < 0) goto bad; #endif if (likely(size == count)) { partial_name = name; } else { - slice = PySequence_GetSlice(parts_tuple, 0, count); - if (unlikely(!slice)) + slice = HPY_LEGACY_OBJECT_FROM(PySequence_GetSlice(HPY_LEGACY_OBJECT_AS(parts_tuple), 0, count)); + if (unlikely(API_IS_NULL(slice))) goto bad; - sep = PyUnicode_FromStringAndSize(".", 1); - if (unlikely(!sep)) + sep = HPY_LEGACY_OBJECT_FROM(PyUnicode_FromStringAndSize(".", 1)); + if (unlikely(API_IS_NULL(sep))) goto bad; - partial_name = PyUnicode_Join(sep, slice); + partial_name = HPY_LEGACY_OBJECT_FROM(PyUnicode_Join(HPY_LEGACY_OBJECT_AS(sep), HPY_LEGACY_OBJECT_AS(slice))); } PyErr_Format( @@ -35,14 +35,14 @@ static PyObject *__Pyx__ImportDottedModule_Error(PyObject *name, PyObject *parts "No module named '%U'", partial_name); bad: - Py_XDECREF(sep); - Py_XDECREF(slice); - Py_XDECREF(partial_name); - return NULL; + PYOBJECT_XCLOSEREF(sep); + PYOBJECT_XCLOSEREF(slice); + PYOBJECT_XCLOSEREF(partial_name); + return API_NULL_VALUE; } -static PyObject *__Pyx__ImportDottedModule_Lookup(PyObject *name) { - PyObject *imported_module; +static PYOBJECT_TYPE __Pyx__ImportDottedModule_Lookup(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE name) { + PYOBJECT_TYPE imported_module; #if CYTHON_COMPILING_IN_PYPY && PYPY_VERSION_NUM < 0x07030400 PyObject *modules = PyImport_GetModuleDict(); if (unlikely(!modules)) @@ -50,138 +50,143 @@ static PyObject *__Pyx__ImportDottedModule_Lookup(PyObject *name) { imported_module = __Pyx_PyDict_GetItemStr(modules, name); Py_XINCREF(imported_module); #else - imported_module = PyImport_GetModule(name); + imported_module = HPY_LEGACY_OBJECT_FROM(PyImport_GetModule(HPY_LEGACY_OBJECT_AS(name))); #endif return imported_module; } -static PyObject *__Pyx_ImportDottedModule_WalkParts(PyObject *module, PyObject *name, PyObject *parts_tuple) { - Py_ssize_t i, nparts; +static PYOBJECT_TYPE __Pyx_ImportDottedModule_WalkParts(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE module, PYOBJECT_TYPE name, PYOBJECT_TYPE parts_tuple) { + API_SSIZE_T i, nparts; #if CYTHON_ASSUME_SAFE_SIZE nparts = PyTuple_GET_SIZE(parts_tuple); #else - nparts = PyTuple_Size(parts_tuple); - if (nparts < 0) return NULL; + nparts = TUPLE_GET_SIZE_SAFE(parts_tuple); + if (nparts < 0) return API_NULL_VALUE; #endif - for (i=1; i < nparts && module; i++) { - PyObject *part, *submodule; + for (i=1; i < nparts && API_IS_NOT_NULL(module); i++) { + PYOBJECT_TYPE part, CAPI_IS_POINTER submodule; #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS part = PyTuple_GET_ITEM(parts_tuple, i); #else - part = __Pyx_PySequence_ITEM(parts_tuple, i); - if (!part) return NULL; + part = SEQUENCE_GET_ITEM(parts_tuple, i); + if (API_IS_NULL(part)) return API_NULL_VALUE; #endif - submodule = __Pyx_PyObject_GetAttrStrNoError(module, part); + submodule = __Pyx_PyObject_GetAttrStrNoError(HPY_CONTEXT_FIRST_ARG_CALL module, part); // We stop if the attribute isn't found, i.e. if submodule is NULL here. #if !(CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS) - Py_DECREF(part); + PYOBJECT_CLOSEREF(part); #endif - Py_DECREF(module); + PYOBJECT_CLOSEREF(module); module = submodule; } - if (unlikely(!module)) { - return __Pyx__ImportDottedModule_Error(name, parts_tuple, i); + if (unlikely(API_IS_NULL(module))) { + return __Pyx__ImportDottedModule_Error(HPY_CONTEXT_FIRST_ARG_CALL name, parts_tuple, i); } return module; } -static PyObject *__Pyx__ImportDottedModule(PyObject *name, PyObject *parts_tuple) { - PyObject *imported_module; - PyObject *module = __Pyx_Import(name, NULL, 0); - if (!parts_tuple || unlikely(!module)) +static PYOBJECT_TYPE __Pyx__ImportDottedModule(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE name, PYOBJECT_TYPE parts_tuple) { + PYOBJECT_TYPE imported_module; + PYOBJECT_TYPE module = __Pyx_Import(HPY_CONTEXT_FIRST_ARG_CALL name, API_NULL_VALUE, 0); + if (API_IS_NULL(parts_tuple) || unlikely(API_IS_NULL(module))) return module; // Look up module in sys.modules, which is safer than the attribute lookups below. - imported_module = __Pyx__ImportDottedModule_Lookup(name); - if (likely(imported_module)) { - Py_DECREF(module); + imported_module = __Pyx__ImportDottedModule_Lookup(HPY_CONTEXT_FIRST_ARG_CALL name); + if (likely(API_IS_NOT_NULL(imported_module))) { + PYOBJECT_CLOSEREF(module); return imported_module; } PyErr_Clear(); - return __Pyx_ImportDottedModule_WalkParts(module, name, parts_tuple); + return __Pyx_ImportDottedModule_WalkParts(HPY_CONTEXT_FIRST_ARG_CALL module, name, parts_tuple); } -static PyObject *__Pyx_ImportDottedModule(PyObject *name, PyObject *parts_tuple) { +static PYOBJECT_TYPE __Pyx_ImportDottedModule(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE name, PYOBJECT_TYPE parts_tuple) { #if CYTHON_COMPILING_IN_CPYTHON - PyObject *module = __Pyx__ImportDottedModule_Lookup(name); - if (likely(module)) { + PYOBJECT_TYPE module = __Pyx__ImportDottedModule_Lookup(HPY_CONTEXT_FIRST_ARG_CALL name); + if (likely(API_IS_NOT_NULL(module))) { // CPython guards against thread-concurrent initialisation in importlib. // In this case, we let PyImport_ImportModuleLevelObject() handle the locking. - PyObject *spec = __Pyx_PyObject_GetAttrStrNoError(module, PYIDENT("__spec__")); - if (likely(spec)) { - PyObject *unsafe = __Pyx_PyObject_GetAttrStrNoError(spec, PYIDENT("_initializing")); - if (likely(!unsafe || !__Pyx_PyObject_IsTrue(unsafe))) { - Py_DECREF(spec); - spec = NULL; + PYOBJECT_TYPE load_spec = PYOBJECT_GLOBAL_LOAD(PYIDENT("__spec__")); + PYOBJECT_TYPE spec = __Pyx_PyObject_GetAttrStrNoError(HPY_CONTEXT_FIRST_ARG_CALL module, load_spec); + PYOBJECT_GLOBAL_CLOSEREF(load_spec); + if (likely(API_IS_NOT_NULL(spec))) { + PYOBJECT_TYPE load_init = PYOBJECT_GLOBAL_LOAD(PYIDENT("_initializing")); + PYOBJECT_TYPE unsafe = __Pyx_PyObject_GetAttrStrNoError(HPY_CONTEXT_FIRST_ARG_CALL spec, load_init); + PYOBJECT_GLOBAL_CLOSEREF(load_init); + if (likely(API_IS_NULL(unsafe) || !__Pyx_PyObject_IsTrue(HPY_CONTEXT_FIRST_ARG_CALL unsafe))) { + PYOBJECT_CLOSEREF(spec); + spec = API_NULL_VALUE; } - Py_XDECREF(unsafe); + PYOBJECT_XCLOSEREF(unsafe); } - if (likely(!spec)) { + if (likely(API_IS_NULL(spec))) { // Not in initialisation phase => use modules as is. PyErr_Clear(); return module; } - Py_DECREF(spec); - Py_DECREF(module); + PYOBJECT_CLOSEREF(spec); + PYOBJECT_CLOSEREF(module); } else if (PyErr_Occurred()) { PyErr_Clear(); } #endif - return __Pyx__ImportDottedModule(name, parts_tuple); + return __Pyx__ImportDottedModule(HPY_CONTEXT_FIRST_ARG_CALL name, parts_tuple); } /////////////// ImportDottedModuleRelFirst.proto /////////////// -static PyObject *__Pyx_ImportDottedModuleRelFirst(PyObject *name, PyObject *parts_tuple); /*proto*/ +static PYOBJECT_TYPE __Pyx_ImportDottedModuleRelFirst(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE name, PYOBJECT_TYPE parts_tuple); /*proto*/ /////////////// ImportDottedModuleRelFirst /////////////// //@requires: ImportDottedModule //@requires: Import -static PyObject *__Pyx_ImportDottedModuleRelFirst(PyObject *name, PyObject *parts_tuple) { - PyObject *module; - PyObject *from_list = NULL; - module = __Pyx_Import(name, from_list, -1); - Py_XDECREF(from_list); - if (module) { - if (parts_tuple) { - module = __Pyx_ImportDottedModule_WalkParts(module, name, parts_tuple); +static PYOBJECT_TYPE __Pyx_ImportDottedModuleRelFirst(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE name, PYOBJECT_TYPE parts_tuple) { + PYOBJECT_TYPE module; + PYOBJECT_TYPE from_list = API_NULL_VALUE; + module = HPY_LEGACY_OBJECT_FROM(__Pyx_Import(HPY_LEGACY_OBJECT_AS(name), HPY_LEGACY_OBJECT_AS(from_list), -1)); + PYOBJECT_XCLOSEREF(from_list); + if (API_IS_NOT_NULL(module)) { + if (API_IS_NOT_NULL(parts_tuple)) { + module = __Pyx_ImportDottedModule_WalkParts(HPY_CONTEXT_FIRST_ARG_CALL module, name, parts_tuple); } return module; } if (unlikely(!PyErr_ExceptionMatches(PyExc_ImportError))) - return NULL; + return API_NULL_VALUE; PyErr_Clear(); // try absolute import - return __Pyx_ImportDottedModule(name, parts_tuple); + return __Pyx_ImportDottedModule(HPY_CONTEXT_FIRST_ARG_CALL name, parts_tuple); } /////////////// Import.proto /////////////// -static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list, int level); /*proto*/ +static PYOBJECT_TYPE __Pyx_Import(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE name, PYOBJECT_TYPE from_list, int level); /*proto*/ /////////////// Import /////////////// //@requires: ObjectHandling.c::PyObjectGetAttrStr //@requires:StringTools.c::IncludeStringH //@substitute: naming -static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list, int level) { - PyObject *module = 0; - PyObject *empty_dict = 0; - PyObject *empty_list = 0; - empty_dict = PyDict_New(); - if (unlikely(!empty_dict)) +static PYOBJECT_TYPE __Pyx_Import(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE name, PYOBJECT_TYPE from_list, int level) { + PYOBJECT_TYPE module = API_DEFAULT_VALUE; + PYOBJECT_TYPE empty_dict = API_DEFAULT_VALUE; + PYOBJECT_TYPE empty_list = API_DEFAULT_VALUE; + empty_dict = DICT_NEW(); + if (unlikely(API_IS_NULL(empty_dict))) goto bad; { + PYOBJECT_TYPE load_moddict = PYOBJECT_GLOBAL_LOAD($moddict_cname); if (level == -1) { if (strchr(__Pyx_MODULE_NAME, '.') != NULL) { /* try package relative import first */ - module = PyImport_ImportModuleLevelObject( - name, $moddict_cname, empty_dict, from_list, 1); - if (unlikely(!module)) { + module = HPY_LEGACY_OBJECT_FROM(PyImport_ImportModuleLevelObject( + HPY_LEGACY_OBJECT_AS(name), HPY_LEGACY_OBJECT_AS(load_moddict), HPY_LEGACY_OBJECT_AS(empty_dict), HPY_LEGACY_OBJECT_AS(from_list), 1)); + if (unlikely(API_IS_NULL(module))) { if (unlikely(!PyErr_ExceptionMatches(PyExc_ImportError))) goto bad; PyErr_Clear(); @@ -189,14 +194,14 @@ static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list, int level) { } level = 0; /* try absolute import on failure */ } - if (!module) { - module = PyImport_ImportModuleLevelObject( - name, $moddict_cname, empty_dict, from_list, level); + if (API_IS_NULL(module)) { + module = HPY_LEGACY_OBJECT_FROM(PyImport_ImportModuleLevelObject( + HPY_LEGACY_OBJECT_AS(name), HPY_LEGACY_OBJECT_AS(load_moddict), HPY_LEGACY_OBJECT_AS(empty_dict), HPY_LEGACY_OBJECT_AS(from_list), level)); } } bad: - Py_XDECREF(empty_dict); - Py_XDECREF(empty_list); + PYOBJECT_XCLOSEREF(empty_dict); + PYOBJECT_XCLOSEREF(empty_list); return module; } diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index cc8da2eec3b..f4ac50e5978 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -1236,7 +1236,9 @@ static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStrWithError(PyObject *dict, #define __Pyx_PyBaseString_Check(obj) PyUnicode_Check(obj) #define __Pyx_PyBaseString_CheckExact(obj) PyUnicode_CheckExact(obj) -#if CYTHON_COMPILING_IN_CPYTHON +#if CYTHON_USING_HPY + #define __Pyx_PySequence_ListKeepNew(obj) HPY_LEGACY_OBJECT_FROM(PySequence_List(HPY_LEGACY_OBJECT_AS(obj))) +#elif CYTHON_COMPILING_IN_CPYTHON #define __Pyx_PySequence_ListKeepNew(obj) \ (likely(PyList_CheckExact(obj) && Py_REFCNT(obj) == 1) ? __Pyx_NewRef(obj) : PySequence_List(obj)) #else @@ -1308,7 +1310,7 @@ static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStrWithError(PyObject *dict, #define PyInt_FromSsize_t PYOBJECT_LONG_FROM_SSIZE_T #define PyInt_AsLong PyLong_AsLong #define PyInt_AS_LONG PyLong_AS_LONG -#define PyInt_AsSsize_t PyLong_AsSsize_t +#define PyInt_AsSsize_t PYOBJECT_LONG_AS_SSIZE_T #define PyInt_AsUnsignedLongMask PyLong_AsUnsignedLongMask #define PyInt_AsUnsignedLongLongMask PyLong_AsUnsignedLongLongMask #define PyNumber_Int PyNumber_Long diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 0cc25ed324f..992f1cbe1c0 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -405,44 +405,54 @@ static PYOBJECT_TYPE __Pyx_PyDict_GetItem(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYP #if CYTHON_USING_HPY #define __Pyx_GetItemInt(HPY_CONTEXT_CNAME, o, i, type, is_signed, to_py_func, is_list, wraparound, boundscheck) \ (__Pyx_fits_Py_ssize_t(i, type, is_signed) ? \ - HPY_LEGACY_OBJECT_FROM(__Pyx_GetItemInt_Fast(HPY_LEGACY_OBJECT_AS(o), (Py_ssize_t)i, is_list, wraparound, boundscheck)) : \ + __Pyx_GetItemInt_Fast(HPY_CONTEXT_CNAME, o, (API_SSIZE_T)i, is_list, wraparound, boundscheck) : \ (is_list ? (PyErr_SetString(PyExc_IndexError, "list index out of range"), HPy_NULL) : \ - HPY_LEGACY_OBJECT_FROM(__Pyx_GetItemInt_Generic(HPY_LEGACY_OBJECT_AS(o), HPY_LEGACY_OBJECT_AS(to_py_func(HPY_CONTEXT_CNAME, i)))))) + __Pyx_GetItemInt_Generic(HPY_CONTEXT_CNAME, o, to_py_func(HPY_CONTEXT_CNAME, i)))) + +{{for type in ['List', 'Tuple']}} +#define __Pyx_GetItemInt_{{type}}(HPY_CONTEXT_CNAME, o, i, type, is_signed, to_py_func, is_list, wraparound, boundscheck) \ + (__Pyx_fits_Py_ssize_t(i, type, is_signed) ? \ + __Pyx_GetItemInt_{{type}}_Fast(HPY_CONTEXT_CNAME, o, (API_SSIZE_T)i, wraparound, boundscheck) : \ + (PyErr_SetString(PyExc_IndexError, "{{ type.lower() }} index out of range"), HPy_NULL)) + +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_GetItemInt_{{type}}_Fast(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE o, + API_SSIZE_T i, int wraparound, int boundscheck); +{{endfor}} #else #define __Pyx_GetItemInt(o, i, type, is_signed, to_py_func, is_list, wraparound, boundscheck) \ (__Pyx_fits_Py_ssize_t(i, type, is_signed) ? \ __Pyx_GetItemInt_Fast(o, (Py_ssize_t)i, is_list, wraparound, boundscheck) : \ (is_list ? (PyErr_SetString(PyExc_IndexError, "list index out of range"), (PyObject*)NULL) : \ __Pyx_GetItemInt_Generic(o, to_py_func(i)))) -#endif {{for type in ['List', 'Tuple']}} #define __Pyx_GetItemInt_{{type}}(o, i, type, is_signed, to_py_func, is_list, wraparound, boundscheck) \ (__Pyx_fits_Py_ssize_t(i, type, is_signed) ? \ __Pyx_GetItemInt_{{type}}_Fast(o, (Py_ssize_t)i, wraparound, boundscheck) : \ (PyErr_SetString(PyExc_IndexError, "{{ type.lower() }} index out of range"), (PyObject*)NULL)) - -static CYTHON_INLINE PyObject *__Pyx_GetItemInt_{{type}}_Fast(PyObject *o, Py_ssize_t i, - int wraparound, int boundscheck); + +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_GetItemInt_{{type}}_Fast(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE o, + API_SSIZE_T i, int wraparound, int boundscheck); {{endfor}} +#endif -static PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j); -static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i, +static PYOBJECT_TYPE __Pyx_GetItemInt_Generic(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE o, PYOBJECT_TYPE j); +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_GetItemInt_Fast(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE o, API_SSIZE_T i, int is_list, int wraparound, int boundscheck); /////////////// GetItemInt /////////////// //@substitute: tempita -static PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j) { - PyObject *r; - if (unlikely(!j)) return NULL; - r = PyObject_GetItem(o, j); - Py_DECREF(j); +static PYOBJECT_TYPE __Pyx_GetItemInt_Generic(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE o, PYOBJECT_TYPE j) { + PYOBJECT_TYPE r; + if (unlikely(API_IS_NULL(j))) return API_NULL_VALUE; + r = PYOBJECT_GET_ITEM(o, j); + PYOBJECT_CLOSEREF(j); return r; } {{for type in ['List', 'Tuple']}} -static CYTHON_INLINE PyObject *__Pyx_GetItemInt_{{type}}_Fast(PyObject *o, Py_ssize_t i, +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_GetItemInt_{{type}}_Fast(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE o, API_SSIZE_T i, CYTHON_NCP_UNUSED int wraparound, CYTHON_NCP_UNUSED int boundscheck) { #if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE && !CYTHON_AVOID_BORROWED_REFS @@ -457,13 +467,13 @@ static CYTHON_INLINE PyObject *__Pyx_GetItemInt_{{type}}_Fast(PyObject *o, Py_ss } return __Pyx_GetItemInt_Generic(o, PyInt_FromSsize_t(i)); #else - return PySequence_GetItem(o, i); + return SEQUENCE_GET_ITEM(o, i); #endif } {{endfor}} -static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i, int is_list, - CYTHON_NCP_UNUSED int wraparound, +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_GetItemInt_Fast(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE o, API_SSIZE_T i, + int is_list, CYTHON_NCP_UNUSED int wraparound, CYTHON_NCP_UNUSED int boundscheck) { #if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE && !CYTHON_AVOID_BORROWED_REFS && CYTHON_USE_TYPE_SLOTS if (is_list || PyList_CheckExact(o)) { @@ -507,14 +517,14 @@ static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i, return sm->sq_item(o, i); } } -#else +#elif !CYTHON_USING_HPY // PySequence_GetItem behaves differently to PyObject_GetItem for i<0 // and possibly some other cases so can't generally be substituted if (is_list || !PyMapping_Check(o)) { return PySequence_GetItem(o, i); } #endif - return __Pyx_GetItemInt_Generic(o, PyInt_FromSsize_t(i)); + return __Pyx_GetItemInt_Generic(HPY_CONTEXT_FIRST_ARG_CALL o, PyInt_FromSsize_t(i)); } /////////////// SetItemInt.proto /////////////// @@ -693,7 +703,7 @@ static CYTHON_INLINE int __Pyx_PyObject_SetSlice(PyObject* obj, PyObject* value, Py_ssize_t cstart, Py_ssize_t cstop, PyObject** _py_start, PyObject** _py_stop, PyObject** _py_slice, int has_cstart, int has_cstop, int wraparound) { - __Pyx_TypeName obj_type_name; + const char *obj_type_name; #if CYTHON_USE_TYPE_SLOTS PyMappingMethods* mp = Py_TYPE(obj)->tp_as_mapping; CYTHON_UNUSED_VAR(wraparound); @@ -715,7 +725,7 @@ static CYTHON_INLINE int __Pyx_PyObject_SetSlice(PyObject* obj, PyObject* value, py_start = *_py_start; } else { if (has_cstart) { - owned_start = py_start = PyInt_FromSsize_t(cstart); + owned_start = py_start = PyLong_FromSsize_t(cstart); if (unlikely(!py_start)) goto bad; } else py_start = Py_None; @@ -724,7 +734,7 @@ static CYTHON_INLINE int __Pyx_PyObject_SetSlice(PyObject* obj, PyObject* value, py_stop = *_py_stop; } else { if (has_cstop) { - owned_stop = py_stop = PyInt_FromSsize_t(cstop); + owned_stop = py_stop = PyLong_FromSsize_t(cstop); if (unlikely(!py_stop)) { Py_XDECREF(owned_start); goto bad; @@ -753,7 +763,7 @@ static CYTHON_INLINE int __Pyx_PyObject_SetSlice(PyObject* obj, PyObject* value, } return result; } - obj_type_name = __Pyx_PyType_GetName(Py_TYPE(obj)); + obj_type_name = (Py_TYPE(obj)->tp_name); PyErr_Format(PyExc_TypeError, {{if access == 'Get'}} "'" __Pyx_FMT_TYPENAME "' object is unsliceable", obj_type_name); @@ -761,7 +771,7 @@ static CYTHON_INLINE int __Pyx_PyObject_SetSlice(PyObject* obj, PyObject* value, "'" __Pyx_FMT_TYPENAME "' object does not support slice %.10s", obj_type_name, value ? "assignment" : "deletion"); {{endif}} - __Pyx_DECREF_TypeName(obj_type_name); + Py_XDECREF(obj_type_name); bad: return {{if access == 'Get'}}NULL{{else}}-1{{endif}}; @@ -823,7 +833,7 @@ __Pyx_PyList_FromArray(PyObject *const *src, Py_ssize_t n) /////////////// SliceTupleAndList.proto /////////////// -#if CYTHON_COMPILING_IN_CPYTHON +#if CYTHON_COMPILING_IN_CPYTHON && !CYTHON_USING_HPY static CYTHON_INLINE PyObject* __Pyx_PyList_GetSlice(PyObject* src, Py_ssize_t start, Py_ssize_t stop); static CYTHON_INLINE PyObject* __Pyx_PyTuple_GetSlice(PyObject* src, Py_ssize_t start, Py_ssize_t stop); #else @@ -835,7 +845,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyTuple_GetSlice(PyObject* src, Py_ssize_t //@requires: TupleAndListFromArray //@substitute: tempita -#if CYTHON_COMPILING_IN_CPYTHON +#if CYTHON_COMPILING_IN_CPYTHON && !CYTHON_USING_HPY static CYTHON_INLINE void __Pyx_crop_slice(Py_ssize_t* _start, Py_ssize_t* _stop, Py_ssize_t* _length) { Py_ssize_t start = *_start, stop = *_stop, length = *_length; if (start < 0) { @@ -1649,7 +1659,7 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStr(HPY_CONTEXT_FIRST_A if (likely(tp->tp_getattro)) return tp->tp_getattro(obj, attr_name); #endif - return PYOBJECT_GET_ATTR_STR(obj, attr_name); + return PYOBJECT_GET_ATTR(obj, attr_name); } #endif @@ -1979,43 +1989,31 @@ static PYOBJECT_TYPE __Pyx__CallUnboundCMethod1(HPY_CONTEXT_FIRST_ARG_DEF __Pyx_ #if CYTHON_USING_HPY if (unlikely(!(cfunc->func == HPyDef_Kind_Meth) && API_IS_NULL(cfunc->method)) && unlikely(__Pyx_TryUnpackUnboundCMethod(HPY_CONTEXT_FIRST_ARG_CALL cfunc) < 0)) return API_NULL_VALUE; #else - if (unlikely(!cfunc->func && API_IS_NULL(cfunc->method)) && unlikely(__Pyx_TryUnpackUnboundCMethod(HPY_CONTEXT_FIRST_ARG_CALL cfunc) < 0)) return API_NULL_VALUE; + if (unlikely(!cfunc->func && !cfunc->method) && unlikely(__Pyx_TryUnpackUnboundCMethod(cfunc) < 0)) return NULL; #endif #if CYTHON_COMPILING_IN_CPYTHON && !CYTHON_USING_HPY if (cfunc->func && (cfunc->flag & METH_VARARGS)) { - TUPLE_BUILDER_TYPE builder; - TUPLE_CREATE_START(args, builder, 1); - if (unlikely(API_IS_NULL(args))) goto bad; -#if !CYTHON_USING_HPY + args = PyTuple_New(1); + if (unlikely(!args)) goto bad; Py_INCREF(arg); -#endif - TUPLE_CREATE_ASSIGN(args, builder, 0, arg); - TUPLE_CREATE_FINALISE(args, builder); -#if !CYTHON_USING_HPY + PyTuple_SET_ITEM(args, 0, arg); if (cfunc->flag & METH_KEYWORDS) result = (*(PyCFunctionWithKeywords)(void*)(PyCFunction)cfunc->func)(self, args, NULL); else result = (*cfunc->func)(self, args); -#else - result = __Pyx_PyObject_Call_h(cfunc->method, args, API_NULL_VALUE); -#endif } else { - TUPLE_BUILDER_TYPE builder; - TUPLE_CREATE_START(args, builder, 2); - if (unlikely(API_IS_NULL(args))) goto bad; -#if !CYTHON_USING_HPY + args = PyTuple_New(2); + if (unlikely(!args)) goto bad; Py_INCREF(self); + PyTuple_SET_ITEM(args, 0, self); Py_INCREF(arg); -#endif - TUPLE_CREATE_ASSIGN(args, builder, 0, self); - TUPLE_CREATE_ASSIGN(args, builder, 1, arg); - TUPLE_CREATE_FINALISE(args, builder); - result = __Pyx_PyObject_Call_h(cfunc->method, args, API_NULL_VALUE); + PyTuple_SET_ITEM(args, 1, arg); + result = __Pyx_PyObject_Call(cfunc->method, args, NULL); } #else - args = TUPLE_PACK(2, self, arg); + PYOBJECT_TYPE args[2] = {self, arg}; if (unlikely(API_IS_NULL(args))) goto bad; - result = __Pyx_PyObject_Call_h(cfunc->method, args, API_NULL_VALUE); + result = __Pyx_PyObject_Call_h(cfunc->method, args, 2, API_NULL_VALUE); #endif bad: PYOBJECT_XCLOSEREF(args); @@ -2053,7 +2051,7 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_CallUnboundCMethod2(HPY_CONTEXT_FIRST_A if (cfunc->flag == (METH_FASTCALL | METH_KEYWORDS)) return (*(__Pyx_PyCFunctionFastWithKeywords)(void*)(PyCFunction)cfunc->func)(self, args, 2, NULL); #else - return __Pyx_PyObject_Call_h(cfunc->method, args, API_NULL_VALUE); + return __Pyx_PyObject_Call_h(cfunc->method, args, 2, API_NULL_VALUE); #endif } @@ -2069,44 +2067,32 @@ static PYOBJECT_TYPE __Pyx__CallUnboundCMethod2(HPY_CONTEXT_FIRST_ARG_DEF __Pyx_ if (unlikely(API_IS_NULL(cfunc->func) && API_IS_NULL(cfunc->method)) && unlikely(__Pyx_TryUnpackUnboundCMethod(HPY_CONTEXT_FIRST_ARG_CALL cfunc) < 0)) return API_NULL_VALUE; #endif #if CYTHON_COMPILING_IN_CPYTHON && !CYTHON_USING_HPY - if (API_IS_NOT_NULL(cfunc->func) && (cfunc->flag & METH_VARARGS)) { - TUPLE_BUILDER_TYPE builder; - TUPLE_CREATE_START(args, builder, 2); - if (unlikely(API_IS_NULL(args))) goto bad; -#if !CYTHON_USING_HPY + if (cfunc->func && (cfunc->flag & METH_VARARGS)) { + args = PyTuple_New(2); + if (unlikely(!args)) goto bad; Py_INCREF(arg1); + PyTuple_SET_ITEM(args, 0, arg1); Py_INCREF(arg2); -#endif - TUPLE_CREATE_ASSIGN(args, builder, 0, arg1); - TUPLE_CREATE_ASSIGN(args, builder, 1, arg2); - TUPLE_CREATE_FINALISE(args, builder); -#if !CYTHON_USING_HPY + PyTuple_SET_ITEM(args, 1, arg2); if (cfunc->flag & METH_KEYWORDS) result = (*(PyCFunctionWithKeywords)(void*)(PyCFunction)cfunc->func)(self, args, NULL); else result = (*cfunc->func)(self, args); -#else - result = __Pyx_PyObject_Call_h(cfunc->method, args, API_NULL_VALUE); -#endif } else { - TUPLE_BUILDER_TYPE builder; - TUPLE_CREATE_START(args, builder, 3); - if (unlikely(API_IS_NULL(args))) goto bad; -#if !CYTHON_USING_HPY + args = PyTuple_New(3); + if (unlikely(!args)) goto bad; Py_INCREF(self); + PyTuple_SET_ITEM(args, 0, self); Py_INCREF(arg1); + PyTuple_SET_ITEM(args, 1, arg1); Py_INCREF(arg2); -#endif - TUPLE_CREATE_ASSIGN(args, builder, 0, self); - TUPLE_CREATE_ASSIGN(args, builder, 1, arg1); - TUPLE_CREATE_ASSIGN(args, builder, 2, arg2); - TUPLE_CREATE_FINALISE(args, builder); - result = __Pyx_PyObject_Call_h(cfunc->method, args, API_NULL_VALUE); + PyTuple_SET_ITEM(args, 2, arg2); + result = __Pyx_PyObject_Call(cfunc->method, args, NULL); } #else - args = TUPLE_PACK(3, self, arg1, arg2); + PYOBJECT_TYPE args[3] = {self, arg1, arg2}; if (unlikely(API_IS_NULL(args))) goto bad; - result = __Pyx_PyObject_Call_h(cfunc->method, args, API_NULL_VALUE); + result = __Pyx_PyObject_Call_h(cfunc->method, args, 3, API_NULL_VALUE); #endif bad: PYOBJECT_XCLOSEREF(args); @@ -2118,6 +2104,7 @@ static PYOBJECT_TYPE __Pyx__CallUnboundCMethod2(HPY_CONTEXT_FIRST_ARG_DEF __Pyx_ #if CYTHON_USING_HPY #define __Pyx_PyObject_FastCall(func, args, nargs) API_CALL_FUNC(func, args, (size_t)(nargs), API_NULL_VALUE) +#define __Pyx_PyObject_FastCallDict(func, args, nargs) API_CALL_FUNC(func, args, (size_t)(nargs), API_NULL_VALUE) #else #define __Pyx_PyObject_FastCall(func, args, nargs) __Pyx_PyObject_FastCallDict(HPY_CONTEXT_FIRST_ARG_CALL func, args, (size_t)(nargs), API_NULL_VALUE) static CYTHON_INLINE PyObject *__Pyx_PyObject_FastCallDict(HPY_CONTEXT_FIRST_ARG_DEF PyObject *func, PyObject **args, size_t nargs, PyObject *kwargs); /*proto*/ @@ -2149,7 +2136,7 @@ static PyObject* __Pyx_PyObject_FastCall_fallback(PyObject *func, PyObject **arg #endif #if !CYTHON_USING_HPY -static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_FastCallDict(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE func, PYOBJECT_TYPE *args, size_t _nargs, PYOBJECT_TYPE kwargs) { +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_FastCallDict(PyObject *func, PyObject **args, size_t _nargs, PyObject *kwargs) { // Special fast paths for 0 and 1 arguments // NOTE: in many cases, this is called with a constant value for nargs // which is known at compile-time. So the branches below will typically @@ -2359,9 +2346,9 @@ static CYTHON_INLINE PyObject* __Pyx_tp_new_kwargs(PyObject* type_obj, PyObject* /////////////// PyObjectCall.proto /////////////// #if CYTHON_USING_HPY -#define __Pyx_PyObject_Call_h(func, arg, kw) API_CALL_FUNC(func, &arg, TUPLE_GET_SIZE(arg), kw) // Will be removed when no longer needed +#define __Pyx_PyObject_Call_h(func, arg, len, kw) API_CALL_FUNC(func, arg, len, kw) // Will be removed when no longer needed #else -#define __Pyx_PyObject_Call_h(func, arg, kw) __Pyx_PyObject_Call(func, arg, kw) +#define __Pyx_PyObject_Call_h(func, arg, len, kw) __Pyx_PyObject_Call(func, arg, kw) #endif #if CYTHON_COMPILING_IN_CPYTHON && !CYTHON_USING_HPY static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw); /*proto*/ diff --git a/Cython/Utility/StringTools.c b/Cython/Utility/StringTools.c index cdb04b51798..254afefa447 100644 --- a/Cython/Utility/StringTools.c +++ b/Cython/Utility/StringTools.c @@ -118,14 +118,14 @@ static CYTHON_INLINE int __Pyx_StrEq(const char *s1, const char *s2) { //////////////////// UnicodeEquals.proto //////////////////// -static CYTHON_INLINE int __Pyx_PyUnicode_Equals(PyObject* s1, PyObject* s2, int equals); /*proto*/ +static CYTHON_INLINE int __Pyx_PyUnicode_Equals(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE s1, PYOBJECT_TYPE s2, int equals); /*proto*/ //////////////////// UnicodeEquals //////////////////// //@requires: BytesEquals -static CYTHON_INLINE int __Pyx_PyUnicode_Equals(PyObject* s1, PyObject* s2, int equals) { +static CYTHON_INLINE int __Pyx_PyUnicode_Equals(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE s1, PYOBJECT_TYPE s2, int equals) { #if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API - return PyObject_RichCompareBool(s1, s2, equals); + return API_RICH_COMPARE_BOOL(s1, s2, equals); #else int s1_is_unicode, s2_is_unicode; if (s1 == s2) { diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index aa578a75c6e..cfff9f8931d 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -7,13 +7,13 @@ #define __Pyx_long_cast(x) ((long)x) #define __Pyx_fits_Py_ssize_t(v, type, is_signed) ( \ - (sizeof(type) < sizeof(Py_ssize_t)) || \ - (sizeof(type) > sizeof(Py_ssize_t) && \ + (sizeof(type) < sizeof(API_SSIZE_T)) || \ + (sizeof(type) > sizeof(API_SSIZE_T) && \ likely(v < (type)PY_SSIZE_T_MAX || \ v == (type)PY_SSIZE_T_MAX) && \ (!is_signed || likely(v > (type)PY_SSIZE_T_MIN || \ v == (type)PY_SSIZE_T_MIN))) || \ - (sizeof(type) == sizeof(Py_ssize_t) && \ + (sizeof(type) == sizeof(API_SSIZE_T) && \ (is_signed || likely(v < (type)PY_SSIZE_T_MAX || \ v == (type)PY_SSIZE_T_MAX))) ) @@ -123,7 +123,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(HPY_CONTEXT_FIRST_ARG_DE #define __Pyx_PySequence_Tuple(obj) \ (likely(PyTuple_CheckExact(obj)) ? __Pyx_NewRef(obj) : PySequence_Tuple(obj)) -static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject*); +static CYTHON_INLINE API_SSIZE_T __Pyx_PyIndex_AsSsize_t(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE); static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t); static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject*); @@ -379,10 +379,10 @@ static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(HPY_CONTEXT_FIRST_ARG_DE {{py: from Cython.Utility import pylong_join }} -static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) { - Py_ssize_t ival; - PyObject *x; - if (likely(PyLong_CheckExact(b))) { +static CYTHON_INLINE API_SSIZE_T __Pyx_PyIndex_AsSsize_t(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE b) { + API_SSIZE_T ival; + PYOBJECT_TYPE x; + if (likely(LONG_CHECK_EXACT(b))) { #if CYTHON_USE_PYLONG_INTERNALS // handle most common case first to avoid indirect branch and optimise branch prediction if (likely(__Pyx_PyLong_IsCompact(b))) { @@ -403,20 +403,20 @@ static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) { } } #endif - return PyLong_AsSsize_t(b); + return PYOBJECT_LONG_AS_SSIZE_T(b); } - x = PyNumber_Index(b); - if (!x) return -1; + x = HPY_LEGACY_OBJECT_FROM(PyNumber_Index(HPY_LEGACY_OBJECT_AS(b))); + if (API_IS_NULL(x)) return -1; ival = PyInt_AsSsize_t(x); - Py_DECREF(x); + PYOBJECT_CLOSEREF(x); return ival; } static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject* o) { - if (sizeof(Py_hash_t) == sizeof(Py_ssize_t)) { - return (Py_hash_t) __Pyx_PyIndex_AsSsize_t(o); - } else { +// if (sizeof(Py_hash_t) == sizeof(Py_ssize_t)) { +// return (Py_hash_t) __Pyx_PyIndex_AsSsize_t(NULL, o); +// } else { Py_ssize_t ival; PyObject *x; x = PyNumber_Index(o); @@ -424,7 +424,7 @@ static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject* o) { ival = PyInt_AsLong(x); Py_DECREF(x); return ival; - } +// } } diff --git a/tests/run/hpy_fannkuch_benchmark.pyx b/tests/run/hpy_fannkuch_benchmark.pyx new file mode 100644 index 00000000000..9aa9516cb14 --- /dev/null +++ b/tests/run/hpy_fannkuch_benchmark.pyx @@ -0,0 +1,54 @@ +""" +The Computer Language Benchmarks Game +http://benchmarksgame.alioth.debian.org/ + +Contributed by Sokolov Yura, modified by Tupteq. +""" + +import pyperf + + +DEFAULT_ARG = 9 + + +def fannkuch(n): + count = list(range(1, n + 1)) + max_flips = 0 + m = n - 1 + r = n + perm1 = list(range(n)) + perm = list(range(n)) + perm1_ins = perm1.insert + perm1_pop = perm1.pop + + while 1: + while r != 1: + count[r - 1] = r + r -= 1 + + if perm1[0] != 0 and perm1[m] != m: + perm = perm1[:] + flips_count = 0 + k = perm[0] + while k: + perm[:k + 1] = perm[k::-1] + flips_count += 1 + k = perm[0] + + if flips_count > max_flips: + max_flips = flips_count + + while r != n: + perm1_ins(r, perm1_pop(0)) + count[r] -= 1 + if count[r] > 0: + break + r += 1 + else: + return max_flips + + +if __name__ == "__main__": + runner = pyperf.Runner() + arg = DEFAULT_ARG + runner.bench_func('fannkuch', fannkuch, arg) From 9187b704f984843a55743062ab03210b5c698d70 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 14 Dec 2023 16:36:57 +0100 Subject: [PATCH 256/286] Made fannkuch test run sometimes --- Cython/Compiler/ExprNodes.py | 14 +++++++++++--- Cython/Utility/HPyUtils.c | 18 +++++++++++++++--- Cython/Utility/ObjectHandling.c | 2 +- tests/run/hpy_fannkuch_benchmark.pyx | 5 +++++ 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index c1b9adcbee7..0dc17afa920 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -2615,16 +2615,24 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, if is_external_ref: self.generate_gotref(code, handle_null=True) assigned = True + load_rhs = code.funcstate.allocate_temp(py_object_type, False) + if rhs.result_as(self.ctype()) in code.globalstate.const_cname_array: + code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_rhs, rhs.result_as(self.ctype()))) + else: + code.putln("%s = %s;" % (load_rhs, rhs.result_as(self.ctype()))) if entry.is_cglobal: - self.generate_decref_set(code, rhs.result_as(self.ctype())) + self.generate_decref_set(code, load_rhs) else: if not self.cf_is_null: if self.cf_maybe_null: - self.generate_xdecref_set(code, rhs.result_as(self.ctype())) + self.generate_xdecref_set(code, load_rhs) else: - self.generate_decref_set(code, rhs.result_as(self.ctype())) + self.generate_decref_set(code, load_rhs) else: assigned = False + if rhs.result_as(self.ctype()) in code.globalstate.const_cname_array: + code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % load_rhs) + code.funcstate.release_temp(load_rhs) if is_external_ref: rhs.generate_giveref(code) if not self.type.is_memoryviewslice: diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 7f836296afe..3ad8b289c80 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -71,9 +71,9 @@ #define API_DICT_TYPE_DEREF API_DICT_TYPE //Type Checks - #define LONG_CHECK(l) HPyNumber_Check(HPY_CONTEXT_CNAME, l) - #define LONG_CHECK_EXACT(l) HPyNumber_Check(HPY_CONTEXT_CNAME, l) - #define FLOAT_CHECK_EXACT(f) HPyNumber_Check(HPY_CONTEXT_CNAME, f) + #define LONG_CHECK(l) HPyLong_Check(HPY_CONTEXT_CNAME, l) + #define LONG_CHECK_EXACT(l) HPyLong_Check(HPY_CONTEXT_CNAME, l) + #define FLOAT_CHECK_EXACT(f) HPyFloat_Check(HPY_CONTEXT_CNAME, f) #define UNICODE_CHECK(u) HPyUnicode_Check(HPY_CONTEXT_CNAME, u) #define DICT_CHECK(o) HPyDict_Check(HPY_CONTEXT_CNAME, o) #define DICT_CHECK_EXACT(o) HPyDict_Check(HPY_CONTEXT_CNAME, o) @@ -443,6 +443,18 @@ static CYTHON_INLINE HPy HPyDict_GetItem_s(HPyContext *ctx, HPy mp, const char * return res; } +static inline int +HPyLong_Check(HPyContext *ctx, HPy obj) +{ + return HPy_TypeCheck(ctx, obj, ctx->h_LongType); +} + +static inline int +HPyFloat_Check(HPyContext *ctx, HPy obj) +{ + return HPy_TypeCheck(ctx, obj, ctx->h_FloatType); +} + static CYTHON_INLINE HPy HPyField_XLoad(HPyContext *ctx, HPy h_item, HPyField field, HPy owner) { if (!HPyField_IsNull(field)) { diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 992f1cbe1c0..9e23296dcb1 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1630,7 +1630,7 @@ static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetAttrStrNoError(HPY_CONTEXT_ #if !CYTHON_USING_HPY result = __Pyx_PyObject_GetAttrStr(obj, attr_name); #else - result = PYOBJECT_GET_ATTR(obj, attr_name); + result = PYOBJECT_GET_ITEM(obj, attr_name); #endif if (unlikely(API_IS_NULL(result))) { __Pyx_PyObject_GetAttrStr_ClearAttributeError(); diff --git a/tests/run/hpy_fannkuch_benchmark.pyx b/tests/run/hpy_fannkuch_benchmark.pyx index 9aa9516cb14..e2f187ddfc0 100644 --- a/tests/run/hpy_fannkuch_benchmark.pyx +++ b/tests/run/hpy_fannkuch_benchmark.pyx @@ -12,6 +12,10 @@ DEFAULT_ARG = 9 def fannkuch(n): + """ + >>> fannkuch(9) + 30 + """ count = list(range(1, n + 1)) max_flips = 0 m = n - 1 @@ -52,3 +56,4 @@ if __name__ == "__main__": runner = pyperf.Runner() arg = DEFAULT_ARG runner.bench_func('fannkuch', fannkuch, arg) + From 363981bee3fa1b80f599ca0ee198be116ae993ec Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Wed, 20 Dec 2023 12:17:03 +0100 Subject: [PATCH 257/286] Fix mem leak due to __Pyx_PyObject_GetSlice --- Cython/Compiler/ExprNodes.py | 2 +- Cython/Utility/ObjectHandling.c | 110 ++++++++++++++++++++++++++++++-- 2 files changed, 107 insertions(+), 5 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 0dc17afa920..795093d211d 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -5643,7 +5643,7 @@ def generate_result_code(self, code): (has_c_start, has_c_stop, c_start, c_stop, py_start, py_stop, py_slice) = self.get_slice_config() code.putln( - "%s = HPY_LEGACY_OBJECT_FROM(__Pyx_PyObject_GetSlice(HPY_LEGACY_OBJECT_AS(%s), %s, %s, HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), %d, %d, %d)); %s" % ( + "%s = __Pyx_PyObject_GetSlice(HPY_CONTEXT_FIRST_ARG_CALL %s, %s, %s, %s, %s, %s, %d, %d, %d); %s" % ( result, self.base.py_result(), c_start, c_stop, diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 9e23296dcb1..9ec606e431f 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -673,14 +673,84 @@ static CYTHON_INLINE int __Pyx_DelItemInt_Fast(PyObject *o, Py_ssize_t i, return __Pyx_DelItem_Generic(o, PyInt_FromSsize_t(i)); } +/////////////// SliceObjectInternal.proto /////////////// +static CYTHON_INLINE PyObject* __Pyx_PyObject_GetSliceInternal(PyObject* obj, + Py_ssize_t cstart, Py_ssize_t cstop, + PyObject** _py_start, PyObject** _py_stop, PyObject** _py_slice, + int has_cstart, int has_cstop, int wraparound); + +/////////////// SliceObjectInternal /////////////// + +static CYTHON_INLINE PyObject* __Pyx_PyObject_GetSliceInternal(PyObject* obj, + Py_ssize_t cstart, Py_ssize_t cstop, + PyObject** _py_start, PyObject** _py_stop, PyObject** _py_slice, + int has_cstart, int has_cstop, int wraparound) { + const char *obj_type_name; +#if CYTHON_USE_TYPE_SLOTS + PyMappingMethods* mp = Py_TYPE(obj)->tp_as_mapping; + CYTHON_UNUSED_VAR(wraparound); + if (likely(mp && mp->mp_subscript)) +#endif + { + PyObject* result; + PyObject *py_slice, *py_start, *py_stop; + if (_py_slice) { + py_slice = *_py_slice; + } else { + PyObject* owned_start = NULL; + PyObject* owned_stop = NULL; + if (_py_start) { + py_start = *_py_start; + } else { + if (has_cstart) { + owned_start = py_start = PyLong_FromSsize_t(cstart); + if (unlikely(!py_start)) goto bad; + } else + py_start = Py_None; + } + if (_py_stop) { + py_stop = *_py_stop; + } else { + if (has_cstop) { + owned_stop = py_stop = PyLong_FromSsize_t(cstop); + if (unlikely(!py_stop)) { + Py_XDECREF(owned_start); + goto bad; + } + } else + py_stop = Py_None; + } + py_slice = PySlice_New(py_start, py_stop, Py_None); + Py_XDECREF(owned_start); + Py_XDECREF(owned_stop); + if (unlikely(!py_slice)) goto bad; + } +#if CYTHON_USE_TYPE_SLOTS + result = mp->mp_subscript(obj, py_slice); +#else + result = PyObject_GetItem(obj, py_slice); +#endif + if (!_py_slice) { + Py_DECREF(py_slice); + } + return result; + } + obj_type_name = (Py_TYPE(obj)->tp_name); + PyErr_Format(PyExc_TypeError, + "'" __Pyx_FMT_TYPENAME "' object is unsliceable", obj_type_name); + Py_XDECREF(obj_type_name); + +bad: + return NULL; +} /////////////// SliceObject.proto /////////////// // we pass pointer addresses to show the C compiler what is NULL and what isn't {{if access == 'Get'}} -static CYTHON_INLINE PyObject* __Pyx_PyObject_GetSlice( - PyObject* obj, Py_ssize_t cstart, Py_ssize_t cstop, - PyObject** py_start, PyObject** py_stop, PyObject** py_slice, +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetSlice(HPY_CONTEXT_FIRST_ARG_DEF + PYOBJECT_TYPE obj, API_SSIZE_T cstart, API_SSIZE_T cstop, + PYOBJECT_TYPE* py_start,PYOBJECT_TYPEPyObject* py_stop, PYOBJECT_TYPE* py_slice, int has_cstart, int has_cstop, int wraparound); {{else}} #define __Pyx_PyObject_DelSlice(obj, cstart, cstop, py_start, py_stop, py_slice, has_cstart, has_cstop, wraparound) \ @@ -694,9 +764,10 @@ static CYTHON_INLINE int __Pyx_PyObject_SetSlice( {{endif}} /////////////// SliceObject /////////////// +//@requires: SliceObjectInternal {{if access == 'Get'}} -static CYTHON_INLINE PyObject* __Pyx_PyObject_GetSlice(PyObject* obj, +static CYTHON_INLINE PyObject* __Pyx_PyObject_GetSliceInternal(PyObject* obj, {{else}} static CYTHON_INLINE int __Pyx_PyObject_SetSlice(PyObject* obj, PyObject* value, {{endif}} @@ -777,6 +848,37 @@ static CYTHON_INLINE int __Pyx_PyObject_SetSlice(PyObject* obj, PyObject* value, return {{if access == 'Get'}}NULL{{else}}-1{{endif}}; } +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetSlice(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, + API_SSIZE_T cstart, API_SSIZE_T cstop, + PYOBJECT_TYPE* _start, PYOBJECT_TYPE* _stop, PYOBJECT_TYPE* _slice, + int has_cstart, int has_cstop, int wraparound) { + +#if CYTHON_USING_HPY + PyObject* py_obj = HPY_LEGACY_OBJECT_AS(obj); + PyObject* py_start = HPY_LEGACY_OBJECT_AS(*_start); + PyObject* py_stop = HPY_LEGACY_OBJECT_AS(*_stop); + PyObject* py_slice = HPY_LEGACY_OBJECT_AS(*_slice); + PyObject* py_res; + PYOBJECT_TYPE res; + + py_res = __Pyx_PyObject_GetSliceInternal(py_obj, cstart, cstop, &py_start, &py_stop, &py_slice, has_cstart, has_cstop, wraparound); + Py_DECREF(py_obj); + + *_start = HPY_LEGACY_OBJECT_FROM(py_start); + *_stop = HPY_LEGACY_OBJECT_FROM(py_stop); + *_slice = HPY_LEGACY_OBJECT_FROM(py_slice); + Py_XDECREF(py_start); + Py_XDECREF(py_stop); + Py_XDECREF(py_slice); + + res = HPY_LEGACY_OBJECT_FROM(py_res); + Py_XDECREF(py_res); + return res; +#else + return __Pyx_PyObject_GetSliceInternal(obj, cstart, cstop, _start, _stop, _slice, has_cstart, has_cstop, wraparound); +#endif +} + /////////////// TupleAndListFromArray.proto /////////////// From a86588e461978452bcd408b8d95acc9f394306da Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Wed, 20 Dec 2023 14:48:59 +0100 Subject: [PATCH 258/286] Introduce API_SLICE_NEW --- Cython/Compiler/ExprNodes.py | 2 +- Cython/Utility/HPyUtils.c | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 795093d211d..2e69b0010fc 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -5909,7 +5909,7 @@ def generate_result_code(self, code): load_stop.allocate(code, needs_decl=True) load_step.allocate(code, needs_decl=True) code.putln( - "%s = HPY_LEGACY_OBJECT_FROM(PySlice_New(HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s))); %s" % ( + "%s = API_SLICE_NEW(%s, %s, %s); %s" % ( self.result(), load_start.temp_cname, load_stop.temp_cname, diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 3ad8b289c80..497f256d6bd 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -189,6 +189,9 @@ //Function Macros #define PYMETHODDEF_TYPE HPyDef + //Sequence Type + #define API_SLICE_NEW(start, stop, step) HPySlice_New(HPY_CONTEXT_CNAME, start, stop, step) + #else //HPy Context Macros #define HPY_CONTEXT_CNAME @@ -377,6 +380,9 @@ //Function Macros #define PYMETHODDEF_TYPE PyMethodDef + //Sequence Type + #define API_SLICE_NEW(start, stop, step) PySlice_New(start, stop, step) + #endif //////////////////// HPyHelperFuncs.proto //////////////////// @@ -464,4 +470,20 @@ static CYTHON_INLINE HPy HPyField_XLoad(HPyContext *ctx, HPy h_item, HPyField fi } } +static inline HPy +HPySlice_New(HPyContext *ctx, HPy start, HPy stop, HPy step) +{ + HPy res; + PyObject *py_start = HPy_AsPyObject(ctx, start); + PyObject *py_stop = HPy_AsPyObject(ctx, stop); + PyObject *py_step = HPy_AsPyObject(ctx, step); + PyObject *py_res = PySlice_New(py_start, py_stop, py_step); + Py_XDECREF(py_start); + Py_XDECREF(py_stop); + Py_XDECREF(py_step); + res = HPy_FromPyObject(ctx, py_res); + Py_XDECREF(py_res); + return res; +} + #endif From 2e47b1a298e52e4a885c15d141d0eaa9793e47c6 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Wed, 20 Dec 2023 14:49:39 +0100 Subject: [PATCH 259/286] Use PYOBJECT_UNICODE_FROM_STRING to create empty string --- Cython/Compiler/ModuleNode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 962308e96a0..9c6e0acb3e5 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -3148,8 +3148,8 @@ def generate_module_init_func(self, imported_modules, env, code): Naming.empty_tuple, code.error_goto_if_null_object("TUPLE_CREATE_EMPTY()", self.pos))) code.putln("PYOBJECT_GLOBAL_STORE(%s, BYTES_FROM_STR_AND_SIZE(\"\", 0)); %s" % ( Naming.empty_bytes, code.error_goto_if_null_object("BYTES_FROM_STR_AND_SIZE(\"\", 0)", self.pos))) - code.putln("PYOBJECT_GLOBAL_STORE(%s, HPY_LEGACY_OBJECT_FROM(PyUnicode_FromStringAndSize(\"\", 0))); %s" % ( - Naming.empty_unicode, code.error_goto_if_null_object("HPY_LEGACY_OBJECT_FROM(PyUnicode_FromStringAndSize(\"\", 0))", self.pos))) + code.putln("PYOBJECT_GLOBAL_STORE(%s, PYOBJECT_UNICODE_FROM_STRING(\"\")); %s" % ( + Naming.empty_unicode, code.error_goto_if_null_object("PYOBJECT_UNICODE_FROM_STRING(\"\")", self.pos))) for ext_type in ('CyFunction', 'FusedFunction', 'Coroutine', 'Generator', 'AsyncGen', 'StopAsyncIteration'): code.putln("#ifdef __Pyx_%s_USED" % ext_type) From 34e2758ed1290a99d906cb9ee87b4d197c4934e7 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Fri, 22 Dec 2023 11:50:06 +0100 Subject: [PATCH 260/286] Fix mem leak in __Pyx_PyObject_CallMethod1 --- Cython/Utility/ObjectHandling.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 9ec606e431f..702b21a5f1c 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -2406,11 +2406,7 @@ static PYOBJECT_TYPE __Pyx_PyObject_CallMethod1(HPY_CONTEXT_FIRST_ARG_DEF PYOBJE #if !(CYTHON_VECTORCALL && __PYX_LIMITED_VERSION_HEX >= 0x030C00A2) static PYOBJECT_TYPE __Pyx__PyObject_CallMethod1(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE method, PYOBJECT_TYPE arg) { // Separate function to avoid excessive inlining. - PYOBJECT_TYPE result = __Pyx_PyObject_CallOneArg(HPY_CONTEXT_FIRST_ARG_CALL method, arg); -#if !CYTHON_USING_HPY - Py_DECREF(method); -#endif - return result; + return __Pyx_PyObject_CallOneArg(HPY_CONTEXT_FIRST_ARG_CALL method, arg); } #endif @@ -2432,7 +2428,9 @@ static PYOBJECT_TYPE __Pyx_PyObject_CallMethod1(HPY_CONTEXT_FIRST_ARG_DEF PYOBJE return result; } if (unlikely(API_IS_NULL(method))) return API_NULL_VALUE; - return __Pyx__PyObject_CallMethod1(HPY_CONTEXT_FIRST_ARG_CALL method, arg); + result = __Pyx__PyObject_CallMethod1(HPY_CONTEXT_FIRST_ARG_CALL method, arg); + PYOBJECT_CLOSEREF(method); + return result; #endif } From 3b7fa80e437a5604c25b29c4caf5c62f12c5e219 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Fri, 22 Dec 2023 14:13:10 +0100 Subject: [PATCH 261/286] Fix mem leak due to __Pyx_PyObject_SetSlice --- Cython/Compiler/ExprNodes.py | 8 +-- Cython/Utility/ObjectHandling.c | 121 ++++++++++++++++++-------------- 2 files changed, 71 insertions(+), 58 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 2e69b0010fc..56201584a07 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -5679,7 +5679,7 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, code.globalstate.use_utility_code(self.set_slice_utility_code) has_c_start, has_c_stop, c_start, c_stop, py_start, py_stop, py_slice = self.get_slice_config() code.put_error_if_neg(self.pos, - "__Pyx_PyObject_SetSlice(HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), %s, %s, HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), HPY_LEGACY_OBJECT_AS(%s), %d, %d, %d)" % ( + "__Pyx_PyObject_SetSlice(HPY_CONTEXT_FIRST_ARG_CALL %s, %s, %s, %s, %s, %s, %s, %d, %d, %d)" % ( self.base.py_result(), rhs.py_result(), c_start, c_stop, @@ -5726,21 +5726,21 @@ def generate_deletion_code(self, code, ignore_nonexisting=False): self.free_subexpr_temps(code) def get_slice_config(self): - has_c_start, c_start, py_start = False, '0', 'API_NULL_VALUE' + has_c_start, c_start, py_start = False, '0', 'NULL' if self.start: has_c_start = not self.start.type.is_pyobject if has_c_start: c_start = self.start.result() else: py_start = '&%s' % self.start.py_result() - has_c_stop, c_stop, py_stop = False, '0', 'API_NULL_VALUE' + has_c_stop, c_stop, py_stop = False, '0', 'NULL' if self.stop: has_c_stop = not self.stop.type.is_pyobject if has_c_stop: c_stop = self.stop.result() else: py_stop = '&%s' % self.stop.py_result() - py_slice = self.slice and '&%s' % self.slice.py_result() or 'API_NULL_VALUE' + py_slice = self.slice and '&%s' % self.slice.py_result() or 'NULL' return (has_c_start, has_c_stop, c_start, c_stop, py_start, py_stop, py_slice) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 702b21a5f1c..6991f27ffa8 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -679,6 +679,11 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_GetSliceInternal(PyObject* obj, PyObject** _py_start, PyObject** _py_stop, PyObject** _py_slice, int has_cstart, int has_cstop, int wraparound); +static CYTHON_INLINE PyObject* __Pyx_PyObject_GetSliceInternal(PyObject* obj, + Py_ssize_t cstart, Py_ssize_t cstop, + PyObject** _py_start, PyObject** _py_stop, PyObject** _py_slice, + int has_cstart, int has_cstop, int wraparound); + /////////////// SliceObjectInternal /////////////// static CYTHON_INLINE PyObject* __Pyx_PyObject_GetSliceInternal(PyObject* obj, @@ -744,33 +749,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_GetSliceInternal(PyObject* obj, return NULL; } -/////////////// SliceObject.proto /////////////// - -// we pass pointer addresses to show the C compiler what is NULL and what isn't -{{if access == 'Get'}} -static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetSlice(HPY_CONTEXT_FIRST_ARG_DEF - PYOBJECT_TYPE obj, API_SSIZE_T cstart, API_SSIZE_T cstop, - PYOBJECT_TYPE* py_start,PYOBJECT_TYPEPyObject* py_stop, PYOBJECT_TYPE* py_slice, - int has_cstart, int has_cstop, int wraparound); -{{else}} -#define __Pyx_PyObject_DelSlice(obj, cstart, cstop, py_start, py_stop, py_slice, has_cstart, has_cstop, wraparound) \ - __Pyx_PyObject_SetSlice(obj, (PyObject*)NULL, cstart, cstop, py_start, py_stop, py_slice, has_cstart, has_cstop, wraparound) - -// we pass pointer addresses to show the C compiler what is NULL and what isn't -static CYTHON_INLINE int __Pyx_PyObject_SetSlice( - PyObject* obj, PyObject* value, Py_ssize_t cstart, Py_ssize_t cstop, - PyObject** py_start, PyObject** py_stop, PyObject** py_slice, - int has_cstart, int has_cstop, int wraparound); -{{endif}} - -/////////////// SliceObject /////////////// -//@requires: SliceObjectInternal - -{{if access == 'Get'}} -static CYTHON_INLINE PyObject* __Pyx_PyObject_GetSliceInternal(PyObject* obj, -{{else}} -static CYTHON_INLINE int __Pyx_PyObject_SetSlice(PyObject* obj, PyObject* value, -{{endif}} +static CYTHON_INLINE int __Pyx_PyObject_SetSliceInternal(PyObject* obj, PyObject* value, Py_ssize_t cstart, Py_ssize_t cstop, PyObject** _py_start, PyObject** _py_stop, PyObject** _py_slice, int has_cstart, int has_cstop, int wraparound) { @@ -778,14 +757,10 @@ static CYTHON_INLINE int __Pyx_PyObject_SetSlice(PyObject* obj, PyObject* value, #if CYTHON_USE_TYPE_SLOTS PyMappingMethods* mp = Py_TYPE(obj)->tp_as_mapping; CYTHON_UNUSED_VAR(wraparound); -{{if access == 'Get'}} - if (likely(mp && mp->mp_subscript)) -{{else}} if (likely(mp && mp->mp_ass_subscript)) -{{endif}} #endif { - {{if access == 'Get'}}PyObject*{{else}}int{{endif}} result; + int result; PyObject *py_slice, *py_start, *py_stop; if (_py_slice) { py_slice = *_py_slice; @@ -819,15 +794,9 @@ static CYTHON_INLINE int __Pyx_PyObject_SetSlice(PyObject* obj, PyObject* value, if (unlikely(!py_slice)) goto bad; } #if CYTHON_USE_TYPE_SLOTS -{{if access == 'Get'}} - result = mp->mp_subscript(obj, py_slice); -#else - result = PyObject_GetItem(obj, py_slice); -{{else}} result = mp->mp_ass_subscript(obj, py_slice, value); #else result = value ? PyObject_SetItem(obj, py_slice, value) : PyObject_DelItem(obj, py_slice); -{{endif}} #endif if (!_py_slice) { Py_DECREF(py_slice); @@ -836,46 +805,90 @@ static CYTHON_INLINE int __Pyx_PyObject_SetSlice(PyObject* obj, PyObject* value, } obj_type_name = (Py_TYPE(obj)->tp_name); PyErr_Format(PyExc_TypeError, -{{if access == 'Get'}} - "'" __Pyx_FMT_TYPENAME "' object is unsliceable", obj_type_name); -{{else}} "'" __Pyx_FMT_TYPENAME "' object does not support slice %.10s", obj_type_name, value ? "assignment" : "deletion"); -{{endif}} Py_XDECREF(obj_type_name); bad: - return {{if access == 'Get'}}NULL{{else}}-1{{endif}}; + return -1; } +/////////////// SliceObject.proto /////////////// + +// we pass pointer addresses to show the C compiler what is NULL and what isn't +{{if access == 'Get'}} +static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetSlice(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, + API_SSIZE_T cstart, API_SSIZE_T cstop, + PYOBJECT_TYPE* py_start, PYOBJECT_TYPE* py_stop, PYOBJECT_TYPE* py_slice, + int has_cstart, int has_cstop, int wraparound); +{{else}} +#define __Pyx_PyObject_DelSlice(obj, cstart, cstop, py_start, py_stop, py_slice, has_cstart, has_cstop, wraparound) \ + __Pyx_PyObject_SetSlice(obj, (PyObject*)NULL, cstart, cstop, py_start, py_stop, py_slice, has_cstart, has_cstop, wraparound) + +// we pass pointer addresses to show the C compiler what is NULL and what isn't +static CYTHON_INLINE int __Pyx_PyObject_SetSlice(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, PYOBJECT_TYPE value, + API_SSIZE_T cstart, API_SSIZE_T cstop, + PYOBJECT_TYPE* py_start, PYOBJECT_TYPE* py_stop, PYOBJECT_TYPE* py_slice, + int has_cstart, int has_cstop, int wraparound); +{{endif}} + +/////////////// SliceObject /////////////// +//@requires: SliceObjectInternal + +{{if access == 'Get'}} static CYTHON_INLINE PYOBJECT_TYPE __Pyx_PyObject_GetSlice(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, +{{else}} +static CYTHON_INLINE int __Pyx_PyObject_SetSlice(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, PYOBJECT_TYPE value, +{{endif}} API_SSIZE_T cstart, API_SSIZE_T cstop, PYOBJECT_TYPE* _start, PYOBJECT_TYPE* _stop, PYOBJECT_TYPE* _slice, int has_cstart, int has_cstop, int wraparound) { #if CYTHON_USING_HPY PyObject* py_obj = HPY_LEGACY_OBJECT_AS(obj); - PyObject* py_start = HPY_LEGACY_OBJECT_AS(*_start); - PyObject* py_stop = HPY_LEGACY_OBJECT_AS(*_stop); - PyObject* py_slice = HPY_LEGACY_OBJECT_AS(*_slice); + PyObject* py_start = _start ? HPY_LEGACY_OBJECT_AS(*_start) : NULL; + PyObject* py_stop = _stop ? HPY_LEGACY_OBJECT_AS(*_stop) : NULL; + PyObject* py_slice = _slice ? HPY_LEGACY_OBJECT_AS(*_slice) : NULL; + +#define LIF(_c, _x) ((_c) ? &(_x) : NULL) + +{{if access == 'Get'}} PyObject* py_res; PYOBJECT_TYPE res; - - py_res = __Pyx_PyObject_GetSliceInternal(py_obj, cstart, cstop, &py_start, &py_stop, &py_slice, has_cstart, has_cstop, wraparound); + py_res = __Pyx_PyObject_GetSliceInternal(py_obj, cstart, cstop, LIF(_start, py_start), LIF(_stop, py_stop), LIF(_slice, py_slice), has_cstart, has_cstop, wraparound); +{{else}} + PyObject* py_value = HPY_LEGACY_OBJECT_AS(value); + int res = __Pyx_PyObject_SetSliceInternal(py_obj, py_value, cstart, cstop, LIF(_start, py_start), LIF(_stop, py_stop), LIF(_slice, py_slice), has_cstart, has_cstop, wraparound); + Py_DECREF(py_value); +{{endif}} Py_DECREF(py_obj); - *_start = HPY_LEGACY_OBJECT_FROM(py_start); - *_stop = HPY_LEGACY_OBJECT_FROM(py_stop); - *_slice = HPY_LEGACY_OBJECT_FROM(py_slice); - Py_XDECREF(py_start); - Py_XDECREF(py_stop); - Py_XDECREF(py_slice); +#undef LIF + if (_start) { + *_start = HPY_LEGACY_OBJECT_FROM(py_start); + Py_XDECREF(py_start); + } + if (_stop) { + *_stop = HPY_LEGACY_OBJECT_FROM(py_stop); + Py_XDECREF(py_stop); + } + if (_slice) { + *_slice = HPY_LEGACY_OBJECT_FROM(py_slice); + Py_XDECREF(py_slice); + } + +{{if access == 'Get'}} res = HPY_LEGACY_OBJECT_FROM(py_res); Py_XDECREF(py_res); +{{endif}} return res; #else +{{if access == 'Get'}} return __Pyx_PyObject_GetSliceInternal(obj, cstart, cstop, _start, _stop, _slice, has_cstart, has_cstop, wraparound); +{{else}} + return __Pyx_PyObject_SetSliceInternal(obj, value, cstart, cstop, _start, _stop, _slice, has_cstart, has_cstop, wraparound); +{{endif}} #endif } From 176e0d6511860be905680219f0f028b29323eabf Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Fri, 22 Dec 2023 17:35:08 +0100 Subject: [PATCH 262/286] Try to improve HPyFloat_CheckExact for CPython ABI mode --- Cython/Utility/HPyUtils.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 497f256d6bd..aaa61371aad 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -73,7 +73,7 @@ //Type Checks #define LONG_CHECK(l) HPyLong_Check(HPY_CONTEXT_CNAME, l) #define LONG_CHECK_EXACT(l) HPyLong_Check(HPY_CONTEXT_CNAME, l) - #define FLOAT_CHECK_EXACT(f) HPyFloat_Check(HPY_CONTEXT_CNAME, f) + #define FLOAT_CHECK_EXACT(f) HPyFloat_CheckExact(HPY_CONTEXT_CNAME, f) #define UNICODE_CHECK(u) HPyUnicode_Check(HPY_CONTEXT_CNAME, u) #define DICT_CHECK(o) HPyDict_Check(HPY_CONTEXT_CNAME, o) #define DICT_CHECK_EXACT(o) HPyDict_Check(HPY_CONTEXT_CNAME, o) @@ -470,6 +470,15 @@ static CYTHON_INLINE HPy HPyField_XLoad(HPyContext *ctx, HPy h_item, HPyField fi } } +static inline int +HPyFloat_CheckExact(HPyContext *ctx, HPy obj) +{ + HPy tp = HPy_Type(ctx, obj); + int res = HPy_Is(ctx, ctx->h_FloatType, tp); + HPy_Close(ctx, tp); + return res; +} + static inline HPy HPySlice_New(HPyContext *ctx, HPy start, HPy stop, HPy step) { From 3eaae427b15a609e18b31008cf1ffa35a9def16d Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Wed, 27 Dec 2023 14:21:25 +0100 Subject: [PATCH 263/286] Fix: LoadGlobalNode did not overwrite TempNode.release --- Cython/Compiler/ExprNodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 56201584a07..43e686f9ab8 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -3634,7 +3634,7 @@ def allocate(self, code, needs_decl=False): def release(self, code): if self.var_name in code.globalstate.const_cname_array: code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % self.temp_cname) - super(self.__class__, self).release(code) + super(LoadGlobalNode, self).release(code) class RawCNameExprNode(ExprNode): subexprs = [] From c1c781bba0a13fe66cc6ce248b1e4a7e98597589 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Wed, 27 Dec 2023 17:20:41 +0100 Subject: [PATCH 264/286] Introduce SEQUENCE_GET_SLICE and HPy_GetSlice --- Cython/Utility/HPyUtils.c | 46 +++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index aaa61371aad..14211973a18 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -130,6 +130,7 @@ //Sequence Type #define SEQUENCE_GET_ITEM(h, pos) HPy_GetItem_i(HPY_CONTEXT_CNAME, h, pos) #define SEQUENCE_SET_ITEM(h, pos, o) HPy_SetItem_i(HPY_CONTEXT_CNAME, h, pos, o) + #define SEQUENCE_GET_SLICE(h, i1, i2) HPy_GetSlice(HPY_CONTEXT_CNAME, h, (i1), (i2)) //Tuple Type #define TUPLE_CREATE_EMPTY() HPyTuple_FromArray(HPY_CONTEXT_CNAME, NULL, 0) @@ -321,6 +322,7 @@ //Sequence Type #define SEQUENCE_GET_ITEM(h, pos) __Pyx_PySequence_ITEM(h, pos) #define SEQUENCE_SET_ITEM(h, pos, o) PySequence_SetItem(h, pos, o) + #define SEQUENCE_GET_SLICE(h, i1, i2) PySequence_GetSlice(h, (i1), (i2)) //Tuple Type #define TUPLE_CREATE_EMPTY() PyTuple_New(0) @@ -482,16 +484,42 @@ HPyFloat_CheckExact(HPyContext *ctx, HPy obj) static inline HPy HPySlice_New(HPyContext *ctx, HPy start, HPy stop, HPy step) { + HPy args[3] = {start, stop, step}; + return HPy_Call(ctx, ctx->h_SliceType, args, 3, HPy_NULL); +} + +static inline HPy +HPy_GetSlice(HPyContext *ctx, HPy h, HPy_ssize_t i1, HPy_ssize_t i2) +{ + HPy start, stop, step, slice; HPy res; - PyObject *py_start = HPy_AsPyObject(ctx, start); - PyObject *py_stop = HPy_AsPyObject(ctx, stop); - PyObject *py_step = HPy_AsPyObject(ctx, step); - PyObject *py_res = PySlice_New(py_start, py_stop, py_step); - Py_XDECREF(py_start); - Py_XDECREF(py_stop); - Py_XDECREF(py_step); - res = HPy_FromPyObject(ctx, py_res); - Py_XDECREF(py_res); + + start = HPyLong_FromSsize_t(ctx, i1); + if (HPy_IsNull(start)) { + return HPy_NULL; + } + + stop = HPyLong_FromSsize_t(ctx, i2); + if (HPy_IsNull(stop)) { + step = HPy_NULL; + goto finish; + } + + step = HPyLong_FromSsize_t(ctx, 1); + if (HPy_IsNull(step)) { + goto finish; + } + + slice = HPySlice_New(ctx, start, stop, step); + if (HPy_IsNull(slice)) { + goto finish; + } + res = HPy_GetItem(ctx, h, slice); + HPy_Close(ctx, slice); +finish: + HPy_Close(ctx, start); + HPy_Close(ctx, stop); + HPy_Close(ctx, step); return res; } From f426eeefdeb0d3174d0bc7d9a17d40a0c250c838 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Wed, 27 Dec 2023 18:37:10 +0100 Subject: [PATCH 265/286] Use SEQUENCE_GET_SLICE; fixes memleak --- Cython/Compiler/ExprNodes.py | 4 ++-- Cython/Utility/ObjectHandling.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 43e686f9ab8..21d1dcbfe12 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -5661,9 +5661,9 @@ def generate_result_code(self, code): TempitaUtilityCode.load_cached("SliceTupleAndList", "ObjectHandling.c")) cfunc = '__Pyx_PyTuple_GetSlice' else: - cfunc = 'PySequence_GetSlice' + cfunc = 'SEQUENCE_GET_SLICE' code.putln( - "%s = HPY_LEGACY_OBJECT_FROM(%s(HPY_LEGACY_OBJECT_AS(%s), %s, %s)); %s" % ( + "%s = %s(%s, %s, %s); %s" % ( result, cfunc, self.base.py_result(), diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 6991f27ffa8..29271d585d9 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -952,8 +952,8 @@ __Pyx_PyList_FromArray(PyObject *const *src, Py_ssize_t n) static CYTHON_INLINE PyObject* __Pyx_PyList_GetSlice(PyObject* src, Py_ssize_t start, Py_ssize_t stop); static CYTHON_INLINE PyObject* __Pyx_PyTuple_GetSlice(PyObject* src, Py_ssize_t start, Py_ssize_t stop); #else -#define __Pyx_PyList_GetSlice(seq, start, stop) PySequence_GetSlice(seq, start, stop) -#define __Pyx_PyTuple_GetSlice(seq, start, stop) PySequence_GetSlice(seq, start, stop) +#define __Pyx_PyList_GetSlice SEQUENCE_GET_SLICE +#define __Pyx_PyTuple_GetSlice SEQUENCE_GET_SLICE #endif /////////////// SliceTupleAndList /////////////// From 9b9b18e880193009b7142f26b4e302ebf60ac4c5 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Wed, 27 Dec 2023 18:48:42 +0100 Subject: [PATCH 266/286] Add missing return stmt --- Cython/Utility/CythonFunction.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index f961f80aad7..034609435c9 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -650,7 +650,7 @@ __Pyx_CyFunction_get_is_coroutine(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObje #if CYTHON_USING_HPY return load_is_coroutine_temp; #else - __Pyx_NewRef(load_is_coroutine_temp); + return __Pyx_NewRef(load_is_coroutine_temp); #endif } ignore: @@ -661,7 +661,7 @@ __Pyx_CyFunction_get_is_coroutine(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObje #if CYTHON_USING_HPY return PYOBJECT_FIELD_LOAD(op, struct_op->func_is_coroutine); #else - __Pyx_NewRef(struct_op->func_is_coroutine); + return __Pyx_NewRef(struct_op->func_is_coroutine); #endif } From 9500157901ef792f7154d967534c3f807cd89eac Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Wed, 27 Dec 2023 18:49:14 +0100 Subject: [PATCH 267/286] Fix operator precedence issues with expanded macros --- Cython/Utility/HPyUtils.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 14211973a18..4bd084ebfe7 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -43,19 +43,19 @@ #define API_NULL_VALUE HPy_NULL #define API_DEFAULT_VALUE HPy_NULL #define API_IS_NULL(h) HPy_IsNull(h) - #define API_IS_NOT_NULL(h) !HPy_IsNull(h) - #define API_NONE_VALUE HPY_CONTEXT_CNAME->h_None + #define API_IS_NOT_NULL(h) (!HPy_IsNull(h)) + #define API_NONE_VALUE (HPY_CONTEXT_CNAME->h_None) #define API_ASSIGN_NONE HPy_Dup(HPY_CONTEXT_CNAME, HPY_CONTEXT_CNAME->h_None) //Boolean Values and Functions - #define API_TRUE HPY_CONTEXT_CNAME->h_True - #define API_FALSE HPY_CONTEXT_CNAME->h_False + #define API_TRUE (HPY_CONTEXT_CNAME->h_True) + #define API_FALSE (HPY_CONTEXT_CNAME->h_False) #define API_IS_TRUE(h) HPy_IsTrue(HPY_CONTEXT_CNAME, h) - #define API_IS_FALSE(h) !HPy_IsTrue(HPY_CONTEXT_CNAME, h) + #define API_IS_FALSE(h) (!HPy_IsTrue(HPY_CONTEXT_CNAME, h)) //General Methods #define API_IS_EQUAL(a, b) HPy_Is(HPY_CONTEXT_CNAME, a, b) - #define API_IS_NOT_EQUAL(a, b) !HPy_Is(HPY_CONTEXT_CNAME, a, b) + #define API_IS_NOT_EQUAL(a, b) (!HPy_Is(HPY_CONTEXT_CNAME, a, b)) #define API_RICH_COMPARE(h1, h2, op) HPy_RichCompare(HPY_CONTEXT_CNAME, h1, h2, op) #define API_RICH_COMPARE_BOOL(h1, h2, op) HPy_RichCompareBool(HPY_CONTEXT_CNAME, h1, h2, op) @@ -232,7 +232,7 @@ //NULL/None Values and functions #define API_NULL_VALUE NULL - #define API_DEFAULT_VALUE 0 + #define API_DEFAULT_VALUE (0) #define API_IS_NULL(h) !h #define API_IS_NOT_NULL(h) h #define API_NONE_VALUE Py_None @@ -242,11 +242,11 @@ #define API_TRUE Py_True #define API_FALSE Py_False #define API_IS_TRUE(h) PyObject_IsTrue(h) - #define API_IS_FALSE(h) !PyObject_Not(h) + #define API_IS_FALSE(h) (!PyObject_Not(h)) //General Methods - #define API_IS_EQUAL(a, b) a==b - #define API_IS_NOT_EQUAL(a, b) a!=b + #define API_IS_EQUAL(a, b) (a==b) + #define API_IS_NOT_EQUAL(a, b) (a!=b) #define API_RICH_COMPARE(h1, h2, op) PyObject_RichCompare(h1, h2, op) #define API_RICH_COMPARE_BOOL(h1, h2, op) PyObject_RichCompareBool(h1, h2, op) From 0741ddcd14b3d5180a38bedc1630468098b06994 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Fri, 29 Dec 2023 09:23:54 +0100 Subject: [PATCH 268/286] Also define __pyx_vectorcallfunc if CYTHON_METH_FASTCALL --- Cython/Utility/ModuleSetupCode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index f4ac50e5978..93b08284aa7 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -963,7 +963,7 @@ class __Pyx_FakeReference { #define __Pyx_PyCFunction_FastCallWithKeywords PyCFunctionWithKeywords #endif -#if CYTHON_VECTORCALL +#if CYTHON_VECTORCALL || CYTHON_METH_FASTCALL #define __pyx_vectorcallfunc vectorcallfunc #define __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET PY_VECTORCALL_ARGUMENTS_OFFSET #define __Pyx_PyVectorcall_NARGS(n) PyVectorcall_NARGS((size_t)(n)) From 97ad0cdf9e5a29a8c2054bf927f51f6b5dee0170 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Fri, 29 Dec 2023 12:21:57 +0100 Subject: [PATCH 269/286] Fix compilation warnings --- Cython/Utility/CythonFunction.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 034609435c9..32a60d0acad 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -689,7 +689,7 @@ __Pyx_CyFunction_get_is_coroutine(HPY_CONTEXT_FIRST_ARG_DEF __pyx_CyFunctionObje // return NULL; //} -#if CYTHON_COMPILING_IN_LIMITED_API +#if CYTHON_COMPILING_IN_LIMITED_API && !CYTHON_USING_HPY static PyObject * __Pyx_CyFunction_get_module(__pyx_CyFunctionObject *op, void *context) { CYTHON_UNUSED_VAR(context); @@ -723,7 +723,7 @@ static PyGetSetDef __pyx_CyFunction_getsets[] = { {"__annotations__", (getter)__Pyx_CyFunction_get_annotations, (setter)__Pyx_CyFunction_set_annotations, 0, 0}, {"_is_coroutine", (getter)__Pyx_CyFunction_get_is_coroutine, 0, 0, 0}, // {"__signature__", (getter)__Pyx_CyFunction_get_signature, 0, 0, 0}, -#if CYTHON_COMPILING_IN_LIMITED_API +#if CYTHON_COMPILING_IN_LIMITED_API && !CYTHON_USING_HPY {"__module__", (getter)__Pyx_CyFunction_get_module, (setter)__Pyx_CyFunction_set_module, 0, 0}, #endif {0, 0, 0, 0, 0} @@ -1293,7 +1293,7 @@ static HPy __Pyx_CyFunction_hpycall_NOARGS_impl(HPyContext *HPY_CONTEXT_CNAME, H def->meth.name, nargs); return HPy_NULL; } - HPyFunc_noargs func_noargs = def->meth.impl; + HPyFunc_noargs func_noargs = (HPyFunc_noargs) def->meth.impl; result = func_noargs(HPY_CONTEXT_CNAME, self); if (self_needs_close) { HPy_Close(HPY_CONTEXT_CNAME, self); @@ -1323,7 +1323,7 @@ static HPy __Pyx_CyFunction_hpycall_KEYWORDS_impl(HPyContext *HPY_CONTEXT_CNAME, return HPy_NULL; } - HPyFunc_keywords func_keywords = def->meth.impl; + HPyFunc_keywords func_keywords = (HPyFunc_keywords) def->meth.impl; result = func_keywords(HPY_CONTEXT_CNAME, self, args, nargs, kwnames); if (self_needs_close) { HPy_Close(HPY_CONTEXT_CNAME, self); From 0b34668eb843b31d997519c5489a5788b892f0fc Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Fri, 29 Dec 2023 12:25:00 +0100 Subject: [PATCH 270/286] Fix: wrong usage of HPy_Call --- Cython/Utility/Exceptions.c | 2 +- Cython/Utility/HPyUtils.c | 4 +++- Cython/Utility/TypeConversion.c | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Cython/Utility/Exceptions.c b/Cython/Utility/Exceptions.c index 889c13c8294..0b5b154e73a 100644 --- a/Cython/Utility/Exceptions.c +++ b/Cython/Utility/Exceptions.c @@ -852,7 +852,7 @@ static PYOBJECT_TYPE __Pyx_PyCode_Replace_For_AddTraceback(HPY_CONTEXT_FIRST_ARG if (likely(API_IS_NOT_NULL(replace))) { PYOBJECT_TYPE result; PYOBJECT_TYPE temp_empty_tuple = PYOBJECT_GLOBAL_LOAD($empty_tuple); - result = API_CALL_FUNC(replace, &temp_empty_tuple, 0, scratch_dict); + result = API_CALL_TUPLE_DICT(replace, temp_empty_tuple, scratch_dict); PYOBJECT_CLOSEREF(replace); PYOBJECT_CLOSEREF(temp_empty_tuple); return result; diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 4bd084ebfe7..153b1bbd87c 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -61,6 +61,7 @@ //API Call Macros #define API_CALL_FUNC(callable, args, nargs, kwnames) HPy_Call(HPY_CONTEXT_CNAME, callable, args, nargs, kwnames) + #define API_CALL_TUPLE_DICT(callable, args, kw) HPy_CallTupleDict(HPY_CONTEXT_CNAME, callable, args, kw) //Type Objects #define API_LONG_TYPE HPY_CONTEXT_CNAME->h_LongType @@ -251,7 +252,8 @@ #define API_RICH_COMPARE_BOOL(h1, h2, op) PyObject_RichCompareBool(h1, h2, op) //API Call Macros - #define API_CALL_FUNC(callable, args, nargs, kwnames) PyObject_Call(callable, args, kwnames) + #define API_CALL_FUNC(callable, args, nargs, kwnames) PyObject_Vectorcall(callable, args, nargs, kwnames) + #define API_CALL_TUPLE_DICT(callable, args, kw) PyObject_Call(callable, args, kw) //Type Objects #define API_LONG_TYPE PyLong_Type diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index cfff9f8931d..8541edaeacc 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -747,7 +747,7 @@ static CYTHON_INLINE PYOBJECT_TYPE {{TO_PY_FUNCTION}}(HPY_CONTEXT_FIRST_ARG_DEF if (API_IS_NULL(kwds)) goto limited_bad; if (DICT_SET_ITEM_STR(kwds, "signed", __Pyx_hNewRef(API_TRUE))) goto limited_bad; } - result = API_CALL_FUNC(from_bytes, &arg_tuple, 2, kwds); + result = API_CALL_TUPLE_DICT(from_bytes, arg_tuple, kwds); #else order_str = PyUnicode_FromString(little ? "little" : "big"); if (!order_str) goto limited_bad; From 65e8f54d85676e6b210a61ca79ec897716d5bdd4 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 8 Jan 2024 15:28:50 +0100 Subject: [PATCH 271/286] Fixed segfaults on CPython ABI --- Cython/Compiler/ExprNodes.py | 6 +++++- Cython/Compiler/UtilNodes.py | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 21d1dcbfe12..1a187c5dc90 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -2619,7 +2619,11 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, if rhs.result_as(self.ctype()) in code.globalstate.const_cname_array: code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (load_rhs, rhs.result_as(self.ctype()))) else: - code.putln("%s = %s;" % (load_rhs, rhs.result_as(self.ctype()))) + code.putln("#if CYTHON_USING_HPY") + code.putln("%s = PYOBJECT_NEWREF(%s);" % (load_rhs, rhs.result_as(self.ctype()))) + code.putln("#else") + code.putln("%s = PYOBJECT_NEWREF(%s);" % (load_rhs, rhs.result_as(self.ctype()))) + code.putln("#endif") if entry.is_cglobal: self.generate_decref_set(code, load_rhs) else: diff --git a/Cython/Compiler/UtilNodes.py b/Cython/Compiler/UtilNodes.py index bca91b15f12..1e1058586ab 100644 --- a/Cython/Compiler/UtilNodes.py +++ b/Cython/Compiler/UtilNodes.py @@ -241,7 +241,11 @@ def setup_temp_expr(self, code): self.temp_expression.make_owned_reference(code) self.temp = code.funcstate.allocate_temp( self.temp_type, manage_ref=True) + code.putln("#if CYTHON_USING_HPY") + code.putln("%s = PYOBJECT_NEWREF(%s);" % (self.temp, self.temp_expression.result())) + code.putln("#else") code.putln("%s = %s;" % (self.temp, self.temp_expression.result())) + code.putln("#endif") self.temp_expression.generate_disposal_code(code) self.temp_expression.free_temps(code) self.lazy_temp.result_code = self.temp From 16dd20c4d72824aaceb3f9ce78489d186fb5d449 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Tue, 16 Jan 2024 14:45:01 +0100 Subject: [PATCH 272/286] Prefer SEQUENCE_SET_SLICE over __Pyx_PyObject_SetSlice if possible --- Cython/Compiler/ExprNodes.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 1a187c5dc90..6cf257171bb 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -5682,14 +5682,19 @@ def generate_assignment_code(self, rhs, code, overloaded_assignment=False, if self.type.is_pyobject: code.globalstate.use_utility_code(self.set_slice_utility_code) has_c_start, has_c_stop, c_start, c_stop, py_start, py_stop, py_slice = self.get_slice_config() - code.put_error_if_neg(self.pos, - "__Pyx_PyObject_SetSlice(HPY_CONTEXT_FIRST_ARG_CALL %s, %s, %s, %s, %s, %s, %s, %d, %d, %d)" % ( - self.base.py_result(), - rhs.py_result(), - c_start, c_stop, - py_start, py_stop, py_slice, - has_c_start, has_c_stop, - bool(code.globalstate.directives['wraparound']))) + if py_start == 'NULL' and py_stop == 'NULL' and py_slice == 'NULL': + code.put_error_if_neg(self.pos, + "SEQUENCE_SET_SLICE(%s, %s, %s, %s)" % ( + self.base.py_result(), c_start, c_stop, rhs.py_result())) + else: + code.put_error_if_neg(self.pos, + "__Pyx_PyObject_SetSlice(HPY_CONTEXT_FIRST_ARG_CALL %s, %s, %s, %s, %s, %s, %s, %d, %d, %d)" % ( + self.base.py_result(), + rhs.py_result(), + c_start, c_stop, + py_start, py_stop, py_slice, + has_c_start, has_c_stop, + bool(code.globalstate.directives['wraparound']))) else: start_offset = self.start_code() if self.start else '0' if rhs.type.is_array: From c715ca83b834d8774814942b44691016f8ee7628 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Tue, 16 Jan 2024 14:46:28 +0100 Subject: [PATCH 273/286] Use HPy_GetItem_i for TUPLE_GET_ITEM(_SAFE) --- Cython/Utility/HPyUtils.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 153b1bbd87c..8aa5829a696 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -135,8 +135,8 @@ //Tuple Type #define TUPLE_CREATE_EMPTY() HPyTuple_FromArray(HPY_CONTEXT_CNAME, NULL, 0) - #define TUPLE_GET_ITEM(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, PYOBJECT_LONG_FROM_LONG(pos)) - #define TUPLE_GET_ITEM_SAFE(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, PYOBJECT_LONG_FROM_LONG(pos)) + #define TUPLE_GET_ITEM(h, pos) HPy_GetItem_i(HPY_CONTEXT_CNAME, h, pos) + #define TUPLE_GET_ITEM_SAFE(h, pos) HPy_GetItem_i(HPY_CONTEXT_CNAME, h, pos) #define TUPLE_GET_SIZE(h) HPy_Length(HPY_CONTEXT_CNAME, h) #define TUPLE_GET_SIZE_SAFE(h) HPy_Length(HPY_CONTEXT_CNAME, h) #define TUPLE_BUILDER_TYPE HPyTupleBuilder From aeedf48657c73fc1f69ee1810df61af9dd863c19 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Tue, 16 Jan 2024 14:47:43 +0100 Subject: [PATCH 274/286] Merge decl of __Pyx__PyObject_PopNewIndex --- Cython/Utility/Optimize.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Cython/Utility/Optimize.c b/Cython/Utility/Optimize.c index b438f2e0f81..5394502223b 100644 --- a/Cython/Utility/Optimize.c +++ b/Cython/Utility/Optimize.c @@ -175,11 +175,7 @@ static PyObject* __Pyx__PyList_PopIndex(PyObject* L, PyObject* py_ix, Py_ssize_t /////////////// pop_index /////////////// //@requires: ObjectHandling.c::PyObjectCallMethod1 -#if CYTHON_USING_HPY -static PYOBJECT_TYPE __Pyx__PyObject_PopNewIndex(HPyContext *HPY_CONTEXT_CNAME, PYOBJECT_TYPE L, PYOBJECT_TYPE py_ix) { -#else -static PYOBJECT_TYPE __Pyx__PyObject_PopNewIndex(PYOBJECT_TYPE L, PYOBJECT_TYPE py_ix) { -#endif +static PYOBJECT_TYPE __Pyx__PyObject_PopNewIndex(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE L, PYOBJECT_TYPE py_ix) { PYOBJECT_TYPE r; if (unlikely(API_IS_NULL(py_ix))) return API_NULL_VALUE; r = __Pyx__PyObject_PopIndex(HPY_CONTEXT_FIRST_ARG_CALL L, py_ix); From 0622cc0b9a6639fc8aebc6c82a30ef48e6be9d7e Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Tue, 16 Jan 2024 14:48:59 +0100 Subject: [PATCH 275/286] Use HPy_GetItem_i for LIST_GET_ITEM --- Cython/Utility/HPyUtils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 8aa5829a696..590e941d22d 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -148,7 +148,7 @@ //List Type #define LIST_CREATE_EMPTY() HPyList_New(HPY_CONTEXT_CNAME, 0) #define LIST_NEW(i) HPyList_New(HPY_CONTEXT_CNAME, i) - #define LIST_GET_ITEM(h, pos) HPy_GetItem(HPY_CONTEXT_CNAME, h, PYOBJECT_LONG_FROM_LONG(pos)) + #define LIST_GET_ITEM(h, pos) HPy_GetItem_i(HPY_CONTEXT_CNAME, h, pos) #define LIST_GET_SIZE(h) HPy_Length(HPY_CONTEXT_CNAME, h) #define LIST_GET_SIZE_SAFE(h) HPy_Length(HPY_CONTEXT_CNAME, h) #define LIST_APPEND(list, h) HPyList_Append(HPY_CONTEXT_CNAME, list, h) From 804bc69cd3ccd7ce2967fb13ebc70b54a7267b14 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Tue, 16 Jan 2024 14:49:32 +0100 Subject: [PATCH 276/286] Introduce SEQUENCE_(GET/SET)_SLICE --- Cython/Utility/HPyUtils.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 590e941d22d..d29f40b320c 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -132,6 +132,8 @@ #define SEQUENCE_GET_ITEM(h, pos) HPy_GetItem_i(HPY_CONTEXT_CNAME, h, pos) #define SEQUENCE_SET_ITEM(h, pos, o) HPy_SetItem_i(HPY_CONTEXT_CNAME, h, pos, o) #define SEQUENCE_GET_SLICE(h, i1, i2) HPy_GetSlice(HPY_CONTEXT_CNAME, h, (i1), (i2)) + #define SEQUENCE_SET_SLICE(h, i1, i2, v) HPy_SetSlice(HPY_CONTEXT_CNAME, (h), (i1), (i2), (v)) + #define SEQUENCE_DEL_SLICE(h, i1, i2) HPy_DelSlice(HPY_CONTEXT_CNAME, (h), (i1), (i2)) //Tuple Type #define TUPLE_CREATE_EMPTY() HPyTuple_FromArray(HPY_CONTEXT_CNAME, NULL, 0) @@ -325,6 +327,8 @@ #define SEQUENCE_GET_ITEM(h, pos) __Pyx_PySequence_ITEM(h, pos) #define SEQUENCE_SET_ITEM(h, pos, o) PySequence_SetItem(h, pos, o) #define SEQUENCE_GET_SLICE(h, i1, i2) PySequence_GetSlice(h, (i1), (i2)) + #define SEQUENCE_SET_SLICE(h, i1, i2, v) PySequence_SetSlice((h), (i1), (i2), (v)) + #define SEQUENCE_DEL_SLICE(h, i1, i2) PySequence_DelSlice((h), (i1), (i2)) //Tuple Type #define TUPLE_CREATE_EMPTY() PyTuple_New(0) From a1fe213d39c424d1efeb64edffa7c9e7a1759919 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Tue, 16 Jan 2024 14:49:48 +0100 Subject: [PATCH 277/286] Use HPyList_Insert for LIST_INSERT --- Cython/Utility/HPyUtils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index d29f40b320c..cdf7a49de85 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -154,7 +154,7 @@ #define LIST_GET_SIZE(h) HPy_Length(HPY_CONTEXT_CNAME, h) #define LIST_GET_SIZE_SAFE(h) HPy_Length(HPY_CONTEXT_CNAME, h) #define LIST_APPEND(list, h) HPyList_Append(HPY_CONTEXT_CNAME, list, h) - #define LIST_INSERT(list, index, h) PyList_Insert(HPY_LEGACY_OBJECT_AS(list), index, HPY_LEGACY_OBJECT_AS(h)) + #define LIST_INSERT(list, index, h) HPyList_Insert(HPY_CONTEXT_CNAME, list, index, h) #define LIST_BUILDER_TYPE HPyListBuilder #define LIST_CREATE_START(target, builder, size) builder = HPyListBuilder_New(HPY_CONTEXT_CNAME, size) #define LIST_CREATE_ASSIGN(tuple, builder, index, item) HPyListBuilder_Set(HPY_CONTEXT_CNAME, builder, index, item) From 12b0a8c74f1d6f899fce9f101b7b70a621cce66a Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Tue, 16 Jan 2024 14:50:06 +0100 Subject: [PATCH 278/286] Remove helper funcs HPySlice_New and HPy_GetSlice --- Cython/Utility/HPyUtils.c | 42 --------------------------------------- 1 file changed, 42 deletions(-) diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index cdf7a49de85..007d67a09db 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -487,46 +487,4 @@ HPyFloat_CheckExact(HPyContext *ctx, HPy obj) return res; } -static inline HPy -HPySlice_New(HPyContext *ctx, HPy start, HPy stop, HPy step) -{ - HPy args[3] = {start, stop, step}; - return HPy_Call(ctx, ctx->h_SliceType, args, 3, HPy_NULL); -} - -static inline HPy -HPy_GetSlice(HPyContext *ctx, HPy h, HPy_ssize_t i1, HPy_ssize_t i2) -{ - HPy start, stop, step, slice; - HPy res; - - start = HPyLong_FromSsize_t(ctx, i1); - if (HPy_IsNull(start)) { - return HPy_NULL; - } - - stop = HPyLong_FromSsize_t(ctx, i2); - if (HPy_IsNull(stop)) { - step = HPy_NULL; - goto finish; - } - - step = HPyLong_FromSsize_t(ctx, 1); - if (HPy_IsNull(step)) { - goto finish; - } - - slice = HPySlice_New(ctx, start, stop, step); - if (HPy_IsNull(slice)) { - goto finish; - } - res = HPy_GetItem(ctx, h, slice); - HPy_Close(ctx, slice); -finish: - HPy_Close(ctx, start); - HPy_Close(ctx, stop); - HPy_Close(ctx, step); - return res; -} - #endif From d832babbe17cc81e36573c2bb41216cc78135a58 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Tue, 16 Jan 2024 14:50:23 +0100 Subject: [PATCH 279/286] Fix: use API_CALL_TUPLE_DICT for __Pyx_PyObject_Call --- Cython/Utility/ObjectHandling.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 29271d585d9..1318d334f23 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -2466,7 +2466,7 @@ static CYTHON_INLINE PyObject* __Pyx_tp_new_kwargs(PyObject* type_obj, PyObject* #if CYTHON_COMPILING_IN_CPYTHON && !CYTHON_USING_HPY static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw); /*proto*/ #else -#define __Pyx_PyObject_Call(func, arg, kw) API_CALL_FUNC(func, arg, 0, kw) +#define __Pyx_PyObject_Call(func, arg, kw) API_CALL_TUPLE_DICT(func, arg, kw) #endif /////////////// PyObjectCall /////////////// From 1199d428831c0d471c5259d04ce8a11046fd99f8 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Thu, 14 Dec 2023 14:47:11 +0100 Subject: [PATCH 280/286] Fixed most errors for fannkuch benchmark --- Cython/Compiler/ExprNodes.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 6cf257171bb..494eee9b1b9 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -5667,7 +5667,7 @@ def generate_result_code(self, code): else: cfunc = 'SEQUENCE_GET_SLICE' code.putln( - "%s = %s(%s, %s, %s); %s" % ( + "%s = HPY_LEGACY_OBJECT_FROM(%s(HPY_LEGACY_OBJECT_AS(%s), %s, %s)); %s" % ( result, cfunc, self.base.py_result(), @@ -5735,21 +5735,21 @@ def generate_deletion_code(self, code, ignore_nonexisting=False): self.free_subexpr_temps(code) def get_slice_config(self): - has_c_start, c_start, py_start = False, '0', 'NULL' + has_c_start, c_start, py_start = False, '0', 'API_NULL_VALUE' if self.start: has_c_start = not self.start.type.is_pyobject if has_c_start: c_start = self.start.result() else: py_start = '&%s' % self.start.py_result() - has_c_stop, c_stop, py_stop = False, '0', 'NULL' + has_c_stop, c_stop, py_stop = False, '0', 'API_NULL_VALUE' if self.stop: has_c_stop = not self.stop.type.is_pyobject if has_c_stop: c_stop = self.stop.result() else: py_stop = '&%s' % self.stop.py_result() - py_slice = self.slice and '&%s' % self.slice.py_result() or 'NULL' + py_slice = self.slice and '&%s' % self.slice.py_result() or 'API_NULL_VALUE' return (has_c_start, has_c_stop, c_start, c_stop, py_start, py_stop, py_slice) From cd7a502d37e6af576e9dbc38fa6cf6d6d591af9b Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Mon, 18 Dec 2023 16:38:54 +0100 Subject: [PATCH 281/286] Make coverage work, start work on float --- Cython/Compiler/Builtin.py | 2 +- Cython/Compiler/Code.py | 1 + Cython/Compiler/ExprNodes.py | 84 ++++++++++++++++++---------- Cython/Compiler/Nodes.py | 32 ++++++++--- Cython/Compiler/PyrexTypes.py | 6 +- Cython/Utility/HPyUtils.c | 14 ++++- Cython/Utility/ImportExport.c | 44 ++++++++------- Cython/Utility/ModuleSetupCode.c | 4 +- Cython/Utility/ObjectHandling.c | 7 ++- tests/run/hpy_coverage_benchmark.pyx | 33 +++++++++++ tests/run/hpy_float.pyx | 64 +++++++++++++++++++++ 11 files changed, 224 insertions(+), 67 deletions(-) create mode 100644 tests/run/hpy_coverage_benchmark.pyx create mode 100644 tests/run/hpy_float.pyx diff --git a/Cython/Compiler/Builtin.py b/Cython/Compiler/Builtin.py index 14c270b8daa..2a2976463bb 100644 --- a/Cython/Compiler/Builtin.py +++ b/Cython/Compiler/Builtin.py @@ -279,7 +279,7 @@ def declare_in_type(self, self_type): # resulting in unintuitive runtime behavior and segfaults. # ("bool", "&PyBool_Type", []), - ("int", "&PyLong_Type", []), + ("int", "API_LONG_TYPE_DEREF", []), ("float", "&PyFloat_Type", []), ("complex", "&PyComplex_Type", [BuiltinAttribute('cval', field_type_name = 'Py_complex'), diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 1631643bd00..5ed873028bb 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -1476,6 +1476,7 @@ def new_const_cname(self, prefix='', value=''): prefix = Naming.interned_prefixes[prefix] else: prefix = Naming.const_prefix + self.const_cname_array.append("%s%s" % (prefix, name_suffix)) return "%s%s" % (prefix, name_suffix) def get_cached_unbound_method(self, type_cname, method_name): diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 494eee9b1b9..7c8ead46e95 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -2873,10 +2873,17 @@ def generate_result_code(self, code): load_result.release(code) else: code.globalstate.use_utility_code(UtilityCode.load_cached("Import", "ImportExport.c")) + load_module_name = LoadGlobalNode(self.pos, self.module_name.py_result()) + load_name_list = LoadGlobalNode(self.pos, self.name_list.py_result() if self.name_list else '0') + load_module_name.allocate(code) + load_name_list.allocate(code) import_code = "__Pyx_Import(HPY_CONTEXT_FIRST_ARG_CALL %s, %s, %d)" % ( - self.module_name.py_result(), - self.name_list.py_result() if self.name_list else '0', + load_module_name.temp_cname, + load_name_list.temp_cname, self.level) + load_module_name.release(code) + load_name_list.release(code) + code.putln("%s = %s; %s" % ( self.result(), @@ -3070,7 +3077,7 @@ def generate_result_code(self, code): self.may_be_a_sequence = not sequence_type.is_builtin_type if self.may_be_a_sequence: code.putln( - "if (likely(PyList_CheckExact(%s)) || PyTuple_CheckExact(%s)) {" % ( + "if (likely(LIST_CHECK_EXACT(%s)) || TUPLE_CHECK_EXACT(%s)) {" % ( self.sequence.py_result(), self.sequence.py_result())) @@ -3096,15 +3103,18 @@ def generate_result_code(self, code): code.putln("--%s;" % self.counter_cname) # len -> last item else: init_value = '0' + temp_result = LoadGlobalNode(self.pos, self.sequence.py_result()) + temp_result.allocate(code) code.putln("#if !CYTHON_USING_HPY") code.putln("%s = __Pyx_NewRef(%s);" % ( - self.result(), + temp_result.temp_cname, self.sequence.py_result())) code.putln("#else") - code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % ( - self.result(), - self.sequence.py_result())) code.putln("#endif") + code.putln("%s = %s;" % (self.result(), temp_result.temp_cname)) + code.putln("#if CYTHON_USING_HPY") + temp_result.release(code) + code.putln("#endif") code.putln("%s = 0;" % self.counter_cname) if not is_builtin_sequence: @@ -3114,10 +3124,10 @@ def generate_result_code(self, code): code.putln("} else {") code.put("%s = -1; " % self.counter_cname) - code.putln("%s = PyObject_GetIter(%s); %s" % ( + code.putln("%s = HPY_LEGACY_OBJECT_FROM(PyObject_GetIter(HPY_LEGACY_OBJECT_AS(%s))); %s" % ( self.result(), self.sequence.py_result(), - code.error_goto_if_null(self.result(), self.pos))) + code.error_goto_if_null_object(self.result(), self.pos))) self.generate_gotref(code) # PyObject_GetIter() fails if "tp_iternext" is not set, but the check below @@ -3196,7 +3206,7 @@ def generate_iter_next_result_code(self, result_name, code): if self.may_be_a_sequence: code.putln("if (likely(!%s)) {" % self.iter_func_ptr) - code.putln("if (likely(PyList_CheckExact(%s))) {" % self.py_result()) + code.putln("if (likely(LIST_CHECK_EXACT(%s))) {" % self.py_result()) self.generate_next_sequence_item('List', result_name, code) code.putln("} else {") self.generate_next_sequence_item('Tuple', result_name, code) @@ -3209,7 +3219,7 @@ def generate_iter_next_result_code(self, result_name, code): result_name, self.iter_func_ptr, self.py_result())) - code.putln("if (unlikely(!%s)) {" % result_name) + code.putln("if (unlikely(API_IS_NULL(%s))) {" % result_name) code.putln("PyObject* exc_type = PyErr_Occurred();") code.putln("if (exc_type) {") code.putln("if (likely(__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) PyErr_Clear();") @@ -3625,18 +3635,19 @@ class LoadGlobalNode(TempNode): def __init__(self, pos, var_name): TempNode.__init__(self, pos, py_object_type) self.var_name = var_name + self.var_name_stripped = self.var_name.replace("CAPI_NEEDS_DEREFERENCE", "").replace("CAPI_IS_POINTER", "").strip() def allocate(self, code, needs_decl=False): super(self.__class__, self).allocate(code, False) if needs_decl: code.putln("PYOBJECT_TYPE %s;" % self.temp_cname) - if self.var_name in code.globalstate.const_cname_array: + if self.var_name_stripped in code.globalstate.const_cname_array: code.putln("%s = PYOBJECT_GLOBAL_LOAD(%s);" % (self.temp_cname, self.var_name)) else: code.putln("%s = %s;" % (self.temp_cname, self.var_name)) - def release(self, code): - if self.var_name in code.globalstate.const_cname_array: + def release_global(self, code): + if self.var_name_stripped in code.globalstate.const_cname_array: code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % self.temp_cname) super(LoadGlobalNode, self).release(code) @@ -3733,7 +3744,7 @@ def generate_evaluation_code(self, code): code.mark_pos(self.pos) self.allocate_temp_result(code) code.globalstate.use_utility_code(UtilityCode.load_cached("JoinPyUnicode", "StringTools.c")) - code.putln('%s = __Pyx_PyUnicode_Join(%s, %d, %s, %s);' % ( + code.putln('%s = __Pyx_PyUnicode_Join(HPY_CONTEXT_FIRST_ARG_CALL %s, %d, %s, %s);' % ( self.result(), values_array, num_items, @@ -3747,7 +3758,7 @@ def generate_evaluation_code(self, code): code.putln("PyMem_Free(%s);" % values_array) code.funcstate.release_temp(values_array) - code.putln(code.error_goto_if_null(self.py_result(), self.pos)) + code.putln(code.error_goto_if_null_object(self.py_result(), self.pos)) self.generate_gotref(code) for node in self.values: @@ -3828,7 +3839,7 @@ def generate_result_code(self, code): if conversion_char: fn = self.find_conversion_func(conversion_char) assert fn is not None, "invalid conversion character found: '%s'" % conversion_char - value_result = '%s(%s)' % (fn, value_result) + value_result = '%s(HPY_LEGACY_OBJECT_AS(%s))' % (fn, value_result) code.globalstate.use_utility_code( UtilityCode.load_cached("PyObjectFormatAndDecref", "StringTools.c")) format_func += 'AndDecref' @@ -3839,12 +3850,16 @@ def generate_result_code(self, code): code.globalstate.use_utility_code( UtilityCode.load_cached("PyObjectFormatSimple", "StringTools.c")) - code.putln("%s = %s(%s, %s); %s" % ( + load_format_spec = LoadGlobalNode(self.pos, format_spec) + load_format_spec.allocate(code) + + code.putln("%s = HPY_LEGACY_OBJECT_FROM(%s(%s, HPY_LEGACY_OBJECT_AS(%s))); %s" % ( self.result(), format_func, value_result, - format_spec, - code.error_goto_if_null(self.result(), self.pos))) + load_format_spec.temp_cname, + code.error_goto_if_null_object(self.result(), self.pos))) + load_format_spec.release(code) self.generate_gotref(code) @@ -5646,15 +5661,18 @@ def generate_result_code(self, code): code.globalstate.use_utility_code(self.get_slice_utility_code) (has_c_start, has_c_stop, c_start, c_stop, py_start, py_stop, py_slice) = self.get_slice_config() + load_pyslice = LoadGlobalNode(self.pos, py_slice) + load_pyslice.allocate(code) code.putln( "%s = __Pyx_PyObject_GetSlice(HPY_CONTEXT_FIRST_ARG_CALL %s, %s, %s, %s, %s, %s, %d, %d, %d); %s" % ( result, self.base.py_result(), c_start, c_stop, - py_start, py_stop, py_slice, + py_start, py_stop, load_pyslice.temp_cname, has_c_start, has_c_stop, bool(code.globalstate.directives['wraparound']), - code.error_goto_if_null(result, self.pos))) + code.error_goto_if_null_object(result, self.pos))) + load_pyslice.release(code) else: if self.base.type is list_type: code.globalstate.use_utility_code( @@ -5741,15 +5759,15 @@ def get_slice_config(self): if has_c_start: c_start = self.start.result() else: - py_start = '&%s' % self.start.py_result() + py_start = 'CAPI_NEEDS_DEREFERENCE %s' % self.start.py_result() has_c_stop, c_stop, py_stop = False, '0', 'API_NULL_VALUE' if self.stop: has_c_stop = not self.stop.type.is_pyobject if has_c_stop: c_stop = self.stop.result() else: - py_stop = '&%s' % self.stop.py_result() - py_slice = self.slice and '&%s' % self.slice.py_result() or 'API_NULL_VALUE' + py_stop = 'CAPI_NEEDS_DEREFERENCE %s' % self.stop.py_result() + py_slice = self.slice and 'CAPI_NEEDS_DEREFERENCE %s' % self.slice.py_result() or 'API_NULL_VALUE' return (has_c_start, has_c_stop, c_start, c_stop, py_start, py_stop, py_slice) @@ -8503,9 +8521,9 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): code.putln('}') if mult_factor is not None and mult_factor.type.is_pyobject: - code.putln('{ PyObject* %s = PyNumber_InPlaceMultiply(%s, %s); %s' % ( + code.putln('{ PYOBJECT_TYPE %s = NUMBER_INPLACE_MULTIPLY(%s, %s); %s' % ( Naming.quick_temp_cname, target, mult_factor.py_result(), - code.error_goto_if_null(Naming.quick_temp_cname, self.pos) + code.error_goto_if_null_object(Naming.quick_temp_cname, self.pos) )) code.put_gotref(Naming.quick_temp_cname, py_object_type) code.put_decref(target, py_object_type) @@ -10500,7 +10518,7 @@ def generate_cyfunction_code(self, code): code.putln('__Pyx_CyFunction_SetDefaultsGetter(%s, %s);' % ( self.result(), def_node.defaults_getter.entry.pyfunc_cname)) if self.annotations_dict: - code.putln('__Pyx_CyFunction_SetAnnotationsDict(%s, %s);' % ( + code.putln('__Pyx_CyFunction_SetAnnotationsDict(HPY_CONTEXT_FIRST_ARG_CALL %s, %s);' % ( self.result(), self.annotations_dict.py_result())) @@ -12305,15 +12323,21 @@ def generate_result_code(self, code): function = self.hpy_operation_function(code) extra_args = ", API_NONE_VALUE" if self.operator == '**' else "" op1_result = self.operand1.py_result() if type1.is_pyobject else self.operand1.result() + load_op1 = LoadGlobalNode(self.pos, op1_result) + load_op2 = LoadGlobalNode(self.pos, op2_result) + load_op1.allocate(code) + load_op2.allocate(code) op2_result = self.operand2.py_result() if type2.is_pyobject else self.operand2.result() code.putln( "%s = %s(HPY_CONTEXT_FIRST_ARG_CALL %s, %s%s); %s" % ( self.result(), function, - op1_result, - op2_result, + load_op1.temp_cname, + load_op2.temp_cname, extra_args, code.error_goto_if_null_object(self.result(), self.pos))) + load_op1.release(code) + load_op2.release(code) code.putln("#endif") elif self.is_temp: # C++ overloaded operators with exception values are currently all diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 53898c8c08b..82fa4f60b7a 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -2449,14 +2449,14 @@ def generate_arg_type_test(self, arg, code): code.globalstate.use_utility_code( UtilityCode.load_cached("ArgTypeTest", "FunctionArguments.c")) typeptr_cname = arg.type.typeptr_cname - arg_code = "((PyObject *)%s)" % arg.entry.cname + arg_code = "(CAST_IF_CAPI(PyObject *)%s)" % arg.entry.cname exact = 0 if arg.type.is_builtin_type and arg.type.require_exact: # 2 is used to indicate that the type is from the annotation # and provide a little extra info on failure. exact = 2 if arg.type_from_annotation else 1 code.putln( - 'if (unlikely(!__Pyx_ArgTypeTest(%s, %s, %d, %s, %s))) %s' % ( + 'if (unlikely(!(__Pyx_ArgTypeTest(%s, %s, %d, %s, %s)))) %s' % ( arg_code, typeptr_cname, arg.accept_none, @@ -5239,12 +5239,19 @@ def generate_execution_code(self, code): self.dict.generate_evaluation_code(code) if self.orig_bases: # update __orig_bases__ if needed - code.putln("if (API_IS_NOT_EQUAL(%s, %s)) {" % (self.bases.result(), self.orig_bases.result())) + from .ExprNodes import LoadGlobalNode + load_bases = LoadGlobalNode(self.pos, self.bases.result()) + load_orig_bases = LoadGlobalNode(self.pos, self.orig_bases.result()) + load_bases.allocate(code) + load_orig_bases.allocate(code) + code.putln("if (API_IS_NOT_EQUAL(%s, %s)) {" % (load_bases.temp_cname, load_orig_bases.temp_cname)) code.putln( code.error_goto_if_neg('DICT_SET_ITEM_STR(%s, "__orig_bases__", %s)' % ( - self.dict.result(), self.orig_bases.result()), + self.dict.result(), load_orig_bases.temp_cname), self.pos )) + load_bases.release(code) + load_orig_bases.release(code) code.putln("}") self.orig_bases.generate_disposal_code(code) self.orig_bases.free_temps(code) @@ -9078,13 +9085,22 @@ def generate_execution_code(self, code): if self.interned_items: code.globalstate.use_utility_code( UtilityCode.load_cached("ImportFrom", "ImportExport.c")) + + from . import ExprNodes + for name, target, coerced_item in self.interned_items: + load_module_result = ExprNodes.LoadGlobalNode(self.pos, self.module.py_result()) + load_intern_id = ExprNodes.LoadGlobalNode(self.pos, code.intern_identifier(name)) + load_module_result.allocate(code) + load_intern_id.allocate(code) code.putln( - '%s = __Pyx_ImportFrom(%s, %s); %s' % ( + '%s = __Pyx_ImportFrom(HPY_CONTEXT_FIRST_ARG_CALL %s, %s); %s' % ( item_temp, - self.module.py_result(), - code.intern_identifier(name), - code.error_goto_if_null(item_temp, self.pos))) + load_module_result.temp_cname, + load_intern_id.temp_cname, + code.error_goto_if_null_object(item_temp, self.pos))) + load_module_result.release(code) + load_intern_id.release(code) code.put_gotref(item_temp, py_object_type) if coerced_item is None: target.generate_assignment_code(self.item, code) diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 47abac9da5a..9d18586bc16 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -1567,7 +1567,7 @@ def type_check_function(self, exact=True): elif type_name == 'frozenset': type_check = 'PyFrozenSet_Check' elif type_name == 'int': - type_check = 'PyLong_Check' + type_check = 'LONG_CHECK' elif type_name == "memoryview": # captialize doesn't catch the 'V' type_check = "PyMemoryView_Check" @@ -1584,12 +1584,12 @@ def type_test_code(self, arg, notnone=False, exact=True): type_check = self.type_check_function(exact=exact) check = 'likely(%s(%s))' % (type_check, arg) if not notnone: - check += '||((%s) == Py_None)' % arg + check += '|| API_IS_EQUAL(%s, API_NONE_VALUE)' % arg if self.name == 'basestring': name = '(PY_MAJOR_VERSION < 3 ? "basestring" : "str")' else: name = '"%s"' % self.name - return check + ' || __Pyx_RaiseUnexpectedTypeError(%s, %s)' % (name, arg) + return check + ' || __Pyx_RaiseUnexpectedTypeError(HPY_CONTEXT_FIRST_ARG_CALL %s, %s)' % (name, arg) def declaration_code(self, entity_code, for_display = 0, dll_linkage = None, pyrex = 0): diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index 007d67a09db..c902637d38a 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -65,6 +65,7 @@ //Type Objects #define API_LONG_TYPE HPY_CONTEXT_CNAME->h_LongType + #define API_LONG_TYPE_DEREF HPY_CONTEXT_CNAME->h_LongType #define API_SSIZE_T HPy_ssize_t #define API_STRING_TYPE HPY_CONTEXT_CNAME->h_UnicodeType #define API_STRING_TYPE_DEREF API_STRING_TYPE @@ -74,11 +75,13 @@ //Type Checks #define LONG_CHECK(l) HPyLong_Check(HPY_CONTEXT_CNAME, l) #define LONG_CHECK_EXACT(l) HPyLong_Check(HPY_CONTEXT_CNAME, l) - #define FLOAT_CHECK_EXACT(f) HPyFloat_CheckExact(HPY_CONTEXT_CNAME, f) + #define LONG_CHECKExact(l) HPyLong_Check(HPY_CONTEXT_CNAME, l) //TODO(HPy): Remove + #define FLOAT_CHECK_EXACT(f) HPyFloat_Check(HPY_CONTEXT_CNAME, f) #define UNICODE_CHECK(u) HPyUnicode_Check(HPY_CONTEXT_CNAME, u) #define DICT_CHECK(o) HPyDict_Check(HPY_CONTEXT_CNAME, o) #define DICT_CHECK_EXACT(o) HPyDict_Check(HPY_CONTEXT_CNAME, o) #define TUPLE_CHECK(o) HPyTuple_Check(HPY_CONTEXT_CNAME, o) + #define TUPLE_CHECK_EXACT(o) HPyTuple_Check(HPY_CONTEXT_CNAME, o) #define PYOBJECT_TYPE_CHECK(o, t) HPy_TypeCheck(HPY_CONTEXT_CNAME, o, t) #define LIST_CHECK(h) HPyList_Check(HPY_CONTEXT_CNAME, h) #define LIST_CHECK_EXACT(h) HPyList_Check(HPY_CONTEXT_CNAME, h) @@ -118,6 +121,9 @@ //Bytes Type - To + //Number Type + #define NUMBER_INPLACE_MULTIPLY(h1, h2) HPy_InPlaceMultiply(HPY_CONTEXT_CNAME, h1, h2) + //Dict Type #define DICT_NEW() HPyDict_New(HPY_CONTEXT_CNAME) #define DICT_COPY(o) HPyDict_Copy(HPY_CONTEXT_CNAME, o) @@ -259,6 +265,7 @@ //Type Objects #define API_LONG_TYPE PyLong_Type + #define API_LONG_TYPE_DEREF &PyLong_Type #define API_SSIZE_T Py_ssize_t #define API_STRING_TYPE PyString_Type #define API_STRING_TYPE_DEREF &PyString_Type @@ -268,11 +275,13 @@ //Number Type Checks #define LONG_CHECK(l) PyLong_Check(l) #define LONG_CHECK_EXACT(l) PyLong_CheckExact(l) + #define LONG_CHECKExact(l) PyLong_CheckExact(l) //TODO(HPy): Remove #define FLOAT_CHECK_EXACT(f) PyFloat_CheckExact(f) #define UNICODE_CHECK(u) PyUnicode_Check(u) #define DICT_CHECK(o) PyDict_Check(o) #define DICT_CHECK_EXACT(o) PyDict_CheckExact(o) #define TUPLE_CHECK(o) PyTuple_Check(o) + #define TUPLE_CHECK_EXACT(o) PyTuple_CheckExact(o) #define PYOBJECT_TYPE_CHECK(o, t) PyObject_TypeCheck(o, t) #define LIST_CHECK(h) PyList_Check(h) #define LIST_CHECK_EXACT(h) PyList_CheckExact(h) @@ -313,6 +322,9 @@ //Bytes Type - To + //Number Type + #define NUMBER_INPLACE_MULTIPLY(h1, h2) PyNumber_InPlaceMultiply(h1, h2) + //Dict Type #define DICT_NEW() PyDict_New() #define DICT_COPY(o) PyDict_Copy(o) diff --git a/Cython/Utility/ImportExport.c b/Cython/Utility/ImportExport.c index 2f22d8ea6ed..3f1b6a04fd3 100644 --- a/Cython/Utility/ImportExport.c +++ b/Cython/Utility/ImportExport.c @@ -71,7 +71,11 @@ static PYOBJECT_TYPE __Pyx_ImportDottedModule_WalkParts(HPY_CONTEXT_FIRST_ARG_DE part = SEQUENCE_GET_ITEM(parts_tuple, i); if (API_IS_NULL(part)) return API_NULL_VALUE; #endif +#if !CYTHON_USING_HPY submodule = __Pyx_PyObject_GetAttrStrNoError(HPY_CONTEXT_FIRST_ARG_CALL module, part); +#else + submodule = PYOBJECT_GET_ITEM(module, part); +#endif // We stop if the attribute isn't found, i.e. if submodule is NULL here. #if !(CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS) PYOBJECT_CLOSEREF(part); @@ -208,30 +212,32 @@ static PYOBJECT_TYPE __Pyx_Import(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE name, /////////////// ImportFrom.proto /////////////// -static PyObject* __Pyx_ImportFrom(PyObject* module, PyObject* name); /*proto*/ +static PYOBJECT_TYPE __Pyx_ImportFrom(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE module, PYOBJECT_TYPE name); /*proto*/ /////////////// ImportFrom /////////////// //@requires: ObjectHandling.c::PyObjectGetAttrStr -static PyObject* __Pyx_ImportFrom(PyObject* module, PyObject* name) { - PyObject* value = __Pyx_PyObject_GetAttrStr(module, name); - if (unlikely(!value) && PyErr_ExceptionMatches(PyExc_AttributeError)) { +static PYOBJECT_TYPE __Pyx_ImportFrom(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE module, PYOBJECT_TYPE name) { + PYOBJECT_TYPE value = __Pyx_PyObject_GetAttrStr(module, name); + if (unlikely(API_IS_NULL(value)) && PyErr_ExceptionMatches(PyExc_AttributeError)) { // 'name' may refer to a (sub-)module which has not finished initialization // yet, and may not be assigned as an attribute to its parent, so try // finding it by full name. const char* module_name_str = 0; - PyObject* module_name = 0; - PyObject* module_dot = 0; - PyObject* full_name = 0; + PYOBJECT_TYPE module_name = API_DEFAULT_VALUE; + PYOBJECT_TYPE module_dot = API_DEFAULT_VALUE; + PYOBJECT_TYPE full_name = API_DEFAULT_VALUE; PyErr_Clear(); - module_name_str = PyModule_GetName(module); + module_name_str = PyModule_GetName(HPY_LEGACY_OBJECT_AS(module)); if (unlikely(!module_name_str)) { goto modbad; } - module_name = PyUnicode_FromString(module_name_str); - if (unlikely(!module_name)) { goto modbad; } - module_dot = PyUnicode_Concat(module_name, PYUNICODE(".")); - if (unlikely(!module_dot)) { goto modbad; } - full_name = PyUnicode_Concat(module_dot, name); - if (unlikely(!full_name)) { goto modbad; } + module_name = PYOBJECT_UNICODE_FROM_STRING(module_name_str); + if (unlikely(API_IS_NULL(module_name))) { goto modbad; } + PYOBJECT_TYPE load_fullstop = PYOBJECT_GLOBAL_LOAD(PYUNICODE(".")); + module_dot = HPY_LEGACY_OBJECT_FROM(PyUnicode_Concat(HPY_LEGACY_OBJECT_AS(module_name), HPY_LEGACY_OBJECT_AS(load_fullstop))); + if (unlikely(API_IS_NULL(module_dot))) { goto modbad; } + PYOBJECT_GLOBAL_CLOSEREF(load_fullstop); + full_name = HPY_LEGACY_OBJECT_FROM(PyUnicode_Concat(HPY_LEGACY_OBJECT_AS(module_dot), HPY_LEGACY_OBJECT_AS(name))); + if (unlikely(API_IS_NULL(full_name))) { goto modbad; } #if CYTHON_COMPILING_IN_PYPY && PYPY_VERSION_NUM < 0x07030400 { PyObject *modules = PyImport_GetModuleDict(); @@ -240,15 +246,15 @@ static PyObject* __Pyx_ImportFrom(PyObject* module, PyObject* name) { value = PyObject_GetItem(modules, full_name); } #else - value = PyImport_GetModule(full_name); + value = HPY_LEGACY_OBJECT_FROM(PyImport_GetModule(HPY_LEGACY_OBJECT_AS(full_name))); #endif modbad: - Py_XDECREF(full_name); - Py_XDECREF(module_dot); - Py_XDECREF(module_name); + PYOBJECT_XCLOSEREF(full_name); + PYOBJECT_XCLOSEREF(module_dot); + PYOBJECT_XCLOSEREF(module_name); } - if (unlikely(!value)) { + if (unlikely(API_IS_NULL(value))) { PyErr_Format(PyExc_ImportError, "cannot import name %S", name); } return value; diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 93b08284aa7..0d92c3a9af4 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -1280,8 +1280,8 @@ static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStrWithError(PyObject *dict, #define __Pyx_PyUnicode_GET_LENGTH(o) PyUnicode_GET_LENGTH(o) #else // These all need exception checks for -1. - #define __Pyx_PyTuple_GET_SIZE(o) PyTuple_Size(o) - #define __Pyx_PyList_GET_SIZE(o) PyList_Size(o) + #define __Pyx_PyTuple_GET_SIZE(o) TUPLE_GET_SIZE(o) + #define __Pyx_PyList_GET_SIZE(o) LIST_GET_SIZE(o) #define __Pyx_PySet_GET_SIZE(o) PySet_Size(o) #define __Pyx_PyBytes_GET_SIZE(o) PyBytes_Size(o) #define __Pyx_PyByteArray_GET_SIZE(o) PyByteArray_Size(o) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 1318d334f23..e9e709e349b 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -3118,6 +3118,7 @@ static CYTHON_INLINE PyObject* __Pyx_PySequence_Multiply(PyObject *seq, Py_ssize typedef PYOBJECT_TYPE __Pyx_TypeName; #define __Pyx_FMT_TYPENAME "%U" static __Pyx_TypeName __Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_DEF PYTYPEOBJECT_TYPE tp); /*proto*/ +#define __Pyx_PyType_GetName_legacy(tp) ((tp)->tp_name) #define __Pyx_DECREF_TypeName(obj) PYOBJECT_XCLOSEREF(obj) #else typedef const char *__Pyx_TypeName; @@ -3151,14 +3152,14 @@ __Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_DEF PYTYPEOBJECT_TYPE tp) /////////////// RaiseUnexpectedTypeError.proto /////////////// -static int __Pyx_RaiseUnexpectedTypeError(const char *expected, PyObject *obj); /*proto*/ +static int __Pyx_RaiseUnexpectedTypeError(HPY_CONTEXT_FIRST_ARG_DEF const char *expected, PYOBJECT_TYPE obj); /*proto*/ /////////////// RaiseUnexpectedTypeError /////////////// static int -__Pyx_RaiseUnexpectedTypeError(const char *expected, PyObject *obj) +__Pyx_RaiseUnexpectedTypeError(HPY_CONTEXT_FIRST_ARG_DEF const char *expected, PYOBJECT_TYPE obj) { - __Pyx_TypeName obj_type_name = __Pyx_PyType_GetName(Py_TYPE(obj)); + __Pyx_TypeName obj_type_name = __Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_CALL GET_TYPE(obj)); PyErr_Format(PyExc_TypeError, "Expected %s, got " __Pyx_FMT_TYPENAME, expected, obj_type_name); __Pyx_DECREF_TypeName(obj_type_name); diff --git a/tests/run/hpy_coverage_benchmark.pyx b/tests/run/hpy_coverage_benchmark.pyx new file mode 100644 index 00000000000..229f4d92af9 --- /dev/null +++ b/tests/run/hpy_coverage_benchmark.pyx @@ -0,0 +1,33 @@ +""" +Benchmark coverage performance with a recursive fibonacci function. +""" + +import coverage +import pyperf + + +def fibonacci(n: int) -> int: + """ + >>> fibonacci(35) + 9227465 + """ + if n <= 1: + return n + return fibonacci(n - 1) + fibonacci(n - 2) + + +def bench_coverage(loops: int) -> None: + range_it = range(loops) + cov = coverage.Coverage() + cov.start() + t0 = pyperf.perf_counter() + for _ in range_it: + fibonacci(25) + cov.stop() + return pyperf.perf_counter() - t0 + + +if __name__ == "__main__": + runner = pyperf.Runner() + runner.metadata['description'] = "Benchmark coverage" + runner.bench_time_func('coverage', bench_coverage) diff --git a/tests/run/hpy_float.pyx b/tests/run/hpy_float.pyx new file mode 100644 index 00000000000..e83be47abb3 --- /dev/null +++ b/tests/run/hpy_float.pyx @@ -0,0 +1,64 @@ +""" +Artificial, floating point-heavy benchmark originally used by Factor. +""" +import pyperf + +from math import sin, cos, sqrt + + +POINTS = 100000 + + +class Point(object): + __slots__ = ('x', 'y', 'z') + + def __init__(self, i): + self.x = x = sin(i) + self.y = cos(i) * 3 + self.z = (x * x) / 2 + + def __repr__(self): + return "" % (self.x, self.y, self.z) + + def normalize(self): + x = self.x + y = self.y + z = self.z + norm = sqrt(x * x + y * y + z * z) + self.x /= norm + self.y /= norm + self.z /= norm + + def maximize(self, other): + self.x = self.x if self.x > other.x else other.x + self.y = self.y if self.y > other.y else other.y + self.z = self.z if self.z > other.z else other.z + return self + + +def maximize(points): + next = points[0] + for p in points[1:]: + next = next.maximize(p) + return next + + +def benchmark(n): + """ + >>> benchmark(5) + + """ + points = [None] * n + for i in range(n): + points[i] = Point(i) + for p in points: + p.normalize() + return maximize(points) + + +if __name__ == "__main__": + runner = pyperf.Runner() + runner.metadata['description'] = "Float benchmark" + + points = POINTS + runner.bench_func('float', benchmark, points) From d69c26f93e12117b52928dac1cdfa7543b0defc0 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 10 Jan 2024 10:18:31 +0100 Subject: [PATCH 282/286] Made float test work for HPy --- Cython/Compiler/ExprNodes.py | 37 +++++++++++++++++++++------------ Cython/Utility/Exceptions.c | 6 +++++- Cython/Utility/ObjectHandling.c | 11 ++++++---- 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 7c8ead46e95..8f9619b302f 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -3082,13 +3082,10 @@ def generate_result_code(self, code): self.sequence.py_result())) if is_builtin_sequence or self.may_be_a_sequence: - code.putln("#if !CYTHON_USING_HPY") - code.putln("%s = %s; __Pyx_INCREF(%s);" % ( + code.putln("%s = PYOBJECT_NEWREF(%s);" % ( self.result(), self.sequence.py_result(), - self.result(), )) - code.putln("#endif") self.counter_cname = code.funcstate.allocate_temp( PyrexTypes.c_py_ssize_t_type, manage_ref=False) if self.reversed: @@ -3105,16 +3102,11 @@ def generate_result_code(self, code): init_value = '0' temp_result = LoadGlobalNode(self.pos, self.sequence.py_result()) temp_result.allocate(code) - code.putln("#if !CYTHON_USING_HPY") - code.putln("%s = __Pyx_NewRef(%s);" % ( + code.putln("%s = PYOBJECT_NEWREF(%s);" % ( temp_result.temp_cname, self.sequence.py_result())) - code.putln("#else") - code.putln("#endif") code.putln("%s = %s;" % (self.result(), temp_result.temp_cname)) - code.putln("#if CYTHON_USING_HPY") - temp_result.release(code) - code.putln("#endif") + temp_result.release(code) code.putln("%s = 0;" % self.counter_cname) if not is_builtin_sequence: @@ -3214,11 +3206,18 @@ def generate_iter_next_result_code(self, result_name, code): code.put("} else ") code.putln("{") + code.putln("#if !CYTHON_USING_HPY") code.putln( "%s = %s(%s);" % ( result_name, self.iter_func_ptr, self.py_result())) + code.putln("#else") + code.putln( + "%s = HPY_LEGACY_OBJECT_FROM(PyIter_Next(HPY_LEGACY_OBJECT_AS(%s)));" % ( + result_name, + self.py_result())) + code.putln("#endif") code.putln("if (unlikely(API_IS_NULL(%s))) {" % result_name) code.putln("PyObject* exc_type = PyErr_Occurred();") code.putln("if (exc_type) {") @@ -3646,7 +3645,7 @@ def allocate(self, code, needs_decl=False): else: code.putln("%s = %s;" % (self.temp_cname, self.var_name)) - def release_global(self, code): + def release(self, code): if self.var_name_stripped in code.globalstate.const_cname_array: code.putln("PYOBJECT_GLOBAL_CLOSEREF(%s);" % self.temp_cname) super(LoadGlobalNode, self).release(code) @@ -5663,6 +5662,17 @@ def generate_result_code(self, code): py_start, py_stop, py_slice) = self.get_slice_config() load_pyslice = LoadGlobalNode(self.pos, py_slice) load_pyslice.allocate(code) + code.putln("#if CYTHON_USING_HPY") + code.putln( + "%s = __Pyx_PyObject_GetSlice(HPY_CONTEXT_FIRST_ARG_CALL %s, %s, %s, &%s, &%s, &%s, %d, %d, %d); %s" % ( + result, + self.base.py_result(), + c_start, c_stop, + py_start, py_stop, load_pyslice.temp_cname, + has_c_start, has_c_stop, + bool(code.globalstate.directives['wraparound']), + code.error_goto_if_null_object(result, self.pos))) + code.putln("#else") code.putln( "%s = __Pyx_PyObject_GetSlice(HPY_CONTEXT_FIRST_ARG_CALL %s, %s, %s, %s, %s, %s, %d, %d, %d); %s" % ( result, @@ -5672,6 +5682,7 @@ def generate_result_code(self, code): has_c_start, has_c_stop, bool(code.globalstate.directives['wraparound']), code.error_goto_if_null_object(result, self.pos))) + code.putln("#endif") load_pyslice.release(code) else: if self.base.type is list_type: @@ -5936,7 +5947,7 @@ def generate_result_code(self, code): load_stop.allocate(code, needs_decl=True) load_step.allocate(code, needs_decl=True) code.putln( - "%s = API_SLICE_NEW(%s, %s, %s); %s" % ( + "PYOBJECT_GLOBAL_STORE(%s, API_SLICE_NEW(%s, %s, %s)); %s" % ( self.result(), load_start.temp_cname, load_stop.temp_cname, diff --git a/Cython/Utility/Exceptions.c b/Cython/Utility/Exceptions.c index 0b5b154e73a..6058f066329 100644 --- a/Cython/Utility/Exceptions.c +++ b/Cython/Utility/Exceptions.c @@ -852,7 +852,11 @@ static PYOBJECT_TYPE __Pyx_PyCode_Replace_For_AddTraceback(HPY_CONTEXT_FIRST_ARG if (likely(API_IS_NOT_NULL(replace))) { PYOBJECT_TYPE result; PYOBJECT_TYPE temp_empty_tuple = PYOBJECT_GLOBAL_LOAD($empty_tuple); - result = API_CALL_TUPLE_DICT(replace, temp_empty_tuple, scratch_dict); +#if CYTHON_USING_HPY + result = HPy_CallTupleDict(HPY_CONTEXT_CNAME, replace, temp_empty_tuple, scratch_dict); +#else + result = API_CALL_FUNC(replace, &temp_empty_tuple, 0, scratch_dict); +#endif PYOBJECT_CLOSEREF(replace); PYOBJECT_CLOSEREF(temp_empty_tuple); return result; diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index e9e709e349b..ad653a9f310 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1308,7 +1308,11 @@ static PYOBJECT_TYPE __Pyx_Py3MetaclassPrepare(HPY_CONTEXT_FIRST_ARG_DEF PYOBJEC PYOBJECT_TYPE ns; if (API_IS_NOT_NULL(metaclass)) { PYOBJECT_TYPE loaded_prepare = PYOBJECT_GLOBAL_LOAD(PYIDENT("__prepare__")); +#if CYTHON_USING_HPY + PYOBJECT_TYPE prep = PYOBJECT_GET_ATTR(metaclass, loaded_prepare); +#else PYOBJECT_TYPE prep = __Pyx_PyObject_GetAttrStrNoError(HPY_CONTEXT_FIRST_ARG_CALL metaclass, loaded_prepare); +#endif PYOBJECT_GLOBAL_CLOSEREF(loaded_prepare); if (API_IS_NOT_NULL(prep)) { #if CYTHON_USING_HPY @@ -3118,7 +3122,6 @@ static CYTHON_INLINE PyObject* __Pyx_PySequence_Multiply(PyObject *seq, Py_ssize typedef PYOBJECT_TYPE __Pyx_TypeName; #define __Pyx_FMT_TYPENAME "%U" static __Pyx_TypeName __Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_DEF PYTYPEOBJECT_TYPE tp); /*proto*/ -#define __Pyx_PyType_GetName_legacy(tp) ((tp)->tp_name) #define __Pyx_DECREF_TypeName(obj) PYOBJECT_XCLOSEREF(obj) #else typedef const char *__Pyx_TypeName; @@ -3152,14 +3155,14 @@ __Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_DEF PYTYPEOBJECT_TYPE tp) /////////////// RaiseUnexpectedTypeError.proto /////////////// -static int __Pyx_RaiseUnexpectedTypeError(HPY_CONTEXT_FIRST_ARG_DEF const char *expected, PYOBJECT_TYPE obj); /*proto*/ +static int __Pyx_RaiseUnexpectedTypeError(const char *expected, PyObject *obj); /*proto*/ /////////////// RaiseUnexpectedTypeError /////////////// static int -__Pyx_RaiseUnexpectedTypeError(HPY_CONTEXT_FIRST_ARG_DEF const char *expected, PYOBJECT_TYPE obj) +__Pyx_RaiseUnexpectedTypeError(const char *expected, PyObject *obj) { - __Pyx_TypeName obj_type_name = __Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_CALL GET_TYPE(obj)); + __Pyx_TypeName obj_type_name = __Pyx_PyType_GetName(Py_TYPE(obj)); PyErr_Format(PyExc_TypeError, "Expected %s, got " __Pyx_FMT_TYPENAME, expected, obj_type_name); __Pyx_DECREF_TypeName(obj_type_name); From 83dba12dd2ff3d673b6ce0934eef1a21bb76a907 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 10 Jan 2024 17:17:36 +0100 Subject: [PATCH 283/286] Fixed issues reintroduced on fannkuch --- Cython/Compiler/ExprNodes.py | 8 ++++---- Cython/Utility/ObjectHandling.c | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 8f9619b302f..6d2e65b3161 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -5696,7 +5696,7 @@ def generate_result_code(self, code): else: cfunc = 'SEQUENCE_GET_SLICE' code.putln( - "%s = HPY_LEGACY_OBJECT_FROM(%s(HPY_LEGACY_OBJECT_AS(%s), %s, %s)); %s" % ( + "%s = %s(%s, %s, %s); %s" % ( result, cfunc, self.base.py_result(), @@ -5764,21 +5764,21 @@ def generate_deletion_code(self, code, ignore_nonexisting=False): self.free_subexpr_temps(code) def get_slice_config(self): - has_c_start, c_start, py_start = False, '0', 'API_NULL_VALUE' + has_c_start, c_start, py_start = False, '0', 'NULL' if self.start: has_c_start = not self.start.type.is_pyobject if has_c_start: c_start = self.start.result() else: py_start = 'CAPI_NEEDS_DEREFERENCE %s' % self.start.py_result() - has_c_stop, c_stop, py_stop = False, '0', 'API_NULL_VALUE' + has_c_stop, c_stop, py_stop = False, '0', 'NULL' if self.stop: has_c_stop = not self.stop.type.is_pyobject if has_c_stop: c_stop = self.stop.result() else: py_stop = 'CAPI_NEEDS_DEREFERENCE %s' % self.stop.py_result() - py_slice = self.slice and 'CAPI_NEEDS_DEREFERENCE %s' % self.slice.py_result() or 'API_NULL_VALUE' + py_slice = self.slice and 'CAPI_NEEDS_DEREFERENCE %s' % self.slice.py_result() or 'NULL' return (has_c_start, has_c_stop, c_start, c_stop, py_start, py_stop, py_slice) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index ad653a9f310..54e751ecc8a 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -3155,14 +3155,14 @@ __Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_DEF PYTYPEOBJECT_TYPE tp) /////////////// RaiseUnexpectedTypeError.proto /////////////// -static int __Pyx_RaiseUnexpectedTypeError(const char *expected, PyObject *obj); /*proto*/ +static int __Pyx_RaiseUnexpectedTypeError(HPY_CONTEXT_FIRST_ARG_DEF const char *expected, PYOBJECT_TYPE obj); /*proto*/ /////////////// RaiseUnexpectedTypeError /////////////// static int -__Pyx_RaiseUnexpectedTypeError(const char *expected, PyObject *obj) +__Pyx_RaiseUnexpectedTypeError(HPY_CONTEXT_FIRST_ARG_DEF const char *expected, PYOBJECT_TYPE obj) { - __Pyx_TypeName obj_type_name = __Pyx_PyType_GetName(Py_TYPE(obj)); + __Pyx_TypeName obj_type_name = __Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_CALL GET_TYPE(obj)); PyErr_Format(PyExc_TypeError, "Expected %s, got " __Pyx_FMT_TYPENAME, expected, obj_type_name); __Pyx_DECREF_TypeName(obj_type_name); From d9dcc4342a5f1dbc36a3eed1d972538d25e67386 Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Fri, 12 Jan 2024 16:26:54 +0100 Subject: [PATCH 284/286] Fixed float issues on GraalPy --- Cython/Compiler/ExprNodes.py | 59 ++++++++++++++++++--------------- Cython/Utility/ObjectHandling.c | 4 +-- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 6d2e65b3161..0500eb0efce 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -4694,22 +4694,33 @@ def generate_setitem_code(self, value, code): else: function = "PYOBJECT_SET_ITEM" - temp_load_index = LoadGlobalNode(self.pos, index_code) - temp_load_index.allocate(code) - temp_load_value = LoadGlobalNode(self.pos, value_code) temp_load_value.allocate(code) - code.putln(code.error_goto_if_neg( + code.putln("//%s" % index_code) + if self.index.type.is_int: + code.putln(code.error_goto_if_neg( "%s(%s, %s, %s%s)" % ( function, self.base.py_result(), - temp_load_index.temp_cname, + index_code, temp_load_value.temp_cname, self.extra_index_params(code)), self.pos)) + else: + temp_load_index = LoadGlobalNode(self.pos, index_code) + temp_load_index.allocate(code) + + code.putln(code.error_goto_if_neg( + "%s(%s, %s, %s%s)" % ( + function, + self.base.py_result(), + temp_load_index.temp_cname, + temp_load_value.temp_cname, + self.extra_index_params(code)), + self.pos)) + temp_load_index.release(code) - temp_load_index.release(code) temp_load_value.release(code) def generate_assignment_code(self, rhs, code, overloaded_assignment=False, @@ -5660,29 +5671,23 @@ def generate_result_code(self, code): code.globalstate.use_utility_code(self.get_slice_utility_code) (has_c_start, has_c_stop, c_start, c_stop, py_start, py_stop, py_slice) = self.get_slice_config() - load_pyslice = LoadGlobalNode(self.pos, py_slice) - load_pyslice.allocate(code) - code.putln("#if CYTHON_USING_HPY") - code.putln( - "%s = __Pyx_PyObject_GetSlice(HPY_CONTEXT_FIRST_ARG_CALL %s, %s, %s, &%s, &%s, &%s, %d, %d, %d); %s" % ( - result, - self.base.py_result(), - c_start, c_stop, - py_start, py_stop, load_pyslice.temp_cname, - has_c_start, has_c_stop, - bool(code.globalstate.directives['wraparound']), - code.error_goto_if_null_object(result, self.pos))) - code.putln("#else") + if not py_slice == "NULL": + load_pyslice = LoadGlobalNode(self.pos, py_slice[1:]) + load_pyslice.allocate(code) + pyslice_maybe_deref = "&" + load_pyslice.temp_cname + else: + load_pyslice = LoadGlobalNode(self.pos, py_slice) + load_pyslice.allocate(code) + pyslice_maybe_deref = load_pyslice.temp_cname code.putln( "%s = __Pyx_PyObject_GetSlice(HPY_CONTEXT_FIRST_ARG_CALL %s, %s, %s, %s, %s, %s, %d, %d, %d); %s" % ( result, self.base.py_result(), c_start, c_stop, - py_start, py_stop, load_pyslice.temp_cname, + py_start, py_stop, pyslice_maybe_deref, has_c_start, has_c_stop, bool(code.globalstate.directives['wraparound']), code.error_goto_if_null_object(result, self.pos))) - code.putln("#endif") load_pyslice.release(code) else: if self.base.type is list_type: @@ -5770,15 +5775,15 @@ def get_slice_config(self): if has_c_start: c_start = self.start.result() else: - py_start = 'CAPI_NEEDS_DEREFERENCE %s' % self.start.py_result() + py_start = '&%s' % self.start.py_result() has_c_stop, c_stop, py_stop = False, '0', 'NULL' if self.stop: has_c_stop = not self.stop.type.is_pyobject if has_c_stop: c_stop = self.stop.result() else: - py_stop = 'CAPI_NEEDS_DEREFERENCE %s' % self.stop.py_result() - py_slice = self.slice and 'CAPI_NEEDS_DEREFERENCE %s' % self.slice.py_result() or 'NULL' + py_stop = '&%s' % self.stop.py_result() + py_slice = self.slice and '&%s' % self.slice.py_result() or 'NULL' return (has_c_start, has_c_stop, c_start, c_stop, py_start, py_stop, py_slice) @@ -8523,13 +8528,13 @@ def generate_sequence_packing_code(self, code, target=None, plain=False): tmp_load_arg.release(code) code.putln("}") - code.putln("%s(%s, %s);" % (build_func, target, tmp_builder)) - code.funcstate.release_temp(tmp_builder) - if c_mult: code.putln('}') #code.funcstate.release_temp(counter) code.putln('}') + + code.putln("%s(%s, %s);" % (build_func, target, tmp_builder)) + code.funcstate.release_temp(tmp_builder) if mult_factor is not None and mult_factor.type.is_pyobject: code.putln('{ PYOBJECT_TYPE %s = NUMBER_INPLACE_MULTIPLY(%s, %s); %s' % ( diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 54e751ecc8a..cf042a79517 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -606,10 +606,8 @@ static CYTHON_INLINE int __Pyx_SetItemInt_Fast(HPY_CONTEXT_FIRST_ARG_DEF PYOBJEC return SEQUENCE_SET_ITEM(o, i, v); } #endif - PYOBJECT_TYPE tmp_load_v = PYOBJECT_GLOBAL_LOAD(v); PYOBJECT_TYPE i_obj = PYOBJECT_LONG_FROM_SSIZE_T(i); - int retval = __Pyx_SetItemInt_Generic(HPY_CONTEXT_FIRST_ARG_CALL o, i_obj, tmp_load_v); - PYOBJECT_CLOSEREF(tmp_load_v); + int retval = __Pyx_SetItemInt_Generic(HPY_CONTEXT_FIRST_ARG_CALL o, i_obj, v); #if CYTHON_USING_HPY PYOBJECT_CLOSEREF(i_obj); #endif From c262bfaaf5ceeffd91eebe10155f791963991f32 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Wed, 17 Jan 2024 08:53:24 +0100 Subject: [PATCH 285/286] Use HPyIter_Next --- Cython/Compiler/ExprNodes.py | 7 +++---- Cython/Utility/HPyUtils.c | 6 ++++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 0500eb0efce..7d308faa39f 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -3214,14 +3214,13 @@ def generate_iter_next_result_code(self, result_name, code): self.py_result())) code.putln("#else") code.putln( - "%s = HPY_LEGACY_OBJECT_FROM(PyIter_Next(HPY_LEGACY_OBJECT_AS(%s)));" % ( + "%s = API_ITER_NEXT(%s);" % ( result_name, self.py_result())) code.putln("#endif") code.putln("if (unlikely(API_IS_NULL(%s))) {" % result_name) - code.putln("PyObject* exc_type = PyErr_Occurred();") - code.putln("if (exc_type) {") - code.putln("if (likely(__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) PyErr_Clear();") + code.putln("if (PYERR_OCCURRED()) {") + code.putln("if (likely(PYERR_EXCEPTIONMATCHES(API_EXC(StopIteration)))) PYERR_CLEAR();") code.putln("else %s" % code.error_goto(self.pos)) code.putln("}") code.putln("break;") diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index c902637d38a..cb63f567205 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -202,6 +202,9 @@ //Sequence Type #define API_SLICE_NEW(start, stop, step) HPySlice_New(HPY_CONTEXT_CNAME, start, stop, step) + //Iterators + #define API_ITER_NEXT(it) HPyIter_Next(HPY_CONTEXT_CNAME, (it)) + #else //HPy Context Macros #define HPY_CONTEXT_CNAME @@ -403,6 +406,9 @@ //Sequence Type #define API_SLICE_NEW(start, stop, step) PySlice_New(start, stop, step) + //Iterators + #define API_ITER_NEXT(it) PyIter_Next(it) + #endif //////////////////// HPyHelperFuncs.proto //////////////////// From 554b37bbd3b6ab2479f257920bf10803fcc4947f Mon Sep 17 00:00:00 2001 From: Du Toit Spies Date: Wed, 24 Jan 2024 14:41:18 +0100 Subject: [PATCH 286/286] Fixed problems from rebase --- Cython/Compiler/ExprNodes.py | 11 +++++++++-- Cython/Utility/CythonFunction.c | 4 ++-- Cython/Utility/FunctionArguments.c | 5 +++-- Cython/Utility/HPyUtils.c | 6 ++++++ Cython/Utility/ModuleSetupCode.c | 2 ++ Cython/Utility/Optimize.c | 2 +- Cython/Utility/StringTools.c | 30 +++++++++++++++++++++--------- Cython/Utility/TypeConversion.c | 2 +- 8 files changed, 45 insertions(+), 17 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 7d308faa39f..e0f4254a3de 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -3721,7 +3721,7 @@ def generate_evaluation_code(self, code): length_parts.append(str(len(node.value))) else: # TODO: add exception handling for these macro calls if not ASSUME_SAFE_SIZE/MACROS - length_parts.append("__Pyx_PyUnicode_GET_LENGTH(%s)" % node.py_result()) + length_parts.append("UNICODE_GET_LENGTH(%s)" % node.py_result()) if node in unknown_nodes: charval_parts.append("__Pyx_PyUnicode_MAX_CHAR_VALUE(%s)" % node.py_result()) @@ -3736,8 +3736,12 @@ def generate_evaluation_code(self, code): code.putln("PyErr_NoMemory(); %s" % code.error_goto(self.pos)) code.putln("}") + global_temp_arr = [] for i, node in enumerate(self.values): - code.putln('%s[%d] = %s;' % (values_array, i, node.py_result())) + temp = LoadGlobalNode(self.pos, node.py_result()) + temp.allocate(code) + global_temp_arr.append(temp) + code.putln('%s[%d] = %s;' % (values_array, i, temp.temp_cname)) code.mark_pos(self.pos) self.allocate_temp_result(code) @@ -3752,6 +3756,9 @@ def generate_evaluation_code(self, code): ' | '.join(charval_parts), )) + for temp in global_temp_arr: + temp.release(code) + if not use_stack_memory: code.putln("PyMem_Free(%s);" % values_array) code.funcstate.release_temp(values_array) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 32a60d0acad..d44af5e9a46 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -1236,7 +1236,7 @@ static PYOBJECT_TYPE __Pyx_CyFunction_CallAsMethod(HPY_CONTEXT_FIRST_ARG_DEF PYO } #endif /* CYTHON_USING_HPY */ -#if CYTHON_METH_FASTCALL && (CYTHON_VECTORCALL || CYTHON_BACKPORT_VECTORCALL) +#if CYTHON_METH_FASTCALL && (CYTHON_VECTORCALL || CYTHON_BACKPORT_VECTORCALL || CYTHON_USING_HPY) // Check that kwnames is empty (if you want to allow keyword arguments, // simply pass kwnames=NULL) and figure out what to do with "self". // Return value: @@ -1255,7 +1255,7 @@ static CYTHON_INLINE int __Pyx_CyFunction_Vectorcall_CheckArgs(HPY_CONTEXT_FIRST } ret = 1; } - if (unlikely(API_IS_NOT_NULL(kwnames)) && unlikely(__Pyx_TUPLE_GET_SIZE(kwnames))) { + if (unlikely(API_IS_NOT_NULL(kwnames)) && unlikely(TUPLE_GET_SIZE(kwnames))) { PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", ""); diff --git a/Cython/Utility/FunctionArguments.c b/Cython/Utility/FunctionArguments.c index ac7efe9fd7d..c3a327ac0f3 100644 --- a/Cython/Utility/FunctionArguments.c +++ b/Cython/Utility/FunctionArguments.c @@ -18,7 +18,7 @@ static int __Pyx__ArgTypeTest(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, PYTYP __Pyx_TypeName obj_type_name; PYOBJECT_TYPE extra_info = PYOBJECT_GLOBAL_LOAD($empty_unicode); int from_annotation_subclass = 0; - if (unlikely(!type)) { + if (unlikely(API_IS_NULL(type))) { PyErr_SetString(PyExc_SystemError, "Missing type object"); return 0; } @@ -28,7 +28,7 @@ static int __Pyx__ArgTypeTest(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, PYTYP // type from annotation if (__Pyx_TypeCheck(obj, type)) { from_annotation_subclass = 1; - extra_info = PYUNICODE("Note that Cython is deliberately stricter than PEP-484 and rejects subclasses of builtin types. If you need to pass subclasses then set the 'annotation_typing' directive to False."); + extra_info = PYOBJECT_GLOBAL_LOAD(PYUNICODE("Note that Cython is deliberately stricter than PEP-484 and rejects subclasses of builtin types. If you need to pass subclasses then set the 'annotation_typing' directive to False.")); } } type_name = __Pyx_PyType_GetName(HPY_CONTEXT_FIRST_ARG_CALL type); @@ -60,6 +60,7 @@ static int __Pyx__ArgTypeTest(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE obj, PYTYP #endif __Pyx_DECREF_TypeName(type_name); __Pyx_DECREF_TypeName(obj_type_name); + PYOBJECT_GLOBAL_CLOSEREF(extra_info); return 0; } diff --git a/Cython/Utility/HPyUtils.c b/Cython/Utility/HPyUtils.c index cb63f567205..41ba47eb409 100644 --- a/Cython/Utility/HPyUtils.c +++ b/Cython/Utility/HPyUtils.c @@ -166,6 +166,9 @@ #define LIST_CREATE_ASSIGN(tuple, builder, index, item) HPyListBuilder_Set(HPY_CONTEXT_CNAME, builder, index, item) #define LIST_CREATE_FINALISE(target, builder) target = HPyListBuilder_Build(HPY_CONTEXT_CNAME, builder); + //Unicode Type + #define UNICODE_GET_LENGTH(s) HPy_Length(HPY_CONTEXT_CNAME, s) + //PyObject/HPy Handle Type #define PYOBJECT_GET_ITEM(o, attr_name) HPy_GetItem(HPY_CONTEXT_CNAME, o, attr_name) #define PYOBJECT_SET_ITEM(o, attr_name, attr_val) HPy_SetItem(HPY_CONTEXT_CNAME, o, attr_name, attr_val) @@ -370,6 +373,9 @@ #define LIST_CREATE_ASSIGN(tuple, builder, index, item) __Pyx_PyList_SET_ITEM(tuple, index, item) #define LIST_CREATE_FINALISE(target, null) + //Unicode Type + #define UNICODE_GET_LENGTH(s) PyUnicode_GetLength(s) + //PyObject/HPy Handle Type #define PYOBJECT_GET_ITEM(o, attr_name) PyObject_GetItem(o, attr_name) #define PYOBJECT_SET_ITEM(o, attr_name, attr_val) PyObject_SetItem(o, attr_name, attr_val) diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 0d92c3a9af4..d10ccfbd9d2 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -464,6 +464,8 @@ #define CYTHON_REFNANNY 0 #undef CYTHON_METH_FASTCALL #define CYTHON_METH_FASTCALL 1 + #undef CYTHON_ASSUME_SAFE_SIZE + #define CYTHON_ASSUME_SAFE_SIZE 0 #undef CYTHON_CLINE_IN_TRACEBACK #define CYTHON_CLINE_IN_TRACEBACK 0 //Is disabled for the Limited API - probably safest to disable it for HPy then #else diff --git a/Cython/Utility/Optimize.c b/Cython/Utility/Optimize.c index 5394502223b..83c2c343fe6 100644 --- a/Cython/Utility/Optimize.c +++ b/Cython/Utility/Optimize.c @@ -1391,7 +1391,7 @@ static {{c_ret_type}} {{cfunc_name}}(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE op1 {{if c_op in '+-*' or op in ('TrueDivide', 'Eq', 'Ne')}} if (FLOAT_CHECK_EXACT({{pyval}})) { const long {{'a' if order == 'CObj' else 'b'}} = intval; - double {{ival}} = __Pyx_PyFloat_AS_DOUBLE(HPY_LEGACY_OBJECT_AS({{pyval}})); + double {{ival}} = __Pyx_PyFloat_AS_DOUBLE({{pyval}}); {{if op in ('Eq', 'Ne')}} if ((double)a {{c_op}} (double)b) { {{return_true}}; diff --git a/Cython/Utility/StringTools.c b/Cython/Utility/StringTools.c index 254afefa447..d3e65e38c3c 100644 --- a/Cython/Utility/StringTools.c +++ b/Cython/Utility/StringTools.c @@ -803,15 +803,15 @@ static CYTHON_INLINE PyObject* __Pyx_PyBytes_Join(PyObject* sep, PyObject* value /////////////// JoinPyUnicode.proto /////////////// -static PyObject* __Pyx_PyUnicode_Join(PyObject** values, Py_ssize_t value_count, Py_ssize_t result_ulength, - Py_UCS4 max_char); +static PYOBJECT_TYPE __Pyx_PyUnicode_Join(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE *values, API_SSIZE_T value_count, + API_SSIZE_T result_ulength, Py_UCS4 max_char); /////////////// JoinPyUnicode /////////////// //@requires: IncludeStringH //@substitute: naming -static PyObject* __Pyx_PyUnicode_Join(PyObject** values, Py_ssize_t value_count, Py_ssize_t result_ulength, - Py_UCS4 max_char) { +static PYOBJECT_TYPE __Pyx_PyUnicode_Join(HPY_CONTEXT_FIRST_ARG_DEF PYOBJECT_TYPE *values, API_SSIZE_T value_count, + API_SSIZE_T result_ulength, Py_UCS4 max_char) { #if CYTHON_USE_UNICODE_INTERNALS && CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS PyObject *result_uval; int result_ukind, kind_shift; @@ -874,9 +874,16 @@ static PyObject* __Pyx_PyUnicode_Join(PyObject** values, Py_ssize_t value_count, return NULL; #else // non-CPython fallback - Py_ssize_t i; - PyObject *result = NULL; - PyObject *value_tuple = PyTuple_New(value_count); + API_SSIZE_T i; +#if CYTHON_USING_HPY + PYOBJECT_TYPE result = PYOBJECT_GLOBAL_LOAD($empty_unicode); + PYOBJECT_TYPE value_tuple = API_NULL_VALUE; + for (i=0; i

!ynM%MD@??zeSm`Z>H~1P-Fm{ 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