diff --git a/Doc/c-api/intro.rst b/Doc/c-api/intro.rst index c3a80234f86116..e42c1dbf420069 100644 --- a/Doc/c-api/intro.rst +++ b/Doc/c-api/intro.rst @@ -526,14 +526,26 @@ to the C language. Outdated macros --------------- -The following macros have been used to features that have been standardized -in C11. +The following :term:`soft deprecated` macros have been used to features that +have been standardized in C11 (or previous standards). .. c:macro:: Py_ALIGNED(num) - Specify alignment to *num* bytes on compilers that support it. + On some GCC-like compilers, specify alignment to *num* bytes. + This does nothing on other compilers. - Consider using the C11 standard ``_Alignas`` specifier over this macro. + Use the standard ``alignas`` specifier rather than this macro. + + .. deprecated:: next + The macro is :term:`soft deprecated`. + +.. c:macro:: PY_FORMAT_SIZE_T + + The :c:func:`printf` formatting modifier for :c:type:`size_t`. + Use ``"z"`` directly instead. + + .. deprecated:: next + The macro is :term:`soft deprecated`. .. c:macro:: Py_LL(number) Py_ULL(number) @@ -546,6 +558,38 @@ in C11. Consider using the C99 standard suffixes ``LL`` and ``LLU`` directly. + .. deprecated:: next + The macro is :term:`soft deprecated`. + +.. c:macro:: PY_LONG_LONG + PY_INT32_T + PY_UINT32_T + PY_INT64_T + PY_UINT64_T + + Aliases for the types :c:type:`!long long`, :c:type:`!int32_t`, + :c:type:`!uint32_t`. :c:type:`!int64_t` and :c:type:`!uint64_t`, + respectively. + Historically, these types needed compiler-specific extensions. + + .. deprecated:: next + These macros are :term:`soft deprecated`. + +.. c:macro:: PY_LLONG_MIN + PY_LLONG_MAX + PY_ULLONG_MAX + PY_SIZE_MAX + + Aliases for the values :c:macro:`!LLONG_MIN`, :c:macro:`!LLONG_MAX`, + :c:macro:`!ULLONG_MAX`, and :c:macro:`!SIZE_MAX`, respectively. + Use these standard names instead. + + The required header, ````, + :ref:`is included ` in ``Python.h``. + + .. deprecated:: next + These macros are :term:`soft deprecated`. + .. c:macro:: Py_MEMCPY(dest, src, n) This is a :term:`soft deprecated` alias to :c:func:`!memcpy`. @@ -554,6 +598,25 @@ in C11. .. deprecated:: 3.14 The macro is :term:`soft deprecated`. +.. c:macro:: Py_UNICODE_SIZE + + Size of the :c:type:`!wchar_t` type. + Use ``sizeof(wchar_t)`` or ``WCHAR_WIDTH/8`` instead. + + The required header for the latter, ````, + :ref:`is included ` in ``Python.h``. + + .. deprecated:: next + The macro is :term:`soft deprecated`. + +.. c:macro:: Py_UNICODE_WIDE + + Defined if ``wchar_t`` can hold a Unicode character (UCS-4). + Use ``sizeof(wchar_t) >= 4`` instead + + .. deprecated:: next + The macro is :term:`soft deprecated`. + .. c:macro:: Py_VA_COPY This is a :term:`soft deprecated` alias to the C99-standard ``va_copy`` @@ -564,6 +627,9 @@ in C11. .. versionchanged:: 3.6 This is now an alias to ``va_copy``. + .. deprecated:: next + The macro is :term:`soft deprecated`. + .. _api-objects: diff --git a/Doc/deprecations/c-api-pending-removal-in-3.15.rst b/Doc/deprecations/c-api-pending-removal-in-3.15.rst index 9927b876760d34..789ec83d2d957a 100644 --- a/Doc/deprecations/c-api-pending-removal-in-3.15.rst +++ b/Doc/deprecations/c-api-pending-removal-in-3.15.rst @@ -7,8 +7,6 @@ Pending removal in Python 3.15 Use :c:func:`PyWeakref_GetRef` instead. The `pythoncapi-compat project `__ can be used to get :c:func:`PyWeakref_GetRef` on Python 3.12 and older. -* :c:type:`Py_UNICODE` type and the :c:macro:`!Py_UNICODE_WIDE` macro: - Use :c:type:`wchar_t` instead. * :c:func:`!PyUnicode_AsDecodedObject`: Use :c:func:`PyCodec_Decode` instead. * :c:func:`!PyUnicode_AsDecodedUnicode`: diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 5e6265a45231db..0910585fe3b957 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -1864,6 +1864,24 @@ Deprecated C APIs use the C11 standard ```` :c:macro:`!INFINITY` instead. (Contributed by Sergey B Kirpichev in :gh:`141004`.) +* The following macros are :term:`soft deprecated`: + + - :c:macro:`Py_ALIGNED`: Prefer ``alignas`` instead. + - :c:macro:`PY_FORMAT_SIZE_T`: Use ``"z"`` directly. + - :c:macro:`Py_LL` & :c:macro:`Py_ULL`: + Use standard suffixes, ``LL`` & ``ULL``. + - :c:macro:`PY_LONG_LONG`, :c:macro:`PY_LLONG_MIN`, :c:macro:`PY_LLONG_MAX`, + :c:macro:`PY_ULLONG_MAX`, :c:macro:`PY_INT32_T`, :c:macro:`PY_UINT32_T`, + :c:macro:`PY_INT64_T`, :c:macro:`PY_UINT64_T`, :c:macro:`PY_SIZE_MAX`: + Use C99 types/limits. + - :c:macro:`Py_UNICODE_SIZE`: Use ``sizeof(wchar_t)`` directly. + - :c:macro:`Py_VA_COPY`: Use ``va_copy`` directly. + + The macro :c:macro:`Py_UNICODE_WIDE`, which was scheduled for removal, + is :term:`soft deprecated` instead. + + (Contributed by Petr Viktorin in :gh:`146175`.) + * :c:macro:`!Py_MATH_El` and :c:macro:`!Py_MATH_PIl` are deprecated since 3.15 and will be removed in 3.20. (Contributed by Sergey B Kirpichev in :gh:`141004`.) diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 96685af02cd0e3..de701ced675cd4 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -144,7 +144,7 @@ static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n) new_refcnt = _Py_IMMORTAL_INITIAL_REFCNT; } # if SIZEOF_VOID_P > 4 - op->ob_refcnt = (PY_UINT32_T)new_refcnt; + op->ob_refcnt = (uint32_t)new_refcnt; # else op->ob_refcnt = new_refcnt; # endif diff --git a/Include/object.h b/Include/object.h index 3fb28035a50547..10d9d76d93454a 100644 --- a/Include/object.h +++ b/Include/object.h @@ -127,7 +127,7 @@ whose size is determined when the object is allocated. struct _object { _Py_ANONYMOUS union { #if SIZEOF_VOID_P > 4 - PY_INT64_T ob_refcnt_full; /* This field is needed for efficient initialization with Clang on ARM */ + int64_t ob_refcnt_full; /* This field is needed for efficient initialization with Clang on ARM */ struct { # if PY_BIG_ENDIAN uint16_t ob_flags; diff --git a/Include/pyport.h b/Include/pyport.h index 1e1702abd99a2c..f7bb5d513b9ae6 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -446,7 +446,9 @@ extern "C" { /* * Specify alignment on compilers that support it. */ -#if defined(__GNUC__) && __GNUC__ >= 3 +#ifdef Py_BUILD_CORE +// always use _Py_ALIGNED_DEF instead +#elif defined(__GNUC__) && __GNUC__ >= 3 #define Py_ALIGNED(x) __attribute__((aligned(x))) #else #define Py_ALIGNED(x) diff --git a/Include/refcount.h b/Include/refcount.h index 51346c7e519321..bcdabad3dcb4ff 100644 --- a/Include/refcount.h +++ b/Include/refcount.h @@ -126,7 +126,7 @@ static inline Py_ALWAYS_INLINE int _Py_IsImmortal(PyObject *op) return (_Py_atomic_load_uint32_relaxed(&op->ob_ref_local) == _Py_IMMORTAL_REFCNT_LOCAL); #elif SIZEOF_VOID_P > 4 - return _Py_CAST(PY_INT32_T, op->ob_refcnt) < 0; + return _Py_CAST(int32_t, op->ob_refcnt) < 0; #else return op->ob_refcnt >= _Py_IMMORTAL_MINIMUM_REFCNT; #endif @@ -164,7 +164,7 @@ static inline void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) { } #ifndef Py_GIL_DISABLED #if SIZEOF_VOID_P > 4 - ob->ob_refcnt = (PY_UINT32_T)refcnt; + ob->ob_refcnt = (uint32_t)refcnt; #else ob->ob_refcnt = refcnt; #endif @@ -278,7 +278,7 @@ static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op) _Py_atomic_add_ssize(&op->ob_ref_shared, (1 << _Py_REF_SHARED_SHIFT)); } #elif SIZEOF_VOID_P > 4 - PY_UINT32_T cur_refcnt = op->ob_refcnt; + uint32_t cur_refcnt = op->ob_refcnt; if (cur_refcnt >= _Py_IMMORTAL_INITIAL_REFCNT) { // the object is immortal _Py_INCREF_IMMORTAL_STAT_INC(); @@ -387,7 +387,7 @@ static inline void Py_DECREF(const char *filename, int lineno, PyObject *op) #if SIZEOF_VOID_P > 4 /* If an object has been freed, it will have a negative full refcnt * If it has not it been freed, will have a very large refcnt */ - if (op->ob_refcnt_full <= 0 || op->ob_refcnt > (((PY_UINT32_T)-1) - (1<<20))) { + if (op->ob_refcnt_full <= 0 || op->ob_refcnt > (((uint32_t)-1) - (1<<20))) { #else if (op->ob_refcnt <= 0) { #endif diff --git a/Include/unicodeobject.h b/Include/unicodeobject.h index b72d581ec25804..29f1d1b01c161f 100644 --- a/Include/unicodeobject.h +++ b/Include/unicodeobject.h @@ -64,13 +64,9 @@ Copyright (c) Corporation for National Research Initiatives. #error Must define SIZEOF_WCHAR_T #endif +/* Soft-deprecated defines */ #define Py_UNICODE_SIZE SIZEOF_WCHAR_T - -/* If wchar_t can be used for UCS-4 storage, set Py_UNICODE_WIDE. - Otherwise, Unicode strings are stored as UCS-2 (with limited support - for UTF-16) */ - -#if Py_UNICODE_SIZE >= 4 +#if SIZEOF_WCHAR_T >= 4 #define Py_UNICODE_WIDE #endif diff --git a/Misc/NEWS.d/next/C_API/2026-03-19-16-50-27.gh-issue-146175.pISQGX.rst b/Misc/NEWS.d/next/C_API/2026-03-19-16-50-27.gh-issue-146175.pISQGX.rst new file mode 100644 index 00000000000000..3563347141d1ba --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-03-19-16-50-27.gh-issue-146175.pISQGX.rst @@ -0,0 +1,12 @@ +The following macros are :term:`soft deprecated`: +:c:macro:`Py_ALIGNED`, +:c:macro:`PY_FORMAT_SIZE_T`, +:c:macro:`Py_LL`, :c:macro:`Py_ULL`, +:c:macro:`PY_LONG_LONG`, :c:macro:`PY_LLONG_MIN`, :c:macro:`PY_LLONG_MAX`, +:c:macro:`PY_ULLONG_MAX`, :c:macro:`PY_INT32_T`, :c:macro:`PY_UINT32_T`, +:c:macro:`PY_INT64_T`, :c:macro:`PY_UINT64_T`, :c:macro:`PY_SIZE_MAX`, +:c:macro:`Py_UNICODE_SIZE`, +:c:macro:`Py_VA_COPY`. + +The macro :c:macro:`Py_UNICODE_WIDE`, which was scheduled for removal, is +:term:`soft deprecated` instead. diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c index 781ca4327f93ae..5debae5b42480b 100644 --- a/Modules/_io/stringio.c +++ b/Modules/_io/stringio.c @@ -111,7 +111,7 @@ resize_buffer(stringio *self, size_t size) alloc = size + 1; } - if (alloc > PY_SIZE_MAX / sizeof(Py_UCS4)) + if (alloc > SIZE_MAX / sizeof(Py_UCS4)) goto overflow; new_buf = (Py_UCS4 *)PyMem_Realloc(self->buf, alloc * sizeof(Py_UCS4)); if (new_buf == NULL) { diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index a25b127f1011b8..813ab7ef130400 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2607,6 +2607,40 @@ create_managed_weakref_nogc_type(PyObject *self, PyObject *Py_UNUSED(args)) } +static PyObject* +test_soft_deprecated_macros(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) { + // Test soft-deprecated macros + Py_ALIGNED(64) char buf[4]; + #ifdef __GNUC__ + // Py_ALIGNED must compile everywhere, but only does something + // on "supported" compilers, i.e. GCC + Py_BUILD_ASSERT(__extension__ __alignof__(buf) >= 64); + #endif + assert(strcmp(PY_FORMAT_SIZE_T, "z") == 0); + Py_BUILD_ASSERT(Py_LL(123) == 123LL); + Py_BUILD_ASSERT(sizeof(Py_LL(123)) == sizeof(long long)); + Py_BUILD_ASSERT(sizeof(Py_ULL(123)) == sizeof(unsigned long long)); + Py_BUILD_ASSERT(sizeof(PY_LONG_LONG) == sizeof(long long)); + Py_BUILD_ASSERT(sizeof(PY_INT32_T) == sizeof(int32_t)); + Py_BUILD_ASSERT(sizeof(PY_UINT32_T) == sizeof(uint32_t)); + Py_BUILD_ASSERT(sizeof(PY_INT64_T) == sizeof(int64_t)); + Py_BUILD_ASSERT(sizeof(PY_UINT64_T) == sizeof(uint64_t)); + Py_BUILD_ASSERT(PY_LLONG_MIN == LLONG_MIN); + Py_BUILD_ASSERT(PY_LLONG_MAX == LLONG_MAX); + Py_BUILD_ASSERT(PY_ULLONG_MAX == ULLONG_MAX); + Py_BUILD_ASSERT(PY_SIZE_MAX == SIZE_MAX); + Py_BUILD_ASSERT(PY_LLONG_MIN == LLONG_MIN); + Py_MEMCPY(buf, "abc", 4); + assert(strcmp(buf, "abc") == 0); + Py_BUILD_ASSERT(Py_UNICODE_SIZE == sizeof(wchar_t)); + #ifdef Py_UNICODE_WIDE + Py_BUILD_ASSERT(sizeof(wchar_t) >= 4); + #else + Py_BUILD_ASSERT(sizeof(wchar_t) < 4); + #endif + Py_RETURN_NONE; +} + static PyMethodDef TestMethods[] = { {"set_errno", set_errno, METH_VARARGS}, {"test_config", test_config, METH_NOARGS}, @@ -2704,6 +2738,7 @@ static PyMethodDef TestMethods[] = { {"toggle_reftrace_printer", toggle_reftrace_printer, METH_O}, {"create_managed_weakref_nogc_type", create_managed_weakref_nogc_type, METH_NOARGS}, + {"test_soft_deprecated_macros", test_soft_deprecated_macros, METH_NOARGS}, {NULL, NULL} /* sentinel */ }; diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index ec6a9840131e4d..8ce17fea8b4157 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -2760,11 +2760,9 @@ array_buffer_getbuf(PyObject *op, Py_buffer *view, int flags) view->internal = NULL; if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) { view->format = (char *)self->ob_descr->formats; -#ifdef Py_UNICODE_WIDE - if (self->ob_descr->typecode == 'u') { + if (sizeof(wchar_t) >= 4 && self->ob_descr->typecode == 'u') { view->format = "w"; } -#endif } self->ob_exports++; diff --git a/Modules/binascii.c b/Modules/binascii.c index f85f32b32e962c..603f4393a3ab1c 100644 --- a/Modules/binascii.c +++ b/Modules/binascii.c @@ -78,7 +78,7 @@ get_binascii_state(PyObject *module) /* Align to 64 bytes for L1 cache line friendliness */ -static const unsigned char table_a2b_base64[] Py_ALIGNED(64) = { +static const _Py_ALIGNED_DEF(64, unsigned char) table_a2b_base64[] = { -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, @@ -110,7 +110,7 @@ static const unsigned char table_a2b_base64[] Py_ALIGNED(64) = { */ /* Align to 64 bytes for L1 cache line friendliness */ -static const unsigned char table_b2a_base64[] Py_ALIGNED(64) = +static const _Py_ALIGNED_DEF(64, unsigned char) table_b2a_base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /* Encode 3 bytes into 4 base64 characters. */ @@ -189,7 +189,7 @@ base64_decode_fast(const unsigned char *in, Py_ssize_t in_len, } -static const unsigned char table_a2b_base85[] Py_ALIGNED(64) = { +static const _Py_ALIGNED_DEF(64, unsigned char) table_a2b_base85[] = { -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,62,-1,63, 64,65,66,-1, 67,68,69,70, -1,71,-1,-1, @@ -209,7 +209,7 @@ static const unsigned char table_a2b_base85[] Py_ALIGNED(64) = { -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, }; -static const unsigned char table_a2b_base85_a85[] Py_ALIGNED(64) = { +static const _Py_ALIGNED_DEF(64, unsigned char) table_a2b_base85_a85[] = { -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, @@ -229,11 +229,11 @@ static const unsigned char table_a2b_base85_a85[] Py_ALIGNED(64) = { -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, }; -static const unsigned char table_b2a_base85[] Py_ALIGNED(64) = +static const _Py_ALIGNED_DEF(64, unsigned char) table_b2a_base85[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ "abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~"; -static const unsigned char table_b2a_base85_a85[] Py_ALIGNED(64) = +static const _Py_ALIGNED_DEF(64, unsigned char) table_b2a_base85_a85[] = "!\"#$%&\'()*+,-./0123456789:;<=>?@" \ "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstu"; diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index d4df40c78e8a4f..ec99330fe87d09 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -6261,7 +6261,7 @@ socket_gethostbyaddr(PyObject *self, PyObject *args) gethostbyaddr_r is 8-byte aligned, which at least llvm-gcc does not ensure. The attribute below instructs the compiler to maintain this alignment. */ - char buf[16384] Py_ALIGNED(8); + _Py_ALIGNED_DEF(8, char) buf[16384]; int buf_len = (sizeof buf) - 1; int errnop; #endif diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index d51a95c69a93b3..954efcc19cebd9 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -5580,15 +5580,14 @@ _Py_EncodeUTF8Ex(const wchar_t *text, char **str, size_t *error_pos, Py_ssize_t ch_pos = i; Py_UCS4 ch = text[i]; i++; -#if Py_UNICODE_SIZE == 2 - if (Py_UNICODE_IS_HIGH_SURROGATE(ch) + if (sizeof(wchar_t) == 2 + && Py_UNICODE_IS_HIGH_SURROGATE(ch) && i < len && Py_UNICODE_IS_LOW_SURROGATE(text[i])) { ch = Py_UNICODE_JOIN_SURROGATES(ch, text[i]); i++; } -#endif if (ch < 0x80) { /* Encode ASCII */ diff --git a/Tools/check-c-api-docs/ignored_c_api.txt b/Tools/check-c-api-docs/ignored_c_api.txt index e464162c52a371..f3a3612b84947a 100644 --- a/Tools/check-c-api-docs/ignored_c_api.txt +++ b/Tools/check-c-api-docs/ignored_c_api.txt @@ -28,19 +28,7 @@ PyExpat_CAPSULE_NAME # pyport.h PYLONG_BITS_IN_DIGIT PY_DWORD_MAX -PY_FORMAT_SIZE_T -PY_INT32_T -PY_INT64_T -PY_LLONG_MAX -PY_LLONG_MIN -PY_LONG_LONG -PY_SIZE_MAX -PY_UINT32_T -PY_UINT64_T -PY_ULLONG_MAX PY_BIG_ENDIAN -# unicodeobject.h -Py_UNICODE_SIZE # cpython/methodobject.h PyCFunction_GET_CLASS # cpython/compile.h