diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 14531d17144..ec1910018e8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -242,6 +242,7 @@ jobs: "3.11", "3.12", "3.13", + "3.14-dev", "pypy3.9", "pypy3.10", "graalpy24.0", @@ -520,8 +521,8 @@ jobs: components: rust-src - name: Install python3 standalone debug build with nox run: | - PBS_RELEASE="20241016" - PBS_PYTHON_VERSION="3.13.0" + PBS_RELEASE="20241219" + PBS_PYTHON_VERSION="3.13.1" PBS_ARCHIVE="cpython-${PBS_PYTHON_VERSION}+${PBS_RELEASE}-x86_64-unknown-linux-gnu-debug-full.tar.zst" wget "https://github.com/indygreg/python-build-standalone/releases/download/${PBS_RELEASE}/${PBS_ARCHIVE}" tar -I zstd -xf "${PBS_ARCHIVE}" diff --git a/newsfragments/4811.fixed.md b/newsfragments/4811.fixed.md new file mode 100644 index 00000000000..cad2a26146b --- /dev/null +++ b/newsfragments/4811.fixed.md @@ -0,0 +1 @@ +Bump supported cpython version to 3.14 for testing diff --git a/noxfile.py b/noxfile.py index 9f94175ab16..d18ed63e556 100644 --- a/noxfile.py +++ b/noxfile.py @@ -41,7 +41,7 @@ PYO3_GUIDE_SRC = PYO3_DIR / "guide" / "src" PYO3_GUIDE_TARGET = PYO3_TARGET / "guide" PYO3_DOCS_TARGET = PYO3_TARGET / "doc" -PY_VERSIONS = ("3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13") +PY_VERSIONS = ("3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14") PYPY_VERSIONS = ("3.9", "3.10") FREE_THREADED_BUILD = bool(sysconfig.get_config_var("Py_GIL_DISABLED")) @@ -677,8 +677,8 @@ def test_version_limits(session: nox.Session): config_file.set("CPython", "3.6") _run_cargo(session, "check", env=env, expect_error=True) - assert "3.14" not in PY_VERSIONS - config_file.set("CPython", "3.14") + assert "3.15" not in PY_VERSIONS + config_file.set("CPython", "3.15") _run_cargo(session, "check", env=env, expect_error=True) # 3.14 CPython should build with forward compatibility diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index f130c2d6557..2c4955dcc6f 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -40,7 +40,7 @@ const MINIMUM_SUPPORTED_VERSION_GRAALPY: PythonVersion = PythonVersion { }; /// Maximum Python version that can be used as minimum required Python version with abi3. -pub(crate) const ABI3_MAX_MINOR: u8 = 12; +pub(crate) const ABI3_MAX_MINOR: u8 = 13; #[cfg(test)] thread_local! { diff --git a/pyo3-ffi/build.rs b/pyo3-ffi/build.rs index ea023de75fa..9f282a6c3b7 100644 --- a/pyo3-ffi/build.rs +++ b/pyo3-ffi/build.rs @@ -17,7 +17,7 @@ const SUPPORTED_VERSIONS_CPYTHON: SupportedVersions = SupportedVersions { min: PythonVersion { major: 3, minor: 7 }, max: PythonVersion { major: 3, - minor: 13, + minor: 14, }, }; diff --git a/pyo3-ffi/src/cpython/unicodeobject.rs b/pyo3-ffi/src/cpython/unicodeobject.rs index fae626b8d25..31c96c042f8 100644 --- a/pyo3-ffi/src/cpython/unicodeobject.rs +++ b/pyo3-ffi/src/cpython/unicodeobject.rs @@ -2,6 +2,8 @@ use crate::Py_hash_t; use crate::{PyObject, Py_UCS1, Py_UCS2, Py_UCS4, Py_ssize_t}; use libc::wchar_t; +#[cfg(Py_3_14)] +use std::os::raw::c_ushort; use std::os::raw::{c_char, c_int, c_uint, c_void}; // skipped Py_UNICODE_ISSPACE() @@ -119,8 +121,10 @@ where #[cfg(not(GraalPy))] const STATE_INTERNED_INDEX: usize = 0; -#[cfg(not(GraalPy))] +#[cfg(all(not(GraalPy), not(Py_3_14)))] const STATE_INTERNED_WIDTH: u8 = 2; +#[cfg(all(not(GraalPy), Py_3_14))] +const STATE_INTERNED_WIDTH: u8 = 16; #[cfg(not(GraalPy))] const STATE_KIND_INDEX: usize = STATE_INTERNED_WIDTH as usize; @@ -138,6 +142,12 @@ const STATE_ASCII_INDEX: usize = #[cfg(not(GraalPy))] const STATE_ASCII_WIDTH: u8 = 1; +#[cfg(all(not(GraalPy), Py_3_12))] +const STATE_STATICALLY_ALLOCATED_INDEX: usize = + (STATE_INTERNED_WIDTH + STATE_KIND_WIDTH + STATE_COMPACT_WIDTH + STATE_ASCII_WIDTH) as usize; +#[cfg(all(not(GraalPy), Py_3_12))] +const STATE_STATICALLY_ALLOCATED_WIDTH: u8 = 1; + #[cfg(not(any(Py_3_12, GraalPy)))] const STATE_READY_INDEX: usize = (STATE_INTERNED_WIDTH + STATE_KIND_WIDTH + STATE_COMPACT_WIDTH + STATE_ASCII_WIDTH) as usize; @@ -165,6 +175,7 @@ struct PyASCIIObjectState { #[allow(clippy::useless_transmute)] impl PyASCIIObjectState { #[inline] + #[cfg(not(Py_3_14))] unsafe fn interned(&self) -> c_uint { std::mem::transmute( self.bitfield @@ -173,6 +184,7 @@ impl PyASCIIObjectState { } #[inline] + #[cfg(not(Py_3_14))] unsafe fn set_interned(&mut self, val: c_uint) { let val: u32 = std::mem::transmute(val); self.bitfield @@ -180,11 +192,36 @@ impl PyASCIIObjectState { } #[inline] + #[cfg(Py_3_14)] + unsafe fn interned(&self) -> u16 { + std::mem::transmute( + self.bitfield + .get(STATE_INTERNED_INDEX, STATE_INTERNED_WIDTH) as u16, + ) + } + + #[inline] + #[cfg(Py_3_14)] + unsafe fn set_interned(&mut self, val: u16) { + let val: u16 = std::mem::transmute(val); + self.bitfield + .set(STATE_INTERNED_INDEX, STATE_INTERNED_WIDTH, val as u64) + } + + #[inline] + #[cfg(not(Py_3_14))] unsafe fn kind(&self) -> c_uint { std::mem::transmute(self.bitfield.get(STATE_KIND_INDEX, STATE_KIND_WIDTH) as u32) } #[inline] + #[cfg(Py_3_14)] + unsafe fn kind(&self) -> c_ushort { + std::mem::transmute(self.bitfield.get(STATE_KIND_INDEX, STATE_KIND_WIDTH) as c_ushort) + } + + #[inline] + #[cfg(not(Py_3_14))] unsafe fn set_kind(&mut self, val: c_uint) { let val: u32 = std::mem::transmute(val); self.bitfield @@ -192,11 +229,27 @@ impl PyASCIIObjectState { } #[inline] + #[cfg(Py_3_14)] + unsafe fn set_kind(&mut self, val: c_ushort) { + let val: c_ushort = std::mem::transmute(val); + self.bitfield + .set(STATE_KIND_INDEX, STATE_KIND_WIDTH, val as u64) + } + + #[inline] + #[cfg(not(Py_3_14))] unsafe fn compact(&self) -> c_uint { std::mem::transmute(self.bitfield.get(STATE_COMPACT_INDEX, STATE_COMPACT_WIDTH) as u32) } #[inline] + #[cfg(Py_3_14)] + unsafe fn compact(&self) -> c_ushort { + std::mem::transmute(self.bitfield.get(STATE_COMPACT_INDEX, STATE_COMPACT_WIDTH) as c_ushort) + } + + #[inline] + #[cfg(not(Py_3_14))] unsafe fn set_compact(&mut self, val: c_uint) { let val: u32 = std::mem::transmute(val); self.bitfield @@ -204,17 +257,81 @@ impl PyASCIIObjectState { } #[inline] + #[cfg(Py_3_14)] + unsafe fn set_compact(&mut self, val: c_ushort) { + let val: c_ushort = std::mem::transmute(val); + self.bitfield + .set(STATE_COMPACT_INDEX, STATE_COMPACT_WIDTH, val as u64) + } + + #[inline] + #[cfg(not(Py_3_14))] unsafe fn ascii(&self) -> c_uint { std::mem::transmute(self.bitfield.get(STATE_ASCII_INDEX, STATE_ASCII_WIDTH) as u32) } #[inline] + #[cfg(not(Py_3_14))] unsafe fn set_ascii(&mut self, val: c_uint) { let val: u32 = std::mem::transmute(val); self.bitfield .set(STATE_ASCII_INDEX, STATE_ASCII_WIDTH, val as u64) } + #[inline] + #[cfg(Py_3_14)] + unsafe fn ascii(&self) -> c_ushort { + std::mem::transmute(self.bitfield.get(STATE_ASCII_INDEX, STATE_ASCII_WIDTH) as c_ushort) + } + + #[inline] + #[cfg(Py_3_14)] + unsafe fn set_ascii(&mut self, val: c_ushort) { + let val: c_ushort = std::mem::transmute(val); + self.bitfield + .set(STATE_ASCII_INDEX, STATE_ASCII_WIDTH, val as u64) + } + + #[cfg(all(Py_3_12, not(Py_3_14)))] + #[inline] + unsafe fn statically_allocated(&self) -> c_uint { + std::mem::transmute(self.bitfield.get( + STATE_STATICALLY_ALLOCATED_INDEX, + STATE_STATICALLY_ALLOCATED_WIDTH, + ) as u32) + } + + #[cfg(all(Py_3_12, not(Py_3_14)))] + #[inline] + unsafe fn set_statically_allocated(&mut self, val: c_uint) { + let val: u32 = std::mem::transmute(val); + self.bitfield.set( + STATE_STATICALLY_ALLOCATED_INDEX, + STATE_STATICALLY_ALLOCATED_WIDTH, + val as u64, + ) + } + + #[inline] + #[cfg(Py_3_14)] + unsafe fn statically_allocated(&self) -> c_ushort { + std::mem::transmute(self.bitfield.get( + STATE_STATICALLY_ALLOCATED_INDEX, + STATE_STATICALLY_ALLOCATED_WIDTH, + ) as c_ushort) + } + + #[inline] + #[cfg(Py_3_14)] + unsafe fn set_statically_allocated(&mut self, val: c_ushort) { + let val: c_ushort = std::mem::transmute(val); + self.bitfield.set( + STATE_STATICALLY_ALLOCATED_INDEX, + STATE_STATICALLY_ALLOCATED_WIDTH, + val as u64, + ) + } + #[cfg(not(Py_3_12))] #[inline] unsafe fn ready(&self) -> c_uint { @@ -258,12 +375,29 @@ pub struct PyASCIIObject { /// Rust doesn't expose bitfields. So we have accessor functions for /// retrieving values. /// + /// Before 3.12: /// unsigned int interned:2; // SSTATE_* constants. /// unsigned int kind:3; // PyUnicode_*_KIND constants. /// unsigned int compact:1; /// unsigned int ascii:1; /// unsigned int ready:1; /// unsigned int :24; + /// + /// 3.12 and 3.13: + /// unsigned int interned:2; // SSTATE_* constants. + /// unsigned int kind:3; // PyUnicode_*_KIND constants. + /// unsigned int compact:1; + /// unsigned int ascii:1; + /// unsigned int statically_allocated:1; + /// unsigned int :24; + /// + /// 3.14 and later: + /// uint16_t interned; // SSTATE_* constants. + /// unsigned short kind:3; // PyUnicode_*_KIND constants. + /// unsigned short compact:1; + /// unsigned short ascii:1; + /// unsigned int statically_allocated:1; + /// unsigned int :10; pub state: u32, #[cfg(not(Py_3_12))] pub wstr: *mut wchar_t, @@ -278,6 +412,7 @@ impl PyASCIIObject { /// Returns one of: [`SSTATE_NOT_INTERNED`], [`SSTATE_INTERNED_MORTAL`], /// [`SSTATE_INTERNED_IMMORTAL`], or [`SSTATE_INTERNED_IMMORTAL_STATIC`]. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn interned(&self) -> c_uint { PyASCIIObjectState::from(self.state).interned() } @@ -289,56 +424,131 @@ impl PyASCIIObject { /// [`SSTATE_INTERNED_MORTAL`], [`SSTATE_INTERNED_IMMORTAL`], or /// [`SSTATE_INTERNED_IMMORTAL_STATIC`] is invalid. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn set_interned(&mut self, val: c_uint) { let mut state = PyASCIIObjectState::from(self.state); state.set_interned(val); self.state = u32::from(state); } + #[cfg_attr(not(Py_3_12), allow(rustdoc::broken_intra_doc_links))] // SSTATE_INTERNED_IMMORTAL_STATIC requires 3.12 + /// Get the `interned` field of the [`PyASCIIObject`] state bitfield. + /// + /// Returns one of: [`SSTATE_NOT_INTERNED`], [`SSTATE_INTERNED_MORTAL`], + /// [`SSTATE_INTERNED_IMMORTAL`], or [`SSTATE_INTERNED_IMMORTAL_STATIC`]. + #[inline] + #[cfg(Py_3_14)] + pub unsafe fn interned(&self) -> u16 { + PyASCIIObjectState::from(self.state).interned() + } + + #[cfg_attr(not(Py_3_12), allow(rustdoc::broken_intra_doc_links))] // SSTATE_INTERNED_IMMORTAL_STATIC requires 3.12 + /// Set the `interned` field of the [`PyASCIIObject`] state bitfield. + /// + /// Calling this function with an argument that is not [`SSTATE_NOT_INTERNED`], + /// [`SSTATE_INTERNED_MORTAL`], [`SSTATE_INTERNED_IMMORTAL`], or + /// [`SSTATE_INTERNED_IMMORTAL_STATIC`] is invalid. + #[inline] + #[cfg(Py_3_14)] + pub unsafe fn set_interned(&mut self, val: u16) { + let mut state = PyASCIIObjectState::from(self.state); + state.set_interned(val); + self.state = u32::from(state); + } + /// Get the `kind` field of the [`PyASCIIObject`] state bitfield. /// /// Returns one of: #[cfg_attr(not(Py_3_12), doc = "[`PyUnicode_WCHAR_KIND`], ")] /// [`PyUnicode_1BYTE_KIND`], [`PyUnicode_2BYTE_KIND`], or [`PyUnicode_4BYTE_KIND`]. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn kind(&self) -> c_uint { PyASCIIObjectState::from(self.state).kind() } + /// Get the `kind` field of the [`PyASCIIObject`] state bitfield. + /// + /// Returns one of: + #[cfg_attr(not(Py_3_12), doc = "[`PyUnicode_WCHAR_KIND`], ")] + /// [`PyUnicode_1BYTE_KIND`], [`PyUnicode_2BYTE_KIND`], or [`PyUnicode_4BYTE_KIND`]. + #[inline] + #[cfg(Py_3_14)] + pub unsafe fn kind(&self) -> c_ushort { + PyASCIIObjectState::from(self.state).kind() + } + /// Set the `kind` field of the [`PyASCIIObject`] state bitfield. /// /// Calling this function with an argument that is not #[cfg_attr(not(Py_3_12), doc = "[`PyUnicode_WCHAR_KIND`], ")] /// [`PyUnicode_1BYTE_KIND`], [`PyUnicode_2BYTE_KIND`], or [`PyUnicode_4BYTE_KIND`] is invalid. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn set_kind(&mut self, val: c_uint) { let mut state = PyASCIIObjectState::from(self.state); state.set_kind(val); self.state = u32::from(state); } + /// Set the `kind` field of the [`PyASCIIObject`] state bitfield. + /// + /// Calling this function with an argument that is not + #[cfg_attr(not(Py_3_12), doc = "[`PyUnicode_WCHAR_KIND`], ")] + /// [`PyUnicode_1BYTE_KIND`], [`PyUnicode_2BYTE_KIND`], or [`PyUnicode_4BYTE_KIND`] is invalid. + #[inline] + #[cfg(Py_3_14)] + pub unsafe fn set_kind(&mut self, val: c_ushort) { + let mut state = PyASCIIObjectState::from(self.state); + state.set_kind(val); + self.state = u32::from(state); + } + /// Get the `compact` field of the [`PyASCIIObject`] state bitfield. /// /// Returns either `0` or `1`. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn compact(&self) -> c_uint { PyASCIIObjectState::from(self.state).compact() } + /// Get the `compact` field of the [`PyASCIIObject`] state bitfield. + /// + /// Returns either `0` or `1`. + #[inline] + #[cfg(Py_3_14)] + pub unsafe fn compact(&self) -> c_ushort { + PyASCIIObjectState::from(self.state).compact() + } + /// Set the `compact` flag of the [`PyASCIIObject`] state bitfield. /// /// Calling this function with an argument that is neither `0` nor `1` is invalid. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn set_compact(&mut self, val: c_uint) { let mut state = PyASCIIObjectState::from(self.state); state.set_compact(val); self.state = u32::from(state); } + /// Set the `compact` flag of the [`PyASCIIObject`] state bitfield. + /// + /// Calling this function with an argument that is neither `0` nor `1` is invalid. + #[inline] + #[cfg(Py_3_14)] + pub unsafe fn set_compact(&mut self, val: c_ushort) { + let mut state = PyASCIIObjectState::from(self.state); + state.set_compact(val); + self.state = u32::from(state); + } + /// Get the `ascii` field of the [`PyASCIIObject`] state bitfield. /// /// Returns either `0` or `1`. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn ascii(&self) -> c_uint { PyASCIIObjectState::from(self.state).ascii() } @@ -347,12 +557,33 @@ impl PyASCIIObject { /// /// Calling this function with an argument that is neither `0` nor `1` is invalid. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn set_ascii(&mut self, val: c_uint) { let mut state = PyASCIIObjectState::from(self.state); state.set_ascii(val); self.state = u32::from(state); } + /// Get the `ascii` field of the [`PyASCIIObject`] state bitfield. + /// + /// Returns either `0` or `1`. + #[inline] + #[cfg(Py_3_14)] + pub unsafe fn ascii(&self) -> c_ushort { + PyASCIIObjectState::from(self.state).ascii() + } + + /// Set the `ascii` flag of the [`PyASCIIObject`] state bitfield. + /// + /// Calling this function with an argument that is neither `0` nor `1` is invalid. + #[inline] + #[cfg(Py_3_14)] + pub unsafe fn set_ascii(&mut self, val: c_ushort) { + let mut state = PyASCIIObjectState::from(self.state); + state.set_ascii(val); + self.state = u32::from(state); + } + /// Get the `ready` field of the [`PyASCIIObject`] state bitfield. /// /// Returns either `0` or `1`. @@ -372,6 +603,46 @@ impl PyASCIIObject { state.set_ready(val); self.state = u32::from(state); } + + /// Get the `statically_allocated` field of the [`PyASCIIObject`] state bitfield. + /// + /// Returns either `0` or `1`. + #[inline] + #[cfg(all(Py_3_12, not(Py_3_14)))] + pub unsafe fn statically_allocated(&self) -> c_uint { + PyASCIIObjectState::from(self.state).statically_allocated() + } + + /// Set the `statically_allocated` flag of the [`PyASCIIObject`] state bitfield. + /// + /// Calling this function with an argument that is neither `0` nor `1` is invalid. + #[inline] + #[cfg(all(Py_3_12, not(Py_3_14)))] + pub unsafe fn set_statically_allocated(&mut self, val: c_uint) { + let mut state = PyASCIIObjectState::from(self.state); + state.set_statically_allocated(val); + self.state = u32::from(state); + } + + /// Get the `statically_allocated` field of the [`PyASCIIObject`] state bitfield. + /// + /// Returns either `0` or `1`. + #[inline] + #[cfg(Py_3_14)] + pub unsafe fn statically_allocated(&self) -> c_ushort { + PyASCIIObjectState::from(self.state).statically_allocated() + } + + /// Set the `statically_allocated` flag of the [`PyASCIIObject`] state bitfield. + /// + /// Calling this function with an argument that is neither `0` nor `1` is invalid. + #[inline] + #[cfg(Py_3_14)] + pub unsafe fn set_statically_allocated(&mut self, val: c_ushort) { + let mut state = PyASCIIObjectState::from(self.state); + state.set_statically_allocated(val); + self.state = u32::from(state); + } } #[repr(C)] @@ -413,7 +684,7 @@ pub const SSTATE_INTERNED_IMMORTAL: c_uint = 2; #[cfg(Py_3_12)] pub const SSTATE_INTERNED_IMMORTAL_STATIC: c_uint = 3; -#[cfg(not(GraalPy))] +#[cfg(all(not(GraalPy), not(Py_3_14)))] #[inline] pub unsafe fn PyUnicode_IS_ASCII(op: *mut PyObject) -> c_uint { debug_assert!(crate::PyUnicode_Check(op) != 0); @@ -423,12 +694,28 @@ pub unsafe fn PyUnicode_IS_ASCII(op: *mut PyObject) -> c_uint { (*(op as *mut PyASCIIObject)).ascii() } -#[cfg(not(GraalPy))] +#[cfg(all(not(GraalPy), not(Py_3_14)))] #[inline] pub unsafe fn PyUnicode_IS_COMPACT(op: *mut PyObject) -> c_uint { (*(op as *mut PyASCIIObject)).compact() } +#[cfg(all(not(GraalPy), Py_3_14))] +#[inline] +pub unsafe fn PyUnicode_IS_ASCII(op: *mut PyObject) -> c_ushort { + debug_assert!(crate::PyUnicode_Check(op) != 0); + #[cfg(not(Py_3_12))] + debug_assert!(PyUnicode_IS_READY(op) != 0); + + (*(op as *mut PyASCIIObject)).ascii() +} + +#[cfg(all(not(GraalPy), Py_3_14))] +#[inline] +pub unsafe fn PyUnicode_IS_COMPACT(op: *mut PyObject) -> c_ushort { + (*(op as *mut PyASCIIObject)).compact() +} + #[cfg(not(GraalPy))] #[inline] pub unsafe fn PyUnicode_IS_COMPACT_ASCII(op: *mut PyObject) -> c_uint { @@ -439,10 +726,20 @@ pub unsafe fn PyUnicode_IS_COMPACT_ASCII(op: *mut PyObject) -> c_uint { #[deprecated(note = "Removed in Python 3.12")] pub const PyUnicode_WCHAR_KIND: c_uint = 0; +#[cfg(not(Py_3_14))] pub const PyUnicode_1BYTE_KIND: c_uint = 1; +#[cfg(not(Py_3_14))] pub const PyUnicode_2BYTE_KIND: c_uint = 2; +#[cfg(not(Py_3_14))] pub const PyUnicode_4BYTE_KIND: c_uint = 4; +#[cfg(Py_3_14)] +pub const PyUnicode_1BYTE_KIND: c_ushort = 1; +#[cfg(Py_3_14)] +pub const PyUnicode_2BYTE_KIND: c_ushort = 2; +#[cfg(Py_3_14)] +pub const PyUnicode_4BYTE_KIND: c_ushort = 4; + #[cfg(not(any(GraalPy, PyPy)))] #[inline] pub unsafe fn PyUnicode_1BYTE_DATA(op: *mut PyObject) -> *mut Py_UCS1 { @@ -461,7 +758,7 @@ pub unsafe fn PyUnicode_4BYTE_DATA(op: *mut PyObject) -> *mut Py_UCS4 { PyUnicode_DATA(op) as *mut Py_UCS4 } -#[cfg(not(GraalPy))] +#[cfg(all(not(GraalPy), not(Py_3_14)))] #[inline] pub unsafe fn PyUnicode_KIND(op: *mut PyObject) -> c_uint { debug_assert!(crate::PyUnicode_Check(op) != 0); @@ -471,6 +768,16 @@ pub unsafe fn PyUnicode_KIND(op: *mut PyObject) -> c_uint { (*(op as *mut PyASCIIObject)).kind() } +#[cfg(all(not(GraalPy), Py_3_14))] +#[inline] +pub unsafe fn PyUnicode_KIND(op: *mut PyObject) -> c_ushort { + debug_assert!(crate::PyUnicode_Check(op) != 0); + #[cfg(not(Py_3_12))] + debug_assert!(PyUnicode_IS_READY(op) != 0); + + (*(op as *mut PyASCIIObject)).kind() +} + #[cfg(not(GraalPy))] #[inline] pub unsafe fn _PyUnicode_COMPACT_DATA(op: *mut PyObject) -> *mut c_void { diff --git a/src/ffi/tests.rs b/src/ffi/tests.rs index 3396e409368..95bc235aeef 100644 --- a/src/ffi/tests.rs +++ b/src/ffi/tests.rs @@ -158,6 +158,11 @@ fn ascii_object_bitfield() { o.set_ready(1); #[cfg(not(Py_3_12))] assert_eq!(o.ready(), 1); + + #[cfg(Py_3_12)] + o.set_statically_allocated(1); + #[cfg(Py_3_12)] + assert_eq!(o.statically_allocated(), 1); } }