Skip to content

Commit 7a2fc86

Browse files
authored
Merge pull request #3342 from davidhewitt/3.12-ffi-check-fixes
fix ffi check failures for 3.12.0b4
2 parents 8ce6c26 + 655de94 commit 7a2fc86

File tree

10 files changed

+93
-20
lines changed

10 files changed

+93
-20
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ on:
2525

2626
jobs:
2727
build:
28-
continue-on-error: ${{ contains(fromJSON('["3.7", "3.12-dev", "pypy3.7", "pypy3.10"]'), inputs.python-version) }}
28+
continue-on-error: ${{ endsWith(inputs.python-version, '-dev') || contains(fromJSON('["3.7", "pypy3.7", "pypy3.10"]'), inputs.python-version) }}
2929
runs-on: ${{ inputs.os }}
3030
steps:
3131
- uses: actions/checkout@v3
@@ -138,7 +138,7 @@ jobs:
138138
- name: Run pyo3-ffi-check
139139
# pypy 3.7 and 3.8 are not PEP 3123 compliant so fail checks here, nor
140140
# is pypy 3.9 on windows
141-
if: ${{ steps.ffi-changes.outputs.changed == 'true' && inputs.rust == 'stable' && inputs.python-version != 'pypy3.7' && inputs.python-version != 'pypy3.8' && !(inputs.python-version == 'pypy3.9' && contains(inputs.os, 'windows')) }}
141+
if: ${{ endsWith(inputs.python-version, '-dev') || (steps.ffi-changes.outputs.changed == 'true' && inputs.rust == 'stable' && inputs.python-version != 'pypy3.7' && inputs.python-version != 'pypy3.8' && !(inputs.python-version == 'pypy3.9' && contains(inputs.os, 'windows'))) }}
142142
run: nox -s ffi-check
143143

144144

newsfragments/3342.changed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Update `pyo3::ffi` struct definitions to be compatible with 3.12.0b4.

pyo3-ffi-check/macro/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ glob = "0.3"
1212
quote = "1"
1313
proc-macro2 = "1"
1414
scraper = "0.17"
15+
pyo3-build-config = { path = "../../pyo3-build-config" }

pyo3-ffi-check/macro/src/lib.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
use std::{env, fs, path::PathBuf};
22

33
use proc_macro2::{Ident, Span, TokenStream, TokenTree};
4+
use pyo3_build_config::PythonVersion;
45
use quote::quote;
56

7+
const PY_3_12: PythonVersion = PythonVersion {
8+
major: 3,
9+
minor: 12,
10+
};
11+
612
/// Macro which expands to multiple macro calls, one per pyo3-ffi struct.
713
#[proc_macro]
814
pub fn for_all_structs(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
@@ -130,16 +136,27 @@ pub fn for_all_fields(input: proc_macro::TokenStream) -> proc_macro::TokenStream
130136
let mut output = TokenStream::new();
131137

132138
for el in html.select(&selector) {
133-
let id = el
139+
let field_name = el
134140
.value()
135141
.id()
136142
.unwrap()
137143
.strip_prefix("structfield.")
138144
.unwrap();
139145

140-
let field_ident = Ident::new(id, Span::call_site());
146+
let field_ident = Ident::new(field_name, Span::call_site());
147+
148+
let bindgen_field_ident = if (pyo3_build_config::get().version >= PY_3_12)
149+
&& struct_name == "PyObject"
150+
&& field_name == "ob_refcnt"
151+
{
152+
// PyObject since 3.12 implements ob_refcnt as a union; bindgen creates
153+
// an anonymous name for the field
154+
Ident::new("__bindgen_anon_1", Span::call_site())
155+
} else {
156+
field_ident.clone()
157+
};
141158

142-
output.extend(quote!(#macro_name!(#struct_name, #field_ident);));
159+
output.extend(quote!(#macro_name!(#struct_name, #field_ident, #bindgen_field_ident);));
143160
}
144161

145162
output.into()

pyo3-ffi-check/src/main.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,11 @@ fn main() {
4747
}
4848

4949
macro_rules! check_field {
50-
($struct_name:ident, $field:ident) => {{
50+
($struct_name:ident, $field:ident, $bindgen_field:ident) => {{
51+
#[allow(clippy::used_underscore_binding)]
5152
let pyo3_ffi_offset = memoffset::offset_of!(pyo3_ffi::$struct_name, $field);
52-
let bindgen_offset = memoffset::offset_of!(bindings::$struct_name, $field);
53+
#[allow(clippy::used_underscore_binding)]
54+
let bindgen_offset = memoffset::offset_of!(bindings::$struct_name, $bindgen_field);
5355

5456
if pyo3_ffi_offset != bindgen_offset {
5557
failed = true;
@@ -79,7 +81,9 @@ fn main() {
7981
non_upper_case_globals,
8082
dead_code,
8183
improper_ctypes,
82-
clippy::all
84+
clippy::all,
85+
// clippy fails with lots of errors if this is not set specifically
86+
clippy::used_underscore_binding
8387
)]
8488
mod bindings {
8589
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

pyo3-ffi/src/cpython/code.rs

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,30 @@ use std::os::raw::{c_char, c_int, c_short, c_uchar, c_void};
66
#[cfg(not(PyPy))]
77
use std::ptr::addr_of_mut;
88

9+
#[cfg(all(Py_3_8, not(PyPy), not(Py_3_11)))]
10+
opaque_struct!(_PyOpcache);
11+
12+
pub const _PY_MONITORING_UNGROUPED_EVENTS: usize = 14;
13+
pub const _PY_MONITORING_EVENTS: usize = 16;
14+
15+
#[cfg(Py_3_12)]
16+
#[repr(C)]
17+
#[derive(Copy, Clone)]
18+
pub struct _Py_Monitors {
19+
pub tools: [u8; _PY_MONITORING_UNGROUPED_EVENTS],
20+
}
21+
922
// skipped _Py_CODEUNIT
23+
1024
// skipped _Py_OPCODE
1125
// skipped _Py_OPARG
1226

13-
#[cfg(all(Py_3_8, not(PyPy), not(Py_3_11)))]
14-
opaque_struct!(_PyOpcache);
27+
// skipped _py_make_codeunit
28+
29+
// skipped _py_set_opcode
30+
31+
// skipped _Py_MAKE_CODEUNIT
32+
// skipped _Py_SET_OPCODE
1533

1634
#[cfg(Py_3_12)]
1735
#[repr(C)]
@@ -23,6 +41,27 @@ pub struct _PyCoCached {
2341
pub _co_freevars: *mut PyObject,
2442
}
2543

44+
#[cfg(Py_3_12)]
45+
#[repr(C)]
46+
#[derive(Copy, Clone)]
47+
pub struct _PyCoLineInstrumentationData {
48+
pub original_opcode: u8,
49+
pub line_delta: i8,
50+
}
51+
52+
#[cfg(Py_3_12)]
53+
#[repr(C)]
54+
#[derive(Copy, Clone)]
55+
pub struct _PyCoMonitoringData {
56+
pub local_monitors: _Py_Monitors,
57+
pub active_monitors: _Py_Monitors,
58+
pub tools: *mut u8,
59+
pub lines: *mut _PyCoLineInstrumentationData,
60+
pub line_tools: *mut u8,
61+
pub per_instruction_opcodes: *mut u8,
62+
pub per_instruction_tools: *mut u8,
63+
}
64+
2665
#[cfg(all(not(PyPy), not(Py_3_7)))]
2766
opaque_struct!(PyCodeObject);
2867

@@ -97,8 +136,7 @@ pub struct PyCodeObject {
97136
pub co_flags: c_int,
98137
#[cfg(not(Py_3_12))]
99138
pub co_warmup: c_int,
100-
#[cfg(Py_3_12)]
101-
pub _co_linearray_entry_size: c_short,
139+
102140
pub co_argcount: c_int,
103141
pub co_posonlyargcount: c_int,
104142
pub co_kwonlyargcount: c_int,
@@ -109,9 +147,12 @@ pub struct PyCodeObject {
109147
#[cfg(Py_3_12)]
110148
pub co_framesize: c_int,
111149
pub co_nlocals: c_int,
150+
#[cfg(not(Py_3_12))]
112151
pub co_nplaincellvars: c_int,
113152
pub co_ncellvars: c_int,
114153
pub co_nfreevars: c_int,
154+
#[cfg(Py_3_12)]
155+
pub co_version: u32,
115156

116157
pub co_localsplusnames: *mut PyObject,
117158
pub co_localspluskinds: *mut PyObject,
@@ -122,13 +163,15 @@ pub struct PyCodeObject {
122163
pub co_weakreflist: *mut PyObject,
123164
#[cfg(not(Py_3_12))]
124165
pub _co_code: *mut PyObject,
125-
#[cfg(Py_3_12)]
126-
pub _co_cached: *mut _PyCoCached,
127166
#[cfg(not(Py_3_12))]
128167
pub _co_linearray: *mut c_char,
129-
pub _co_firsttraceable: c_int,
130168
#[cfg(Py_3_12)]
131-
pub _co_linearray: *mut c_char,
169+
pub _co_cached: *mut _PyCoCached,
170+
#[cfg(Py_3_12)]
171+
pub _co_instrumentation_version: u64,
172+
#[cfg(Py_3_12)]
173+
pub _co_monitoring: *mut _PyCoMonitoringData,
174+
pub _co_firsttraceable: c_int,
132175
pub co_extra: *mut c_void,
133176
pub co_code_adaptive: [c_char; 1],
134177
}

pyo3-ffi/src/cpython/funcobject.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ pub struct PyFunctionObject {
4141
pub func_weakreflist: *mut PyObject,
4242
pub func_module: *mut PyObject,
4343
pub func_annotations: *mut PyObject,
44+
#[cfg(Py_3_12)]
45+
pub func_typeparams: *mut PyObject,
4446
pub vectorcall: Option<crate::vectorcallfunc>,
4547
#[cfg(Py_3_11)]
4648
pub func_version: u32,

pyo3-ffi/src/cpython/genobject.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub struct PyGenObject {
1616
pub gi_frame: *mut PyFrameObject,
1717
#[cfg(not(Py_3_10))]
1818
pub gi_running: c_int,
19+
#[cfg(not(Py_3_12))]
1920
pub gi_code: *mut PyObject,
2021
pub gi_weakreflist: *mut PyObject,
2122
pub gi_name: *mut PyObject,

pyo3-ffi/src/cpython/object.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,8 @@ pub struct PyTypeObject {
299299
#[derive(Clone)]
300300
pub struct _specialization_cache {
301301
pub getitem: *mut PyObject,
302+
#[cfg(Py_3_12)]
303+
pub getitem_version: u32,
302304
}
303305

304306
#[repr(C)]

pyo3-ffi/src/cpython/unicodeobject.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -287,17 +287,19 @@ impl PyASCIIObject {
287287

288288
/// Get the `kind` field of the [`PyASCIIObject`] state bitfield.
289289
///
290-
/// Returns one of: [`PyUnicode_WCHAR_KIND`], [`PyUnicode_1BYTE_KIND`], [`PyUnicode_2BYTE_KIND`],
291-
/// [`PyUnicode_4BYTE_KIND`]
290+
/// Returns one of:
291+
#[cfg_attr(not(Py_3_12), doc = "[`PyUnicode_WCHAR_KIND`], ")]
292+
/// [`PyUnicode_1BYTE_KIND`], [`PyUnicode_2BYTE_KIND`], or [`PyUnicode_4BYTE_KIND`].
292293
#[inline]
293294
pub unsafe fn kind(&self) -> c_uint {
294295
PyASCIIObjectState::from(self.state).kind()
295296
}
296297

297298
/// Set the `kind` field of the [`PyASCIIObject`] state bitfield.
298299
///
299-
/// Calling this function with an argument that is not [`PyUnicode_WCHAR_KIND`], [`PyUnicode_1BYTE_KIND`],
300-
/// [`PyUnicode_2BYTE_KIND`], or [`PyUnicode_4BYTE_KIND`] is invalid.
300+
/// Calling this function with an argument that is not
301+
#[cfg_attr(not(Py_3_12), doc = "[`PyUnicode_WCHAR_KIND`], ")]
302+
/// [`PyUnicode_1BYTE_KIND`], [`PyUnicode_2BYTE_KIND`], or [`PyUnicode_4BYTE_KIND`] is invalid.
301303
#[inline]
302304
pub unsafe fn set_kind(&mut self, val: c_uint) {
303305
let mut state = PyASCIIObjectState::from(self.state);

0 commit comments

Comments
 (0)