]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-149044: Implement PEP 820 – PySlot: Unified slot system for the C API (GH-149055)
authorPetr Viktorin <encukou@gmail.com>
Tue, 5 May 2026 07:18:04 +0000 (09:18 +0200)
committerGitHub <noreply@github.com>
Tue, 5 May 2026 07:18:04 +0000 (09:18 +0200)
Co-authored-by: Peter Bierma <zintensitydev@gmail.com>
56 files changed:
.gitattributes
Doc/c-api/extension-modules.rst
Doc/c-api/index.rst
Doc/c-api/module.rst
Doc/c-api/slots.rst [new file with mode: 0644]
Doc/c-api/type.rst
Doc/c-api/typeobj.rst
Doc/data/stable_abi.dat
Doc/extending/first-extension-module.rst
Doc/includes/capi-extension/spammodule-01.c
Doc/whatsnew/3.15.rst
Include/Python.h
Include/exports.h
Include/internal/pycore_importdl.h
Include/internal/pycore_slots.h [new file with mode: 0644]
Include/internal/pycore_slots_generated.h [new file with mode: 0644]
Include/moduleobject.h
Include/object.h
Include/pytypedefs.h
Include/slots.h [new file with mode: 0644]
Include/slots_generated.h [moved from Include/typeslots.h with 60% similarity]
Lib/test/test_capi/test_misc.py
Lib/test/test_capi/test_module.py
Lib/test/test_capi/test_slots.py [new file with mode: 0644]
Lib/test/test_cext/extension.c
Lib/test/test_stable_abi_ctypes.py
Makefile.pre.in
Misc/NEWS.d/next/C_API/2026-04-27-10-56-22.gh-issue-149044.TbOcUS.rst [new file with mode: 0644]
Misc/stable_abi.toml
Modules/Setup.stdlib.in
Modules/_testcapi/module.c
Modules/_testcapi/type.c
Modules/_testlimitedcapi.c
Modules/_testlimitedcapi/parts.h
Modules/_testlimitedcapi/slots.c [new file with mode: 0644]
Modules/_testmultiphase.c
Objects/moduleobject.c
Objects/typeobject.c
Objects/typeslots.inc [deleted file]
Objects/typeslots.py [deleted file]
PC/python3dll.c
PCbuild/_freeze_module.vcxproj
PCbuild/_freeze_module.vcxproj.filters
PCbuild/_testlimitedcapi.vcxproj
PCbuild/_testlimitedcapi.vcxproj.filters
PCbuild/pythoncore.vcxproj
PCbuild/pythoncore.vcxproj.filters
Python/import.c
Python/slots.c [new file with mode: 0644]
Python/slots.toml [new file with mode: 0644]
Python/slots_generated.c [new file with mode: 0644]
Tools/build/.ruff.toml
Tools/build/generate_slots.py [new file with mode: 0755]
Tools/c-analyzer/cpython/ignored.tsv
configure
configure.ac

index f4d65dfd1dfbd3bba0c37bd4ab3d589443894757..4d52f900b93179e3fb5e3ef641fe9dec8773f18a 100644 (file)
@@ -84,6 +84,7 @@ Include/internal/pycore_uop_ids.h                   generated
 Include/internal/pycore_uop_metadata.h              generated
 Include/opcode.h                                    generated
 Include/opcode_ids.h                                generated
+Include/slots_generated.h                           generated
 Include/token.h                                     generated
 Lib/_opcode_metadata.py                             generated
 Lib/idlelib/help.html                               generated
@@ -99,7 +100,6 @@ Lib/token.py                                        generated
 Misc/sbom.spdx.json                                 generated
 Modules/_testinternalcapi/test_cases.c.h            generated
 Modules/_testinternalcapi/test_targets.h            generated
-Objects/typeslots.inc                               generated
 PC/python3dll.c                                     generated
 Parser/parser.c                                     generated
 Parser/token.c                                      generated
@@ -110,6 +110,7 @@ Python/generated_cases.c.h                          generated
 Python/optimizer_cases.c.h                          generated
 Python/opcode_targets.h                             generated
 Python/record_functions.c.h                         generated
+Python/slots_generated.c                            generated
 Python/stdlib_module_names.h                        generated
 Tools/peg_generator/pegen/grammar_parser.py         generated
 aclocal.m4                                          generated
index 7bc04970b19503cb28943865d819151ec51ade87..34ee86c7876ae742d682c3278ac4eff7e437b951 100644 (file)
@@ -38,7 +38,7 @@ Extension export hook
 
 The export hook must be an exported function with the following signature:
 
-.. c:function:: PyModuleDef_Slot *PyModExport_modulename(void)
+.. c:function:: PySlot *PyModExport_modulename(void)
 
 For modules with ASCII-only names, the :ref:`export hook <extension-export-hook>`
 must be named :samp:`PyModExport_{<name>}`,
@@ -57,7 +57,7 @@ Python's *punycode* encoding with hyphens replaced by underscores. In Python:
             suffix = b'U_' + name.encode('punycode').replace(b'-', b'_')
         return b'PyModExport' + suffix
 
-The export hook returns an array of :c:type:`PyModuleDef_Slot` entries,
+The export hook returns an array of :c:type:`PySlot` entries,
 terminated by an entry with a slot ID of ``0``.
 These slots describe how the module should be created and initialized.
 
@@ -75,7 +75,7 @@ It is recommended to define the export hook function using a helper macro:
    Declare an extension module export hook.
    This macro:
 
-   * specifies the :c:expr:`PyModuleDef_Slot*` return type,
+   * specifies the :c:expr:`PySlot*` return type,
    * adds any special linkage declarations required by the platform, and
    * for C++, declares the function as ``extern "C"``.
 
@@ -83,12 +83,12 @@ For example, a module called ``spam`` would be defined like this::
 
    PyABIInfo_VAR(abi_info);
 
-   static PyModuleDef_Slot spam_slots[] = {
-       {Py_mod_abi, &abi_info},
-       {Py_mod_name, "spam"},
-       {Py_mod_init, spam_init_function},
+   static PySlot spam_slots[] = {
+       PySlot_STATIC_DATA(Py_mod_abi, &abi_info),
+       PySlot_STATIC_DATA(Py_mod_name, "spam"),
+       PySlot_FUNC(Py_mod_init, spam_init_function),
        ...
-       {0, NULL},
+       PySlot_END
    };
 
    PyMODEXPORT_FUNC
index eabe00f4004001f3c9848037a17080926d141177..051f6fd765e8506cc9b73af93e91fe8bcecb5500 100644 (file)
@@ -18,6 +18,7 @@ document the API functions in detail.
    refcounting.rst
    exceptions.rst
    extension-modules.rst
+   slots.rst
    utilities.rst
    abstract.rst
    concrete.rst
index b67ca671a2a11852628a8f48bc5a3f2d5bad71e1..7229ea6c69ee4615835dc7ccfbc61203cad5eee9 100644 (file)
@@ -133,14 +133,16 @@ Module Objects
       unencodable filenames, use :c:func:`PyModule_GetFilenameObject` instead.
 
 
+.. _c_module_slots:
 .. _pymoduledef_slot:
 
 Module definition
 -----------------
 
 Modules created using the C API are typically defined using an
-array of :dfn:`slots`.
-The slots provide a "description" of how a module should be created.
+array of :c:type:`PySlot` structs, which provides a "description" of how a
+module should be created.
+See :ref:`capi-slots` for more information on slots in general.
 
 .. versionchanged:: 3.15
 
@@ -158,30 +160,12 @@ Unless specified otherwise, the same slot ID may not be repeated
 in an array of slots.
 
 
-.. c:type:: PyModuleDef_Slot
-
-   .. c:member:: int slot
-
-      A slot ID, chosen from the available ``Py_mod_*`` values explained below.
-
-      An ID of 0 marks the end of a :c:type:`!PyModuleDef_Slot` array.
-
-   .. c:member:: void* value
-
-      Value of the slot, whose meaning depends on the slot ID.
-
-      The value may not be NULL.
-      To leave a slot out, omit the :c:type:`PyModuleDef_Slot` entry entirely.
-
-   .. versionadded:: 3.5
-
-
 Metadata slots
 ..............
 
 .. c:macro:: Py_mod_name
 
-   :c:type:`Slot ID <PyModuleDef_Slot.slot>` for the name of the new module,
+   :c:member:`Slot ID <PySlot.sl_id>` for the name of the new module,
    as a NUL-terminated UTF8-encoded ``const char *``.
 
    Note that modules are typically created using a
@@ -196,7 +180,7 @@ Metadata slots
 
 .. c:macro:: Py_mod_doc
 
-   :c:type:`Slot ID <PyModuleDef_Slot.slot>` for the docstring of the new
+   :c:type:`Slot ID <PySlot.sl_id>` for the docstring of the new
    module, as a NUL-terminated UTF8-encoded ``const char *``.
 
    Usually it is set to a variable created with :c:macro:`PyDoc_STRVAR`.
@@ -211,7 +195,7 @@ Feature slots
 
 .. c:macro:: Py_mod_abi
 
-   :c:type:`Slot ID <PyModuleDef_Slot.slot>` whose value points to
+   :c:member:`Slot ID <PySlot.sl_id>` whose value points to
    a :c:struct:`PyABIInfo` structure describing the ABI that
    the extension is using.
 
@@ -222,8 +206,8 @@ Feature slots
 
       PyABIInfo_VAR(abi_info);
 
-      static PyModuleDef_Slot mymodule_slots[] = {
-         {Py_mod_abi, &abi_info},
+      static PySlot mymodule_slots[] = {
+         PySlot_DATA(Py_mod_abi, &abi_info),
          ...
       };
 
@@ -237,7 +221,7 @@ Feature slots
 
 .. c:macro:: Py_mod_multiple_interpreters
 
-   :c:type:`Slot ID <PyModuleDef_Slot.slot>` whose value is one of:
+   :c:member:`Slot ID <PySlot.sl_id>` whose value is one of:
 
    .. c:namespace:: NULL
 
@@ -267,7 +251,7 @@ Feature slots
 
 .. c:macro:: Py_mod_gil
 
-   :c:type:`Slot ID <PyModuleDef_Slot.slot>` whose value is one of:
+   :c:member:`Slot ID <PySlot.sl_id>` whose value is one of:
 
    .. c:namespace:: NULL
 
@@ -296,7 +280,7 @@ Creation and initialization slots
 
 .. c:macro:: Py_mod_create
 
-   :c:type:`Slot ID <PyModuleDef_Slot.slot>` for a function that creates
+   :c:member:`Slot ID <PySlot.sl_id>` for a function that creates
    the module object itself.
    The function must have the signature:
 
@@ -346,7 +330,7 @@ Creation and initialization slots
 
 .. c:macro:: Py_mod_exec
 
-   :c:type:`Slot ID <PyModuleDef_Slot.slot>` for a function that will
+   :c:member:`Slot ID <PySlot.sl_id>` for a function that will
    :dfn:`execute`, or initialize, the module.
    This function does the equivalent to executing the code of a Python module:
    typically, it adds classes and constants to the module.
@@ -375,7 +359,7 @@ Creation and initialization slots
 
 .. c:macro:: Py_mod_methods
 
-   :c:type:`Slot ID <PyModuleDef_Slot.slot>` for a table of module-level
+   :c:member:`Slot ID <PySlot.sl_id>` for a table of module-level
    functions, as an array of :c:type:`PyMethodDef` values suitable as the
    *functions* argument to :c:func:`PyModule_AddFunctions`.
 
@@ -446,12 +430,12 @@ To retrieve the state from a given module, use the following functions:
 Slots for defining module state
 ...............................
 
-The following :c:member:`PyModuleDef_Slot.slot` IDs are available for
+The following :c:member:`slot IDs <PySlot.sl_id>`  are available for
 defining the module state.
 
 .. c:macro:: Py_mod_state_size
 
-   :c:type:`Slot ID <PyModuleDef_Slot.slot>` for the size of the module state,
+   :c:member:`Slot ID <PySlot.sl_id>` for the size of the module state,
    in bytes.
 
    Setting the value to a non-negative value means that the module can be
@@ -468,7 +452,7 @@ defining the module state.
 
 .. c:macro:: Py_mod_state_traverse
 
-   :c:type:`Slot ID <PyModuleDef_Slot.slot>` for a traversal function to call
+   :c:member:`Slot ID <PySlot.sl_id>` for a traversal function to call
    during GC traversal of the module object.
 
    The signature of the function, and meanings of the arguments,
@@ -491,7 +475,7 @@ defining the module state.
 
 .. c:macro:: Py_mod_state_clear
 
-   :c:type:`Slot ID <PyModuleDef_Slot.slot>` for a clear function to call
+   :c:member:`Slot ID <PySlot.sl_id>` for a clear function to call
    during GC clearing of the module object.
 
    The signature of the function is:
@@ -519,7 +503,7 @@ defining the module state.
 
 .. c:macro:: Py_mod_state_free
 
-   :c:type:`Slot ID <PyModuleDef_Slot.slot>` for a function to call during
+   :c:member:`Slot ID <PySlot.sl_id>` for a function to call during
    deallocation of the module object.
 
    The signature of the function is:
@@ -578,7 +562,7 @@ A module's token -- and the *your_token* value to use in the above code -- is:
 
 .. c:macro:: Py_mod_token
 
-   :c:type:`Slot ID <PyModuleDef_Slot.slot>` for the module token.
+   :c:member:`Slot ID <PySlot.sl_id>` for the module token.
 
    If you use this slot to set the module token (rather than rely on the
    default), you must ensure that:
@@ -617,14 +601,14 @@ Creating extension modules dynamically
 The following functions may be used to create an extension module dynamically,
 rather than from an extension's :ref:`export hook <extension-export-hook>`.
 
-.. c:function:: PyObject *PyModule_FromSlotsAndSpec(const PyModuleDef_Slot *slots, PyObject *spec)
+.. c:function:: PyObject *PyModule_FromSlotsAndSpec(const PySlot *slots, PyObject *spec)
 
    Create a new module object, given an array of :ref:`slots <pymoduledef_slot>`
    and the :py:class:`~importlib.machinery.ModuleSpec` *spec*.
 
-   The *slots* argument must point to an array of :c:type:`PyModuleDef_Slot`
+   The *slots* argument must point to an array of :c:type:`PySlot`
    structures, terminated by an entry with slot ID of 0
-   (typically written as ``{0}`` or ``{0, NULL}`` in C).
+   (typically written as :c:macro:`PySlot_END`).
    The array must include a :c:data:`Py_mod_abi` entry.
 
    The *spec* argument may be any ``ModuleSpec``-like object, as described
@@ -640,10 +624,6 @@ rather than from an extension's :ref:`export hook <extension-export-hook>`.
    must be called to fully initialize a module.
    (See also :ref:`multi-phase-initialization`.)
 
-   The *slots* array only needs to be valid for the duration of the
-   :c:func:`!PyModule_FromSlotsAndSpec` call.
-   In particular, it may be heap-allocated.
-
    .. versionadded:: 3.15
 
 .. c:function:: int PyModule_Exec(PyObject *module)
@@ -738,6 +718,8 @@ remove it.
    .. c:member:: PyModuleDef_Slot* m_slots
 
       An array of additional slots, terminated by a ``{0, NULL}`` entry.
+      Note that the entries use the older :c:type:`PyModuleDef_Slot` structure,
+      rather than :c:type:`PySlot`.
 
       If the array contains slots corresponding to :c:type:`PyModuleDef`
       members, the values must match.
@@ -752,6 +734,39 @@ remove it.
 
            .. c:member:: inquiry m_reload
 
+      .. c:namespace:: NULL
+
+      .. c:type:: PyModuleDef_Slot
+
+         Older structure defining additional slots of a module.
+
+         Note that a :c:type:`!PyModuleDef_Slot` array may be included in a
+         :c:type:`!PySlot` array using :c:macro:`Py_mod_slots`,
+         and vice versa using :c:macro:`Py_slot_subslots`.
+
+         Each :c:type:`!PyModuleDef_Slot` structure ``modslot`` is interpreted
+         as the following :c:type:`PySlot` structure::
+
+            (PySlot){
+               .sl_id=modslot.slot,
+               .sl_flags=PySlot_INTPTR | sub_static,
+               .sl_ptr=modslot.value
+            }
+
+         where ``sub_static`` is ``PySlot_STATIC`` if the slot requires
+         the flag (such as for :c:macro:`Py_mod_methods`), or if this flag
+         is present on the "parent" :c:macro:`!Py_mod_slots` slot (if any).
+
+         .. c:member:: int slot
+
+            Corresponds to :c:member:`PySlot.sl_id`.
+
+         .. c:member:: void* value
+
+            Corresponds to :c:member:`PySlot.sl_ptr`.
+
+         .. versionadded:: 3.5
+
    .. c:member:: traverseproc m_traverse
                  inquiry m_clear
                  freefunc m_free
@@ -774,6 +789,14 @@ remove it.
    The type of ``PyModuleDef`` objects.
 
 
+.. c:macro:: Py_mod_slots
+
+   :c:member:`Slot ID <PySlot.sl_id>` that works like
+   :c:macro:`Py_slot_subslots`, except it specifies an array of
+   :c:type:`PyModuleDef_Slot` structures.
+
+   .. versionadded:: next
+
 .. _moduledef-dynamic:
 
 The following API can be used to create modules from a :c:type:`!PyModuleDef`
@@ -813,6 +836,10 @@ struct:
 
    .. versionadded:: 3.5
 
+   .. soft-deprecated:: next
+
+      Prefer :c:func:`PyModule_FromSlotsAndSpec` in new code.
+
 .. c:function:: PyObject * PyModule_FromDefAndSpec2(PyModuleDef *def, PyObject *spec, int module_api_version)
 
    Create a new module object, given the definition in *def* and the
@@ -833,12 +860,22 @@ struct:
 
    .. versionadded:: 3.5
 
+   .. soft-deprecated:: next
+
+      Prefer :c:func:`PyModule_FromSlotsAndSpec` in new code.
+
 .. c:function:: int PyModule_ExecDef(PyObject *module, PyModuleDef *def)
 
    Process any execution slots (:c:data:`Py_mod_exec`) given in *def*.
 
    .. versionadded:: 3.5
 
+   .. soft-deprecated:: next
+
+      To run a module's own execution slots, prefer :c:func:`PyModule_Exec`,
+      which works on modules that were not created from a
+      :c:type:`PyModuleDef` structure.
+
 .. c:macro:: PYTHON_API_VERSION
              PYTHON_API_STRING
 
diff --git a/Doc/c-api/slots.rst b/Doc/c-api/slots.rst
new file mode 100644 (file)
index 0000000..66fb6cd
--- /dev/null
@@ -0,0 +1,240 @@
+.. highlight:: c
+
+.. _capi-slots:
+
+Definition slots
+================
+
+To define :ref:`module objects <moduleobjects>` and
+:ref:`classes <creating-heap-types>` using the C API, you may use
+an array of *slots* -- essentally, key-value pairs that describe features
+of the object to create.
+This decouples the data from the structures used at runtime, allowing CPython
+-- and other Python C API implementations -- to update the stuctures without
+breaking backwards compatibility.
+
+This section documents slots in general.
+For object-specific behavior and slot values, see documentation for functions
+that apply slots:
+
+- :c:func:`PyType_FromSlots` for types;
+- :c:func:`PyModule_FromSlotsAndSpec` and :ref:`extension-export-hook`
+  for modules.
+
+When slots are passed to a function that applies them, the function will
+not modify the slot array, nor any data it points to (recursively).
+After the function is done, the caller is allowed to modify or deallocate
+the array and any data it points to (recursively), except data
+explicitly marked with :c:macro:`PySlot_STATIC`.
+
+Except when documented otherwise, multiple slots with the same ID
+(:c:member:`~PySlot.sl_id`) may not occur in a single slots array.
+
+.. versionadded:: next
+
+   Slot arrays generalize an earlier way of defining objects:
+   using :c:type:`PyType_Spec` with :c:type:`PyType_Slot` for types, and
+   :c:type:`PyModuleDef` with :c:type:`PyModuleDef_Slot` for modules.
+   The earlier API is :term:`soft deprecated`; there are no plans to remove it.
+
+Entries of the slots array use the following structure:
+
+.. c:type:: PySlot
+
+   An entry in a slots array. Defined as:
+
+   .. code-block:: c
+
+    typedef struct {
+        uint16_t sl_id;
+        uint16_t sl_flags;
+        uint32_t _reserved;  // must be 0
+        union {
+            void *sl_ptr;
+            void (*sl_func)(void);
+            Py_ssize_t sl_size;
+            int64_t sl_int64;
+            uint64_t sl_uint64;
+        };
+    } PySlot;
+
+   .. c:member:: uint16_t sl_id
+
+      A slot ID, chosen from:
+
+      - ``Py_slot_*`` values documented in :ref:`pyslot-common-ids` below;
+      - ``Py_mod_*`` values for modules, as documented in :ref:`c_module_slots`;
+      - Values for types, as documented in :ref:`pyslot_type_slot_ids`.
+
+      A :c:member:`!sl_id` of zero (:c:macro:`Py_slot_end`) marks the end of a
+      slots array.
+
+   .. c:member:: void *sl_ptr
+                 void (*sl_func)(void)
+                 Py_ssize_t sl_size
+                 int64_t sl_int64
+                 uint64_t sl_uint64
+
+      The data for the slot.
+      These members are part of an anonymous union;
+      the member to use depends on which data type is required by the slot ID:
+      data pointer, function pointer, size, signed or unsigned
+      integer, respectively.
+
+      Except when documented otherwise for a specific slot ID, pointers
+      (that is :c:member:`!sl_ptr` and :c:member:`!sl_func`) may not be NULL.
+
+   .. c:member:: uint16_t sl_flags
+
+      Zero or more of the following flags, OR-ed together:
+
+      .. c:namespace:: NULL
+
+      .. c:macro:: PySlot_STATIC
+
+         All data the slot points to is statically allocated and constant.
+         Thus, the interpreter does not need to copy the information.
+
+         This flag is implied for function pointers.
+
+         The flag applies even to data the slot points to “indirectly”,
+         except for slots nested via :c:macro:`Py_slot_subslots` which may
+         have their own :c:macro:`!PySlot_STATIC` flags.
+         For example, if applied to a :c:macro:`Py_tp_members` slot that
+         points to an array of :c:type:`PyMemberDef` structures,
+         then the entire array, as well as the name and doc strings
+         in its elements, must be static and constant.
+
+      .. c:macro:: PySlot_INTPTR
+
+         The data is stored in ``sl_ptr``; CPython will cast it to
+         the appropriate type.
+
+         This flag can simplify porting from the older :c:type:`PyType_Slot`
+         and :c:type:`PyModuleDef_Slot` structures.
+
+      .. c:macro:: PySlot_OPTIONAL
+
+         If the slot ID is unknown, the interpreter should ignore the
+         slot, rather than fail.
+
+         For example, if Python 3.16 adds a new feature with a new slot ID,attr
+         the corresponding slot may be marked :c:macro:`!PySlot_OPTIONAL`
+         so that Python 3.15 ignores it.
+
+         Note that the "optionality" only applies to unknown slot IDs.
+         This flag does not make Python skip invalid values of known slots.
+
+   .. versionadded:: next
+
+
+Convenience macros
+------------------
+
+.. c:macro:: PySlot_DATA(name, value)
+             PySlot_FUNC(name, value)
+             PySlot_SIZE(name, value)
+             PySlot_INT64(name, value)
+             PySlot_UINT64(name, value)
+             PySlot_STATIC_DATA(name, value)
+
+   Convenience macros to define :c:type:`!PySlot` structures with
+   :c:member:`~PySlot.sl_id` and a particular union member set.
+
+   :c:macro:`!PySlot_STATIC_DATA` sets the :c:macro:`PySlot_STATIC` flag;
+   others set no flags.
+
+   Note that these macros use *designated initializers*, a C language feature
+   that C++ added in the 2020 version of the standard.
+   If your code needs to be compatible with C++11 or older,
+   use :c:macro:`PySlot_PTR` instead.
+
+   Defined as::
+
+      #define PySlot_DATA(NAME, VALUE) \
+         {.sl_id=NAME, .sl_ptr=(void*)(VALUE)}
+
+      #define PySlot_FUNC(NAME, VALUE) \
+         {.sl_id=NAME, .sl_func=(VALUE)}
+
+      #define PySlot_SIZE(NAME, VALUE) \
+         {.sl_id=NAME, .sl_size=(VALUE)}
+
+      #define PySlot_INT64(NAME, VALUE) \
+         {.sl_id=NAME, .sl_int64=(VALUE)}
+
+      #define PySlot_UINT64(NAME, VALUE) \
+         {.sl_id=NAME, .sl_uint64=(VALUE)}
+
+      #define PySlot_STATIC_DATA(NAME, VALUE) \
+         {.sl_id=NAME, .sl_flags=PySlot_STATIC, .sl_ptr=(VALUE)}
+
+   .. versionadded:: next
+
+.. c:macro:: PySlot_END
+
+   Convenience macro to mark the end of a :c:type:`!PySlot` array.
+
+   Defined as::
+
+      #define PySlot_END {0}
+
+   .. versionadded:: next
+
+.. c:macro:: PySlot_PTR(name, value)
+             PySlot_PTR_STATIC(name, value)
+
+   Convenience macros for use in C++11-compatible code.
+   This version of C++ does not allow setting arbitrary union members in
+   literals; instead, these macros set the :c:macro:`PySlot_INTPTR` flag and cast
+   the value to ``(void*)``.
+
+   Defined as::
+
+      #define PySlot_PTR(NAME, VALUE) \
+         {NAME, PySlot_INTPTR, {0}, {(void*)(VALUE)}}
+
+      #define PySlot_PTR_STATIC(NAME, VALUE) \
+         {NAME, PySlot_INTPTR|Py_SLOT_STATIC, {0}, {(void*)(VALUE)}}
+
+   .. versionadded:: next
+
+.. _pyslot-common-ids:
+
+Common slot IDs
+---------------
+
+The following slot IDs may be used in both type and module definitions.
+
+.. c:macro:: Py_slot_end
+
+   Marks the end of a slots array.
+   Defined as zero.
+
+   .. versionadded:: next
+
+.. c:macro:: Py_slot_subslots
+
+   Nested slots array.
+
+   The value (:c:member:`~PySlot.sl_ptr`) should point to an array of
+   :c:type:`PySlot` structures.
+   The slots in the array (up to but not including the zero-ID
+   terminator) will be treated as if they were inserted if the current
+   slot array, at the point :c:macro:`!Py_slot_subslots` appears.
+
+   Slot nesting depth is limited to 5 levels.
+   This restriction may be lifted in the future.
+
+   .. versionadded:: next
+
+.. c:macro:: Py_slot_invalid
+
+   Reserved; will always be treated as an unknown slot ID.
+   Defined as ``UINT16_MAX`` (``0xFFFF``).
+
+   When used with the :c:macro:`PySlot_OPTIONAL` flag, defines a slot with
+   no effect.
+   Without the flag, processing a slot with this ID will fail.
+
+   .. versionadded:: next
index c9bb5c3f09ac18631a2ded6d2dccc32c3c8be8cf..1794427a19ee6b431eb87c4cce681d83fc65f9e4 100644 (file)
@@ -3,7 +3,7 @@
 .. _typeobjects:
 
 Type Objects
-------------
+============
 
 .. index:: pair: object; type
 
@@ -384,36 +384,19 @@ Type Objects
       * :py:mod:`weakref`
 
 
-Creating Heap-Allocated Types
-.............................
-
-The following functions and structs are used to create
-:ref:`heap types <heap-types>`.
+.. _creating-heap-types:
 
-.. c:function:: PyObject* PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases)
+Creating Heap-Allocated Types
+-----------------------------
 
-   Create and return a :ref:`heap type <heap-types>` from the *spec*
-   (see :c:macro:`Py_TPFLAGS_HEAPTYPE`).
+The following function is used to create :ref:`heap types <heap-types>`:
 
-   The metaclass *metaclass* is used to construct the resulting type object.
-   When *metaclass* is ``NULL``, the metaclass is derived from *bases*
-   (or *Py_tp_base[s]* slots if *bases* is ``NULL``, see below).
+.. c:function:: PyObject *PyType_FromSlots(const PySlot *slots)
 
-   Metaclasses that override :c:member:`~PyTypeObject.tp_new` are not
-   supported, except if ``tp_new`` is ``NULL``.
-
-   The *bases* argument can be used to specify base classes; it can either
-   be only one class or a tuple of classes.
-   If *bases* is ``NULL``, the :c:data:`Py_tp_bases` slot is used instead.
-   If that also is ``NULL``, the :c:data:`Py_tp_base` slot is used instead.
-   If that also is ``NULL``, the new type derives from :class:`object`.
-
-   The *module* argument can be used to record the module in which the new
-   class is defined. It must be a module object or ``NULL``.
-   If not ``NULL``, the module is associated with the new type and can later be
-   retrieved with :c:func:`PyType_GetModule`.
-   The associated module is not inherited by subclasses; it must be specified
-   for each class individually.
+   Create and return a :ref:`heap type <heap-types>` from a :c:type:`!PySlot`
+   array.
+   See :ref:`capi-slots` for general information on slots,
+   and :ref:`pyslot_type_slot_ids` for slots specific to type creation.
 
    This function calls :c:func:`PyType_Ready` on the new type.
 
@@ -430,8 +413,376 @@ The following functions and structs are used to create
    * :py:meth:`~object.__init_subclass__` is not called on any bases.
    * :py:meth:`~object.__set_name__` is not called on new descriptors.
 
+   Slots are typically defined as a global static constant arrays.
+   However, sometimes slot values are not statically known at compile time.
+   For example, slots like :c:data:`Py_tp_bases`, :c:data:`Py_tp_metaclass`
+   and :c:data:`Py_tp_module` require live Python objects.
+   In this case, it is recommended to put such slots on the stack,
+   and use :c:macro:`Py_slot_subslots` to refer to an array of static slots.
+   For example::
+
+      static const PySlot my_slots[] = {
+         PySlot_STATIC_DATA(Py_tp_name, "MyClass"),
+         PySlot_FUNC(Py_tp_repr, my_repr_func),
+         ...
+         PySlot_END
+      };
+
+      PyObject *make_my_class(PyObject *module) {
+         PySlot all_slots[] = {
+            PySlot_STATIC_DATA(Py_slot_subslots, my_slots),
+            PySlot_DATA(Py_tp_module, module),
+            PySlot_END
+         };
+         return PyType_FromSlots(all_slots);
+      }
+
+Heap types created without the :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` flag may be
+modified, for example by setting attributes on them, as with classes defined
+in Python code.
+Sometimes, such modifications are necessary to fully initialize a type,
+but you may wish to prevent users from changing the type after
+the initialization is done:
+
+.. c:function:: int PyType_Freeze(PyTypeObject *type)
+
+   Make a type immutable: set the :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` flag.
+
+   All base classes of *type* must be immutable.
+
+   On success, return ``0``.
+   On error, set an exception and return ``-1``.
+
+   The type must not be used before it's made immutable. For example, type
+   instances must not be created before the type is made immutable.
+
+   .. versionadded:: 3.14
+
+
+.. _pyslot_type_slot_ids:
+
+Type slot IDs
+.............
+
+Most type slot IDs are named like the field names of the structures
+:c:type:`PyTypeObject`, :c:type:`PyNumberMethods`,
+:c:type:`PySequenceMethods`, :c:type:`PyMappingMethods` and
+:c:type:`PyAsyncMethods` with an added ``Py_`` prefix.
+For example, use:
+
+* :c:data:`Py_tp_dealloc` to set :c:member:`PyTypeObject.tp_dealloc`
+* :c:data:`Py_nb_add` to set :c:member:`PyNumberMethods.nb_add`
+* :c:data:`Py_sq_length` to set :c:member:`PySequenceMethods.sq_length`
+
+The following slots need additional considerations when specified as slots:
+
+* :c:data:`Py_tp_name`
+* :c:data:`Py_tp_basicsize` and :c:data:`Py_tp_extra_basicsize`
+* :c:data:`Py_tp_itemsize`
+* :c:data:`Py_tp_flags`
+
+Additional slots do not directly correspond to a :c:type:`!PyTypeObject`
+struct field:
+
+* :c:data:`Py_tp_token`
+* :c:data:`Py_tp_metaclass`
+* :c:data:`Py_tp_module`
+
+The following “offset” fields cannot be set using :c:type:`PyType_Slot`:
+
+* :c:member:`~PyTypeObject.tp_weaklistoffset`
+  (use :c:macro:`Py_TPFLAGS_MANAGED_WEAKREF` instead if possible)
+* :c:member:`~PyTypeObject.tp_dictoffset`
+  (use :c:macro:`Py_TPFLAGS_MANAGED_DICT` instead if possible)
+* :c:member:`~PyTypeObject.tp_vectorcall_offset`
+  (use ``"__vectorcalloffset__"`` in :ref:`PyMemberDef <pymemberdef-offsets>`)
+
+If it is not possible to switch to a ``MANAGED`` flag (for example,
+for vectorcall or to support Python older than 3.12), specify the
+offset in :c:data:`Py_tp_members`.
+See :ref:`PyMemberDef documentation <pymemberdef-offsets>`
+for details.
+
+The following internal fields cannot be set at all when creating a heap
+type:
+
+* :c:member:`~PyTypeObject.tp_dict`,
+  :c:member:`~PyTypeObject.tp_mro`,
+  :c:member:`~PyTypeObject.tp_cache`,
+  :c:member:`~PyTypeObject.tp_subclasses`, and
+  :c:member:`~PyTypeObject.tp_weaklist`.
+
+The :c:data:`Py_tp_base` slot is equivalent to :c:data:`Py_tp_bases`;
+both may be set either to a type or a tuple of types.
+If both are specified, the value of :c:data:`Py_tp_bases`
+is used.
+
+Slot values may not be ``NULL``, except for the following:
+
+* :c:data:`Py_tp_doc`
+* :c:data:`Py_tp_token` (for clarity, prefer :c:data:`Py_TP_USE_SPEC`
+  rather than ``NULL``)
+
+.. versionchanged:: 3.9
+   Slots in :c:type:`PyBufferProcs` may be set in the unlimited API.
+
+.. versionchanged:: 3.11
+   :c:member:`~PyBufferProcs.bf_getbuffer` and
+   :c:member:`~PyBufferProcs.bf_releasebuffer` are now available
+   under the :ref:`limited API <limited-c-api>`.
+
+.. versionchanged:: 3.14
+   The field :c:member:`~PyTypeObject.tp_vectorcall` can now be set
+   using :c:data:`Py_tp_vectorcall`.  See the field's documentation
+   for details.
+
+.. versionchanged:: 3.15
+   The :c:data:`Py_tp_bases` slot may be set to a single type object,
+   making it equivalent to the :c:data:`Py_tp_base` slot.
+   Previously, a tuple of types was required.
+
+The following slots correspond to fields in the underlying type structure,
+but need extra remarks for use as slots:
+
+.. c:macro:: Py_tp_name
+
+   :c:member:`Slot ID <PySlot.sl_id>` for the name of the type,
+   used to set :c:member:`PyTypeObject.tp_name`.
+
+   This slot (or :c:func:`PyType_Spec.name`) is required to create a type.
+
+   This may not be used in :c:member:`PyType_Spec.slots`.
+   Use :c:func:`PyType_Spec.name` instead.
+
+   .. impl-detail::
+
+      CPython processes slots in order.
+      It is recommended to put ``Py_tp_name`` at the beginning of the slots
+      array, so that if processing of a later slots fails, error messages
+      can include the name.
+
+   .. versionadded:: next
+
+.. c:macro:: Py_tp_basicsize
+
+   :c:member:`Slot ID <PySlot.sl_id>` for the size of the instance in bytes.
+   It is used to set :c:member:`PyTypeObject.tp_basicsize`.
+
+   The value must be positive.
+
+   This may not be used in :c:member:`PyType_Spec.slots`.
+   Use :c:func:`PyType_Spec.basicsize` instead.
+
+   This slot may not be used with :c:func:`PyType_GetSlot`.
+   Use :c:member:`PyTypeObject.tp_basicsize` instead if needed, but be aware
+   that a type's size is often considered an implementation detail.
+
+   .. versionadded:: next
+
+.. c:macro:: Py_tp_extra_basicsize
+
+   :c:member:`Slot ID <PySlot.sl_id>` for type data size in bytes, that is,
+   how much space instances of the class need *in addition*
+   to space needed for superclasses.
+
+   The value is used, together with the size of superclasses, to set
+   :c:member:`PyTypeObject.tp_basicsize`.
+   Python will insert padding as needed to meet
+   :c:member:`!tp_basicsize`'s alignment requirements.
+
+   Use :c:func:`PyObject_GetTypeData` to get a pointer to subclass-specific
+   memory reserved this way.
+
+   The value must be positive.
+   To specify that instances need no additional size (that is, size should be
+   inherited), omit the :c:macro:`!Py_tp_extra_basicsize` slot rather than
+   set it to zero.
+
+   Specifying both :c:macro:`Py_tp_basicsize` and
+   :c:macro:`!Py_tp_extra_basicsize` is an error.
+
+   This may not be used in :c:member:`PyType_Spec.slots`.
+   Use negative :c:func:`PyType_Spec.basicsize` instead.
+
+   This slot may not be used with :c:func:`PyType_GetSlot`.
+
+   .. versionadded:: next
+
+.. c:macro:: Py_tp_itemsize
+
+   :c:member:`Slot ID <PySlot.sl_id>` for the size of one element of a
+   variable-size type, in bytes.
+   Used to set :c:member:`PyTypeObject.tp_itemsize`.
+   See :c:member:`!tp_itemsize` documentation for caveats.
+
+   The value must be positive.
+
+   If this slot is missing, :c:member:`~PyTypeObject.tp_itemsize` is inherited.
+   Extending arbitrary variable-sized classes is dangerous,
+   since some types use a fixed offset for variable-sized memory,
+   which can then overlap fixed-sized memory used by a subclass.
+   To help prevent mistakes, inheriting ``itemsize`` is only possible
+   in the following situations:
+
+   - The base is not variable-sized (its
+     :c:member:`~PyTypeObject.tp_itemsize`).
+   - The requested :c:member:`PyType_Spec.basicsize` is positive,
+     suggesting that the memory layout of the base class is known.
+   - The requested :c:member:`PyType_Spec.basicsize` is zero,
+     suggesting that the subclass does not access the instance's memory
+     directly.
+   - With the :c:macro:`Py_TPFLAGS_ITEMS_AT_END` flag.
+
+   This may not be used in :c:member:`PyType_Spec.slots`.
+   Use :c:func:`PyType_Spec.itemsize` instead.
+
+   This slot may not be used with :c:func:`PyType_GetSlot`.
+
+   .. versionadded:: next
+
+.. c:macro:: Py_tp_flags
+
+   :c:member:`Slot ID <PySlot.sl_id>` for type flags, used to set
+   :c:member:`PyTypeObject.tp_flags`.
+
+   The ``Py_TPFLAGS_HEAPTYPE`` flag is not set,
+   :c:func:`PyType_FromSpecWithBases` sets it automatically.
+
+   This may not be used in :c:member:`PyType_Spec.slots`.
+   Use negative :c:func:`PyType_Spec.basicsize` instead.
+
+   This slot may not be used with :c:func:`PyType_GetSlot`.
+   Use :c:func:`PyType_GetFlags` instead.
+
+   .. versionadded:: next
+
+The following slots do not correspond to public fields in the
+underlying structures:
+
+.. c:macro:: Py_tp_metaclass
+
+   :c:member:`Slot ID <PySlot.sl_id>` for the metaclass used to construct
+   the resulting type object.
+   When omitted the metaclass is derived from bases
+   (:c:macro:`Py_tp_bases` or the *bases* argument of
+   :c:func:`PyType_FromMetaclass`).
+
+   Metaclasses that override :c:member:`~PyTypeObject.tp_new` are not
+   supported, except if ``tp_new`` is ``NULL``.
+
+   This may not be used in :c:member:`PyType_Spec.slots`.
+   Use :c:func:`PyType_FromMetaclass` to specify a metaclass with
+   :c:type:`!PyType_Spec`.
+
+   This slot may not be used with :c:func:`PyType_GetSlot`.
+   Use :c:func:`Py_TYPE` on the type object instead.
+
+   .. versionadded:: next
+
+.. c:macro:: Py_tp_module
+
+   :c:member:`Slot ID <PySlot.sl_id>` for recording the module in which
+   the new class is defined.
+
+   The value must be a module object.
+   The module is associated with the new type and can later be
+   retrieved with :c:func:`PyType_GetModule`.
+   The associated module is not inherited by subclasses; it must be specified
+   for each class individually.
+
+   This may not be used in :c:member:`PyType_Spec.slots`.
+   Use :c:func:`PyType_FromMetaclass` to specify a module with
+   :c:type:`!PyType_Spec`.
+
+   This slot may not be used with :c:func:`PyType_GetSlot`.
+   Use :c:func:`PyType_GetModule` instead.
+
+   .. versionadded:: next
+
+.. c:macro:: Py_tp_token
+
+   :c:member:`Slot ID <PySlot.sl_id>` for recording a static memory layout ID
+   for a class.
+
+   If the class is defined using a :c:type:`PyType_Spec`, and that spec is
+   statically allocated, the token can be set to the spec using the special
+   value :c:data:`Py_TP_USE_SPEC`:
+
+   .. code-block:: c
+
+      static PyType_Slot foo_slots[] = {
+         {Py_tp_token, Py_TP_USE_SPEC},
+
+   It can also be set to an arbitrary pointer, but you must ensure that:
+
+   * The pointer outlives the class, so it's not reused for something else
+     while the class exists.
+   * It "belongs" to the extension module where the class lives, so it will not
+     clash with other extensions.
+
+   Use :c:func:`PyType_GetBaseByToken` to check if a class's superclass has
+   a given token -- that is, check whether the memory layout is compatible.
+
+   To get the token for a given class (without considering superclasses),
+   use :c:func:`PyType_GetSlot` with ``Py_tp_token``.
+
+   .. versionadded:: 3.14
+
+   .. c:namespace:: NULL
+
+   .. c:macro:: Py_TP_USE_SPEC
+
+      Used as a value with :c:data:`Py_tp_token` to set the token to the
+      class's :c:type:`PyType_Spec`.
+      May only be used for classes defined using :c:type:`!PyType_Spec`.
+
+      Expands to ``NULL``.
+
+      .. versionadded:: 3.14
+
+.. c:macro:: Py_tp_slots
+
+   :c:member:`Slot ID <PySlot.sl_id>` that works like
+   :c:macro:`Py_slot_subslots`, except it specifies an array of
+   :c:type:`PyType_Slot` structures.
+
+   .. versionadded:: next
+
+
+Soft-deprecated API
+-------------------
+
+The following functions are :term:`soft deprecated`.
+They will continue to work, but new features will be added as slots for
+:c:func:`PyType_FromSlots`, not as arguments to new ``PyType_From*`` functions.
+
+.. c:function:: PyObject* PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases)
+
+   Create and return a :ref:`heap type <heap-types>` from the *spec*
+   (see :c:macro:`Py_TPFLAGS_HEAPTYPE`).
+
+   A non-``NULL`` *metaclass* argument corresponds to the
+   :c:macro:`Py_tp_metaclass` slot.
+
+   A non-``NULL`` *bases* argument corresponds to the :c:data:`Py_tp_bases`
+   slot, and takes precedence over :c:data:`Py_tp_bases` and
+   :c:data:`Py_tp_bases` slots.
+
+   A non-``NULL`` *module* argument corresponds to the
+   :c:macro:`Py_tp_module` slot.
+
+   This function calls :c:func:`PyType_Ready` on the new type.
+
+   Note that this function does *not* fully match the behavior of
+   calling :py:class:`type() <type>` or using the :keyword:`class` statement.
+   See the note in :c:func:`PyType_FromSlots` documentation for details.
+
    .. versionadded:: 3.12
 
+   .. soft-deprecated:: next
+
+      Prefer :c:func:`PyType_FromSlots` in new code.
+
 
 .. c:function:: PyObject* PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases)
 
@@ -459,6 +810,10 @@ The following functions and structs are used to create
       Creating classes whose metaclass overrides
       :c:member:`~PyTypeObject.tp_new` is no longer allowed.
 
+   .. soft-deprecated:: next
+
+      Prefer :c:func:`PyType_FromSlots` in new code.
+
 
 .. c:function:: PyObject* PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases)
 
@@ -481,6 +836,10 @@ The following functions and structs are used to create
       Creating classes whose metaclass overrides
       :c:member:`~PyTypeObject.tp_new` is no longer allowed.
 
+   .. soft-deprecated:: next
+
+      Prefer :c:func:`PyType_FromSlots` in new code.
+
 
 .. c:function:: PyObject* PyType_FromSpec(PyType_Spec *spec)
 
@@ -502,20 +861,9 @@ The following functions and structs are used to create
       Creating classes whose metaclass overrides
       :c:member:`~PyTypeObject.tp_new` is no longer allowed.
 
+   .. soft-deprecated:: next
 
-.. c:function:: int PyType_Freeze(PyTypeObject *type)
-
-   Make a type immutable: set the :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` flag.
-
-   All base classes of *type* must be immutable.
-
-   On success, return ``0``.
-   On error, set an exception and return ``-1``.
-
-   The type must not be used before it's made immutable. For example, type
-   instances must not be created before the type is made immutable.
-
-   .. versionadded:: 3.14
+      Prefer :c:func:`PyType_FromSlots` in new code.
 
 .. raw:: html
 
@@ -528,27 +876,23 @@ The following functions and structs are used to create
 
 .. c:type:: PyType_Spec
 
-   Structure defining a type's behavior.
+   Structure defining a type's behavior, used for soft-deprecated functions
+   like :c:func:`PyType_FromMetaclass`.
+
+   This structure contains several members that can instead be specified
+   as :ref:`slots <pyslot_type_slot_ids>` for :c:func:`PyType_FromSlots`,
+   and an array of slot entries with a simpler structure.
 
    .. c:member:: const char* name
 
-      Name of the type, used to set :c:member:`PyTypeObject.tp_name`.
+      Corresponds to :c:macro:`Py_tp_name`.
 
    .. c:member:: int basicsize
 
-      If positive, specifies the size of the instance in bytes.
-      It is used to set :c:member:`PyTypeObject.tp_basicsize`.
-
-      If zero, specifies that :c:member:`~PyTypeObject.tp_basicsize`
-      should be inherited.
+      If positive, corresponds to :c:macro:`Py_tp_basicsize`.
 
-      If negative, the absolute value specifies how much space instances of the
-      class need *in addition* to the superclass.
-      Use :c:func:`PyObject_GetTypeData` to get a pointer to subclass-specific
-      memory reserved this way.
-      For negative :c:member:`!basicsize`, Python will insert padding when
-      needed to meet :c:member:`~PyTypeObject.tp_basicsize`'s alignment
-      requirements.
+      If negative, corresponds to :c:macro:`Py_tp_extra_basicsize` set to
+      the absolute value.
 
       .. versionchanged:: 3.12
 
@@ -556,160 +900,53 @@ The following functions and structs are used to create
 
    .. c:member:: int itemsize
 
-      Size of one element of a variable-size type, in bytes.
-      Used to set :c:member:`PyTypeObject.tp_itemsize`.
-      See ``tp_itemsize`` documentation for caveats.
-
-      If zero, :c:member:`~PyTypeObject.tp_itemsize` is inherited.
-      Extending arbitrary variable-sized classes is dangerous,
-      since some types use a fixed offset for variable-sized memory,
-      which can then overlap fixed-sized memory used by a subclass.
-      To help prevent mistakes, inheriting ``itemsize`` is only possible
-      in the following situations:
-
-      - The base is not variable-sized (its
-        :c:member:`~PyTypeObject.tp_itemsize`).
-      - The requested :c:member:`PyType_Spec.basicsize` is positive,
-        suggesting that the memory layout of the base class is known.
-      - The requested :c:member:`PyType_Spec.basicsize` is zero,
-        suggesting that the subclass does not access the instance's memory
-        directly.
-      - With the :c:macro:`Py_TPFLAGS_ITEMS_AT_END` flag.
+      Corresponds to :c:macro:`Py_tp_itemsize`.
 
    .. c:member:: unsigned int flags
 
-      Type flags, used to set :c:member:`PyTypeObject.tp_flags`.
-
-      If the ``Py_TPFLAGS_HEAPTYPE`` flag is not set,
-      :c:func:`PyType_FromSpecWithBases` sets it automatically.
+      Corresponds to :c:macro:`Py_tp_flags`.
 
    .. c:member:: PyType_Slot *slots
 
-      Array of :c:type:`PyType_Slot` structures.
-      Terminated by the special slot value ``{0, NULL}``.
+      Array of :c:type:`PyType_Slot` (not :c:type:`PySlot`) structures.
 
+      Terminated by the special slot value ``{0, NULL}``.
       Each slot ID should be specified at most once.
 
-.. raw:: html
-
-   <!-- Keep old URL fragments working (see gh-97908) -->
-   <span id='c.PyType_Slot.PyType_Slot.slot'></span>
-   <span id='c.PyType_Slot.PyType_Slot.pfunc'></span>
-
-.. c:type:: PyType_Slot
-
-   Structure defining optional functionality of a type, containing a slot ID
-   and a value pointer.
-
-   .. c:member:: int slot
+      .. c:namespace:: NULL
 
-      A slot ID.
+      .. raw:: html
 
-      Slot IDs are named like the field names of the structures
-      :c:type:`PyTypeObject`, :c:type:`PyNumberMethods`,
-      :c:type:`PySequenceMethods`, :c:type:`PyMappingMethods` and
-      :c:type:`PyAsyncMethods` with an added ``Py_`` prefix.
-      For example, use:
+         <!-- Keep old URL fragments working (see gh-97908) -->
+         <span id='c.PyType_Slot.PyType_Slot.slot'></span>
+         <span id='c.PyType_Slot.PyType_Slot.pfunc'></span>
 
-      * :c:data:`Py_tp_dealloc` to set :c:member:`PyTypeObject.tp_dealloc`
-      * :c:data:`Py_nb_add` to set :c:member:`PyNumberMethods.nb_add`
-      * :c:data:`Py_sq_length` to set :c:member:`PySequenceMethods.sq_length`
+      .. c:type:: PyType_Slot
 
-      An additional slot is supported that does not correspond to a
-      :c:type:`!PyTypeObject` struct field:
+         Structure defining optional functionality of a type, used for
+         soft-deprecated functions like :c:func:`PyType_FromMetaclass`.
 
-      * :c:data:`Py_tp_token`
+         Note that a :c:type:`!PyType_Slot` array may be included in a
+         :c:type:`!PySlot` array using :c:macro:`Py_tp_slots`,
+         and vice versa using :c:macro:`Py_slot_subslots`.
 
-      The following “offset” fields cannot be set using :c:type:`PyType_Slot`:
+         Each :c:type:`!PyType_Slot` structure ``tpslot`` is interpreted
+         as the following :c:type:`PySlot` structure::
 
-      * :c:member:`~PyTypeObject.tp_weaklistoffset`
-        (use :c:macro:`Py_TPFLAGS_MANAGED_WEAKREF` instead if possible)
-      * :c:member:`~PyTypeObject.tp_dictoffset`
-        (use :c:macro:`Py_TPFLAGS_MANAGED_DICT` instead if possible)
-      * :c:member:`~PyTypeObject.tp_vectorcall_offset`
-        (use ``"__vectorcalloffset__"`` in
-        :ref:`PyMemberDef <pymemberdef-offsets>`)
+            (PySlot){
+               .sl_id=tpslot.slot,
+               .sl_flags=PySlot_INTPTR | sub_static,
+               .sl_ptr=tpslot.func
+            }
 
-      If it is not possible to switch to a ``MANAGED`` flag (for example,
-      for vectorcall or to support Python older than 3.12), specify the
-      offset in :c:data:`Py_tp_members`.
-      See :ref:`PyMemberDef documentation <pymemberdef-offsets>`
-      for details.
+         where ``sub_static`` is ``PySlot_STATIC`` if the slot requires
+         the flag (such as for :c:macro:`Py_tp_methods`), or if this flag
+         is present on the "parent" :c:macro:`!Py_tp_slots` slot (if any).
 
-      The following internal fields cannot be set at all when creating a heap
-      type:
+         .. c:member:: int slot
 
-      * :c:member:`~PyTypeObject.tp_dict`,
-        :c:member:`~PyTypeObject.tp_mro`,
-        :c:member:`~PyTypeObject.tp_cache`,
-        :c:member:`~PyTypeObject.tp_subclasses`, and
-        :c:member:`~PyTypeObject.tp_weaklist`.
+            Corresponds to :c:member:`PySlot.sl_id`.
 
-      Setting :c:data:`Py_tp_bases` or :c:data:`Py_tp_base` may be
-      problematic on some platforms.
-      To avoid issues, use the *bases* argument of
-      :c:func:`PyType_FromSpecWithBases` instead.
+         .. c:member:: void *pfunc
 
-      .. versionchanged:: 3.9
-         Slots in :c:type:`PyBufferProcs` may be set in the unlimited API.
-
-      .. versionchanged:: 3.11
-         :c:member:`~PyBufferProcs.bf_getbuffer` and
-         :c:member:`~PyBufferProcs.bf_releasebuffer` are now available
-         under the :ref:`limited API <limited-c-api>`.
-
-      .. versionchanged:: 3.14
-         The field :c:member:`~PyTypeObject.tp_vectorcall` can now be set
-         using :c:data:`Py_tp_vectorcall`.  See the field's documentation
-         for details.
-
-   .. c:member:: void *pfunc
-
-      The desired value of the slot. In most cases, this is a pointer
-      to a function.
-
-      *pfunc* values may not be ``NULL``, except for the following slots:
-
-      * :c:data:`Py_tp_doc`
-      * :c:data:`Py_tp_token` (for clarity, prefer :c:data:`Py_TP_USE_SPEC`
-        rather than ``NULL``)
-
-
-.. c:macro:: Py_tp_token
-
-   A :c:member:`~PyType_Slot.slot` that records a static memory layout ID
-   for a class.
-
-   If the :c:type:`PyType_Spec` of the class is statically
-   allocated, the token can be set to the spec using the special value
-   :c:data:`Py_TP_USE_SPEC`:
-
-   .. code-block:: c
-
-      static PyType_Slot foo_slots[] = {
-         {Py_tp_token, Py_TP_USE_SPEC},
-
-   It can also be set to an arbitrary pointer, but you must ensure that:
-
-   * The pointer outlives the class, so it's not reused for something else
-     while the class exists.
-   * It "belongs" to the extension module where the class lives, so it will not
-     clash with other extensions.
-
-   Use :c:func:`PyType_GetBaseByToken` to check if a class's superclass has
-   a given token -- that is, check whether the memory layout is compatible.
-
-   To get the token for a given class (without considering superclasses),
-   use :c:func:`PyType_GetSlot` with ``Py_tp_token``.
-
-   .. versionadded:: 3.14
-
-   .. c:namespace:: NULL
-
-   .. c:macro:: Py_TP_USE_SPEC
-
-      Used as a value with :c:data:`Py_tp_token` to set the token to the
-      class's :c:type:`PyType_Spec`.
-      Expands to ``NULL``.
-
-      .. versionadded:: 3.14
+            Corresponds to :c:member:`PySlot.sl_ptr`.
index d3d8239365f9bf99054d60da38e5d43fe9f065e6..38db69e5c6db96da2dd9e683d71d484ca0666a5e 100644 (file)
@@ -555,6 +555,9 @@ and :c:data:`PyType_Type` effectively act as defaults.)
 
 .. c:member:: const char* PyTypeObject.tp_name
 
+   See :c:macro:`Py_tp_name` for the corresponding
+   :c:member:`Slot ID <PySlot.sl_id>`.
+
    Pointer to a NUL-terminated string containing the name of the type. For types
    that are accessible as module globals, the string should be the full module
    name, followed by a dot, followed by the type name; for built-in types, it
@@ -594,6 +597,10 @@ and :c:data:`PyType_Type` effectively act as defaults.)
 
    These fields allow calculating the size in bytes of instances of the type.
 
+   See :c:macro:`Py_tp_basicsize`, :c:macro:`Py_tp_extra_basicsize` and
+   :c:macro:`Py_tp_itemsize` for the corresponding
+   :c:member:`Slot IDs <PySlot.sl_id>`.
+
    There are two kinds of types: types with fixed-length instances have a zero
    :c:member:`!tp_itemsize` field, types with variable-length instances have a non-zero
    :c:member:`!tp_itemsize` field.  For a type with fixed-length instances, all
@@ -1133,6 +1140,9 @@ and :c:data:`PyType_Type` effectively act as defaults.)
 
 .. c:member:: unsigned long PyTypeObject.tp_flags
 
+   See :c:macro:`Py_tp_flags` for the corresponding
+   :c:member:`Slot ID <PySlot.sl_id>`.
+
    This field is a bit mask of various flags.  Some flags indicate variant
    semantics for certain situations; others are used to indicate that certain
    fields in the type object (or in the extension structures referenced via
index 49277b57b877d40602326fdc4aef43ce55c4d41c..804e9c82e7818b308c2532e9ae210426c541d75b 100644 (file)
@@ -676,6 +676,19 @@ func,PySlice_GetIndicesEx,3.2,,
 func,PySlice_New,3.2,,
 data,PySlice_Type,3.2,,
 func,PySlice_Unpack,3.7,,
+type,PySlot,3.15,,full-abi
+macro,PySlot_DATA,3.15,,
+macro,PySlot_END,3.15,,
+macro,PySlot_FUNC,3.15,,
+macro,PySlot_INT64,3.15,,
+macro,PySlot_INTPTR,3.15,,
+macro,PySlot_OPTIONAL,3.15,,
+macro,PySlot_PTR,3.15,,
+macro,PySlot_PTR_STATIC,3.15,,
+macro,PySlot_SIZE,3.15,,
+macro,PySlot_STATIC,3.15,,
+macro,PySlot_STATIC_DATA,3.15,,
+macro,PySlot_UINT64,3.15,,
 func,PyState_AddModule,3.3,,
 func,PyState_FindModule,3.2,,
 func,PyState_RemoveModule,3.3,,
@@ -755,6 +768,7 @@ func,PyType_ClearCache,3.2,,
 func,PyType_Freeze,3.14,,
 func,PyType_FromMetaclass,3.12,,
 func,PyType_FromModuleAndSpec,3.10,,
+func,PyType_FromSlots,3.15,,
 func,PyType_FromSpec,3.2,,
 func,PyType_FromSpecWithBases,3.3,,
 func,PyType_GenericAlloc,3.2,,
@@ -1018,6 +1032,7 @@ macro,Py_mod_gil,3.13,,
 macro,Py_mod_methods,3.15,,
 macro,Py_mod_multiple_interpreters,3.12,,
 macro,Py_mod_name,3.15,,
+macro,Py_mod_slots,3.15,,
 macro,Py_mod_state_clear,3.15,,
 macro,Py_mod_state_free,3.15,,
 macro,Py_mod_state_size,3.15,,
@@ -1061,6 +1076,9 @@ macro,Py_nb_rshift,3.2,,
 macro,Py_nb_subtract,3.2,,
 macro,Py_nb_true_divide,3.2,,
 macro,Py_nb_xor,3.2,,
+macro,Py_slot_end,3.15,,
+macro,Py_slot_invalid,3.15,,
+macro,Py_slot_subslots,3.15,,
 macro,Py_sq_ass_item,3.2,,
 macro,Py_sq_concat,3.2,,
 macro,Py_sq_contains,3.2,,
@@ -1073,6 +1091,7 @@ type,Py_ssize_t,3.2,,
 macro,Py_tp_alloc,3.2,,
 macro,Py_tp_base,3.2,,
 macro,Py_tp_bases,3.2,,
+macro,Py_tp_basicsize,3.15,,
 macro,Py_tp_call,3.2,,
 macro,Py_tp_clear,3.2,,
 macro,Py_tp_dealloc,3.2,,
@@ -1080,7 +1099,9 @@ macro,Py_tp_del,3.2,,
 macro,Py_tp_descr_get,3.2,,
 macro,Py_tp_descr_set,3.2,,
 macro,Py_tp_doc,3.2,,
+macro,Py_tp_extra_basicsize,3.15,,
 macro,Py_tp_finalize,3.5,,
+macro,Py_tp_flags,3.15,,
 macro,Py_tp_free,3.2,,
 macro,Py_tp_getattr,3.2,,
 macro,Py_tp_getattro,3.2,,
@@ -1088,15 +1109,20 @@ macro,Py_tp_getset,3.2,,
 macro,Py_tp_hash,3.2,,
 macro,Py_tp_init,3.2,,
 macro,Py_tp_is_gc,3.2,,
+macro,Py_tp_itemsize,3.15,,
 macro,Py_tp_iter,3.2,,
 macro,Py_tp_iternext,3.2,,
 macro,Py_tp_members,3.2,,
+macro,Py_tp_metaclass,3.15,,
 macro,Py_tp_methods,3.2,,
+macro,Py_tp_module,3.15,,
+macro,Py_tp_name,3.15,,
 macro,Py_tp_new,3.2,,
 macro,Py_tp_repr,3.2,,
 macro,Py_tp_richcompare,3.2,,
 macro,Py_tp_setattr,3.2,,
 macro,Py_tp_setattro,3.2,,
+macro,Py_tp_slots,3.15,,
 macro,Py_tp_str,3.2,,
 macro,Py_tp_token,3.14,,
 macro,Py_tp_traverse,3.2,,
index cd755a98f7f5f4ca215f015b1c2faa739650b810..894f5bdbb8f09c220b4ae00f87ca183724c2fa0f 100644 (file)
@@ -259,21 +259,25 @@ Rather than ``NULL``, the export hook should return the information needed to
 create a module.
 Let's start with the basics: the name and docstring.
 
-The information should be defined in a ``static`` array of
-:c:type:`PyModuleDef_Slot` entries, which are essentially key-value pairs.
+The information should be defined in an array of
+:c:type:`PySlot` entries, which are essentially key-value pairs.
 Define this array just before your export hook:
 
 .. code-block:: c
 
    PyABIInfo_VAR(abi_info);
 
-   static PyModuleDef_Slot spam_slots[] = {
-      {Py_mod_abi, &abi_info},
-      {Py_mod_name, "spam"},
-      {Py_mod_doc, "A wonderful module with an example function"},
-      {0, NULL}
+   static PySlot spam_slots[] = {
+      PySlot_STATIC_DATA(Py_mod_abi, &abi_info),
+      PySlot_STATIC_DATA(Py_mod_name, "spam"),
+      PySlot_STATIC_DATA(Py_mod_doc, "A wonderful module with an example function"),
+      PySlot_END
    };
 
+The :c:macro:`PySlot_STATIC_DATA` macro is used when the slot value
+(here: ``&abi_info``, ``"spam"``, and the docstring) is a pointer to constant,
+statically allocated data.
+
 The ``PyABIInfo_VAR(abi_info);`` macro and the :c:data:`Py_mod_abi` slot
 are a bit of boilerplate that helps prevent extensions compiled for
 a different version of Python from crashing the interpreter.
@@ -281,7 +285,8 @@ a different version of Python from crashing the interpreter.
 For both :c:data:`Py_mod_name` and :c:data:`Py_mod_doc`, the values are C
 strings -- that is, NUL-terminated, UTF-8 encoded byte arrays.
 
-Note the zero-filled sentinel entry at the end.
+Note ``PySlot_END`` sentinel entry at the end.
+This marks the end of the array.
 If you forget it, you'll trigger undefined behavior.
 
 The array is defined as ``static`` -- that is, not visible outside this ``.c`` file.
index 0bc34ef57445cb9ffc0f51c7754bddd816184d13..8ddb416e7d6cd08755213fe8c2264c156fd962ae 100644 (file)
@@ -37,12 +37,12 @@ static PyMethodDef spam_methods[] = {
 
 PyABIInfo_VAR(abi_info);
 
-static PyModuleDef_Slot spam_slots[] = {
-    {Py_mod_abi, &abi_info},
-    {Py_mod_name, "spam"},
-    {Py_mod_doc, "A wonderful module with an example function"},
-    {Py_mod_methods, spam_methods},
-    {0, NULL}
+static PySlot spam_slots[] = {
+    PySlot_STATIC_DATA(Py_mod_abi, &abi_info),
+    PySlot_STATIC_DATA(Py_mod_name, "spam"),
+    PySlot_STATIC_DATA(Py_mod_doc, "A wonderful module with an example function"),
+    PySlot_STATIC_DATA(Py_mod_methods, spam_methods),
+    PySlot_END
 };
 
 /// Export hook prototype
index 7c4ff0d8775168c8a934263ea6cb180fcf09dba6..90954e06e6c1e9934c37e93bad846a8bfec182b2 100644 (file)
@@ -88,6 +88,7 @@ Summary -- Release highlights
 * :pep:`782`: :ref:`A new PyBytesWriter C API to create a Python bytes object
   <whatsnew315-pybyteswriter>`
 * :pep:`803`: :ref:`Stable ABI for Free-Threaded Builds <whatsnew315-abi3t>`
+* :pep:`820`: :ref:`PySlot: Unified slot system for the C API <whatsnew315-pyslot>`
 * :ref:`The JIT compiler has been significantly upgraded <whatsnew315-jit>`
 * :ref:`Improved error messages <whatsnew315-improved-error-messages>`
 * :ref:`The official Windows 64-bit binaries now use the tail-calling interpreter
@@ -2213,6 +2214,43 @@ New features
   It should only be used for debugging.
   (Contributed by Victor Stinner in :gh:`141070`.)
 
+.. _whatsnew315-pyslot:
+
+* Implement :pep:`820`: ``PySlot`` -- Unified slot system for the C API.
+  See :ref:`capi-slots` for documentation.
+
+  This adds:
+
+  * The :c:type:`PySlot` struct;
+  * the :c:func:`PyType_FromSlots` function;
+  * new slot IDs: :c:macro:`Py_slot_end`, :c:macro:`Py_slot_invalid`;
+    :c:macro:`Py_slot_subslots`, :c:macro:`Py_tp_slots`
+    :c:macro:`Py_mod_slots`;
+    :c:macro:`Py_tp_name`, :c:macro:`Py_tp_basicsize`,
+    :c:macro:`Py_tp_extra_basicsize`, :c:macro:`Py_tp_itemsize`,
+    :c:macro:`Py_tp_flags`, :c:macro:`Py_tp_metaclass`,
+    :c:macro:`Py_tp_module`, :c:macro:`Py_tp_flags`;
+  * convenience macros: :c:macro:`PySlot_DATA`, :c:macro:`PySlot_FUNC`,
+    :c:macro:`PySlot_SIZE` :c:macro:`PySlot_INT64`, :c:macro:`PySlot_UINT64`,
+    :c:macro:`PySlot_STATIC_DATA`, :c:macro:`PySlot_END`,
+    :c:macro:`PySlot_PTR`, :c:macro:`PySlot_PTR_STATIC`.
+
+  The :c:func:`PyModule_FromSlotsAndSpec` function and
+  ``PyModExport`` :ref:`module export hook <extension-export-hook>` also
+  use the new :c:type:`!PySlot` struct.
+
+  These following functions are :term:`soft deprecated`:
+
+  * :c:func:`PyType_FromSpec`
+  * :c:func:`PyType_FromSpecWithBases`
+  * :c:func:`PyType_FromModuleAndSpec`
+  * :c:func:`PyType_FromMetaclass`
+  * :c:func:`PyModule_FromDefAndSpec`
+  * :c:func:`PyModule_FromDefAndSpec2`
+  * :c:func:`PyModule_ExecDef`
+
+  (Contributed by Petr Viktorin in :gh:`149044`.)
+
 * Add :c:func:`PyUnstable_ThreadState_SetStackProtection` and
   :c:func:`PyUnstable_ThreadState_ResetStackProtection` functions to set
   the stack protection base address and stack protection size of a Python
index d5e38b8b0201eec62a6ac724e7f7dcb465773610..337119c15fe8b6f6c055f8bc0575740b877ff1af 100644 (file)
@@ -79,7 +79,8 @@ __pragma(warning(disable: 4201))
 #include "object.h"
 #include "refcount.h"
 #include "objimpl.h"
-#include "typeslots.h"
+#include "slots.h"
+#include "slots_generated.h"
 #include "pyhash.h"
 #include "cpython/pydebug.h"
 #include "bytearrayobject.h"
index a863ecb33078aba2369f860a9e8f0ea39a672f75..18692283005e59686fbafaba78933f3052444b15 100644 (file)
     #define PyMODINIT_FUNC _PyINIT_FUNC_DECLSPEC PyObject*
 #endif
 #ifndef PyMODEXPORT_FUNC
-    #define PyMODEXPORT_FUNC _PyINIT_FUNC_DECLSPEC PyModuleDef_Slot*
+    #define PyMODEXPORT_FUNC _PyINIT_FUNC_DECLSPEC PySlot*
 #endif
 
 #endif /* Py_EXPORTS_H */
index f60c5510d2007529199c50e0d4380e3b2c521835..9ed87a544234c596b8aec457da8d9a0f6bb540f4 100644 (file)
@@ -124,7 +124,7 @@ extern void _Py_ext_module_loader_result_apply_error(
 
 /* The module init function. */
 typedef PyObject *(*PyModInitFunction)(void);
-typedef PyModuleDef_Slot *(*PyModExportFunction)(void);
+typedef PySlot *(*PyModExportFunction)(void);
 #ifdef HAVE_DYNAMIC_LOADING
 extern int _PyImport_GetModuleExportHooks(
     struct _Py_ext_module_loader_info *info,
diff --git a/Include/internal/pycore_slots.h b/Include/internal/pycore_slots.h
new file mode 100644 (file)
index 0000000..960a632
--- /dev/null
@@ -0,0 +1,138 @@
+#ifndef _Py_PYCORE_SLOTS_H
+#define _Py_PYCORE_SLOTS_H
+
+#ifndef Py_BUILD_CORE
+#  error "this header requires Py_BUILD_CORE define"
+#endif
+
+#include <stdbool.h>
+
+/* Slot data type */
+typedef enum _PySlot_DTYPE {
+    _PySlot_DTYPE_VOID,
+    _PySlot_DTYPE_FUNC,
+    _PySlot_DTYPE_PTR,
+    _PySlot_DTYPE_SIZE,
+    _PySlot_DTYPE_INT64,
+    _PySlot_DTYPE_UINT64,
+}_PySlot_DTYPE;
+
+/* Slot kind, used to identify:
+ * - the thing the slot initializes (type/module/special)
+ * - the struct type (PySlot/PyType_Slot/PyModuleDef_Slot)
+ */
+typedef enum _PySlot_KIND {
+    _PySlot_KIND_TYPE,
+    _PySlot_KIND_MOD,
+    _PySlot_KIND_COMPAT,
+    _PySlot_KIND_SLOT,
+} _PySlot_KIND;
+
+typedef enum _PySlot_PROBLEM_HANDLING {
+    _PySlot_PROBLEM_ALLOW,
+    _PySlot_PROBLEM_DEPRECATED,
+    _PySlot_PROBLEM_REJECT,
+} _PySlot_PROBLEM_HANDLING;
+
+PyAPI_DATA(const char *const) _PySlot_names[];
+
+#define _PySlot_MAX_NESTING 5
+
+/* State for one nesting level of a slots iterator */
+typedef struct _PySlotIterator_state {
+    union {
+        // tagged by slot_struct_kind:
+        const PySlot *slot;               // with _PySlot_KIND_SLOT
+        const PyType_Slot *tp_slot;       // with _PySlot_KIND_TYPE
+        const PyModuleDef_Slot *mod_slot; // with _PySlot_KIND_MOD
+        const void *any_slot;
+    };
+    _PySlot_KIND slot_struct_kind;
+} _PySlotIterator_state;
+
+#define _PySlot_SEEN_ENTRY_BITS (8 * sizeof(unsigned int))
+
+/* State for a slots iterator */
+typedef struct {
+    _PySlotIterator_state *state;
+    _PySlotIterator_state states[_PySlot_MAX_NESTING];
+    unsigned int seen[_Py_slot_COUNT / _PySlot_SEEN_ENTRY_BITS + 1];
+    _PySlot_KIND kind;
+    uint8_t recursion_level;
+    bool is_at_end :1;
+    bool is_first_run :1;
+
+    // Name of the object (type/module) being defined, NULL if unknown.
+    // Must be set by the callers as soon as it's known.
+    const char *name;
+
+    /* Output information: */
+
+    // The slot. Always a copy; may be modified by caller of the iterator.
+    PySlot current;
+
+} _PySlotIterator;
+
+/* Initialize an iterator using a PySlot array */
+PyAPI_FUNC(void)
+_PySlotIterator_Init(_PySlotIterator *it, const PySlot *slots,
+                     _PySlot_KIND result_kind);
+
+/* Initialize an iterator using a legacy slot array */
+PyAPI_FUNC(void)
+_PySlotIterator_InitLegacy(_PySlotIterator *it, const void *slots,
+                           _PySlot_KIND kind);
+
+/* Reset a *successfully exhausted* iterator to the beginning.
+ * The *slots* must be the same as for the previous
+ * `_PySlotIterator_InitWithKind` call.
+ * (Unlike creating a new iterator, we can skip some validation after Rewind.)
+ */
+PyAPI_FUNC(void) _PySlotIterator_Rewind(_PySlotIterator *it, const void *slots);
+
+/* Iteration function.
+ *
+ * Return false at the end (when successfully exhausted).
+ * Otherwise (even on error), fill output information in `it` and return true.
+ *
+ * On error, set an exception and set `it->current.sl_id` to `Py_slot_invalid`.
+ */
+PyAPI_FUNC(bool) _PySlotIterator_Next(_PySlotIterator *it);
+
+/* Return 1 if given slot was "seen" by an earlier _PySlotIterator_Next call.
+ * (This state is not reset by rewinding.)
+ */
+PyAPI_FUNC(bool) _PySlotIterator_SawSlot(_PySlotIterator *, int);
+
+static inline const char *
+_PySlot_GetName(uint16_t id)
+{
+    if (id >= _Py_slot_COUNT) {
+        return "<unknown_slot>";
+    }
+    if (id == Py_slot_invalid) {
+        return "Py_slot_invalid";
+    }
+    return _PySlot_names[id];
+}
+
+static inline void
+_PySlot_err_bad_slot(char *kind, uint16_t id)
+{
+    if (id < _Py_slot_COUNT) {
+        PyErr_Format(PyExc_SystemError, "invalid %s slot %d (%s)",
+                    kind, (unsigned int)id, _PySlot_names[id]);
+    }
+    else if (id == Py_slot_invalid) {
+        PyErr_Format(PyExc_SystemError, "invalid slot (Py_slot_invalid, %u)",
+                     (unsigned int)id);
+    }
+    else {
+        PyErr_Format(PyExc_SystemError, "unknown %s slot ID %u",
+                     kind, (unsigned int)id);
+    }
+}
+
+#include "internal/pycore_slots_generated.h"
+
+#endif // _Py_PYCORE_SLOTS_H
diff --git a/Include/internal/pycore_slots_generated.h b/Include/internal/pycore_slots_generated.h
new file mode 100644 (file)
index 0000000..73a7707
--- /dev/null
@@ -0,0 +1,958 @@
+/* Generated by Tools/build/generate_slots.py */
+
+#ifndef _PY_HAVE_INTERNAL_SLOTS_GENERATED_H
+#define _PY_HAVE_INTERNAL_SLOTS_GENERATED_H
+
+static inline uint16_t
+_PySlot_resolve_type_slot(uint16_t slot_id)
+{
+    switch (slot_id) {
+        case 1:
+            return Py_bf_getbuffer;
+        case 2:
+            return Py_bf_releasebuffer;
+        case 3:
+            return Py_mp_ass_subscript;
+        case 4:
+            return Py_mp_length;
+        case Py_slot_end:
+        case Py_mp_subscript:
+        case Py_nb_absolute:
+        case Py_nb_add:
+        case Py_nb_and:
+        case Py_nb_bool:
+        case Py_nb_divmod:
+        case Py_nb_float:
+        case Py_nb_floor_divide:
+        case Py_nb_index:
+        case Py_nb_inplace_add:
+        case Py_nb_inplace_and:
+        case Py_nb_inplace_floor_divide:
+        case Py_nb_inplace_lshift:
+        case Py_nb_inplace_multiply:
+        case Py_nb_inplace_or:
+        case Py_nb_inplace_power:
+        case Py_nb_inplace_remainder:
+        case Py_nb_inplace_rshift:
+        case Py_nb_inplace_subtract:
+        case Py_nb_inplace_true_divide:
+        case Py_nb_inplace_xor:
+        case Py_nb_int:
+        case Py_nb_invert:
+        case Py_nb_lshift:
+        case Py_nb_multiply:
+        case Py_nb_negative:
+        case Py_nb_or:
+        case Py_nb_positive:
+        case Py_nb_power:
+        case Py_nb_remainder:
+        case Py_nb_rshift:
+        case Py_nb_subtract:
+        case Py_nb_true_divide:
+        case Py_nb_xor:
+        case Py_sq_ass_item:
+        case Py_sq_concat:
+        case Py_sq_contains:
+        case Py_sq_inplace_concat:
+        case Py_sq_inplace_repeat:
+        case Py_sq_item:
+        case Py_sq_length:
+        case Py_sq_repeat:
+        case Py_tp_alloc:
+        case Py_tp_base:
+        case Py_tp_bases:
+        case Py_tp_call:
+        case Py_tp_clear:
+        case Py_tp_dealloc:
+        case Py_tp_del:
+        case Py_tp_descr_get:
+        case Py_tp_descr_set:
+        case Py_tp_doc:
+        case Py_tp_getattr:
+        case Py_tp_getattro:
+        case Py_tp_hash:
+        case Py_tp_init:
+        case Py_tp_is_gc:
+        case Py_tp_iter:
+        case Py_tp_iternext:
+        case Py_tp_methods:
+        case Py_tp_new:
+        case Py_tp_repr:
+        case Py_tp_richcompare:
+        case Py_tp_setattr:
+        case Py_tp_setattro:
+        case Py_tp_str:
+        case Py_tp_traverse:
+        case Py_tp_members:
+        case Py_tp_getset:
+        case Py_tp_free:
+        case Py_nb_matrix_multiply:
+        case Py_nb_inplace_matrix_multiply:
+        case Py_am_await:
+        case Py_am_aiter:
+        case Py_am_anext:
+        case Py_tp_finalize:
+        case Py_am_send:
+        case Py_tp_vectorcall:
+        case Py_tp_token:
+        case Py_bf_getbuffer:
+        case Py_bf_releasebuffer:
+        case Py_mp_ass_subscript:
+        case Py_mp_length:
+        case Py_slot_subslots:
+        case Py_tp_slots:
+        case Py_tp_name:
+        case Py_tp_basicsize:
+        case Py_tp_extra_basicsize:
+        case Py_tp_itemsize:
+        case Py_tp_flags:
+        case Py_tp_metaclass:
+        case Py_tp_module:
+            return slot_id;
+        default:
+            return Py_slot_invalid;
+    }
+}
+
+static inline uint16_t
+_PySlot_resolve_mod_slot(uint16_t slot_id)
+{
+    switch (slot_id) {
+        case 1:
+            return Py_mod_create;
+        case 2:
+            return Py_mod_exec;
+        case 3:
+            return Py_mod_multiple_interpreters;
+        case 4:
+            return Py_mod_gil;
+        case Py_slot_end:
+        case Py_mod_create:
+        case Py_mod_exec:
+        case Py_mod_multiple_interpreters:
+        case Py_mod_gil:
+        case Py_slot_subslots:
+        case Py_mod_slots:
+        case Py_mod_name:
+        case Py_mod_doc:
+        case Py_mod_state_size:
+        case Py_mod_methods:
+        case Py_mod_state_traverse:
+        case Py_mod_state_clear:
+        case Py_mod_state_free:
+        case Py_mod_abi:
+        case Py_mod_token:
+            return slot_id;
+        default:
+            return Py_slot_invalid;
+    }
+}
+
+static inline void*
+_PySlot_type_getslot(PyTypeObject *tp, uint16_t slot_id)
+{
+    switch (slot_id) {
+        case Py_mp_subscript:
+            if (!(tp->tp_as_mapping)) return NULL;
+            return (void*)tp->tp_as_mapping->mp_subscript;
+        case Py_nb_absolute:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_absolute;
+        case Py_nb_add:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_add;
+        case Py_nb_and:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_and;
+        case Py_nb_bool:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_bool;
+        case Py_nb_divmod:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_divmod;
+        case Py_nb_float:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_float;
+        case Py_nb_floor_divide:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_floor_divide;
+        case Py_nb_index:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_index;
+        case Py_nb_inplace_add:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_inplace_add;
+        case Py_nb_inplace_and:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_inplace_and;
+        case Py_nb_inplace_floor_divide:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_inplace_floor_divide;
+        case Py_nb_inplace_lshift:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_inplace_lshift;
+        case Py_nb_inplace_multiply:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_inplace_multiply;
+        case Py_nb_inplace_or:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_inplace_or;
+        case Py_nb_inplace_power:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_inplace_power;
+        case Py_nb_inplace_remainder:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_inplace_remainder;
+        case Py_nb_inplace_rshift:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_inplace_rshift;
+        case Py_nb_inplace_subtract:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_inplace_subtract;
+        case Py_nb_inplace_true_divide:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_inplace_true_divide;
+        case Py_nb_inplace_xor:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_inplace_xor;
+        case Py_nb_int:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_int;
+        case Py_nb_invert:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_invert;
+        case Py_nb_lshift:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_lshift;
+        case Py_nb_multiply:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_multiply;
+        case Py_nb_negative:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_negative;
+        case Py_nb_or:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_or;
+        case Py_nb_positive:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_positive;
+        case Py_nb_power:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_power;
+        case Py_nb_remainder:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_remainder;
+        case Py_nb_rshift:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_rshift;
+        case Py_nb_subtract:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_subtract;
+        case Py_nb_true_divide:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_true_divide;
+        case Py_nb_xor:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_xor;
+        case Py_sq_ass_item:
+            if (!(tp->tp_as_sequence)) return NULL;
+            return (void*)tp->tp_as_sequence->sq_ass_item;
+        case Py_sq_concat:
+            if (!(tp->tp_as_sequence)) return NULL;
+            return (void*)tp->tp_as_sequence->sq_concat;
+        case Py_sq_contains:
+            if (!(tp->tp_as_sequence)) return NULL;
+            return (void*)tp->tp_as_sequence->sq_contains;
+        case Py_sq_inplace_concat:
+            if (!(tp->tp_as_sequence)) return NULL;
+            return (void*)tp->tp_as_sequence->sq_inplace_concat;
+        case Py_sq_inplace_repeat:
+            if (!(tp->tp_as_sequence)) return NULL;
+            return (void*)tp->tp_as_sequence->sq_inplace_repeat;
+        case Py_sq_item:
+            if (!(tp->tp_as_sequence)) return NULL;
+            return (void*)tp->tp_as_sequence->sq_item;
+        case Py_sq_length:
+            if (!(tp->tp_as_sequence)) return NULL;
+            return (void*)tp->tp_as_sequence->sq_length;
+        case Py_sq_repeat:
+            if (!(tp->tp_as_sequence)) return NULL;
+            return (void*)tp->tp_as_sequence->sq_repeat;
+        case Py_tp_alloc:
+            return (void*)tp->tp_alloc;
+        case Py_tp_base:
+            return (void*)tp->tp_base;
+        case Py_tp_bases:
+            return (void*)tp->tp_bases;
+        case Py_tp_call:
+            return (void*)tp->tp_call;
+        case Py_tp_clear:
+            return (void*)tp->tp_clear;
+        case Py_tp_dealloc:
+            return (void*)tp->tp_dealloc;
+        case Py_tp_del:
+            return (void*)tp->tp_del;
+        case Py_tp_descr_get:
+            return (void*)tp->tp_descr_get;
+        case Py_tp_descr_set:
+            return (void*)tp->tp_descr_set;
+        case Py_tp_doc:
+            return (void*)tp->tp_doc;
+        case Py_tp_getattr:
+            return (void*)tp->tp_getattr;
+        case Py_tp_getattro:
+            return (void*)tp->tp_getattro;
+        case Py_tp_hash:
+            return (void*)tp->tp_hash;
+        case Py_tp_init:
+            return (void*)tp->tp_init;
+        case Py_tp_is_gc:
+            return (void*)tp->tp_is_gc;
+        case Py_tp_iter:
+            return (void*)tp->tp_iter;
+        case Py_tp_iternext:
+            return (void*)tp->tp_iternext;
+        case Py_tp_methods:
+            return (void*)tp->tp_methods;
+        case Py_tp_new:
+            return (void*)tp->tp_new;
+        case Py_tp_repr:
+            return (void*)tp->tp_repr;
+        case Py_tp_richcompare:
+            return (void*)tp->tp_richcompare;
+        case Py_tp_setattr:
+            return (void*)tp->tp_setattr;
+        case Py_tp_setattro:
+            return (void*)tp->tp_setattro;
+        case Py_tp_str:
+            return (void*)tp->tp_str;
+        case Py_tp_traverse:
+            return (void*)tp->tp_traverse;
+        case Py_tp_members:
+            return (void*)tp->tp_members;
+        case Py_tp_getset:
+            return (void*)tp->tp_getset;
+        case Py_tp_free:
+            return (void*)tp->tp_free;
+        case Py_nb_matrix_multiply:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_matrix_multiply;
+        case Py_nb_inplace_matrix_multiply:
+            if (!(tp->tp_as_number)) return NULL;
+            return (void*)tp->tp_as_number->nb_inplace_matrix_multiply;
+        case Py_am_await:
+            if (!(tp->tp_as_async)) return NULL;
+            return (void*)tp->tp_as_async->am_await;
+        case Py_am_aiter:
+            if (!(tp->tp_as_async)) return NULL;
+            return (void*)tp->tp_as_async->am_aiter;
+        case Py_am_anext:
+            if (!(tp->tp_as_async)) return NULL;
+            return (void*)tp->tp_as_async->am_anext;
+        case Py_tp_finalize:
+            return (void*)tp->tp_finalize;
+        case Py_am_send:
+            if (!(tp->tp_as_async)) return NULL;
+            return (void*)tp->tp_as_async->am_send;
+        case Py_tp_vectorcall:
+            return (void*)tp->tp_vectorcall;
+        case Py_tp_token:
+            if (!(tp->tp_flags & Py_TPFLAGS_HEAPTYPE)) return NULL;
+            return (void*)((PyHeapTypeObject*)tp)->ht_token;
+        case Py_bf_getbuffer:
+            if (!(tp->tp_as_buffer)) return NULL;
+            return (void*)tp->tp_as_buffer->bf_getbuffer;
+        case Py_bf_releasebuffer:
+            if (!(tp->tp_as_buffer)) return NULL;
+            return (void*)tp->tp_as_buffer->bf_releasebuffer;
+        case Py_mp_ass_subscript:
+            if (!(tp->tp_as_mapping)) return NULL;
+            return (void*)tp->tp_as_mapping->mp_ass_subscript;
+        case Py_mp_length:
+            if (!(tp->tp_as_mapping)) return NULL;
+            return (void*)tp->tp_as_mapping->mp_length;
+    }
+    _PySlot_err_bad_slot("PyType_GetSlot", slot_id);
+    return NULL;
+}
+
+static inline void
+_PySlot_heaptype_apply_field_slot(PyHeapTypeObject *ht, PySlot slot)
+{
+    switch (slot.sl_id) {
+        case Py_mp_subscript:
+            ht->as_mapping.mp_subscript = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_absolute:
+            ht->as_number.nb_absolute = (unaryfunc)slot.sl_func;
+            break;
+        case Py_nb_add:
+            ht->as_number.nb_add = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_and:
+            ht->as_number.nb_and = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_bool:
+            ht->as_number.nb_bool = (inquiry)slot.sl_func;
+            break;
+        case Py_nb_divmod:
+            ht->as_number.nb_divmod = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_float:
+            ht->as_number.nb_float = (unaryfunc)slot.sl_func;
+            break;
+        case Py_nb_floor_divide:
+            ht->as_number.nb_floor_divide = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_index:
+            ht->as_number.nb_index = (unaryfunc)slot.sl_func;
+            break;
+        case Py_nb_inplace_add:
+            ht->as_number.nb_inplace_add = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_inplace_and:
+            ht->as_number.nb_inplace_and = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_inplace_floor_divide:
+            ht->as_number.nb_inplace_floor_divide = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_inplace_lshift:
+            ht->as_number.nb_inplace_lshift = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_inplace_multiply:
+            ht->as_number.nb_inplace_multiply = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_inplace_or:
+            ht->as_number.nb_inplace_or = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_inplace_power:
+            ht->as_number.nb_inplace_power = (ternaryfunc)slot.sl_func;
+            break;
+        case Py_nb_inplace_remainder:
+            ht->as_number.nb_inplace_remainder = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_inplace_rshift:
+            ht->as_number.nb_inplace_rshift = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_inplace_subtract:
+            ht->as_number.nb_inplace_subtract = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_inplace_true_divide:
+            ht->as_number.nb_inplace_true_divide = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_inplace_xor:
+            ht->as_number.nb_inplace_xor = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_int:
+            ht->as_number.nb_int = (unaryfunc)slot.sl_func;
+            break;
+        case Py_nb_invert:
+            ht->as_number.nb_invert = (unaryfunc)slot.sl_func;
+            break;
+        case Py_nb_lshift:
+            ht->as_number.nb_lshift = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_multiply:
+            ht->as_number.nb_multiply = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_negative:
+            ht->as_number.nb_negative = (unaryfunc)slot.sl_func;
+            break;
+        case Py_nb_or:
+            ht->as_number.nb_or = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_positive:
+            ht->as_number.nb_positive = (unaryfunc)slot.sl_func;
+            break;
+        case Py_nb_power:
+            ht->as_number.nb_power = (ternaryfunc)slot.sl_func;
+            break;
+        case Py_nb_remainder:
+            ht->as_number.nb_remainder = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_rshift:
+            ht->as_number.nb_rshift = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_subtract:
+            ht->as_number.nb_subtract = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_true_divide:
+            ht->as_number.nb_true_divide = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_xor:
+            ht->as_number.nb_xor = (binaryfunc)slot.sl_func;
+            break;
+        case Py_sq_ass_item:
+            ht->as_sequence.sq_ass_item = (ssizeobjargproc)slot.sl_func;
+            break;
+        case Py_sq_concat:
+            ht->as_sequence.sq_concat = (binaryfunc)slot.sl_func;
+            break;
+        case Py_sq_contains:
+            ht->as_sequence.sq_contains = (objobjproc)slot.sl_func;
+            break;
+        case Py_sq_inplace_concat:
+            ht->as_sequence.sq_inplace_concat = (binaryfunc)slot.sl_func;
+            break;
+        case Py_sq_inplace_repeat:
+            ht->as_sequence.sq_inplace_repeat = (ssizeargfunc)slot.sl_func;
+            break;
+        case Py_sq_item:
+            ht->as_sequence.sq_item = (ssizeargfunc)slot.sl_func;
+            break;
+        case Py_sq_length:
+            ht->as_sequence.sq_length = (lenfunc)slot.sl_func;
+            break;
+        case Py_sq_repeat:
+            ht->as_sequence.sq_repeat = (ssizeargfunc)slot.sl_func;
+            break;
+        case Py_tp_alloc:
+            ht->ht_type.tp_alloc = (allocfunc)slot.sl_func;
+            break;
+        case Py_tp_base:
+            ht->ht_type.tp_base = slot.sl_ptr;
+            break;
+        case Py_tp_bases:
+            ht->ht_type.tp_bases = slot.sl_ptr;
+            break;
+        case Py_tp_call:
+            ht->ht_type.tp_call = (ternaryfunc)slot.sl_func;
+            break;
+        case Py_tp_clear:
+            ht->ht_type.tp_clear = (inquiry)slot.sl_func;
+            break;
+        case Py_tp_dealloc:
+            ht->ht_type.tp_dealloc = (destructor)slot.sl_func;
+            break;
+        case Py_tp_del:
+            ht->ht_type.tp_del = (destructor)slot.sl_func;
+            break;
+        case Py_tp_descr_get:
+            ht->ht_type.tp_descr_get = (descrgetfunc)slot.sl_func;
+            break;
+        case Py_tp_descr_set:
+            ht->ht_type.tp_descr_set = (descrsetfunc)slot.sl_func;
+            break;
+        case Py_tp_doc:
+            ht->ht_type.tp_doc = slot.sl_ptr;
+            break;
+        case Py_tp_getattr:
+            ht->ht_type.tp_getattr = (getattrfunc)slot.sl_func;
+            break;
+        case Py_tp_getattro:
+            ht->ht_type.tp_getattro = (getattrofunc)slot.sl_func;
+            break;
+        case Py_tp_hash:
+            ht->ht_type.tp_hash = (hashfunc)slot.sl_func;
+            break;
+        case Py_tp_init:
+            ht->ht_type.tp_init = (initproc)slot.sl_func;
+            break;
+        case Py_tp_is_gc:
+            ht->ht_type.tp_is_gc = (inquiry)slot.sl_func;
+            break;
+        case Py_tp_iter:
+            ht->ht_type.tp_iter = (getiterfunc)slot.sl_func;
+            break;
+        case Py_tp_iternext:
+            ht->ht_type.tp_iternext = (iternextfunc)slot.sl_func;
+            break;
+        case Py_tp_methods:
+            ht->ht_type.tp_methods = slot.sl_ptr;
+            break;
+        case Py_tp_new:
+            ht->ht_type.tp_new = (newfunc)slot.sl_func;
+            break;
+        case Py_tp_repr:
+            ht->ht_type.tp_repr = (reprfunc)slot.sl_func;
+            break;
+        case Py_tp_richcompare:
+            ht->ht_type.tp_richcompare = (richcmpfunc)slot.sl_func;
+            break;
+        case Py_tp_setattr:
+            ht->ht_type.tp_setattr = (setattrfunc)slot.sl_func;
+            break;
+        case Py_tp_setattro:
+            ht->ht_type.tp_setattro = (setattrofunc)slot.sl_func;
+            break;
+        case Py_tp_str:
+            ht->ht_type.tp_str = (reprfunc)slot.sl_func;
+            break;
+        case Py_tp_traverse:
+            ht->ht_type.tp_traverse = (traverseproc)slot.sl_func;
+            break;
+        case Py_tp_members:
+            ht->ht_type.tp_members = slot.sl_ptr;
+            break;
+        case Py_tp_getset:
+            ht->ht_type.tp_getset = slot.sl_ptr;
+            break;
+        case Py_tp_free:
+            ht->ht_type.tp_free = (freefunc)slot.sl_func;
+            break;
+        case Py_nb_matrix_multiply:
+            ht->as_number.nb_matrix_multiply = (binaryfunc)slot.sl_func;
+            break;
+        case Py_nb_inplace_matrix_multiply:
+            ht->as_number.nb_inplace_matrix_multiply = (binaryfunc)slot.sl_func;
+            break;
+        case Py_am_await:
+            ht->as_async.am_await = (unaryfunc)slot.sl_func;
+            break;
+        case Py_am_aiter:
+            ht->as_async.am_aiter = (unaryfunc)slot.sl_func;
+            break;
+        case Py_am_anext:
+            ht->as_async.am_anext = (unaryfunc)slot.sl_func;
+            break;
+        case Py_tp_finalize:
+            ht->ht_type.tp_finalize = (destructor)slot.sl_func;
+            break;
+        case Py_am_send:
+            ht->as_async.am_send = (sendfunc)slot.sl_func;
+            break;
+        case Py_tp_vectorcall:
+            ht->ht_type.tp_vectorcall = (vectorcallfunc)slot.sl_func;
+            break;
+        case Py_bf_getbuffer:
+            ht->as_buffer.bf_getbuffer = (getbufferproc)slot.sl_func;
+            break;
+        case Py_bf_releasebuffer:
+            ht->as_buffer.bf_releasebuffer = (releasebufferproc)slot.sl_func;
+            break;
+        case Py_mp_ass_subscript:
+            ht->as_mapping.mp_ass_subscript = (objobjargproc)slot.sl_func;
+            break;
+        case Py_mp_length:
+            ht->as_mapping.mp_length = (lenfunc)slot.sl_func;
+            break;
+    }
+}
+
+static inline _PySlot_DTYPE
+_PySlot_get_dtype(uint16_t slot_id)
+{
+    switch (slot_id) {
+        case Py_slot_end: return _PySlot_DTYPE_VOID;
+        case Py_mp_subscript: return _PySlot_DTYPE_FUNC;
+        case Py_nb_absolute: return _PySlot_DTYPE_FUNC;
+        case Py_nb_add: return _PySlot_DTYPE_FUNC;
+        case Py_nb_and: return _PySlot_DTYPE_FUNC;
+        case Py_nb_bool: return _PySlot_DTYPE_FUNC;
+        case Py_nb_divmod: return _PySlot_DTYPE_FUNC;
+        case Py_nb_float: return _PySlot_DTYPE_FUNC;
+        case Py_nb_floor_divide: return _PySlot_DTYPE_FUNC;
+        case Py_nb_index: return _PySlot_DTYPE_FUNC;
+        case Py_nb_inplace_add: return _PySlot_DTYPE_FUNC;
+        case Py_nb_inplace_and: return _PySlot_DTYPE_FUNC;
+        case Py_nb_inplace_floor_divide: return _PySlot_DTYPE_FUNC;
+        case Py_nb_inplace_lshift: return _PySlot_DTYPE_FUNC;
+        case Py_nb_inplace_multiply: return _PySlot_DTYPE_FUNC;
+        case Py_nb_inplace_or: return _PySlot_DTYPE_FUNC;
+        case Py_nb_inplace_power: return _PySlot_DTYPE_FUNC;
+        case Py_nb_inplace_remainder: return _PySlot_DTYPE_FUNC;
+        case Py_nb_inplace_rshift: return _PySlot_DTYPE_FUNC;
+        case Py_nb_inplace_subtract: return _PySlot_DTYPE_FUNC;
+        case Py_nb_inplace_true_divide: return _PySlot_DTYPE_FUNC;
+        case Py_nb_inplace_xor: return _PySlot_DTYPE_FUNC;
+        case Py_nb_int: return _PySlot_DTYPE_FUNC;
+        case Py_nb_invert: return _PySlot_DTYPE_FUNC;
+        case Py_nb_lshift: return _PySlot_DTYPE_FUNC;
+        case Py_nb_multiply: return _PySlot_DTYPE_FUNC;
+        case Py_nb_negative: return _PySlot_DTYPE_FUNC;
+        case Py_nb_or: return _PySlot_DTYPE_FUNC;
+        case Py_nb_positive: return _PySlot_DTYPE_FUNC;
+        case Py_nb_power: return _PySlot_DTYPE_FUNC;
+        case Py_nb_remainder: return _PySlot_DTYPE_FUNC;
+        case Py_nb_rshift: return _PySlot_DTYPE_FUNC;
+        case Py_nb_subtract: return _PySlot_DTYPE_FUNC;
+        case Py_nb_true_divide: return _PySlot_DTYPE_FUNC;
+        case Py_nb_xor: return _PySlot_DTYPE_FUNC;
+        case Py_sq_ass_item: return _PySlot_DTYPE_FUNC;
+        case Py_sq_concat: return _PySlot_DTYPE_FUNC;
+        case Py_sq_contains: return _PySlot_DTYPE_FUNC;
+        case Py_sq_inplace_concat: return _PySlot_DTYPE_FUNC;
+        case Py_sq_inplace_repeat: return _PySlot_DTYPE_FUNC;
+        case Py_sq_item: return _PySlot_DTYPE_FUNC;
+        case Py_sq_length: return _PySlot_DTYPE_FUNC;
+        case Py_sq_repeat: return _PySlot_DTYPE_FUNC;
+        case Py_tp_alloc: return _PySlot_DTYPE_FUNC;
+        case Py_tp_base: return _PySlot_DTYPE_PTR;
+        case Py_tp_bases: return _PySlot_DTYPE_PTR;
+        case Py_tp_call: return _PySlot_DTYPE_FUNC;
+        case Py_tp_clear: return _PySlot_DTYPE_FUNC;
+        case Py_tp_dealloc: return _PySlot_DTYPE_FUNC;
+        case Py_tp_del: return _PySlot_DTYPE_FUNC;
+        case Py_tp_descr_get: return _PySlot_DTYPE_FUNC;
+        case Py_tp_descr_set: return _PySlot_DTYPE_FUNC;
+        case Py_tp_doc: return _PySlot_DTYPE_PTR;
+        case Py_tp_getattr: return _PySlot_DTYPE_FUNC;
+        case Py_tp_getattro: return _PySlot_DTYPE_FUNC;
+        case Py_tp_hash: return _PySlot_DTYPE_FUNC;
+        case Py_tp_init: return _PySlot_DTYPE_FUNC;
+        case Py_tp_is_gc: return _PySlot_DTYPE_FUNC;
+        case Py_tp_iter: return _PySlot_DTYPE_FUNC;
+        case Py_tp_iternext: return _PySlot_DTYPE_FUNC;
+        case Py_tp_methods: return _PySlot_DTYPE_PTR;
+        case Py_tp_new: return _PySlot_DTYPE_FUNC;
+        case Py_tp_repr: return _PySlot_DTYPE_FUNC;
+        case Py_tp_richcompare: return _PySlot_DTYPE_FUNC;
+        case Py_tp_setattr: return _PySlot_DTYPE_FUNC;
+        case Py_tp_setattro: return _PySlot_DTYPE_FUNC;
+        case Py_tp_str: return _PySlot_DTYPE_FUNC;
+        case Py_tp_traverse: return _PySlot_DTYPE_FUNC;
+        case Py_tp_members: return _PySlot_DTYPE_PTR;
+        case Py_tp_getset: return _PySlot_DTYPE_PTR;
+        case Py_tp_free: return _PySlot_DTYPE_FUNC;
+        case Py_nb_matrix_multiply: return _PySlot_DTYPE_FUNC;
+        case Py_nb_inplace_matrix_multiply: return _PySlot_DTYPE_FUNC;
+        case Py_am_await: return _PySlot_DTYPE_FUNC;
+        case Py_am_aiter: return _PySlot_DTYPE_FUNC;
+        case Py_am_anext: return _PySlot_DTYPE_FUNC;
+        case Py_tp_finalize: return _PySlot_DTYPE_FUNC;
+        case Py_am_send: return _PySlot_DTYPE_FUNC;
+        case Py_tp_vectorcall: return _PySlot_DTYPE_FUNC;
+        case Py_tp_token: return _PySlot_DTYPE_PTR;
+        case Py_mod_create: return _PySlot_DTYPE_FUNC;
+        case Py_mod_exec: return _PySlot_DTYPE_FUNC;
+        case Py_mod_multiple_interpreters: return _PySlot_DTYPE_UINT64;
+        case Py_mod_gil: return _PySlot_DTYPE_UINT64;
+        case Py_bf_getbuffer: return _PySlot_DTYPE_FUNC;
+        case Py_bf_releasebuffer: return _PySlot_DTYPE_FUNC;
+        case Py_mp_ass_subscript: return _PySlot_DTYPE_FUNC;
+        case Py_mp_length: return _PySlot_DTYPE_FUNC;
+        case Py_slot_subslots: return _PySlot_DTYPE_PTR;
+        case Py_tp_slots: return _PySlot_DTYPE_PTR;
+        case Py_mod_slots: return _PySlot_DTYPE_PTR;
+        case Py_tp_name: return _PySlot_DTYPE_PTR;
+        case Py_tp_basicsize: return _PySlot_DTYPE_SIZE;
+        case Py_tp_extra_basicsize: return _PySlot_DTYPE_SIZE;
+        case Py_tp_itemsize: return _PySlot_DTYPE_SIZE;
+        case Py_tp_flags: return _PySlot_DTYPE_UINT64;
+        case Py_mod_name: return _PySlot_DTYPE_PTR;
+        case Py_mod_doc: return _PySlot_DTYPE_PTR;
+        case Py_mod_state_size: return _PySlot_DTYPE_SIZE;
+        case Py_mod_methods: return _PySlot_DTYPE_PTR;
+        case Py_mod_state_traverse: return _PySlot_DTYPE_FUNC;
+        case Py_mod_state_clear: return _PySlot_DTYPE_FUNC;
+        case Py_mod_state_free: return _PySlot_DTYPE_FUNC;
+        case Py_tp_metaclass: return _PySlot_DTYPE_PTR;
+        case Py_tp_module: return _PySlot_DTYPE_PTR;
+        case Py_mod_abi: return _PySlot_DTYPE_PTR;
+        case Py_mod_token: return _PySlot_DTYPE_PTR;
+        default: return _PySlot_DTYPE_VOID;
+    }
+}
+
+static inline _PySlot_PROBLEM_HANDLING
+_PySlot_get_duplicate_handling(uint16_t slot_id)
+{
+    switch (slot_id) {
+        case Py_mp_subscript:
+        case Py_nb_absolute:
+        case Py_nb_add:
+        case Py_nb_and:
+        case Py_nb_bool:
+        case Py_nb_divmod:
+        case Py_nb_float:
+        case Py_nb_floor_divide:
+        case Py_nb_index:
+        case Py_nb_inplace_add:
+        case Py_nb_inplace_and:
+        case Py_nb_inplace_floor_divide:
+        case Py_nb_inplace_lshift:
+        case Py_nb_inplace_multiply:
+        case Py_nb_inplace_or:
+        case Py_nb_inplace_power:
+        case Py_nb_inplace_remainder:
+        case Py_nb_inplace_rshift:
+        case Py_nb_inplace_subtract:
+        case Py_nb_inplace_true_divide:
+        case Py_nb_inplace_xor:
+        case Py_nb_int:
+        case Py_nb_invert:
+        case Py_nb_lshift:
+        case Py_nb_multiply:
+        case Py_nb_negative:
+        case Py_nb_or:
+        case Py_nb_positive:
+        case Py_nb_power:
+        case Py_nb_remainder:
+        case Py_nb_rshift:
+        case Py_nb_subtract:
+        case Py_nb_true_divide:
+        case Py_nb_xor:
+        case Py_sq_ass_item:
+        case Py_sq_concat:
+        case Py_sq_contains:
+        case Py_sq_inplace_concat:
+        case Py_sq_inplace_repeat:
+        case Py_sq_item:
+        case Py_sq_length:
+        case Py_sq_repeat:
+        case Py_tp_alloc:
+        case Py_tp_base:
+        case Py_tp_bases:
+        case Py_tp_call:
+        case Py_tp_clear:
+        case Py_tp_dealloc:
+        case Py_tp_del:
+        case Py_tp_descr_get:
+        case Py_tp_descr_set:
+        case Py_tp_getattr:
+        case Py_tp_getattro:
+        case Py_tp_hash:
+        case Py_tp_init:
+        case Py_tp_is_gc:
+        case Py_tp_iter:
+        case Py_tp_iternext:
+        case Py_tp_methods:
+        case Py_tp_new:
+        case Py_tp_repr:
+        case Py_tp_richcompare:
+        case Py_tp_setattr:
+        case Py_tp_setattro:
+        case Py_tp_str:
+        case Py_tp_traverse:
+        case Py_tp_getset:
+        case Py_tp_free:
+        case Py_nb_matrix_multiply:
+        case Py_nb_inplace_matrix_multiply:
+        case Py_am_await:
+        case Py_am_aiter:
+        case Py_am_anext:
+        case Py_tp_finalize:
+        case Py_am_send:
+        case Py_tp_vectorcall:
+        case Py_tp_token:
+        case Py_bf_getbuffer:
+        case Py_bf_releasebuffer:
+        case Py_mp_ass_subscript:
+        case Py_mp_length:
+            return _PySlot_PROBLEM_DEPRECATED;
+        case Py_mod_exec:
+        case Py_mod_abi:
+            return _PySlot_PROBLEM_ALLOW;
+        default:
+            return _PySlot_PROBLEM_REJECT;
+    }
+}
+
+static inline _PySlot_PROBLEM_HANDLING
+_PySlot_get_null_handling(uint16_t slot_id)
+{
+    switch (slot_id) {
+        case Py_slot_end:
+        case Py_tp_doc:
+        case Py_tp_token:
+        case Py_mod_multiple_interpreters:
+        case Py_mod_gil:
+        case Py_slot_subslots:
+        case Py_tp_slots:
+        case Py_mod_slots:
+        case Py_tp_basicsize:
+        case Py_tp_extra_basicsize:
+        case Py_tp_itemsize:
+        case Py_tp_flags:
+        case Py_mod_state_size:
+            return _PySlot_PROBLEM_ALLOW;
+        case Py_mp_subscript:
+        case Py_nb_absolute:
+        case Py_nb_add:
+        case Py_nb_and:
+        case Py_nb_bool:
+        case Py_nb_divmod:
+        case Py_nb_float:
+        case Py_nb_floor_divide:
+        case Py_nb_index:
+        case Py_nb_inplace_add:
+        case Py_nb_inplace_and:
+        case Py_nb_inplace_floor_divide:
+        case Py_nb_inplace_lshift:
+        case Py_nb_inplace_multiply:
+        case Py_nb_inplace_or:
+        case Py_nb_inplace_power:
+        case Py_nb_inplace_remainder:
+        case Py_nb_inplace_rshift:
+        case Py_nb_inplace_subtract:
+        case Py_nb_inplace_true_divide:
+        case Py_nb_inplace_xor:
+        case Py_nb_int:
+        case Py_nb_invert:
+        case Py_nb_lshift:
+        case Py_nb_multiply:
+        case Py_nb_negative:
+        case Py_nb_or:
+        case Py_nb_positive:
+        case Py_nb_power:
+        case Py_nb_remainder:
+        case Py_nb_rshift:
+        case Py_nb_subtract:
+        case Py_nb_true_divide:
+        case Py_nb_xor:
+        case Py_sq_ass_item:
+        case Py_sq_concat:
+        case Py_sq_contains:
+        case Py_sq_inplace_concat:
+        case Py_sq_inplace_repeat:
+        case Py_sq_item:
+        case Py_sq_length:
+        case Py_sq_repeat:
+        case Py_tp_alloc:
+        case Py_tp_base:
+        case Py_tp_bases:
+        case Py_tp_call:
+        case Py_tp_clear:
+        case Py_tp_dealloc:
+        case Py_tp_del:
+        case Py_tp_descr_get:
+        case Py_tp_descr_set:
+        case Py_tp_getattr:
+        case Py_tp_getattro:
+        case Py_tp_hash:
+        case Py_tp_init:
+        case Py_tp_is_gc:
+        case Py_tp_iter:
+        case Py_tp_iternext:
+        case Py_tp_methods:
+        case Py_tp_new:
+        case Py_tp_repr:
+        case Py_tp_richcompare:
+        case Py_tp_setattr:
+        case Py_tp_setattro:
+        case Py_tp_str:
+        case Py_tp_traverse:
+        case Py_tp_getset:
+        case Py_tp_free:
+        case Py_nb_matrix_multiply:
+        case Py_nb_inplace_matrix_multiply:
+        case Py_am_await:
+        case Py_am_aiter:
+        case Py_am_anext:
+        case Py_tp_finalize:
+        case Py_am_send:
+        case Py_tp_vectorcall:
+        case Py_mod_create:
+        case Py_bf_getbuffer:
+        case Py_bf_releasebuffer:
+        case Py_mp_ass_subscript:
+        case Py_mp_length:
+            return _PySlot_PROBLEM_DEPRECATED;
+        default:
+            return _PySlot_PROBLEM_REJECT;
+    }
+}
+
+static inline bool
+_PySlot_get_must_be_static(uint16_t slot_id)
+{
+    switch (slot_id) {
+        case Py_tp_methods: return true;
+        case Py_tp_members: return true;
+        case Py_tp_getset: return true;
+        case Py_mod_methods: return true;
+    }
+    return false;
+}
+
+#endif /* _PY_HAVE_INTERNAL_SLOTS_GENERATED_H */
index c2fb1f85165f7d2dd04ae08856f47638ffe6a717..88c66672ff164a8adbb844e387a88ceaf917e240 100644 (file)
@@ -73,31 +73,6 @@ struct PyModuleDef_Slot {
     void *value;
 };
 
-#define Py_mod_create 1
-#define Py_mod_exec 2
-#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030c0000
-#  define Py_mod_multiple_interpreters 3
-#endif
-#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030d0000
-#  define Py_mod_gil 4
-#endif
-#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15)
-#  define Py_mod_abi 5
-#  define Py_mod_name 6
-#  define Py_mod_doc 7
-#  define Py_mod_state_size 8
-#  define Py_mod_methods 9
-#  define Py_mod_state_traverse 10
-#  define Py_mod_state_clear 11
-#  define Py_mod_state_free 12
-#  define Py_mod_token 13
-#endif
-
-
-#ifndef Py_LIMITED_API
-#define _Py_mod_LAST_SLOT 13
-#endif
-
 #endif /* New in 3.5 */
 
 /* for Py_mod_multiple_interpreters: */
@@ -120,7 +95,7 @@ PyAPI_FUNC(int) PyUnstable_Module_SetGIL(PyObject *module, void *gil);
 #endif
 
 #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15)
-PyAPI_FUNC(PyObject *) PyModule_FromSlotsAndSpec(const PyModuleDef_Slot *slots,
+PyAPI_FUNC(PyObject *) PyModule_FromSlotsAndSpec(const PySlot *slots,
                                                  PyObject *spec);
 PyAPI_FUNC(int) PyModule_Exec(PyObject *module);
 PyAPI_FUNC(int) PyModule_GetStateSize(PyObject *module, Py_ssize_t *result);
index 043be0260b1aec4ade77dfb06ffcba1f4d2eb18a..20c2dab4401fef04de8800657e85e02fadb9e408 100644 (file)
@@ -364,6 +364,9 @@ PyAPI_FUNC(Py_ssize_t) PyType_GetTypeDataSize(PyTypeObject *cls);
 PyAPI_FUNC(int) PyType_GetBaseByToken(PyTypeObject *, void *, PyTypeObject **);
 #define Py_TP_USE_SPEC NULL
 #endif
+#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15)
+PyAPI_FUNC(PyObject *) PyType_FromSlots(struct PySlot *slots);
+#endif
 
 /* Generic type check */
 PyAPI_FUNC(int) PyType_IsSubtype(PyTypeObject *, PyTypeObject *);
index e78ed56a3b67cd19a6fbce550cb9b145c719ed31..1d0b4e3e85ef74a4061178a2297cfd9c5b6bac49 100644 (file)
@@ -14,6 +14,7 @@ typedef struct PyModuleDef_Slot PyModuleDef_Slot;
 typedef struct PyMethodDef PyMethodDef;
 typedef struct PyGetSetDef PyGetSetDef;
 typedef struct PyMemberDef PyMemberDef;
+typedef struct PySlot PySlot;
 
 typedef struct _object PyObject;
 typedef struct _longobject PyLongObject;
diff --git a/Include/slots.h b/Include/slots.h
new file mode 100644 (file)
index 0000000..4bf7bda
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef _Py_HAVE_SLOTS_H
+#define _Py_HAVE_SLOTS_H
+
+typedef void (*_Py_funcptr_t)(void);
+
+struct PySlot {
+    uint16_t sl_id;
+    uint16_t sl_flags;
+    _Py_ANONYMOUS union {
+        uint32_t sl_reserved; // must be 0
+    };
+    _Py_ANONYMOUS union {
+        void *sl_ptr;
+        _Py_funcptr_t sl_func;
+        Py_ssize_t sl_size;
+        int64_t sl_int64;
+        uint64_t sl_uint64;
+    };
+};
+
+#define PySlot_OPTIONAL 0x0001
+#define PySlot_STATIC 0x0002
+#define PySlot_INTPTR 0x0004
+
+#define Py_slot_invalid 0xffff
+
+#define PySlot_DATA(NAME, VALUE) \
+    {.sl_id=(NAME), .sl_flags=PySlot_INTPTR, .sl_ptr=(void*)(VALUE)}
+
+#define PySlot_FUNC(NAME, VALUE) \
+    {.sl_id=(NAME), .sl_func=(_Py_funcptr_t)(VALUE)}
+
+#define PySlot_SIZE(NAME, VALUE) \
+    {.sl_id=(NAME), .sl_size=(Py_ssize_t)(VALUE)}
+
+#define PySlot_INT64(NAME, VALUE) \
+    {.sl_id=(NAME), .sl_int64=(int64_t)(VALUE)}
+
+#define PySlot_UINT64(NAME, VALUE) \
+    {.sl_id=(NAME), .sl_uint64=(uint64_t)(VALUE)}
+
+#define PySlot_STATIC_DATA(NAME, VALUE) \
+    {.sl_id=(NAME), .sl_flags=PySlot_STATIC, .sl_ptr=(VALUE)}
+
+#define PySlot_END {0}
+
+
+// Macros without designated initializers (for C++11 and below):
+
+#define PySlot_PTR(NAME, VALUE) \
+    {(NAME), PySlot_INTPTR, {0}, {(void*)(VALUE)}}
+
+#define PySlot_PTR_STATIC(NAME, VALUE) \
+    {(NAME), PySlot_INTPTR | PySlot_STATIC, {0}, {(void*)(VALUE)}}
+
+#endif  // _Py_HAVE_SLOTS_H
similarity index 60%
rename from Include/typeslots.h
rename to Include/slots_generated.h
index a7f3017ec02e92f69f8194b11f51d2ceee172a6c..42edd3ad4c69ffd5062f0fd5087c4ee2e0811d39 100644 (file)
@@ -1,8 +1,15 @@
-/* Do not renumber the file; these numbers are part of the stable ABI. */
-#define Py_bf_getbuffer 1
-#define Py_bf_releasebuffer 2
-#define Py_mp_ass_subscript 3
-#define Py_mp_length 4
+/* Generated by Tools/build/generate_slots.py */
+
+#ifndef _PY_HAVE_SLOTS_GENERATED_H
+#define _PY_HAVE_SLOTS_GENERATED_H
+
+#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15)
+#define _Py_SLOT_COMPAT_VALUE(OLD, NEW) NEW
+#else
+#define _Py_SLOT_COMPAT_VALUE(OLD, NEW) OLD
+#endif
+
+#define Py_slot_end 0
 #define Py_mp_subscript 5
 #define Py_nb_absolute 6
 #define Py_nb_add 7
 #define Py_am_await 77
 #define Py_am_aiter 78
 #define Py_am_anext 79
-#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03050000
-/* New in 3.5 */
 #define Py_tp_finalize 80
-#endif
-#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000
-/* New in 3.10 */
 #define Py_am_send 81
-#endif
-#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030E0000
-/* New in 3.14 */
 #define Py_tp_vectorcall 82
-#endif
-#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030E0000
-/* New in 3.14 */
 #define Py_tp_token 83
-#endif
+#define Py_mod_create _Py_SLOT_COMPAT_VALUE(1, 84)
+#define Py_mod_exec _Py_SLOT_COMPAT_VALUE(2, 85)
+#define Py_mod_multiple_interpreters _Py_SLOT_COMPAT_VALUE(3, 86)
+#define Py_mod_gil _Py_SLOT_COMPAT_VALUE(4, 87)
+#define Py_bf_getbuffer _Py_SLOT_COMPAT_VALUE(1, 88)
+#define Py_bf_releasebuffer _Py_SLOT_COMPAT_VALUE(2, 89)
+#define Py_mp_ass_subscript _Py_SLOT_COMPAT_VALUE(3, 90)
+#define Py_mp_length _Py_SLOT_COMPAT_VALUE(4, 91)
+#define Py_slot_subslots 92
+#define Py_tp_slots 93
+#define Py_mod_slots 94
+#define Py_tp_name 95
+#define Py_tp_basicsize 96
+#define Py_tp_extra_basicsize 97
+#define Py_tp_itemsize 98
+#define Py_tp_flags 99
+#define Py_mod_name 100
+#define Py_mod_doc 101
+#define Py_mod_state_size 102
+#define Py_mod_methods 103
+#define Py_mod_state_traverse 104
+#define Py_mod_state_clear 105
+#define Py_mod_state_free 106
+#define Py_tp_metaclass 107
+#define Py_tp_module 108
+#define Py_mod_abi 109
+#define Py_mod_token 110
+
+#define _Py_slot_COUNT 111
+#endif /* _PY_HAVE_SLOTS_GENERATED_H */
index 4c16bbd4cb0acf16948249f7afa48aa0baf49877..3debc6369e89fb4c5d4ff3b99885479f86392ba8 100644 (file)
@@ -923,8 +923,8 @@ class CAPITest(unittest.TestCase):
 
     def test_tp_bases_slot_none(self):
         self.assertRaisesRegex(
-            SystemError,
-            "Py_tp_bases is not a tuple",
+            TypeError,
+            "metaclass conflict",
             _testcapi.create_heapctype_with_none_bases_slot
         )
 
@@ -1055,13 +1055,13 @@ class TestHeapTypeRelative(unittest.TestCase):
     def test_heaptype_relative_members_errors(self):
         with self.assertRaisesRegex(
                 SystemError,
-                r"With Py_RELATIVE_OFFSET, basicsize must be negative"):
+                r"With Py_RELATIVE_OFFSET, basicsize must be extended"):
             _testlimitedcapi.make_heaptype_with_member(0, 1234, 0, True)
         with self.assertRaisesRegex(
-                SystemError, r"Member offset out of range \(0\.\.-basicsize\)"):
+                SystemError, r"Member offset out of range \(0\.\.extra_basicsize\)"):
             _testlimitedcapi.make_heaptype_with_member(0, -8, 1234, True)
         with self.assertRaisesRegex(
-                SystemError, r"Member offset out of range \(0\.\.-basicsize\)"):
+                SystemError, r"Member offset must not be negative"):
             _testlimitedcapi.make_heaptype_with_member(0, -8, -1, True)
 
         Sub = _testlimitedcapi.make_heaptype_with_member(0, -8, 0, True)
@@ -1078,7 +1078,7 @@ class TestHeapTypeRelative(unittest.TestCase):
             with self.subTest(member_name=member_name):
                 with self.assertRaisesRegex(
                         SystemError,
-                        r"With Py_RELATIVE_OFFSET, basicsize must be negative."):
+                        r"With Py_RELATIVE_OFFSET, basicsize must be extended"):
                     _testlimitedcapi.make_heaptype_with_member(
                         basicsize=sys.getsizeof(object()) + 100,
                         add_relative_flag=True,
@@ -1089,7 +1089,7 @@ class TestHeapTypeRelative(unittest.TestCase):
                         )
                 with self.assertRaisesRegex(
                         SystemError,
-                        r"Member offset out of range \(0\.\.-basicsize\)"):
+                        r"Member offset must not be negative"):
                     _testlimitedcapi.make_heaptype_with_member(
                         basicsize=-8,
                         add_relative_flag=True,
@@ -1098,6 +1098,17 @@ class TestHeapTypeRelative(unittest.TestCase):
                         member_type=_testlimitedcapi.Py_T_PYSSIZET,
                         member_flags=_testlimitedcapi.Py_READONLY,
                         )
+                with self.assertRaisesRegex(
+                        SystemError,
+                        r"Member offset out of range \(0\.\.extra_basicsize\)"):
+                    _testlimitedcapi.make_heaptype_with_member(
+                        basicsize=-8,
+                        add_relative_flag=True,
+                        member_name=member_name,
+                        member_offset=1234,
+                        member_type=_testlimitedcapi.Py_T_PYSSIZET,
+                        member_flags=_testlimitedcapi.Py_READONLY,
+                        )
                 with self.assertRaisesRegex(
                         SystemError,
                         r"type of %s must be Py_T_PYSSIZET" % member_name):
index c32ca1098edc56b4691e1b94a06e0a338f579b55..29e1ce5b9af87f54fb332bd06c440c1d7d7985ef 100644 (file)
@@ -150,11 +150,13 @@ class TestModFromSlotsAndSpec(unittest.TestCase):
                 with self.assertRaises(SystemError) as cm:
                     _testcapi.module_from_slots_repeat_slot(spec)
                 self.assertIn(name, str(cm.exception))
-                self.assertIn("more than one", str(cm.exception))
+                self.assertRegex(
+                    str(cm.exception),
+                    rf"^module( [_\w]+)? has multiple {name}( \(\d+\))? slots$")
 
     def test_null_def_slot(self):
         """Slots that replace PyModuleDef fields can't be NULL"""
-        for name in (*DEF_SLOTS, 'Py_mod_exec'):
+        for name in {*DEF_SLOTS, 'Py_mod_exec'} - {'Py_mod_state_size'}:
             with self.subTest(name):
                 spec = FakeSpec()
                 spec._test_slot_id = getattr(_testcapi, name)
diff --git a/Lib/test/test_capi/test_slots.py b/Lib/test/test_capi/test_slots.py
new file mode 100644 (file)
index 0000000..c78b118
--- /dev/null
@@ -0,0 +1,314 @@
+from test.support import import_helper, subTests
+import contextlib
+import unittest
+import types
+import sys
+
+_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
+_testcapi = import_helper.import_module('_testcapi')
+
+class FakeSpec:
+    name = 'module'
+
+
+# See Modules/_testlimitedcapi/slots.c for the definitions.
+# This module is full of "magic constants" which simply need to match
+# between the C and Python part of the tests.
+
+class TypeSlotsTests(unittest.TestCase):
+    def test_basic_type_slots(self):
+        cls = _testlimitedcapi.type_from_slots("basic")
+        self.assertEqual(cls.__name__, "MyType")
+
+        # Py_TPFLAGS_IMMUTABLETYPE is *not* set
+        cls.attr = 123
+
+        # Py_TPFLAGS_BASETYPE is *not* set
+        with self.assertRaises(TypeError):
+            class Sub(cls):
+                pass
+
+    def test_mod_slot_in_type(self):
+        with self.assertRaisesRegex(SystemError, "invalid.* 100 .*Py_mod_name"):
+            _testlimitedcapi.type_from_slots("foreign_slot")
+
+    def test_size_slots(self):
+        cls = _testlimitedcapi.type_from_slots("basicsize")
+        self.assertGreaterEqual(cls.__basicsize__, 256)
+
+        cls = _testlimitedcapi.type_from_slots("extra_basicsize")
+        self.assertGreaterEqual(cls.__basicsize__, object.__basicsize__ + 256)
+
+        cls = _testlimitedcapi.type_from_slots("itemsize")
+        self.assertGreaterEqual(cls.__itemsize__, 16)
+
+    def test_flag_slots(self):
+        cls = _testlimitedcapi.type_from_slots("flags")
+        with self.assertRaises(TypeError):
+            # Py_TPFLAGS_IMMUTABLETYPE is set
+            cls.attr = 123
+        class Sub(cls):
+            # Py_TPFLAGS_BASETYPE is set
+            pass
+
+    def test_func_slots(self):
+        cls = _testlimitedcapi.type_from_slots("matmul_123")
+        self.assertEqual(cls() @ None, 123)
+
+    def test_optional_end(self):
+        with self.assertRaisesRegex(SystemError, "invalid flags.*Py_slot_end"):
+            cls = _testlimitedcapi.type_from_slots("optional_end")
+
+    def test_invalid(self):
+        with self.assertRaisesRegex(SystemError, "Py_slot_invalid"):
+            cls = _testlimitedcapi.type_from_slots("invalid")
+        with self.assertRaisesRegex(SystemError, f"slot ID {0xfbad}"):
+            cls = _testlimitedcapi.type_from_slots("invalid_fbad")
+
+        cls = _testlimitedcapi.type_from_slots("optional_invalid")
+        self.assertGreaterEqual(cls.__basicsize__, object.__basicsize__ + 256)
+
+        cls = _testlimitedcapi.type_from_slots("optional_invalid_fbad")
+        self.assertGreaterEqual(cls.__basicsize__, object.__basicsize__ + 256)
+
+    @subTests("case_name", ["old_slot_numbers", "new_slot_numbers"])
+    def test_compat_slot_numbers(self, case_name):
+        cls = _testlimitedcapi.type_from_slots(case_name)
+        obj = cls()
+
+        # Py_bf_getbuffer (1), Py_bf_releasebuffer (2)
+        self.assertEqual(obj.buf_counter, 0)
+        mem = memoryview(obj)
+        self.assertEqual(bytes(mem), b"buf\0")
+        self.assertEqual(obj.buf_counter, 1)
+        mem.release()
+        self.assertEqual(obj.buf_counter, 0)
+
+        # Py_mp_ass_subscript (3)
+        with self.assertRaises(KeyError):
+            obj["key"] = "value"
+
+        # Py_mp_length (4)
+        self.assertEqual(len(obj), 456)
+
+    def test_nonstatic_tp_members(self):
+        with self.assertRaisesRegex(SystemError, "Py_tp_members.*STATIC"):
+            _testlimitedcapi.type_from_slots("nonstatic_tp_members")
+
+    def test_intptr_flags(self):
+        cls = _testlimitedcapi.type_from_slots("intptr_flags_macro")
+        with self.assertRaises(TypeError):
+            # Py_TPFLAGS_IMMUTABLETYPE is set
+            cls.attr = 123
+
+        cls = _testlimitedcapi.type_from_slots("intptr_flags_struct")
+        with self.assertRaises(TypeError):
+            # Py_TPFLAGS_IMMUTABLETYPE is set
+            cls.attr = 123
+
+        cls = _testlimitedcapi.type_from_slots("intptr_static")
+        cls.attribute = 123
+
+    def test_nested(self):
+        cls = _testlimitedcapi.type_from_slots("nested")
+        self.assertEqual(cls() + 1, 123)
+        self.assertEqual(cls() - 1, 234)
+
+        cls = _testlimitedcapi.type_from_slots("nested_max")
+        self.assertEqual(cls() + 1, 123)
+        self.assertEqual(cls() - 1, 234)
+        self.assertEqual(cls() * 1, 345)
+        self.assertEqual(cls() / 1, 456)
+        self.assertEqual(cls() % 1, 567)
+
+        with self.assertRaisesRegex(SystemError, "too many levels"):
+            _testlimitedcapi.type_from_slots("nested_over_limit")
+
+        cls = _testlimitedcapi.type_from_slots("nested_old")
+        self.assertEqual(cls() + 1, 123)
+        self.assertEqual(cls() - 1, 234)
+
+        cls = _testlimitedcapi.type_from_slots("nested_old_max")
+        self.assertEqual(cls() + 1, 123)
+        self.assertEqual(cls() - 1, 234)
+        self.assertEqual(cls() * 1, 345)
+        self.assertEqual(cls() / 1, 456)
+        self.assertEqual(cls() % 1, 567)
+
+        with self.assertRaisesRegex(SystemError, "too many levels"):
+            _testlimitedcapi.type_from_slots("nested_old_over_limit")
+
+        cls = _testlimitedcapi.type_from_slots("nested_pingpong")
+        self.assertEqual(cls() + 1, 123)
+        self.assertEqual(cls() - 1, 234)
+        self.assertEqual(cls() * 1, 345)
+        self.assertEqual(cls() / 1, 456)
+        self.assertEqual(cls() % 1, 567)
+
+    # Slot names aren't exposed to Python yet; see Include/slots_generated.h
+    # for the definitions.
+
+    @subTests("slot_number", [
+        *range(1, 83),      # Original slots
+        *range(88, 92),     # New compat slot values
+        *range(95, 99),     # Slots for PyType_Spec fields
+        *range(107, 109),   # Slots for PyType_FromMetaclass args
+    ])
+    def test_null_slot_handling(self, slot_number):
+        if slot_number == 56:
+            # Py_tp_doc
+            return
+        elif slot_number == 72 or slot_number >= 95:
+            # Py_tp_members; all new slots
+            ctx = self.assertRaisesRegex(
+                SystemError, "NULL not allowed|must be positive")
+            ctx_old = ctx
+        else:
+            ctx = self.assertWarnsRegex(DeprecationWarning, "NULL")
+            ctx_old = contextlib.nullcontext()
+        with ctx:
+            _testlimitedcapi.type_from_null_slot(slot_number)
+        if slot_number < 95:
+            with ctx_old:
+                _testlimitedcapi.type_from_null_spec_slot(slot_number)
+
+    def test_repeat_warning(self):
+        with self.assertWarnsRegex(DeprecationWarning, "multiple"):
+            cls = _testlimitedcapi.type_from_slots("repeat_add")
+        self.assertEqual(cls() + 1, 456)
+
+    def test_repeat_error(self):
+        with self.assertRaisesRegex(SystemError, "multiple"):
+            cls = _testlimitedcapi.type_from_slots("repeat_module")
+
+class ModuleSlotsTests(unittest.TestCase):
+    def test_basic_module_slots(self):
+        mod = _testlimitedcapi.module_from_slots("basic", FakeSpec())
+        self.assertIsInstance(mod, types.ModuleType)
+
+    def test_type_slot_in_module(self):
+        with self.assertRaisesRegex(SystemError, "invalid.* 95 .*Py_tp_name"):
+            _testlimitedcapi.module_from_slots("foreign_slot", FakeSpec())
+
+    def test_size_slots(self):
+        mod = _testlimitedcapi.module_from_slots("state_size", FakeSpec())
+        self.assertEqual(mod.state_size, 42)
+
+    def test_flag_slots(self):
+        mod = _testlimitedcapi.module_from_slots("multi_interp", FakeSpec())
+
+    def test_exec_slot(self):
+        mod = _testlimitedcapi.module_from_slots("exec", FakeSpec())
+        self.assertEqual(mod.exec_done, "yes")
+
+    def test_optional_end(self):
+        with self.assertRaisesRegex(SystemError, "invalid flags.*Py_slot_end"):
+            _testlimitedcapi.module_from_slots("optional_end", FakeSpec())
+
+    def test_invalid(self):
+        with self.assertRaisesRegex(SystemError, "Py_slot_invalid"):
+            _testlimitedcapi.module_from_slots("invalid", FakeSpec())
+        with self.assertRaisesRegex(SystemError, f"slot ID {0xfbad}"):
+            _testlimitedcapi.module_from_slots("invalid_fbad", FakeSpec())
+
+        mod = _testlimitedcapi.module_from_slots("optional_invalid", FakeSpec())
+        self.assertEqual(mod.exec_done, "yes")
+
+        mod = _testlimitedcapi.module_from_slots("optional_invalid_fbad", FakeSpec())
+        self.assertEqual(mod.exec_done, "yes")
+
+    @subTests("case_name", ["old_slot_numbers", "new_slot_numbers"])
+    def test_compat_slot_numbers(self, case_name):
+        mod = _testlimitedcapi.module_from_slots(case_name, FakeSpec())
+        self.assertEqual(mod.exec_done, "yes")
+
+    @subTests("case_name", ["old_slot_number_create", "new_slot_number_create"])
+    def test_compat_slot_number_create(self, case_name):
+        spec = FakeSpec()
+        mod = _testlimitedcapi.module_from_slots(case_name, spec)
+        self.assertIs(mod, spec)
+
+    @subTests("slot_number", [4, 87])
+    def test_compat_slot_number_gil(self, slot_number):
+        spec = FakeSpec()
+        gil_enabled = sys._is_gil_enabled()
+        mod = _testlimitedcapi.module_from_gil_slot(slot_number, spec)
+        self.assertEqual(gil_enabled, sys._is_gil_enabled())
+
+    def test_nonstatic_mod_methods(self):
+        with self.assertRaisesRegex(SystemError, "Py_mod_methods.*STATIC"):
+            _testlimitedcapi.module_from_slots("nonstatic_mod_methods",
+                                               FakeSpec())
+
+    def test_intptr_methods(self):
+        mod = _testlimitedcapi.module_from_slots("intptr_methods",
+                                                 FakeSpec())
+        self.assertEqual(mod.type_from_slots.__name__, "type_from_slots")
+
+    def test_nested(self):
+        mod = _testlimitedcapi.module_from_slots("nested", FakeSpec())
+        self.assertEqual(mod.exec_done, "yes")
+        self.assertEqual(mod.__doc__, "doc")
+
+        mod = _testlimitedcapi.module_from_slots("nested_max", FakeSpec())
+        self.assertEqual(mod.exec_done, "yes")
+        self.assertEqual(mod.state_size, 53)
+        self.assertEqual(mod.__doc__, "doc")
+
+        with self.assertRaisesRegex(SystemError, "too many levels"):
+            _testlimitedcapi.module_from_slots("nested_over_limit", FakeSpec())
+
+        mod = _testlimitedcapi.module_from_slots("nested_old", FakeSpec())
+        self.assertEqual(mod.exec_done, "yes")
+        self.assertEqual(mod.__doc__, "doc")
+
+        mod = _testlimitedcapi.module_from_slots("nested_old_max", FakeSpec())
+        self.assertEqual(mod.exec_done, "yes")
+        self.assertEqual(mod.state_size, 53)
+        self.assertEqual(mod.__doc__, "doc")
+
+        with self.assertRaisesRegex(SystemError, "too many levels"):
+            _testlimitedcapi.module_from_slots("nested_old_over_limit", FakeSpec())
+
+        mod = _testlimitedcapi.module_from_slots("nested_pingpong", FakeSpec())
+        self.assertEqual(mod.exec_done, "yes")
+        self.assertEqual(mod.state_size, 53)
+        self.assertEqual(mod.__doc__, "doc")
+
+    def test_nested_nonstatic_from_def(self):
+        with self.assertRaisesRegex(SystemError, "must be static"):
+            _testcapi.module_from_def_nonstatic_nested(FakeSpec())
+
+    # Slot names aren't exposed to Python yet; see Include/slots_generated.h
+    # for the definitions.
+
+    @subTests("slot_number", [
+        *range(1, 5),       # Old compat slot values
+        *range(84, 88),     # New compat slot values
+        *range(100, 107),   # Slots for PyModuleDef fields
+        *range(109, 111),   # Slots new in 3.15
+    ])
+    def test_null_slot_handling(self, slot_number):
+        if slot_number in {3, 86, 4, 87, 102}:
+            # Py_mod_mult.interp., Py_mod_gil, Py_mod_state_size
+            return
+        elif slot_number in {2, 85} or slot_number > 85:
+            # Py_mod_exec, new slots
+            ctx = self.assertRaisesRegex(SystemError, "NULL not allowed")
+            ctx_old = ctx
+        else:
+            ctx = self.assertWarnsRegex(DeprecationWarning, "NULL")
+            ctx_old = contextlib.nullcontext()
+        with ctx:
+            _testlimitedcapi.module_from_null_slot(slot_number, FakeSpec())
+        with ctx_old:
+            _testcapi.module_from_null_def_slot(slot_number,
+                                                FakeSpec())
+
+    def test_repeat_error(self):
+        with self.assertRaisesRegex(SystemError, "multiple"):
+            _testlimitedcapi.module_from_slots("repeat_create", FakeSpec())
+        with self.assertRaisesRegex(SystemError, "multiple"):
+            _testlimitedcapi.module_from_slots("repeat_exec", FakeSpec())
+        with self.assertRaisesRegex(SystemError, "multiple"):
+            _testlimitedcapi.module_from_slots("repeat_gil", FakeSpec())
index 46d00f3845eaa62665cb0e1a0bbf6aabc2d6c1b7..895eca50f03b9850aea1037ca48994d1004db6d4 100644 (file)
@@ -125,13 +125,13 @@ _Py_COMP_DIAG_PUSH
 PyDoc_STRVAR(_testcext_doc, "C test extension.");
 PyABIInfo_VAR(abi_info);
 
-static PyModuleDef_Slot _testcext_slots[] = {
-    {Py_mod_abi, &abi_info},
-    {Py_mod_name, STR(MODULE_NAME)},
-    {Py_mod_doc, (void*)(char*)_testcext_doc},
-    {Py_mod_exec, (void*)_testcext_exec},
-    {Py_mod_methods, _testcext_methods},
-    {0, NULL}
+static PySlot _testcext_slots[] = {
+    PySlot_STATIC_DATA(Py_mod_abi, &abi_info),
+    PySlot_STATIC_DATA(Py_mod_name, STR(MODULE_NAME)),
+    PySlot_STATIC_DATA(Py_mod_doc, (void*)(char*)_testcext_doc),
+    PySlot_FUNC(Py_mod_exec, (void*)_testcext_exec),
+    PySlot_STATIC_DATA(Py_mod_methods, _testcext_methods),
+    PySlot_END,
 };
 
 _Py_COMP_DIAG_POP
index 7b348ff298a4b25adedac6ab9f1d52e8999eaf5e..a7b864c701be772c0c403814791e6e793a60130a 100644 (file)
@@ -743,6 +743,7 @@ SYMBOL_NAMES = (
     "PyType_Freeze",
     "PyType_FromMetaclass",
     "PyType_FromModuleAndSpec",
+    "PyType_FromSlots",
     "PyType_FromSpec",
     "PyType_FromSpecWithBases",
     "PyType_GenericAlloc",
index 5789d33e7f4456ff1b2d6e42898fda7b358660e7..2834226ff0c31f4535b40683c2d0362e4f00df04 100644 (file)
@@ -497,6 +497,8 @@ PYTHON_OBJS=        \
                Python/qsbr.o \
                Python/bootstrap_hash.o \
                Python/specialize.o \
+               Python/slots.o \
+               Python/slots_generated.o \
                Python/stackrefs.o \
                Python/structmember.o \
                Python/symtable.o \
@@ -1244,12 +1246,13 @@ PYTHON_HEADERS= \
                $(srcdir)/Include/refcount.h \
                $(srcdir)/Include/setobject.h \
                $(srcdir)/Include/sliceobject.h \
+               $(srcdir)/Include/slots.h \
+               $(srcdir)/Include/slots_generated.h \
                $(srcdir)/Include/structmember.h \
                $(srcdir)/Include/structseq.h \
                $(srcdir)/Include/sysmodule.h \
                $(srcdir)/Include/traceback.h \
                $(srcdir)/Include/tupleobject.h \
-               $(srcdir)/Include/typeslots.h \
                $(srcdir)/Include/unicodeobject.h \
                $(srcdir)/Include/warnings.h \
                $(srcdir)/Include/weakrefobject.h \
@@ -1432,6 +1435,8 @@ PYTHON_HEADERS= \
                $(srcdir)/Include/internal/pycore_setobject.h \
                $(srcdir)/Include/internal/pycore_signal.h \
                $(srcdir)/Include/internal/pycore_sliceobject.h \
+               $(srcdir)/Include/internal/pycore_slots.h \
+               $(srcdir)/Include/internal/pycore_slots_generated.h \
                $(srcdir)/Include/internal/pycore_stats.h \
                $(srcdir)/Include/internal/pycore_strhex.h \
                $(srcdir)/Include/internal/pycore_stackref.h \
@@ -1941,7 +1946,7 @@ regen-unicodedata:
 
 # "clinic" is regenerated implicitly via "regen-global-objects".
 .PHONY: regen-all
-regen-all: regen-cases regen-typeslots \
+regen-all: regen-cases regen-slots \
        regen-token regen-ast regen-keyword regen-sre regen-frozen \
        regen-pegen-metaparser regen-pegen regen-test-frozenmain \
        regen-test-levenshtein regen-global-objects
@@ -2314,16 +2319,17 @@ Python/import.o: $(srcdir)/Include/pydtrace.h
 Python/pydtrace.o: $(srcdir)/Include/pydtrace.d $(DTRACE_DEPS)
        CC="$(CC)" CFLAGS="$(CFLAGS)" $(DTRACE) $(DFLAGS) -o $@ -G -s $(srcdir)/Include/pydtrace.d $(DTRACE_DEPS)
 
-Objects/typeobject.o: Objects/typeslots.inc
-
 .PHONY: regen-typeslots
 regen-typeslots:
-       # Regenerate Objects/typeslots.inc from Include/typeslotsh
-       # using Objects/typeslots.py
-       $(PYTHON_FOR_REGEN) $(srcdir)/Objects/typeslots.py \
-               < $(srcdir)/Include/typeslots.h \
-               $(srcdir)/Objects/typeslots.inc.new
-       $(UPDATE_FILE) $(srcdir)/Objects/typeslots.inc $(srcdir)/Objects/typeslots.inc.new
+       echo 'NOTE: "regen-typeslots" was renamed to "regen-slots"'
+       $(MAKE) regen-slots
+
+.PHONY: regen-slots
+regen-slots: Python/slots.toml
+       # Regenerate {Python,Include}/slots_generated.{c,h}
+       # from Python/slots.toml using Tools/build/generate_slots.py
+       $(PYTHON_FOR_REGEN) $(srcdir)/Tools/build/generate_slots.py \
+               --generate-all
 
 $(LIBRARY_OBJS) $(MODOBJS) Programs/python.o: $(PYTHON_HEADERS)
 
diff --git a/Misc/NEWS.d/next/C_API/2026-04-27-10-56-22.gh-issue-149044.TbOcUS.rst b/Misc/NEWS.d/next/C_API/2026-04-27-10-56-22.gh-issue-149044.TbOcUS.rst
new file mode 100644 (file)
index 0000000..d7bb38f
--- /dev/null
@@ -0,0 +1 @@
+Implement :pep:`820`: Unified slot system for the C API.
index bd3ea54582580997206f7cc41ca7cef560e949ee..6d63a6796b173996ab42bf6ab2611aa7e4d34f45 100644 (file)
 [const.Py_sq_inplace_repeat]
     added = '3.2'
 [const.Py_mp_length]
+    # Note: value changed in 3.15 (old value is still accepted)
     added = '3.2'
 [const.Py_mp_subscript]
     added = '3.2'
 [const.Py_mp_ass_subscript]
+    # Note: value changed in 3.15 (old value is still accepted)
     added = '3.2'
 
 [typedef.Py_uintptr_t]
 [data.PyModuleDef_Type]
     added = '3.5'
 [const.Py_mod_create]
+    # Note: value changed in 3.15 (old value is still accepted)
     added = '3.5'
 [const.Py_mod_exec]
+    # Note: value changed in 3.15 (old value is still accepted)
     added = '3.5'
 [struct.PyModuleDef_Slot]
     added = '3.5'
 [function.PyMemoryView_FromBuffer]
     added = '3.11'
 [const.Py_bf_getbuffer]
+    # Note: value changed in 3.15 (old value is still accepted)
     added = '3.11'
 [const.Py_bf_releasebuffer]
+    # Note: value changed in 3.15 (old value is still accepted)
     added = '3.11'
 
 # Constants for Py_buffer API added to this list in Python 3.11.1 (https://github.com/python/cpython/issues/98680)
 [const.Py_TPFLAGS_ITEMS_AT_END]
     added = '3.12'
 [const.Py_mod_multiple_interpreters]
+    # Note: value changed in 3.15 (old value is still accepted)
     added = '3.12'
 
 [function.PyImport_AddModuleRef]
 [function.PyEval_GetFrameLocals]
     added = '3.13'
 [const.Py_mod_gil]
+    # Note: value changed in 3.15 (old value is still accepted)
     added = '3.13'
 
 [function.Py_TYPE]
     added = '3.15'
 [macro.Py_END_CRITICAL_SECTION2]
     added = '3.15'
+[struct.PySlot]
+    added = '3.15'
+    struct_abi_kind = 'full-abi'
+[function.PyType_FromSlots]
+    added = '3.15'
+[const.PySlot_OPTIONAL]
+    added = '3.15'
+[const.PySlot_STATIC]
+    added = '3.15'
+[const.PySlot_INTPTR]
+    added = '3.15'
+[macro.PySlot_DATA]
+    added = '3.15'
+[macro.PySlot_FUNC]
+    added = '3.15'
+[macro.PySlot_SIZE]
+    added = '3.15'
+[macro.PySlot_INT64]
+    added = '3.15'
+[macro.PySlot_UINT64]
+    added = '3.15'
+[macro.PySlot_STATIC_DATA]
+    added = '3.15'
+[macro.PySlot_END]
+    added = '3.15'
+[macro.PySlot_PTR]
+    added = '3.15'
+[macro.PySlot_PTR_STATIC]
+    added = '3.15'
+[const.Py_slot_end]
+    added = '3.15'
+[const.Py_slot_invalid]
+    added = '3.15'
+[const.Py_slot_subslots]
+    added = '3.15'
+[const.Py_tp_slots]
+    added = '3.15'
+[const.Py_mod_slots]
+    added = '3.15'
+[const.Py_tp_name]
+    added = '3.15'
+[const.Py_tp_basicsize]
+    added = '3.15'
+[const.Py_tp_extra_basicsize]
+    added = '3.15'
+[const.Py_tp_itemsize]
+    added = '3.15'
+[const.Py_tp_flags]
+    added = '3.15'
+[const.Py_tp_metaclass]
+    added = '3.15'
+[const.Py_tp_module]
+    added = '3.15'
 
 # PEP 757 import/export API.
+
 [function.PyLong_GetNativeLayout]
     added = '3.15'
 [function.PyLong_Export]
index a274c312d99313e2244ee8a2fda8e3be328157b5..19765bc313555b711f7f74ec38058b1a9c6e153b 100644 (file)
 @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
 @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c _testinternalcapi/complex.c _testinternalcapi/interpreter.c _testinternalcapi/tuple.c
 @MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/modsupport.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c _testcapi/frame.c _testcapi/type.c _testcapi/function.c _testcapi/module.c
-@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/threadstate.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c
+@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/slots.c _testlimitedcapi/sys.c _testlimitedcapi/threadstate.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c
 @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
 @MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
 
index 29eda7e008c6cb3812c841f84ef059be1d8d4c72..b0b4a4f4c39d8f9d9e6943720ed29661791581e0 100644 (file)
@@ -13,8 +13,8 @@ PyABIInfo_VAR(abi_info);
 static PyObject *
 module_from_slots_empty(PyObject *self, PyObject *spec)
 {
-    PyModuleDef_Slot slots[] = {
-        {0},
+    PySlot slots[] = {
+        PySlot_END,
     };
     return PyModule_FromSlotsAndSpec(slots, spec);
 }
@@ -22,9 +22,9 @@ module_from_slots_empty(PyObject *self, PyObject *spec)
 static PyObject *
 module_from_slots_minimal(PyObject *self, PyObject *spec)
 {
-    PyModuleDef_Slot slots[] = {
-        {Py_mod_abi, &abi_info},
-        {0},
+    PySlot slots[] = {
+        PySlot_DATA(Py_mod_abi, &abi_info),
+        PySlot_END,
     };
     return PyModule_FromSlotsAndSpec(slots, spec);
 }
@@ -38,12 +38,12 @@ module_from_slots_null(PyObject *self, PyObject *spec)
 static PyObject *
 module_from_slots_name(PyObject *self, PyObject *spec)
 {
-    PyModuleDef_Slot slots[] = {
-        {Py_mod_abi, &abi_info},
-        {Py_mod_name, "currently ignored..."},
-        {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
-        {Py_mod_gil, Py_MOD_GIL_NOT_USED},
-        {0},
+    PySlot slots[] = {
+        PySlot_DATA(Py_mod_abi, &abi_info),
+        PySlot_DATA(Py_mod_name, "currently ignored..."),
+        PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+        PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
+        PySlot_END,
     };
     return PyModule_FromSlotsAndSpec(slots, spec);
 }
@@ -51,12 +51,12 @@ module_from_slots_name(PyObject *self, PyObject *spec)
 static PyObject *
 module_from_slots_doc(PyObject *self, PyObject *spec)
 {
-    PyModuleDef_Slot slots[] = {
-        {Py_mod_abi, &abi_info},
-        {Py_mod_doc, "the docstring"},
-        {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
-        {Py_mod_gil, Py_MOD_GIL_NOT_USED},
-        {0},
+    PySlot slots[] = {
+        PySlot_DATA(Py_mod_abi, &abi_info),
+        PySlot_DATA(Py_mod_doc, "the docstring"),
+        PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+        PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
+        PySlot_END,
     };
     return PyModule_FromSlotsAndSpec(slots, spec);
 }
@@ -64,12 +64,12 @@ module_from_slots_doc(PyObject *self, PyObject *spec)
 static PyObject *
 module_from_slots_size(PyObject *self, PyObject *spec)
 {
-    PyModuleDef_Slot slots[] = {
-        {Py_mod_abi, &abi_info},
-        {Py_mod_state_size, (void*)123},
-        {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
-        {Py_mod_gil, Py_MOD_GIL_NOT_USED},
-        {0},
+    PySlot slots[] = {
+        PySlot_DATA(Py_mod_abi, &abi_info),
+        PySlot_SIZE(Py_mod_state_size, (void*)123),
+        PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+        PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
+        PySlot_END,
     };
     PyObject *mod = PyModule_FromSlotsAndSpec(slots, spec);
     if (!mod) {
@@ -92,12 +92,12 @@ static PyMethodDef a_methoddef_array[] = {
 static PyObject *
 module_from_slots_methods(PyObject *self, PyObject *spec)
 {
-    PyModuleDef_Slot slots[] = {
-        {Py_mod_abi, &abi_info},
-        {Py_mod_methods, a_methoddef_array},
-        {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
-        {Py_mod_gil, Py_MOD_GIL_NOT_USED},
-        {0},
+    PySlot slots[] = {
+        PySlot_DATA(Py_mod_abi, &abi_info),
+        PySlot_STATIC_DATA(Py_mod_methods, a_methoddef_array),
+        PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+        PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
+        PySlot_END,
     };
     return PyModule_FromSlotsAndSpec(slots, spec);
 }
@@ -111,14 +111,14 @@ static void noop_free(void *self) { }
 static PyObject *
 module_from_slots_gc(PyObject *self, PyObject *spec)
 {
-    PyModuleDef_Slot slots[] = {
-        {Py_mod_abi, &abi_info},
-        {Py_mod_state_traverse, noop_traverse},
-        {Py_mod_state_clear, noop_clear},
-        {Py_mod_state_free, noop_free},
-        {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
-        {Py_mod_gil, Py_MOD_GIL_NOT_USED},
-        {0},
+    PySlot slots[] = {
+        PySlot_DATA(Py_mod_abi, &abi_info),
+        PySlot_FUNC(Py_mod_state_traverse, noop_traverse),
+        PySlot_FUNC(Py_mod_state_clear, noop_clear),
+        PySlot_FUNC(Py_mod_state_free, noop_free),
+        PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+        PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
+        PySlot_END,
     };
     PyObject *mod = PyModule_FromSlotsAndSpec(slots, spec);
     if (!mod) {
@@ -144,12 +144,12 @@ static const char test_token;
 static PyObject *
 module_from_slots_token(PyObject *self, PyObject *spec)
 {
-    PyModuleDef_Slot slots[] = {
-        {Py_mod_abi, &abi_info},
-        {Py_mod_token, (void*)&test_token},
-        {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
-        {Py_mod_gil, Py_MOD_GIL_NOT_USED},
-        {0},
+    PySlot slots[] = {
+        PySlot_DATA(Py_mod_abi, &abi_info),
+        PySlot_DATA(Py_mod_token, (void*)&test_token),
+        PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+        PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
+        PySlot_END,
     };
     PyObject *mod = PyModule_FromSlotsAndSpec(slots, spec);
     if (!mod) {
@@ -175,12 +175,12 @@ simple_exec(PyObject *module)
 static PyObject *
 module_from_slots_exec(PyObject *self, PyObject *spec)
 {
-    PyModuleDef_Slot slots[] = {
-        {Py_mod_abi, &abi_info},
-        {Py_mod_exec, simple_exec},
-        {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
-        {Py_mod_gil, Py_MOD_GIL_NOT_USED},
-        {0},
+    PySlot slots[] = {
+        PySlot_DATA(Py_mod_abi, &abi_info),
+        PySlot_FUNC(Py_mod_exec, simple_exec),
+        PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+        PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
+        PySlot_END,
     };
     PyObject *mod = PyModule_FromSlotsAndSpec(slots, spec);
     if (!mod) {
@@ -209,12 +209,12 @@ create_attr_from_spec(PyObject *spec, PyModuleDef *def)
 static PyObject *
 module_from_slots_create(PyObject *self, PyObject *spec)
 {
-    PyModuleDef_Slot slots[] = {
-        {Py_mod_abi, &abi_info},
-        {Py_mod_create, create_attr_from_spec},
-        {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
-        {Py_mod_gil, Py_MOD_GIL_NOT_USED},
-        {0},
+    const PySlot slots[] = {
+        PySlot_DATA(Py_mod_abi, &abi_info),
+        PySlot_FUNC(Py_mod_create, create_attr_from_spec),
+        PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+        PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
+        PySlot_END,
     };
     return PyModule_FromSlotsAndSpec(slots, spec);
 }
@@ -241,13 +241,13 @@ module_from_slots_repeat_slot(PyObject *self, PyObject *spec)
     if (slot_id < 0) {
         return NULL;
     }
-    PyModuleDef_Slot slots[] = {
-        {Py_mod_abi, &abi_info},
-        {slot_id, "anything"},
-        {slot_id, "anything else"},
-        {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
-        {Py_mod_gil, Py_MOD_GIL_NOT_USED},
-        {0},
+    const PySlot slots[] = {
+        PySlot_DATA(Py_mod_abi, &abi_info),
+        PySlot_PTR_STATIC(slot_id, "anything"),
+        PySlot_PTR_STATIC(slot_id, "anything_else"),
+        PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+        PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
+        PySlot_END,
     };
     return PyModule_FromSlotsAndSpec(slots, spec);
 }
@@ -259,12 +259,12 @@ module_from_slots_null_slot(PyObject *self, PyObject *spec)
     if (slot_id < 0) {
         return NULL;
     }
-    PyModuleDef_Slot slots[] = {
-        {slot_id, NULL},
-        {Py_mod_abi, &abi_info},
-        {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
-        {Py_mod_gil, Py_MOD_GIL_NOT_USED},
-        {0},
+    const PySlot slots[] = {
+        PySlot_DATA(Py_mod_abi, &abi_info),
+        PySlot_PTR_STATIC(slot_id, NULL),
+        PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+        PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
+        PySlot_END,
     };
     return PyModule_FromSlotsAndSpec(slots, spec);
 }
@@ -347,12 +347,12 @@ module_from_bad_abiinfo(PyObject *self, PyObject *spec)
         1, 0,
         .abi_version=0x02080000,
     };
-    PyModuleDef_Slot slots[] = {
-        {Py_mod_abi, &abi_info},
-        {Py_mod_abi, &bad_abi_info},
-        {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
-        {Py_mod_gil, Py_MOD_GIL_NOT_USED},
-        {0},
+    PySlot slots[] = {
+        PySlot_DATA(Py_mod_abi, &abi_info),
+        PySlot_DATA(Py_mod_abi, &bad_abi_info),
+        PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+        PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
+        PySlot_END,
     };
     return PyModule_FromSlotsAndSpec(slots, spec);
 }
@@ -365,14 +365,14 @@ module_from_multiple_abiinfo(PyObject *self, PyObject *spec)
         .flags=PyABIInfo_STABLE | PyABIInfo_FREETHREADING_AGNOSTIC,
         .abi_version=0x03040000,
     };
-    PyModuleDef_Slot slots[] = {
-        {Py_mod_abi, &abi_info},
-        {Py_mod_abi, &abi_info},
-        {Py_mod_abi, &extra_abi_info},
-        {Py_mod_abi, &extra_abi_info},
-        {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
-        {Py_mod_gil, Py_MOD_GIL_NOT_USED},
-        {0},
+    PySlot slots[] = {
+        PySlot_DATA(Py_mod_abi, &abi_info),
+        PySlot_DATA(Py_mod_abi, &abi_info),
+        PySlot_DATA(Py_mod_abi, &extra_abi_info),
+        PySlot_DATA(Py_mod_abi, &extra_abi_info),
+        PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+        PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
+        PySlot_END,
     };
     return PyModule_FromSlotsAndSpec(slots, spec);
 }
@@ -466,6 +466,53 @@ pymodule_get_state_size(PyObject *self, PyObject *module)
     return PyLong_FromSsize_t(size);
 }
 
+static PyObject *
+module_from_null_def_slot(PyObject* Py_UNUSED(module), PyObject *args)
+{
+    long slot_number;
+    PyObject *spec;
+    if (!PyArg_ParseTuple(args, "lO", &slot_number, &spec)) {
+        return NULL;
+    }
+    static PyModuleDef_Slot slots[] = {
+        {0, NULL},
+        {0},
+    };
+    static PyModuleDef def = {
+        PyModuleDef_HEAD_INIT,
+        .m_name = "mymod",
+        .m_slots = slots,
+    };
+    // hack: def is supposed to be constant.
+    // Don't do this at home; use PyModule_FromSlotsAndSpec throwaway
+    // definitions!
+    slots[0].slot = slot_number;
+    return PyModule_FromDefAndSpec(&def, spec);
+}
+
+static PyObject *
+module_from_def_nonstatic_nested(PyObject* Py_UNUSED(module), PyObject *spec)
+{
+    static PyModuleDef_Slot subsubslots[] = {
+        {Py_mod_gil, Py_MOD_GIL_NOT_USED},
+        {0},
+    };
+    static PySlot subslots[] = {
+        PySlot_DATA(Py_mod_slots, subsubslots),
+        PySlot_END,
+    };
+    static PyModuleDef_Slot slots[] = {
+        {Py_slot_subslots, subslots},
+        {0},
+    };
+    static PyModuleDef def = {
+        PyModuleDef_HEAD_INIT,
+        .m_name = "mymod",
+        .m_slots = slots,
+    };
+    return PyModule_FromDefAndSpec(&def, spec);
+}
+
 static PyMethodDef test_methods[] = {
     {"module_from_slots_empty", module_from_slots_empty, METH_O},
     {"module_from_slots_minimal", module_from_slots_minimal, METH_O},
@@ -489,6 +536,8 @@ static PyMethodDef test_methods[] = {
     {"pymodule_get_def", pymodule_get_def, METH_O},
     {"pymodule_get_state_size", pymodule_get_state_size, METH_O},
     {"pymodule_exec", pymodule_exec, METH_O},
+    {"module_from_null_def_slot", module_from_null_def_slot, METH_VARARGS},
+    {"module_from_def_nonstatic_nested", module_from_def_nonstatic_nested, METH_O},
     {NULL},
 };
 
index 9bef58d1f836687f7e6e41b038fadde822fa906f..f566efa0ca15ae64893ae0752da8bfcde8885cdf 100644 (file)
@@ -110,9 +110,9 @@ test_get_statictype_slots(PyObject *self, PyObject *Py_UNUSED(ignored))
         return NULL;
     }
 
-    void *over_value = PyType_GetSlot(&PyLong_Type, Py_bf_releasebuffer + 1);
+    void *over_value = PyType_GetSlot(&PyLong_Type, Py_mod_name + 1);
     if (over_value != NULL) {
-        PyErr_SetString(PyExc_AssertionError, "mismatch: max+1 of long");
+        PyErr_SetString(PyExc_AssertionError, "mismatch: mod_name of long");
         return NULL;
     }
 
index d3eb02d47273474b082b69e5f289ebc12968dc34..5f2be0dd43954e327a3cbb2a81312fd6aac52412 100644 (file)
@@ -74,6 +74,9 @@ PyInit__testlimitedcapi(void)
     if (_PyTestLimitedCAPI_Init_Set(mod) < 0) {
         return NULL;
     }
+    if (_PyTestLimitedCAPI_Init_Slots(mod) < 0) {
+        return NULL;
+    }
     if (_PyTestLimitedCAPI_Init_Sys(mod) < 0) {
         return NULL;
     }
index 1cbb4f5659af0f59fadc1cffffab2e53ea06ffb6..1eea4f74d14416ce0006e3abf032ce117694b8ab 100644 (file)
@@ -37,6 +37,7 @@ int _PyTestLimitedCAPI_Init_List(PyObject *module);
 int _PyTestLimitedCAPI_Init_Long(PyObject *module);
 int _PyTestLimitedCAPI_Init_PyOS(PyObject *module);
 int _PyTestLimitedCAPI_Init_Set(PyObject *module);
+int _PyTestLimitedCAPI_Init_Slots(PyObject *module);
 int _PyTestLimitedCAPI_Init_Sys(PyObject *module);
 int _PyTestLimitedCAPI_Init_ThreadState(PyObject *module);
 int _PyTestLimitedCAPI_Init_Tuple(PyObject *module);
diff --git a/Modules/_testlimitedcapi/slots.c b/Modules/_testlimitedcapi/slots.c
new file mode 100644 (file)
index 0000000..7a8d646
--- /dev/null
@@ -0,0 +1,629 @@
+#define Py_LIMITED_API 0x030f0000
+
+#include "parts.h"
+
+PyABIInfo_VAR(abi_info);
+
+/* Define a bunch of (mostly nonsensical) functions to put in slots, so
+ * Lib/test/test_capi/test_slots.py can verify they've been assigned to
+ * the right slots.
+
+ * This module is full of "magic constants" which simply need to match
+ * between the C and Python part of the tests.
+ */
+
+// getbufferproc: export buffer; increment a counter
+static int
+demo_getbuffer(PyObject *exporter, Py_buffer *view, int flags)
+{
+    Py_INCREF(exporter);
+    // PyObject_GetTypeData & Py_TYPE: safe on non-subclassable type
+    int *data = PyObject_GetTypeData(exporter, Py_TYPE(exporter));
+    if (!data) {
+        return -1;
+    }
+    (*data)++;
+    return PyBuffer_FillInfo(view, exporter, "buf", 4, 1, flags);
+}
+
+// releasebufferproc: release buffer; decrement a counter
+static void
+demo_releasebuffer(PyObject *exporter, Py_buffer *view)
+{
+    Py_DECREF(exporter);
+    // PyObject_GetTypeData & Py_TYPE: safe on non-subclassable type
+    int *data = PyObject_GetTypeData(exporter, Py_TYPE(exporter));
+    if (!data) {
+        PyErr_WriteUnraisable(exporter);
+        return;
+    }
+    (*data)--;
+    return;
+}
+
+// objobjargproc: raise KeyError
+static int
+demo_ass_subscript(PyObject *o, PyObject *key, PyObject *v)
+{
+    PyErr_Format(PyExc_KeyError, "I don't like that key");
+    return -1;
+}
+
+// lenfunc: report 456
+static Py_ssize_t
+demo_length(PyObject *o)
+{
+    return (Py_ssize_t)456;
+}
+
+// binaryfunc; return constant value
+static PyObject *binop_123(PyObject* a, PyObject *b) { return PyLong_FromLong(123); }
+static PyObject *binop_234(PyObject* a, PyObject *b) { return PyLong_FromLong(234); }
+static PyObject *binop_345(PyObject* a, PyObject *b) { return PyLong_FromLong(345); }
+static PyObject *binop_456(PyObject* a, PyObject *b) { return PyLong_FromLong(456); }
+static PyObject *binop_567(PyObject* a, PyObject *b) { return PyLong_FromLong(567); }
+static PyObject *binop_678(PyObject* a, PyObject *b) { return PyLong_FromLong(678); }
+
+static PyObject *
+type_from_slots(PyObject* module, PyObject *args)
+{
+    char *case_name;
+    if (!PyArg_ParseTuple(args, "s", &case_name)) {
+        return NULL;
+    }
+#define CASE(NAME)                                                          \
+    if (strcmp(case_name, NAME) == 0) {                                     \
+        return PyType_FromSlots((PySlot[]) {                                \
+            PySlot_DATA(Py_tp_name, "_testlimitedcapi.MyType"),             \
+            PySlot_DATA(Py_tp_module, module),                              \
+    /////////////////////////////////////////////////////////////////////////
+#define ENDCASE()                                                           \
+            PySlot_END                                                      \
+        });                                                                 \
+    }                                                                       \
+    /////////////////////////////////////////////////////////////////////////
+
+    CASE("basic")
+    ENDCASE()
+    CASE("foreign_slot")
+        PySlot_DATA(Py_mod_name, "this is not a module"),
+    ENDCASE()
+    CASE("basicsize")
+        PySlot_SIZE(Py_tp_basicsize, 256),
+    ENDCASE()
+    CASE("extra_basicsize")
+        PySlot_SIZE(Py_tp_extra_basicsize, 256),
+    ENDCASE()
+    CASE("itemsize")
+        PySlot_SIZE(Py_tp_itemsize, 16),
+    ENDCASE()
+    CASE("flags")
+        PySlot_UINT64(Py_tp_flags,
+                      Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_BASETYPE),
+    ENDCASE()
+    CASE("matmul_123")
+        PySlot_FUNC(Py_nb_matrix_multiply, binop_123),
+    ENDCASE()
+    CASE("optional_end")
+        {.sl_flags=PySlot_OPTIONAL},
+    ENDCASE()
+    CASE("invalid")
+        {.sl_id=Py_slot_invalid},
+    ENDCASE()
+    CASE("invalid_fbad")
+        {.sl_id=0xfbad},
+    ENDCASE()
+    CASE("optional_invalid")
+        {.sl_id=Py_slot_invalid, .sl_flags=PySlot_OPTIONAL},
+        PySlot_SIZE(Py_tp_extra_basicsize, 256),
+    ENDCASE()
+    CASE("optional_invalid_fbad")
+        {.sl_id=0xfbad, .sl_flags=PySlot_OPTIONAL},
+        PySlot_SIZE(Py_tp_extra_basicsize, 256),
+    ENDCASE()
+    CASE("old_slot_numbers")
+        PySlot_FUNC(1, demo_getbuffer),
+        PySlot_FUNC(2, demo_releasebuffer),
+        PySlot_FUNC(3, demo_ass_subscript),
+        PySlot_FUNC(4, demo_length),
+        PySlot_SIZE(Py_tp_extra_basicsize, sizeof(int)),
+        PySlot_STATIC_DATA(Py_tp_members, ((PyMemberDef[]) {
+            {"buf_counter", Py_T_INT, 0, Py_READONLY | Py_RELATIVE_OFFSET},
+            {NULL},
+        })),
+    ENDCASE()
+    CASE("new_slot_numbers")
+        PySlot_FUNC(88, demo_getbuffer),
+        PySlot_FUNC(89, demo_releasebuffer),
+        PySlot_FUNC(90, demo_ass_subscript),
+        PySlot_FUNC(91, demo_length),
+        PySlot_SIZE(Py_tp_extra_basicsize, sizeof(int)),
+        PySlot_STATIC_DATA(Py_tp_members, ((PyMemberDef[]) {
+            {"buf_counter", Py_T_INT, 0, Py_READONLY | Py_RELATIVE_OFFSET},
+            {NULL},
+        })),
+    ENDCASE()
+    CASE("nonstatic_tp_members")
+        PySlot_SIZE(Py_tp_extra_basicsize, sizeof(int)),
+        PySlot_DATA(Py_tp_members, ((PyMemberDef[]) {
+            {"buf_counter", Py_T_INT, 0, Py_READONLY | Py_RELATIVE_OFFSET},
+            {NULL},
+        })),
+    ENDCASE()
+    CASE("intptr_flags_macro")
+        PySlot_PTR(Py_tp_flags, (void*)(intptr_t)Py_TPFLAGS_IMMUTABLETYPE),
+    ENDCASE()
+    CASE("intptr_flags_struct")
+        {.sl_id=Py_tp_flags,
+         .sl_flags=PySlot_INTPTR,
+         .sl_ptr=(void*)(intptr_t)Py_TPFLAGS_IMMUTABLETYPE,
+         },
+    ENDCASE()
+    CASE("intptr_static")
+        PySlot_SIZE(Py_tp_extra_basicsize, sizeof(int)),
+        PySlot_PTR_STATIC(Py_tp_members, ((PyMemberDef[]) {
+            {"attribute", Py_T_INT, 0, Py_READONLY | Py_RELATIVE_OFFSET},
+            {NULL},
+        })),
+    ENDCASE()
+    CASE("nested")
+        PySlot_FUNC(Py_nb_add, binop_123),
+        PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
+            PySlot_FUNC(Py_nb_subtract, binop_234),
+            PySlot_END,
+        })),
+    ENDCASE()
+    CASE("nested_max")
+        PySlot_FUNC(Py_nb_add, binop_123),
+        PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
+            PySlot_FUNC(Py_nb_subtract, binop_234),
+            PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
+                PySlot_FUNC(Py_nb_multiply, binop_345),
+                PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
+                    PySlot_FUNC(Py_nb_true_divide, binop_456),
+                    PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
+                        PySlot_FUNC(Py_nb_remainder, binop_567),
+                        PySlot_END
+                    })),
+                    PySlot_END,
+                })),
+                PySlot_END,
+            })),
+            PySlot_END,
+        })),
+    ENDCASE()
+    CASE("nested_over_limit")
+        PySlot_FUNC(Py_nb_add, binop_123),
+        PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
+            PySlot_FUNC(Py_nb_subtract, binop_234),
+            PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
+                PySlot_FUNC(Py_nb_multiply, binop_345),
+                PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
+                    PySlot_FUNC(Py_nb_true_divide, binop_456),
+                    PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
+                        PySlot_FUNC(Py_nb_remainder, binop_567),
+                        PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
+                            PySlot_FUNC(Py_nb_xor, binop_678),
+                            PySlot_END
+                        })),
+                        PySlot_END
+                    })),
+                    PySlot_END,
+                })),
+                PySlot_END,
+            })),
+            PySlot_END,
+        })),
+    ENDCASE()
+    CASE("nested_old")
+        PySlot_FUNC(Py_nb_add, binop_123),
+        PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) {
+            {Py_nb_subtract, binop_234},
+            {0},
+        })),
+    ENDCASE()
+    CASE("nested_old_max")
+        PySlot_FUNC(Py_nb_add, binop_123),
+        PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) {
+            {Py_nb_subtract, binop_234},
+            {Py_tp_slots, ((PyType_Slot[]) {
+                {Py_nb_multiply, binop_345},
+                {Py_tp_slots, ((PyType_Slot[]) {
+                    {Py_nb_true_divide, binop_456},
+                    {Py_tp_slots, ((PyType_Slot[]) {
+                        {Py_nb_remainder, binop_567},
+                        {0},
+                    })},
+                    {0},
+                })},
+                {0},
+            })},
+            {0},
+        })),
+    ENDCASE()
+    CASE("nested_old_over_limit")
+        PySlot_FUNC(Py_nb_add, binop_123),
+        PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) {
+            {Py_nb_subtract, binop_234},
+            {Py_tp_slots, ((PyType_Slot[]) {
+                {Py_nb_multiply, binop_345},
+                {Py_tp_slots, ((PyType_Slot[]) {
+                    {Py_nb_true_divide, binop_456},
+                    {Py_tp_slots, ((PyType_Slot[]) {
+                        {Py_nb_remainder, binop_567},
+                        {Py_tp_slots, ((PyType_Slot[]) {
+                            {Py_nb_xor, binop_678},
+                            {0},
+                        })},
+                        {0},
+                    })},
+                    {0},
+                })},
+                {0},
+            })},
+            {0},
+        })),
+    ENDCASE()
+    CASE("nested_pingpong")
+        PySlot_FUNC(Py_nb_add, binop_123),
+        PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) {
+            {Py_nb_subtract, binop_234},
+            {Py_slot_subslots, ((PySlot[]) {
+                PySlot_FUNC(Py_nb_multiply, binop_345),
+                PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) {
+                    {Py_nb_true_divide, binop_456},
+                    {Py_slot_subslots, ((PySlot[]) {
+                        PySlot_FUNC(Py_nb_remainder, binop_567),
+                        PySlot_END
+                    })},
+                    {0},
+                })),
+                PySlot_END,
+            })},
+            {0},
+        })),
+    ENDCASE()
+    CASE("repeat_add")
+        PySlot_FUNC(Py_nb_add, binop_123),
+        PySlot_FUNC(Py_nb_add, binop_456),
+    ENDCASE()
+    CASE("repeat_module")
+        PySlot_DATA(Py_tp_module, Py_True),
+        PySlot_DATA(Py_tp_module, Py_False),
+    ENDCASE()
+
+#undef CASE
+#undef ENDCASE
+    PyErr_Format(PyExc_SystemError, "bad case: %s", case_name);
+    return NULL;
+}
+
+static PyObject *
+type_from_null_slot(PyObject* module, PyObject *args)
+{
+    long slot_number;
+    if (!PyArg_ParseTuple(args, "l", &slot_number)) {
+        return NULL;
+    }
+    return PyType_FromSlots((PySlot[]) {
+        PySlot_DATA(Py_tp_name, "_testlimitedcapi.MyType"),
+        PySlot_DATA(Py_tp_module, module),
+        PySlot_PTR_STATIC((uint16_t)slot_number, NULL),
+        PySlot_END
+    });
+}
+
+static PyObject *
+type_from_null_spec_slot(PyObject* Py_UNUSED(module), PyObject *args)
+{
+    long slot_number;
+    if (!PyArg_ParseTuple(args, "l", &slot_number)) {
+        return NULL;
+    }
+    return PyType_FromSpec(&(PyType_Spec) {
+        .name = "_testlimitedcapi.MyType",
+        .slots = (PyType_Slot[]) {
+            {slot_number, NULL},
+            {0},
+        },
+    });
+}
+
+static PyObject *
+demo_create(PyObject *spec, PyModuleDef *def)
+{
+    assert(def == NULL);
+    return Py_NewRef(spec);
+}
+
+static int
+demo_exec(PyObject *mod)
+{
+    return PyModule_AddStringConstant(mod, "exec_done", "yes");
+}
+
+static PyMethodDef *TestMethods;
+
+static PyObject *
+module_from_slots(PyObject* Py_UNUSED(module), PyObject *args)
+{
+    PyObject *spec;
+    char *case_name;
+    if (!PyArg_ParseTuple(args, "sO", &case_name, &spec)) {
+        return NULL;
+    }
+    PyObject *mod = NULL;
+#define CASE(NAME)                                                          \
+    if (strcmp(case_name, NAME) == 0) {                                     \
+        mod = PyModule_FromSlotsAndSpec((PySlot[]) {                        \
+            PySlot_DATA(Py_mod_abi, &abi_info),                             \
+            PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),                   \
+    /////////////////////////////////////////////////////////////////////////
+#define ENDCASE()                                                           \
+            PySlot_END                                                      \
+        }, spec);                                                           \
+    }                                                                       \
+    /////////////////////////////////////////////////////////////////////////
+
+    CASE("basic")
+    ENDCASE()
+    CASE("foreign_slot")
+        PySlot_DATA(Py_tp_name, "this is not a type"),
+    ENDCASE()
+    CASE("state_size")
+        PySlot_SIZE(Py_mod_state_size, 42),
+    ENDCASE()
+    CASE("multi_interp")
+        PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+    ENDCASE()
+    CASE("exec")
+        PySlot_FUNC(Py_mod_exec, demo_exec),
+    ENDCASE()
+    CASE("optional_end")
+        {.sl_flags=PySlot_OPTIONAL},
+    ENDCASE()
+    CASE("invalid")
+        {.sl_id=Py_slot_invalid},
+    ENDCASE()
+    CASE("invalid_fbad")
+        {.sl_id=0xfbad},
+    ENDCASE()
+    CASE("optional_invalid")
+        {.sl_id=Py_slot_invalid, .sl_flags=PySlot_OPTIONAL},
+        PySlot_SIZE(Py_mod_exec, demo_exec),
+    ENDCASE()
+    CASE("optional_invalid_fbad")
+        {.sl_id=0xfbad, .sl_flags=PySlot_OPTIONAL},
+        PySlot_SIZE(Py_mod_exec, demo_exec),
+    ENDCASE()
+    CASE("old_slot_numbers")
+        // 1: see old_slot_number_create case
+        PySlot_FUNC(2, demo_exec),
+        PySlot_DATA(3, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+        // 4: see module_from_gil_slot function
+    ENDCASE()
+    CASE("new_slot_numbers")
+        // 84: see new_slot_number_create case
+        PySlot_FUNC(85, demo_exec),
+        PySlot_FUNC(86, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+        // 87: see module_from_gil_slot function
+    ENDCASE()
+    CASE("old_slot_number_create")
+        PySlot_FUNC(1, demo_create),
+    ENDCASE()
+    CASE("new_slot_number_create")
+        PySlot_FUNC(84, demo_create),
+    ENDCASE()
+    CASE("nonstatic_mod_methods")
+        PySlot_DATA(Py_mod_methods, TestMethods),
+    ENDCASE()
+    CASE("intptr_methods")
+        PySlot_PTR_STATIC(Py_mod_methods, TestMethods),
+    ENDCASE()
+    CASE("nested")
+        PySlot_FUNC(Py_mod_exec, demo_exec),
+        PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
+            PySlot_DATA(Py_mod_doc, "doc"),
+            PySlot_END,
+        })),
+    ENDCASE()
+    CASE("nested_max")
+        PySlot_FUNC(Py_mod_exec, demo_exec),
+        PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
+            PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
+                PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
+                    PySlot_SIZE(Py_mod_state_size, 53),
+                    PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
+                        PySlot_DATA(Py_mod_doc, "doc"),
+                        PySlot_END
+                    })),
+                    PySlot_END,
+                })),
+                PySlot_END,
+            })),
+            PySlot_END,
+        })),
+    ENDCASE()
+    CASE("nested_over_limit")
+        PySlot_FUNC(Py_mod_exec, demo_exec),
+        PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
+            PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
+                PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
+                    PySlot_SIZE(Py_mod_state_size, 53),
+                    PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
+                        PySlot_DATA(Py_mod_doc, "doc"),
+                        PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
+                            PySlot_END
+                        })),
+                        PySlot_END
+                    })),
+                    PySlot_END,
+                })),
+                PySlot_END,
+            })),
+            PySlot_END,
+        })),
+    ENDCASE()
+    CASE("nested_old")
+        PySlot_FUNC(Py_mod_exec, demo_exec),
+        PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) {
+            {Py_mod_doc, "doc"},
+            {0},
+        })),
+    ENDCASE()
+    CASE("nested_old_max")
+        PySlot_FUNC(Py_mod_exec, demo_exec),
+        PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) {
+            {Py_mod_slots, ((PyModuleDef_Slot[]) {
+                {Py_mod_state_size, (void*)(intptr_t)53},
+                {Py_mod_slots, ((PyModuleDef_Slot[]) {
+                    {Py_mod_slots, ((PyModuleDef_Slot[]) {
+                        {Py_mod_doc, "doc"},
+                        {0},
+                    })},
+                    {0},
+                })},
+                {0},
+            })},
+            {0},
+        })),
+    ENDCASE()
+    CASE("nested_old_over_limit")
+        PySlot_FUNC(Py_mod_exec, demo_exec),
+        PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) {
+            {Py_mod_slots, ((PyModuleDef_Slot[]) {
+                {Py_mod_state_size, (void*)(intptr_t)53},
+                {Py_mod_slots, ((PyModuleDef_Slot[]) {
+                    {Py_mod_slots, ((PyModuleDef_Slot[]) {
+                        {Py_mod_slots, ((PyModuleDef_Slot[]) {
+                            {Py_mod_doc, "doc"},
+                            {0},
+                        })},
+                        {0},
+                    })},
+                    {0},
+                })},
+                {0},
+            })},
+            {0},
+        })),
+    ENDCASE()
+    CASE("nested_pingpong")
+        PySlot_FUNC(Py_mod_exec, demo_exec),
+        PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) {
+            {Py_slot_subslots, ((PySlot[]) {
+                PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) {
+                    {Py_mod_state_size, (void*)(intptr_t)53},
+                    {Py_slot_subslots, ((PySlot[]) {
+                        PySlot_DATA(Py_mod_doc, "doc"),
+                        PySlot_END
+                    })},
+                    {0},
+                })),
+                PySlot_END,
+            })},
+            {0},
+        })),
+    ENDCASE()
+    CASE("repeat_create")
+        PySlot_DATA(Py_mod_create, demo_create),
+        PySlot_DATA(Py_mod_create, demo_create),
+        PySlot_DATA(Py_mod_create, demo_create),
+    ENDCASE()
+    CASE("repeat_gil")
+        PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
+        PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
+        PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
+    ENDCASE()
+    CASE("repeat_exec")
+        PySlot_FUNC(Py_mod_exec, demo_exec),
+        PySlot_FUNC(Py_mod_exec, demo_exec),
+    ENDCASE()
+
+#undef CASE
+#undef ENDCASE
+    if (!mod) {
+        if (!PyErr_Occurred()) {
+            PyErr_Format(PyExc_SystemError, "bad case: %s", case_name);
+            return NULL;
+        }
+        return NULL;
+    }
+    if (PyModule_Check(mod)) {
+        Py_ssize_t size;
+        if (PyModule_GetStateSize(mod, &size) < 0) {
+            Py_DECREF(mod);
+            return NULL;
+        }
+        if (PyModule_AddIntConstant(mod, "state_size", (long)size) < 0) {
+            Py_DECREF(mod);
+            return NULL;
+        }
+        if (PyModule_Exec(mod) < 0) {
+            return NULL;
+        }
+    }
+    return mod;
+}
+
+static PyObject *
+module_from_gil_slot(PyObject* Py_UNUSED(module), PyObject *args)
+{
+    long slot_number;
+    PyObject *spec;
+    if (!PyArg_ParseTuple(args, "lO", &slot_number, &spec)) {
+        return NULL;
+    }
+    return PyModule_FromSlotsAndSpec((PySlot[]) {
+        PySlot_DATA(Py_mod_abi, &abi_info),
+        PySlot_PTR_STATIC((uint16_t)slot_number, Py_MOD_GIL_NOT_USED),
+        PySlot_END
+    }, spec);
+}
+
+static PyObject *
+module_from_null_slot(PyObject* Py_UNUSED(module), PyObject *args)
+{
+    long slot_number;
+    PyObject *spec;
+    if (!PyArg_ParseTuple(args, "lO", &slot_number, &spec)) {
+        return NULL;
+    }
+    uint16_t maybe_gil_slot = Py_mod_gil;
+    if ((slot_number == 4) || (slot_number == 87)) {
+        // Do not repeat the GIL slot
+        maybe_gil_slot = Py_slot_invalid;
+    }
+    return PyModule_FromSlotsAndSpec((PySlot[]) {
+        PySlot_DATA(Py_mod_abi, &abi_info),
+        PySlot_DATA(Py_mod_name, "mymod"),
+        PySlot_PTR_STATIC((uint16_t)slot_number, NULL),
+        {
+            .sl_id=maybe_gil_slot,
+            .sl_flags=PySlot_OPTIONAL,
+            .sl_ptr=Py_MOD_GIL_NOT_USED,
+        },
+        PySlot_END
+    }, spec);
+}
+
+static PyMethodDef _TestMethods[] = {
+    {"type_from_slots", type_from_slots, METH_VARARGS},
+    {"module_from_gil_slot", module_from_gil_slot, METH_VARARGS},
+    {"type_from_null_slot", type_from_null_slot, METH_VARARGS},
+    {"type_from_null_spec_slot", type_from_null_spec_slot, METH_VARARGS},
+    {"module_from_slots", module_from_slots, METH_VARARGS},
+    {"module_from_null_slot", module_from_null_slot, METH_VARARGS},
+    {NULL},
+};
+static PyMethodDef *TestMethods = _TestMethods;
+
+int
+_PyTestLimitedCAPI_Init_Slots(PyObject *m)
+{
+    if (PyModule_AddFunctions(m, TestMethods) < 0) {
+        return -1;
+    }
+
+    return 0;
+}
index b0668e32eb57f4b208c1eaa65e52da43f2e2a809..128e3f79ecd99c27b54bb1ca7e7751ee56429987 100644 (file)
@@ -603,7 +603,7 @@ PyInit__testmultiphase_null_slots(void)
 /**** Problematic modules ****/
 
 static PyModuleDef_Slot slots_bad_large[] = {
-    {_Py_mod_LAST_SLOT + 1, NULL},
+    {Py_slot_invalid, NULL},
     {0, NULL},
 };
 
@@ -1051,12 +1051,12 @@ PyABIInfo_VAR(abi_info);
 PyMODEXPORT_FUNC
 PyModExport__test_from_modexport(void)
 {
-    static PyModuleDef_Slot slots[] = {
-        {Py_mod_abi, &abi_info},
-        {Py_mod_name, "_test_from_modexport"},
-        {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
-        {Py_mod_gil, Py_MOD_GIL_NOT_USED},
-        {0},
+    static PySlot slots[] = {
+        PySlot_DATA(Py_mod_abi, &abi_info),
+        PySlot_DATA(Py_mod_name, "_test_from_modexport"),
+        PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+        PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
+        PySlot_END,
     };
     return slots;
 }
@@ -1064,12 +1064,12 @@ PyModExport__test_from_modexport(void)
 PyMODEXPORT_FUNC
 PyModExport__test_from_modexport_gil_used(void)
 {
-    static PyModuleDef_Slot slots[] = {
-        {Py_mod_abi, &abi_info},
-        {Py_mod_name, "_test_from_modexport_gil_used"},
-        {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
-        {Py_mod_gil, Py_MOD_GIL_USED},
-        {0},
+    static PySlot slots[] = {
+        PySlot_DATA(Py_mod_abi, &abi_info),
+        PySlot_DATA(Py_mod_name, "_test_from_modexport_gil_used"),
+        PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+        PySlot_DATA(Py_mod_gil, Py_MOD_GIL_USED),
+        PySlot_END,
     };
     return slots;
 }
@@ -1115,13 +1115,13 @@ modexport_create_string(PyObject *spec, PyModuleDef *def)
 PyMODEXPORT_FUNC
 PyModExport__test_from_modexport_create_nonmodule(void)
 {
-    static PyModuleDef_Slot slots[] = {
-        {Py_mod_abi, &abi_info},
-        {Py_mod_name, "_test_from_modexport_create_nonmodule"},
-        {Py_mod_create, modexport_create_string},
-        {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
-        {Py_mod_gil, Py_MOD_GIL_NOT_USED},
-        {0},
+    static PySlot slots[] = {
+        PySlot_DATA(Py_mod_abi, &abi_info),
+        PySlot_DATA(Py_mod_name, "_test_from_modexport_create_nonmodule"),
+        PySlot_FUNC(Py_mod_create, modexport_create_string),
+        PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+        PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
+        PySlot_END,
     };
     return slots;
 }
@@ -1129,19 +1129,19 @@ PyModExport__test_from_modexport_create_nonmodule(void)
 PyMODEXPORT_FUNC
 PyModExport__test_from_modexport_create_nonmodule_gil_used(void)
 {
-    static PyModuleDef_Slot slots[] = {
-        {Py_mod_abi, &abi_info},
-        {Py_mod_name, "_test_from_modexport_create_nonmodule"},
-        {Py_mod_create, modexport_create_string},
-        {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
-        {Py_mod_gil, Py_MOD_GIL_USED},
-        {0},
+    static PySlot slots[] = {
+        PySlot_DATA(Py_mod_abi, &abi_info),
+        PySlot_DATA(Py_mod_name, "_test_from_modexport_create_nonmodule"),
+        PySlot_FUNC(Py_mod_create, modexport_create_string),
+        PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+        PySlot_DATA(Py_mod_gil, Py_MOD_GIL_USED),
+        PySlot_END,
     };
     return slots;
 }
 
-static PyModuleDef_Slot modexport_empty_slots[] = {
-    {0},
+static PySlot modexport_empty_slots[] = {
+    PySlot_END,
 };
 
 PyMODEXPORT_FUNC
@@ -1151,9 +1151,9 @@ PyModExport__test_from_modexport_empty_slots(void)
 }
 
 
-static PyModuleDef_Slot modexport_minimal_slots[] = {
-    {Py_mod_abi, &abi_info},
-    {0},
+static PySlot modexport_minimal_slots[] = {
+    PySlot_DATA(Py_mod_abi, &abi_info),
+    PySlot_END,
 };
 
 PyMODEXPORT_FUNC
@@ -1234,18 +1234,18 @@ PyModExport__test_from_modexport_smoke(void)
         {"get_modexport_minimal_slots", modexport_get_minimal_slots, METH_NOARGS},
         {0},
     };
-    static PyModuleDef_Slot slots[] = {
-        {Py_mod_abi, &abi_info},
-        {Py_mod_name, "_test_from_modexport_smoke"},
-        {Py_mod_doc, "the expected docstring"},
-        {Py_mod_exec, modexport_smoke_exec},
-        {Py_mod_state_size, (void*)sizeof(int)},
-        {Py_mod_methods, methods},
-        {Py_mod_state_free, modexport_smoke_free},
-        {Py_mod_token, (void*)&modexport_smoke_test_token},
-        {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
-        {Py_mod_gil, Py_MOD_GIL_NOT_USED},
-        {0},
+    static PySlot slots[] = {
+        PySlot_DATA(Py_mod_abi, &abi_info),
+        PySlot_DATA(Py_mod_name, "_test_from_modexport_smoke"),
+        PySlot_DATA(Py_mod_doc, "the expected docstring"),
+        PySlot_FUNC(Py_mod_exec, modexport_smoke_exec),
+        PySlot_SIZE(Py_mod_state_size, (void*)sizeof(int)),
+        PySlot_STATIC_DATA(Py_mod_methods, methods),
+        PySlot_FUNC(Py_mod_state_free, modexport_smoke_free),
+        PySlot_DATA(Py_mod_token, (void*)&modexport_smoke_test_token),
+        PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+        PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
+        PySlot_END,
     };
     return slots;
 }
index 19e5134d5cf26b77aac6e4ff965076fa58aa58b9..b7d2e5ffde4fe7d4e5e67bff1fbca381434db248 100644 (file)
@@ -14,6 +14,7 @@
 #include "pycore_object.h"        // _PyType_AllocNoTrack
 #include "pycore_pyerrors.h"      // _PyErr_FormatFromCause()
 #include "pycore_pystate.h"       // _PyInterpreterState_GET()
+#include "pycore_slots.h"         // _PySlotIterator_Init
 #include "pycore_unicodeobject.h" // _PyUnicode_EqualToASCIIString()
 #include "pycore_weakref.h"       // FT_CLEAR_WEAKREFS()
 
@@ -396,22 +397,20 @@ _PyModule_CreateInitialized(PyModuleDef* module, int module_api_version)
     return (PyObject*)m;
 }
 
+typedef PyObject *(*createfunc_t)(PyObject *, PyModuleDef*);
+
 static PyObject *
-module_from_def_and_spec(
-    PyModuleDef* def_like, /* not necessarily a valid Python object */
+module_from_slots_and_spec(
+    const PySlot *slots,
     PyObject *spec,
     int module_api_version,
     PyModuleDef* original_def /* NULL if not defined by a def */)
 {
-    PyModuleDef_Slot* cur_slot;
-    PyObject *(*create)(PyObject *, PyModuleDef*) = NULL;
+    createfunc_t create = NULL;
     PyObject *nameobj;
     PyObject *m = NULL;
-    int has_multiple_interpreters_slot = 0;
-    void *multiple_interpreters = (void *)0;
-    int has_gil_slot = 0;
+    uint64_t multiple_interpreters = (uint64_t)Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED;
     bool requires_gil = true;
-    int has_execution_slots = 0;
     const char *name;
     int ret;
     void *token = NULL;
@@ -431,142 +430,57 @@ module_from_def_and_spec(
         goto error;
     }
 
-    if (def_like->m_size < 0) {
-        PyErr_Format(
-            PyExc_SystemError,
-            "module %s: m_size may not be negative for multi-phase initialization",
-            name);
-        goto error;
+    _PySlotIterator it;
+
+    PyModuleDef _dummy_def = {0};
+    PyModuleDef *def_like;
+    if (slots) {
+        assert(!original_def);
+        def_like = &_dummy_def;
+         _PySlotIterator_Init(&it, slots, _PySlot_KIND_MOD);
     }
+    else {
+        assert(original_def);
+        def_like = original_def;
+         _PySlotIterator_InitLegacy(&it, def_like->m_slots, _PySlot_KIND_MOD);
+    }
+    it.name = name;
 
-    bool seen_m_name_slot = false;
-    bool seen_m_doc_slot = false;
-    bool seen_m_size_slot = false;
-    bool seen_m_methods_slot = false;
-    bool seen_m_traverse_slot = false;
-    bool seen_m_clear_slot = false;
-    bool seen_m_free_slot = false;
-    bool seen_m_abi_slot = false;
-    for (cur_slot = def_like->m_slots; cur_slot && cur_slot->slot; cur_slot++) {
-
-        // Macro to copy a non-NULL, non-repeatable slot.
-#define COPY_NONNULL_SLOT(SLOTNAME, TYPE, DEST)                         \
-        do {                                                            \
-            if (!(TYPE)(cur_slot->value)) {                             \
-                PyErr_Format(                                           \
-                    PyExc_SystemError,                                  \
-                    "module %s: %s must not be NULL",                   \
-                    name, SLOTNAME);                                    \
-                goto error;                                             \
-            }                                                           \
-            DEST = (TYPE)(cur_slot->value);                             \
-        } while (0);                                                    \
-        /////////////////////////////////////////////////////////////////
-
-        // Macro to copy a non-NULL, non-repeatable slot to def_like.
-#define COPY_DEF_SLOT(SLOTNAME, TYPE, MEMBER)                           \
-        do {                                                            \
-            if (seen_ ## MEMBER ## _slot) {                             \
-                PyErr_Format(                                           \
-                    PyExc_SystemError,                                  \
-                    "module %s has more than one %s slot",              \
-                    name, SLOTNAME);                                    \
-                goto error;                                             \
-            }                                                           \
-            seen_ ## MEMBER ## _slot = true;                            \
-            if (original_def) {                                         \
-                TYPE orig_value = (TYPE)original_def->MEMBER;           \
-                TYPE new_value = (TYPE)cur_slot->value;                 \
-                if (orig_value != new_value) {                          \
-                    PyErr_Format(                                       \
-                        PyExc_SystemError,                              \
-                        "module %s: %s conflicts with "                 \
-                        "PyModuleDef." #MEMBER,                         \
-                        name, SLOTNAME);                                \
-                    goto error;                                         \
-                }                                                       \
-            }                                                           \
-            COPY_NONNULL_SLOT(SLOTNAME, TYPE, (def_like->MEMBER))       \
-        } while (0);                                                    \
-        /////////////////////////////////////////////////////////////////
-
-        // Macro to copy a non-NULL, non-repeatable slot without a
-        // corresponding PyModuleDef member.
-        // DEST must be initially NULL (so we don't need a seen_* flag).
-#define COPY_NONDEF_SLOT(SLOTNAME, TYPE, DEST)                          \
-        do {                                                            \
-            if (DEST) {                                                 \
-                PyErr_Format(                                           \
-                    PyExc_SystemError,                                  \
-                    "module %s has more than one %s slot",              \
-                    name, SLOTNAME);                                    \
-                goto error;                                             \
-            }                                                           \
-            COPY_NONNULL_SLOT(SLOTNAME, TYPE, DEST)                     \
-        } while (0);                                                    \
-        /////////////////////////////////////////////////////////////////
-
-        // Define the whole common case
-#define DEF_SLOT_CASE(SLOT, TYPE, MEMBER)                               \
-        case SLOT:                                                      \
-            COPY_DEF_SLOT(#SLOT, TYPE, MEMBER);                         \
-            break;                                                      \
-        /////////////////////////////////////////////////////////////////
-        switch (cur_slot->slot) {
+    while (_PySlotIterator_Next(&it)) {
+        switch (it.current.sl_id) {
+            case Py_slot_invalid:
+                goto error;
             case Py_mod_create:
-                if (create) {
-                    PyErr_Format(
-                        PyExc_SystemError,
-                        "module %s has multiple create slots",
-                        name);
-                    goto error;
-                }
-                create = cur_slot->value;
+                create = (createfunc_t)it.current.sl_func;
                 break;
             case Py_mod_exec:
-                has_execution_slots = 1;
                 if (!original_def) {
-                    COPY_NONDEF_SLOT("Py_mod_exec", _Py_modexecfunc, m_exec);
+                    if (m_exec) {
+                        PyErr_Format(
+                            PyExc_SystemError,
+                            "module %s has multiple Py_mod_exec slots",
+                            name);
+                        goto error;
+                    }
+                    m_exec = (_Py_modexecfunc)it.current.sl_func;
                 }
                 break;
             case Py_mod_multiple_interpreters:
-                if (has_multiple_interpreters_slot) {
-                    PyErr_Format(
-                        PyExc_SystemError,
-                        "module %s has more than one 'multiple interpreters' "
-                        "slots",
-                        name);
-                    goto error;
-                }
-                multiple_interpreters = cur_slot->value;
-                has_multiple_interpreters_slot = 1;
+                multiple_interpreters = it.current.sl_uint64;
                 break;
             case Py_mod_gil:
-                if (has_gil_slot) {
-                    PyErr_Format(
-                       PyExc_SystemError,
-                       "module %s has more than one 'gil' slot",
-                       name);
-                    goto error;
+                {
+                    uint64_t val = it.current.sl_uint64;
+                    requires_gil = (val != (uint64_t)Py_MOD_GIL_NOT_USED);
                 }
-                requires_gil = (cur_slot->value != Py_MOD_GIL_NOT_USED);
-                has_gil_slot = 1;
                 break;
             case Py_mod_abi:
-                if (PyABIInfo_Check((PyABIInfo *)cur_slot->value, name) < 0) {
+                if (PyABIInfo_Check(it.current.sl_ptr, name) < 0) {
                     goto error;
                 }
-                seen_m_abi_slot = true;
                 break;
-            DEF_SLOT_CASE(Py_mod_name, char*, m_name)
-            DEF_SLOT_CASE(Py_mod_doc, char*, m_doc)
-            DEF_SLOT_CASE(Py_mod_state_size, Py_ssize_t, m_size)
-            DEF_SLOT_CASE(Py_mod_methods, PyMethodDef*, m_methods)
-            DEF_SLOT_CASE(Py_mod_state_traverse, traverseproc, m_traverse)
-            DEF_SLOT_CASE(Py_mod_state_clear, inquiry, m_clear)
-            DEF_SLOT_CASE(Py_mod_state_free, freefunc, m_free)
             case Py_mod_token:
-                if (original_def && original_def != cur_slot->value) {
+                if (original_def && original_def != it.current.sl_ptr) {
                     PyErr_Format(
                         PyExc_SystemError,
                         "module %s: arbitrary Py_mod_token not "
@@ -574,22 +488,40 @@ module_from_def_and_spec(
                         name);
                     goto error;
                 }
-                COPY_NONDEF_SLOT("Py_mod_token", void*, token);
+                token = it.current.sl_ptr;
                 break;
-            default:
-                assert(cur_slot->slot < 0 || cur_slot->slot > _Py_mod_LAST_SLOT);
-                PyErr_Format(
-                    PyExc_SystemError,
-                    "module %s uses unknown slot ID %i",
-                    name, cur_slot->slot);
-                goto error;
-        }
+            // Common case: Copy a PEP 793 slot to def_like
+#define DEF_SLOT_CASE(SLOT, TYPE, SL_MEMBER, MEMBER)                        \
+            case SLOT:                                                      \
+                do {                                                        \
+                    if (original_def) {                                     \
+                        TYPE orig_value = (TYPE)original_def->MEMBER;       \
+                        TYPE new_value = (TYPE)it.current.SL_MEMBER;        \
+                        if (orig_value != new_value) {                      \
+                            PyErr_Format(                                   \
+                                PyExc_SystemError,                          \
+                                "module %s: %s conflicts with "             \
+                                "PyModuleDef." #MEMBER,                     \
+                                name, _PySlot_GetName(it.current.sl_id));   \
+                            goto error;                                     \
+                        }                                                   \
+                    }                                                       \
+                    (def_like->MEMBER) = (TYPE)it.current.SL_MEMBER;        \
+                } while (0);                                                \
+                break;                                                      \
+            /////////////////////////////////////////////////////////////////
+            DEF_SLOT_CASE(Py_mod_name, char*, sl_ptr, m_name)
+            DEF_SLOT_CASE(Py_mod_doc, char*, sl_ptr, m_doc)
+            DEF_SLOT_CASE(Py_mod_state_size, Py_ssize_t, sl_size, m_size)
+            DEF_SLOT_CASE(Py_mod_methods, PyMethodDef*, sl_ptr, m_methods)
+            DEF_SLOT_CASE(Py_mod_state_traverse,
+                          traverseproc, sl_func, m_traverse)
+            DEF_SLOT_CASE(Py_mod_state_clear, inquiry, sl_func, m_clear)
+            DEF_SLOT_CASE(Py_mod_state_free, freefunc, sl_func, m_free)
 #undef DEF_SLOT_CASE
-#undef COPY_DEF_SLOT
-#undef COPY_NONDEF_SLOT
-#undef COPY_NONNULL_SLOT
+        }
     }
-    if (!original_def && !seen_m_abi_slot) {
+    if (!original_def && !_PySlotIterator_SawSlot(&it, Py_mod_abi)) {
         PyErr_Format(
             PyExc_SystemError,
             "module %s does not define Py_mod_abi,"
@@ -614,19 +546,24 @@ module_from_def_and_spec(
     }
 #endif
 
+    if (def_like->m_size < 0) {
+        PyErr_Format(
+            PyExc_SystemError,
+            "module %s: m_size may not be negative for multi-phase initialization",
+            name);
+        goto error;
+    }
+
     /* By default, multi-phase init modules are expected
        to work under multiple interpreters. */
-    if (!has_multiple_interpreters_slot) {
-        multiple_interpreters = Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED;
-    }
-    if (multiple_interpreters == Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED) {
+    if (multiple_interpreters == (int64_t)(intptr_t)Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED) {
         if (!_Py_IsMainInterpreter(interp)
             && _PyImport_CheckSubinterpIncompatibleExtensionAllowed(name) < 0)
         {
             goto error;
         }
     }
-    else if (multiple_interpreters != Py_MOD_PER_INTERPRETER_GIL_SUPPORTED
+    else if (multiple_interpreters != (int64_t)(intptr_t)Py_MOD_PER_INTERPRETER_GIL_SUPPORTED
              && interp->ceval.own_gil
              && !_Py_IsMainInterpreter(interp)
              && _PyImport_CheckSubinterpIncompatibleExtensionAllowed(name) < 0)
@@ -688,7 +625,7 @@ module_from_def_and_spec(
                 name);
             goto error;
         }
-        if (has_execution_slots) {
+        if (_PySlotIterator_SawSlot(&it, Py_mod_exec)) {
             PyErr_Format(
                 PyExc_SystemError,
                 "module %s specifies execution slots, but did not create "
@@ -733,11 +670,11 @@ PyObject *
 PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_version)
 {
     PyModuleDef_Init(def);
-    return module_from_def_and_spec(def, spec, module_api_version, def);
+    return module_from_slots_and_spec(NULL, spec, module_api_version, def);
 }
 
 PyObject *
-PyModule_FromSlotsAndSpec(const PyModuleDef_Slot *slots, PyObject *spec)
+PyModule_FromSlotsAndSpec(const PySlot *slots, PyObject *spec)
 {
     if (!slots) {
         PyErr_SetString(
@@ -745,11 +682,9 @@ PyModule_FromSlotsAndSpec(const PyModuleDef_Slot *slots, PyObject *spec)
             "PyModule_FromSlotsAndSpec called with NULL slots");
         return NULL;
     }
-    // Fill in enough of a PyModuleDef to pass to common machinery
-    PyModuleDef def_like = {.m_slots = (PyModuleDef_Slot *)slots};
 
-    return module_from_def_and_spec(&def_like, spec, PYTHON_API_VERSION,
-                                    NULL);
+    return module_from_slots_and_spec(slots, spec, PYTHON_API_VERSION,
+                                      NULL);
 }
 
 #ifdef Py_GIL_DISABLED
@@ -835,8 +770,6 @@ PyModule_Exec(PyObject *module)
 int
 PyModule_ExecDef(PyObject *module, PyModuleDef *def)
 {
-    PyModuleDef_Slot *cur_slot;
-
     if (alloc_state(module) < 0) {
         return -1;
     }
@@ -847,13 +780,19 @@ PyModule_ExecDef(PyObject *module, PyModuleDef *def)
         return 0;
     }
 
-    for (cur_slot = def->m_slots; cur_slot && cur_slot->slot; cur_slot++) {
-        if (cur_slot->slot == Py_mod_exec) {
-            int (*func)(PyObject *) = cur_slot->value;
-            if (run_exec_func(module, func) < 0) {
+    _PySlotIterator it;
+    _PySlotIterator_InitLegacy(&it, def->m_slots, _PySlot_KIND_MOD);
+    while (_PySlotIterator_Next(&it)) {
+        _Py_modexecfunc func;
+        switch (it.current.sl_id) {
+            case Py_slot_invalid:
                 return -1;
-            }
-            continue;
+            case Py_mod_exec:
+                func = (_Py_modexecfunc)it.current.sl_func;
+                if (run_exec_func(module, func) < 0) {
+                    return -1;
+                }
+                break;
         }
     }
     return 0;
index fb3c7101410683561c5f317161c3b89cbcdaf26a..041dfecccd323034baa45b809d7baa1477737390 100644 (file)
@@ -18,6 +18,7 @@
 #include "pycore_pyatomic_ft_wrappers.h"
 #include "pycore_pyerrors.h"      // _PyErr_Occurred()
 #include "pycore_pystate.h"       // _PyThreadState_GET()
+#include "pycore_slots.h"         // _PySlotIterator_Init
 #include "pycore_symtable.h"      // _Py_Mangle()
 #include "pycore_tuple.h"         // _PyTuple_FromPair
 #include "pycore_typeobject.h"    // struct type_cache
@@ -198,11 +199,6 @@ type_lock_allow_release(void)
 
 #define PyTypeObject_CAST(op)   ((PyTypeObject *)(op))
 
-typedef struct PySlot_Offset {
-    short subslot_offset;
-    short slot_offset;
-} PySlot_Offset;
-
 static void
 slot_bf_releasebuffer(PyObject *self, Py_buffer *buffer);
 
@@ -5112,21 +5108,6 @@ type_vectorcall(PyObject *metatype, PyObject *const *args,
     return _PyObject_MakeTpCall(tstate, metatype, args, nargs, kwnames);
 }
 
-/* An array of type slot offsets corresponding to Py_tp_* constants,
-  * for use in e.g. PyType_Spec and PyType_GetSlot.
-  * Each entry has two offsets: "slot_offset" and "subslot_offset".
-  * If is subslot_offset is -1, slot_offset is an offset within the
-  * PyTypeObject struct.
-  * Otherwise slot_offset is an offset to a pointer to a sub-slots struct
-  * (such as "tp_as_number"), and subslot_offset is the offset within
-  * that struct.
-  * The actual table is generated by a script.
-  */
-static const PySlot_Offset pyslot_offsets[] = {
-    {0, 0},
-#include "typeslots.inc"
-};
-
 /* Align up to the nearest multiple of alignof(max_align_t)
  * (like _Py_ALIGN_UP, but for a size rather than pointer)
  */
@@ -5136,43 +5117,6 @@ _align_up(Py_ssize_t size)
     return (size + ALIGNOF_MAX_ALIGN_T - 1) & ~(ALIGNOF_MAX_ALIGN_T - 1);
 }
 
-/* Given a PyType_FromMetaclass `bases` argument (NULL, type, or tuple of
- * types), return a tuple of types.
- */
-inline static PyObject *
-get_bases_tuple(PyObject *bases_in, PyType_Spec *spec)
-{
-    if (!bases_in) {
-        /* Default: look in the spec, fall back to (type,). */
-        PyTypeObject *base = &PyBaseObject_Type;  // borrowed ref
-        PyObject *bases = NULL;  // borrowed ref
-        const PyType_Slot *slot;
-        for (slot = spec->slots; slot->slot; slot++) {
-            switch (slot->slot) {
-                case Py_tp_base:
-                    base = slot->pfunc;
-                    break;
-                case Py_tp_bases:
-                    bases = slot->pfunc;
-                    break;
-            }
-        }
-        if (!bases) {
-            return PyTuple_Pack(1, base);
-        }
-        if (PyTuple_Check(bases)) {
-            return Py_NewRef(bases);
-        }
-        PyErr_SetString(PyExc_SystemError, "Py_tp_bases is not a tuple");
-        return NULL;
-    }
-    if (PyTuple_Check(bases_in)) {
-        return Py_NewRef(bases_in);
-    }
-    // Not a tuple, should be a single type
-    return PyTuple_Pack(1, bases_in);
-}
-
 static inline int
 check_basicsize_includes_size_and_offsets(PyTypeObject* type)
 {
@@ -5274,10 +5218,11 @@ special_offset_from_member(
     return -1;
 }
 
-PyObject *
-PyType_FromMetaclass(
-    PyTypeObject *metaclass, PyObject *module,
-    PyType_Spec *spec, PyObject *bases_in)
+
+static PyObject *
+type_from_slots_or_spec(
+    PySlot *slots, PyType_Spec *spec,
+    PyTypeObject *metaclass, PyObject *module, PyObject *bases_in)
 {
     /* Invariant: A non-NULL value in one of these means this function holds
      * a strong reference or owns allocated memory.
@@ -5292,47 +5237,130 @@ PyType_FromMetaclass(
 
     int r;
 
-    /* Prepare slots that need special handling.
-     * Keep in mind that a slot can be given multiple times:
-     * if that would cause trouble (leaks, UB, ...), raise an exception.
-     */
+    /* First pass of slots */
 
-    const PyType_Slot *slot;
     Py_ssize_t nmembers = 0;
     const PyMemberDef *weaklistoffset_member = NULL;
     const PyMemberDef *dictoffset_member = NULL;
     const PyMemberDef *vectorcalloffset_member = NULL;
-    char *res_start;
+    Py_ssize_t basicsize = 0;
+    Py_ssize_t extra_basicsize = 0;
+    Py_ssize_t itemsize = 0;
+    int flags = 0;
+    void *token = NULL;
 
-    for (slot = spec->slots; slot->slot; slot++) {
-        if (slot->slot < 0
-            || (size_t)slot->slot >= Py_ARRAY_LENGTH(pyslot_offsets)) {
-            PyErr_SetString(PyExc_RuntimeError, "invalid slot offset");
-            goto finally;
+    bool have_relative_members = false;
+    Py_ssize_t max_relative_offset = 0;
+
+    PyObject *bases_slot = NULL; /* borrowed from the slots */
+
+    _PySlotIterator it;
+
+    if (spec) {
+        assert(!slots);
+        if (spec->basicsize > 0) {
+            basicsize = spec->basicsize;
         }
-        switch (slot->slot) {
-        case Py_tp_members:
-            if (nmembers != 0) {
+        if (spec->basicsize < 0) {
+            extra_basicsize = -spec->basicsize;
+        }
+        itemsize = spec->itemsize;
+        flags = spec->flags;
+        _PySlotIterator_InitLegacy(&it, spec->slots, _PySlot_KIND_TYPE);
+        it.name = spec->name;
+    }
+    else {
+        assert(!spec);
+        assert(!metaclass);
+        assert(!module);
+        assert(!bases_in);
+        _PySlotIterator_Init(&it, slots, _PySlot_KIND_TYPE);
+    }
+
+    #define NO_SPEC                                         \
+        if (spec) {                                         \
+            PyErr_Format(                                   \
+                PyExc_SystemError,                          \
+                "%s must not be used with PyType_Spec",     \
+                _PySlot_GetName(it.current.sl_id));         \
+            goto finally;                                   \
+        }                                                   \
+        /////////////////////////////////////////////////////
+
+    while (_PySlotIterator_Next(&it)) {
+        switch (it.current.sl_id) {
+        case Py_slot_invalid:
+            goto finally;
+        case Py_tp_name:
+            NO_SPEC;
+            it.name = it.current.sl_ptr;
+            break;
+        case Py_tp_metaclass:
+            NO_SPEC;
+            metaclass = it.current.sl_ptr;
+            break;
+        case Py_tp_module:
+            NO_SPEC;
+            module = it.current.sl_ptr;
+            break;
+        case Py_tp_bases:
+            bases_slot = it.current.sl_ptr;
+            break;
+        case Py_tp_base:
+            if (!_PySlotIterator_SawSlot(&it, Py_tp_bases)) {
+                bases_slot = it.current.sl_ptr;
+            }
+            break;
+        case Py_tp_basicsize:
+            NO_SPEC;
+            basicsize = it.current.sl_size;
+            if (basicsize <= 0) {
                 PyErr_SetString(
                     PyExc_SystemError,
-                    "Multiple Py_tp_members slots are not supported.");
+                    "Py_tp_basicsize must be positive");
                 goto finally;
             }
-            for (const PyMemberDef *memb = slot->pfunc; memb->name != NULL; memb++) {
+            break;
+        case Py_tp_extra_basicsize:
+            NO_SPEC;
+            extra_basicsize = it.current.sl_size;
+            if (extra_basicsize <= 0) {
+                PyErr_SetString(
+                    PyExc_SystemError,
+                    "Py_tp_extra_basicsize must be positive");
+                goto finally;
+            }
+            break;
+        case Py_tp_itemsize:
+            NO_SPEC;
+            itemsize = it.current.sl_size;
+            if (itemsize <= 0) {
+                PyErr_SetString(
+                    PyExc_SystemError,
+                    "Py_tp_itemsize must be positive");
+                goto finally;
+            }
+            break;
+        case Py_tp_flags:
+            NO_SPEC;
+            flags = (int)it.current.sl_uint64;
+            break;
+        case Py_tp_members:
+            for (const PyMemberDef *memb = it.current.sl_ptr;
+                 memb->name != NULL;
+                 memb++)
+            {
                 nmembers++;
                 if (memb->flags & Py_RELATIVE_OFFSET) {
-                    if (spec->basicsize > 0) {
+                    if (memb->offset < 0) {
                         PyErr_SetString(
                             PyExc_SystemError,
-                            "With Py_RELATIVE_OFFSET, basicsize must be negative.");
-                        goto finally;
-                    }
-                    if (memb->offset < 0 || memb->offset >= -spec->basicsize) {
-                        PyErr_SetString(
-                            PyExc_SystemError,
-                            "Member offset out of range (0..-basicsize)");
+                            "Member offset must not be negative");
                         goto finally;
                     }
+                    have_relative_members = true;
+                    max_relative_offset = Py_MAX(max_relative_offset,
+                                                 memb->offset);
                 }
                 if (strcmp(memb->name, "__weaklistoffset__") == 0) {
                     weaklistoffset_member = memb;
@@ -5345,43 +5373,86 @@ PyType_FromMetaclass(
                 }
             }
             break;
+        case Py_tp_token:
+            token = it.current.sl_ptr;
+            if (token == Py_TP_USE_SPEC) {
+                if (!spec) {
+                    PyErr_SetString(
+                        PyExc_SystemError,
+                        "Py_tp_token: Py_TP_USE_SPEC (NULL) can only be "
+                        "used with PyType_Spec");
+                    goto finally;
+                }
+                token = spec;
+            }
+            break;
         case Py_tp_doc:
             /* For the docstring slot, which usually points to a static string
                literal, we need to make a copy */
-            if (tp_doc != NULL) {
-                PyErr_SetString(
-                    PyExc_SystemError,
-                    "Multiple Py_tp_doc slots are not supported.");
-                goto finally;
-            }
-            if (slot->pfunc == NULL) {
+            if (it.current.sl_ptr == NULL) {
                 PyMem_Free(tp_doc);
                 tp_doc = NULL;
             }
             else {
-                size_t len = strlen(slot->pfunc)+1;
+                size_t len = strlen(it.current.sl_ptr)+1;
                 tp_doc = PyMem_Malloc(len);
                 if (tp_doc == NULL) {
                     PyErr_NoMemory();
                     goto finally;
                 }
-                memcpy(tp_doc, slot->pfunc, len);
+                memcpy(tp_doc, it.current.sl_ptr, len);
             }
             break;
         }
     }
+    #undef NO_SPEC
 
-    /* Prepare the type name and qualname */
+    /* Required slots & bad combinations */
 
-    if (spec->name == NULL) {
-        PyErr_SetString(PyExc_SystemError,
-                        "Type spec does not define the name field.");
+    if (it.name == NULL) {
+        if (spec) {
+            PyErr_SetString(PyExc_SystemError,
+                            "Type spec does not define the name field.");
+        }
+        else {
+            PyErr_SetString(PyExc_SystemError,
+                            "Py_tp_name slot is required.");
+        }
         goto finally;
     }
 
-    const char *s = strrchr(spec->name, '.');
+    if (_PySlotIterator_SawSlot(&it, Py_tp_basicsize)
+        && _PySlotIterator_SawSlot(&it, Py_tp_extra_basicsize))
+    {
+        PyErr_Format(
+            PyExc_SystemError,
+            "type %s: Py_tp_basicsize and Py_tp_extra_basicsize are "
+            "mutually exclusive",
+            it.name);
+        goto finally;
+    }
+
+    if (have_relative_members) {
+        if (!extra_basicsize) {
+            PyErr_SetString(
+                PyExc_SystemError,
+                "With Py_RELATIVE_OFFSET, basicsize must be extended");
+            goto finally;
+        }
+        if (max_relative_offset >= extra_basicsize) {
+            PyErr_SetString(
+                PyExc_SystemError,
+                "Member offset out of range (0..extra_basicsize)");
+            goto finally;
+        }
+    }
+
+    /* Prepare the type name and qualname */
+
+    assert(it.name);
+    const char *s = strrchr(it.name, '.');
     if (s == NULL) {
-        s = spec->name;
+        s = it.name;
     }
     else {
         s++;
@@ -5392,7 +5463,7 @@ PyType_FromMetaclass(
         goto finally;
     }
 
-    /* Copy spec->name to a buffer we own.
+    /* Copy the name to a buffer we own.
     *
     * Unfortunately, we can't use tp_name directly (with some
     * flag saying that it should be deallocated with the type),
@@ -5401,28 +5472,42 @@ PyType_FromMetaclass(
     * So, we use a separate buffer, _ht_tpname, that's always
     * deallocated with the type (if it's non-NULL).
     */
-    Py_ssize_t name_buf_len = strlen(spec->name) + 1;
+    Py_ssize_t name_buf_len = strlen(it.name) + 1;
     _ht_tpname = PyMem_Malloc(name_buf_len);
     if (_ht_tpname == NULL) {
         goto finally;
     }
-    memcpy(_ht_tpname, spec->name, name_buf_len);
+    memcpy(_ht_tpname, it.name, name_buf_len);
 
     /* Get a tuple of bases.
      * bases is a strong reference (unlike bases_in).
+     * (This is convoluted for backwards compatibility -- preserving priority
+     * of the various ways to specify bases)
      */
-    bases = get_bases_tuple(bases_in, spec);
+    if (!bases_in) {
+        bases_in = bases_slot;
+    }
+    if (bases_in) {
+        if (PyTuple_Check(bases_in)) {
+            bases = Py_NewRef(bases_in);
+        }
+        else {
+            bases = PyTuple_Pack(1, bases_in);
+        }
+    }
+    else {
+        bases = PyTuple_Pack(1, &PyBaseObject_Type);
+    }
     if (!bases) {
         goto finally;
     }
 
-    /* If this is an immutable type, check if all bases are also immutable,
-     * and (for now) fire a deprecation warning if not.
+    /* If this is an immutable type, check if all bases are also immutable.
      * (This isn't necessary for static types: those can't have heap bases,
      * and only heap types can be mutable.)
      */
-    if (spec->flags & Py_TPFLAGS_IMMUTABLETYPE) {
-        if (check_immutable_bases(spec->name, bases, 0) < 0) {
+    if (flags & Py_TPFLAGS_IMMUTABLETYPE) {
+        if (check_immutable_bases(it.name, bases, 0) < 0) {
             goto finally;
         }
     }
@@ -5460,20 +5545,16 @@ PyType_FromMetaclass(
 
     /* Calculate sizes */
 
-    Py_ssize_t basicsize = spec->basicsize;
-    Py_ssize_t type_data_offset = spec->basicsize;
-    if (basicsize == 0) {
-        /* Inherit */
-        basicsize = base->tp_basicsize;
-    }
-    else if (basicsize < 0) {
+    Py_ssize_t type_data_offset = basicsize;
+    if (extra_basicsize) {
         /* Extend */
+        assert(basicsize == 0);
         type_data_offset = _align_up(base->tp_basicsize);
-        basicsize = type_data_offset + _align_up(-spec->basicsize);
+        basicsize = type_data_offset + _align_up(extra_basicsize);
 
         /* Inheriting variable-sized types is limited */
         if (base->tp_itemsize
-            && !((base->tp_flags | spec->flags) & Py_TPFLAGS_ITEMS_AT_END))
+            && !((base->tp_flags | flags) & Py_TPFLAGS_ITEMS_AT_END))
         {
             PyErr_SetString(
                 PyExc_SystemError,
@@ -5481,8 +5562,10 @@ PyType_FromMetaclass(
             goto finally;
         }
     }
-
-    Py_ssize_t itemsize = spec->itemsize;
+    if (basicsize == 0) {
+        /* Inherit */
+        basicsize = base->tp_basicsize;
+    }
 
     /* Compute special offsets */
 
@@ -5514,11 +5597,10 @@ PyType_FromMetaclass(
     if (res == NULL) {
         goto finally;
     }
-    res_start = (char*)res;
 
     type = &res->ht_type;
     /* The flags must be initialized early, before the GC traverses us */
-    type_set_flags(type, spec->flags | Py_TPFLAGS_HEAPTYPE);
+    type_set_flags(type, flags | Py_TPFLAGS_HEAPTYPE);
 
     res->ht_module = Py_XNewRef(module);
 
@@ -5547,15 +5629,20 @@ PyType_FromMetaclass(
     res->_ht_tpname = _ht_tpname;
     _ht_tpname = NULL;  // Give ownership to the type
 
+    res->ht_token = token;
+
     /* Copy the sizes */
 
     type->tp_basicsize = basicsize;
     type->tp_itemsize = itemsize;
 
-    /* Copy all the ordinary slots */
+    /* Second pass of slots: copy most of them into the type */
 
-    for (slot = spec->slots; slot->slot; slot++) {
-        switch (slot->slot) {
+    _PySlotIterator_Rewind(&it, spec ? (void*)spec->slots : (void*)slots);
+    while (_PySlotIterator_Next(&it)) {
+        switch (it.current.sl_id) {
+        case Py_slot_invalid:
+            goto finally;
         case Py_tp_base:
         case Py_tp_bases:
         case Py_tp_doc:
@@ -5565,7 +5652,7 @@ PyType_FromMetaclass(
             {
                 /* Move the slots to the heap type itself */
                 size_t len = Py_TYPE(type)->tp_itemsize * nmembers;
-                memcpy(_PyHeapType_GET_MEMBERS(res), slot->pfunc, len);
+                memcpy(_PyHeapType_GET_MEMBERS(res), it.current.sl_ptr, len);
                 type->tp_members = _PyHeapType_GET_MEMBERS(res);
                 PyMemberDef *memb;
                 Py_ssize_t i;
@@ -5579,26 +5666,8 @@ PyType_FromMetaclass(
                 }
             }
             break;
-        case Py_tp_token:
-            {
-                res->ht_token = slot->pfunc == Py_TP_USE_SPEC ? spec : slot->pfunc;
-            }
-            break;
         default:
-            {
-                /* Copy other slots directly */
-                PySlot_Offset slotoffsets = pyslot_offsets[slot->slot];
-                short slot_offset = slotoffsets.slot_offset;
-                if (slotoffsets.subslot_offset == -1) {
-                    /* Set a slot in the main PyTypeObject */
-                    *(void**)((char*)res_start + slot_offset) = slot->pfunc;
-                }
-                else {
-                    void *procs = *(void**)((char*)res_start + slot_offset);
-                    short subslot_offset = slotoffsets.subslot_offset;
-                    *(void**)((char*)procs + subslot_offset) = slot->pfunc;
-                }
-            }
+            _PySlot_heaptype_apply_field_slot(res, it.current);
             break;
         }
     }
@@ -5664,10 +5733,10 @@ PyType_FromMetaclass(
         goto finally;
     }
     if (r == 0) {
-        s = strrchr(spec->name, '.');
+        s = strrchr(it.name, '.');
         if (s != NULL) {
             PyObject *modname = PyUnicode_FromStringAndSize(
-                    spec->name, (Py_ssize_t)(s - spec->name));
+                    it.name, (Py_ssize_t)(s - it.name));
             if (modname == NULL) {
                 goto finally;
             }
@@ -5680,7 +5749,7 @@ PyType_FromMetaclass(
         else {
             if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
                     "builtin type %.200s has no __module__ attribute",
-                    spec->name))
+                    it.name))
                 goto finally;
         }
     }
@@ -5702,22 +5771,36 @@ PyType_FromMetaclass(
     return (PyObject*)res;
 }
 
+PyObject *
+PyType_FromSlots(PySlot *slots)
+{
+    return type_from_slots_or_spec(slots, NULL, NULL, NULL, NULL);
+}
+
+PyObject *
+PyType_FromMetaclass(
+    PyTypeObject *metaclass, PyObject *module,
+    PyType_Spec *spec, PyObject *bases)
+{
+    return type_from_slots_or_spec(NULL, spec, metaclass, module, bases);
+}
+
 PyObject *
 PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases)
 {
-    return PyType_FromMetaclass(NULL, module, spec, bases);
+    return type_from_slots_or_spec(NULL, spec, NULL, module, bases);
 }
 
 PyObject *
 PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases)
 {
-    return PyType_FromMetaclass(NULL, NULL, spec, bases);
+    return type_from_slots_or_spec(NULL, spec, NULL, NULL, bases);
 }
 
 PyObject *
 PyType_FromSpec(PyType_Spec *spec)
 {
-    return PyType_FromMetaclass(NULL, NULL, spec, NULL);
+    return type_from_slots_or_spec(NULL, spec, NULL, NULL, NULL);
 }
 
 PyObject *
@@ -5739,32 +5822,10 @@ PyType_GetModuleName(PyTypeObject *type)
 }
 
 void *
-PyType_GetSlot(PyTypeObject *type, int slot)
+PyType_GetSlot(PyTypeObject *type, int slot_in)
 {
-    void *parent_slot;
-    int slots_len = Py_ARRAY_LENGTH(pyslot_offsets);
-
-    if (slot <= 0 || slot >= slots_len) {
-        PyErr_BadInternalCall();
-        return NULL;
-    }
-    int slot_offset = pyslot_offsets[slot].slot_offset;
-
-    if (slot_offset >= (int)sizeof(PyTypeObject)) {
-        if (!_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) {
-            return NULL;
-        }
-    }
-
-    parent_slot = *(void**)((char*)type + slot_offset);
-    if (parent_slot == NULL) {
-        return NULL;
-    }
-    /* Return slot directly if we have no sub slot. */
-    if (pyslot_offsets[slot].subslot_offset == -1) {
-        return parent_slot;
-    }
-    return *(void**)((char*)parent_slot + pyslot_offsets[slot].subslot_offset);
+    uint16_t slot = _PySlot_resolve_type_slot(slot_in);
+    return _PySlot_type_getslot(type, slot);
 }
 
 PyObject *
diff --git a/Objects/typeslots.inc b/Objects/typeslots.inc
deleted file mode 100644 (file)
index 642160f..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/* Generated by typeslots.py */
-{offsetof(PyBufferProcs, bf_getbuffer), offsetof(PyTypeObject, tp_as_buffer)},
-{offsetof(PyBufferProcs, bf_releasebuffer), offsetof(PyTypeObject, tp_as_buffer)},
-{offsetof(PyMappingMethods, mp_ass_subscript), offsetof(PyTypeObject, tp_as_mapping)},
-{offsetof(PyMappingMethods, mp_length), offsetof(PyTypeObject, tp_as_mapping)},
-{offsetof(PyMappingMethods, mp_subscript), offsetof(PyTypeObject, tp_as_mapping)},
-{offsetof(PyNumberMethods, nb_absolute), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_add), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_and), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_bool), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_divmod), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_float), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_floor_divide), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_index), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_inplace_add), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_inplace_and), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_inplace_floor_divide), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_inplace_lshift), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_inplace_multiply), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_inplace_or), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_inplace_power), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_inplace_remainder), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_inplace_rshift), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_inplace_subtract), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_inplace_true_divide), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_inplace_xor), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_int), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_invert), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_lshift), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_multiply), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_negative), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_or), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_positive), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_power), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_remainder), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_rshift), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_subtract), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_true_divide), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_xor), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PySequenceMethods, sq_ass_item), offsetof(PyTypeObject, tp_as_sequence)},
-{offsetof(PySequenceMethods, sq_concat), offsetof(PyTypeObject, tp_as_sequence)},
-{offsetof(PySequenceMethods, sq_contains), offsetof(PyTypeObject, tp_as_sequence)},
-{offsetof(PySequenceMethods, sq_inplace_concat), offsetof(PyTypeObject, tp_as_sequence)},
-{offsetof(PySequenceMethods, sq_inplace_repeat), offsetof(PyTypeObject, tp_as_sequence)},
-{offsetof(PySequenceMethods, sq_item), offsetof(PyTypeObject, tp_as_sequence)},
-{offsetof(PySequenceMethods, sq_length), offsetof(PyTypeObject, tp_as_sequence)},
-{offsetof(PySequenceMethods, sq_repeat), offsetof(PyTypeObject, tp_as_sequence)},
-{-1, offsetof(PyTypeObject, tp_alloc)},
-{-1, offsetof(PyTypeObject, tp_base)},
-{-1, offsetof(PyTypeObject, tp_bases)},
-{-1, offsetof(PyTypeObject, tp_call)},
-{-1, offsetof(PyTypeObject, tp_clear)},
-{-1, offsetof(PyTypeObject, tp_dealloc)},
-{-1, offsetof(PyTypeObject, tp_del)},
-{-1, offsetof(PyTypeObject, tp_descr_get)},
-{-1, offsetof(PyTypeObject, tp_descr_set)},
-{-1, offsetof(PyTypeObject, tp_doc)},
-{-1, offsetof(PyTypeObject, tp_getattr)},
-{-1, offsetof(PyTypeObject, tp_getattro)},
-{-1, offsetof(PyTypeObject, tp_hash)},
-{-1, offsetof(PyTypeObject, tp_init)},
-{-1, offsetof(PyTypeObject, tp_is_gc)},
-{-1, offsetof(PyTypeObject, tp_iter)},
-{-1, offsetof(PyTypeObject, tp_iternext)},
-{-1, offsetof(PyTypeObject, tp_methods)},
-{-1, offsetof(PyTypeObject, tp_new)},
-{-1, offsetof(PyTypeObject, tp_repr)},
-{-1, offsetof(PyTypeObject, tp_richcompare)},
-{-1, offsetof(PyTypeObject, tp_setattr)},
-{-1, offsetof(PyTypeObject, tp_setattro)},
-{-1, offsetof(PyTypeObject, tp_str)},
-{-1, offsetof(PyTypeObject, tp_traverse)},
-{-1, offsetof(PyTypeObject, tp_members)},
-{-1, offsetof(PyTypeObject, tp_getset)},
-{-1, offsetof(PyTypeObject, tp_free)},
-{offsetof(PyNumberMethods, nb_matrix_multiply), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyNumberMethods, nb_inplace_matrix_multiply), offsetof(PyTypeObject, tp_as_number)},
-{offsetof(PyAsyncMethods, am_await), offsetof(PyTypeObject, tp_as_async)},
-{offsetof(PyAsyncMethods, am_aiter), offsetof(PyTypeObject, tp_as_async)},
-{offsetof(PyAsyncMethods, am_anext), offsetof(PyTypeObject, tp_as_async)},
-{-1, offsetof(PyTypeObject, tp_finalize)},
-{offsetof(PyAsyncMethods, am_send), offsetof(PyTypeObject, tp_as_async)},
-{-1, offsetof(PyTypeObject, tp_vectorcall)},
-{-1, offsetof(PyHeapTypeObject, ht_token)},
diff --git a/Objects/typeslots.py b/Objects/typeslots.py
deleted file mode 100755 (executable)
index c7f8a33..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/usr/bin/python
-# Usage: typeslots.py < Include/typeslots.h typeslots.inc
-
-import sys, re
-
-
-def generate_typeslots(out=sys.stdout):
-    out.write("/* Generated by typeslots.py */\n")
-    res = {}
-    for line in sys.stdin:
-        m = re.match("#define Py_([a-z_]+) ([0-9]+)", line)
-        if not m:
-            continue
-
-        member = m.group(1)
-        if member == "tp_token":
-            # The heap type structure (ht_*) is an implementation detail;
-            # the public slot for it has a familiar `tp_` prefix
-            member = '{-1, offsetof(PyHeapTypeObject, ht_token)}'
-        elif member.startswith("tp_"):
-            member = f'{{-1, offsetof(PyTypeObject, {member})}}'
-        elif member.startswith("am_"):
-            member = (f'{{offsetof(PyAsyncMethods, {member}),'+
-                      ' offsetof(PyTypeObject, tp_as_async)}')
-        elif member.startswith("nb_"):
-            member = (f'{{offsetof(PyNumberMethods, {member}),'+
-                      ' offsetof(PyTypeObject, tp_as_number)}')
-        elif member.startswith("mp_"):
-            member = (f'{{offsetof(PyMappingMethods, {member}),'+
-                      ' offsetof(PyTypeObject, tp_as_mapping)}')
-        elif member.startswith("sq_"):
-            member = (f'{{offsetof(PySequenceMethods, {member}),'+
-                      ' offsetof(PyTypeObject, tp_as_sequence)}')
-        elif member.startswith("bf_"):
-            member = (f'{{offsetof(PyBufferProcs, {member}),'+
-                      ' offsetof(PyTypeObject, tp_as_buffer)}')
-        res[int(m.group(2))] = member
-
-    M = max(res.keys())+1
-    for i in range(1,M):
-        if i in res:
-            out.write("%s,\n" % res[i])
-        else:
-            out.write("{0, 0},\n")
-
-
-def main():
-    if len(sys.argv) == 2:
-        with open(sys.argv[1], "w") as f:
-            generate_typeslots(f)
-    else:
-        generate_typeslots()
-
-if __name__ == "__main__":
-    main()
index 3303fccf0261affdf59877817f45da6feda52f5a..3f29382f9b0b342303168e616241ae2dd0a94b8a 100755 (executable)
@@ -681,6 +681,7 @@ EXPORT_FUNC(PyType_ClearCache)
 EXPORT_FUNC(PyType_Freeze)
 EXPORT_FUNC(PyType_FromMetaclass)
 EXPORT_FUNC(PyType_FromModuleAndSpec)
+EXPORT_FUNC(PyType_FromSlots)
 EXPORT_FUNC(PyType_FromSpec)
 EXPORT_FUNC(PyType_FromSpecWithBases)
 EXPORT_FUNC(PyType_GenericAlloc)
index 953973a2ad32df7bce6ede5cf3ae9e75a7e68bbb..aae8e59faad7557389e684d2dc23a470d959223d 100644 (file)
     <ClCompile Include="..\Python\pytime.c" />
     <ClCompile Include="..\Python\qsbr.c" />
     <ClCompile Include="..\Python\remote_debugging.c" />
+    <ClCompile Include="..\Python\slots.c" />
+    <ClCompile Include="..\Python\slots_generated.c" />
     <ClCompile Include="..\Python\specialize.c" />
     <ClCompile Include="..\Python\structmember.c" />
     <ClCompile Include="..\Python\suggestions.c" />
index 13db4d93f54518db387af372b5962f571b99788a..e76acb5d32385808bd209aa2d2fe4e0bed647ce3 100644 (file)
     <ClCompile Include="..\Python\remote_debugging.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\Python\slots.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\Python\slots_generated.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="..\Python\specialize.c">
       <Filter>Source Files</Filter>
     </ClCompile>
index 3d70517fbe31e8c73110f2263d21ead0e427c8dd..34841ff9780a0111a448c873d7571ff3a44cc696 100644 (file)
     <ClCompile Include="..\Modules\_testlimitedcapi\object.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\pyos.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\set.c" />
+    <ClCompile Include="..\Modules\_testlimitedcapi\slots.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\sys.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\threadstate.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\tuple.c" />
index 5e0a0f65cfcc3d39d450224cdabe16107248b316..a29973786c9485dbfcc7cb2d16a70a5b68f1c5bb 100644 (file)
@@ -24,6 +24,7 @@
     <ClCompile Include="..\Modules\_testlimitedcapi\object.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\pyos.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\set.c" />
+    <ClCompile Include="..\Modules\_testlimitedcapi\slots.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\sys.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\testcapi_long.h" />
     <ClCompile Include="..\Modules\_testlimitedcapi\threadstate.c" />
index f820c3dd64f58c050b9fd658f507971df8c593ce..e306fe9a9615ad07699f9c8df3e97b6226f7a464 100644 (file)
     <ClInclude Include="..\Include\internal\pycore_setobject.h" />
     <ClInclude Include="..\Include\internal\pycore_signal.h" />
     <ClInclude Include="..\Include\internal\pycore_sliceobject.h" />
+    <ClInclude Include="..\Include\internal\pycore_slots.h" />
+    <ClInclude Include="..\Include\internal\pycore_slots_generated.h" />
     <ClInclude Include="..\Include\internal\pycore_stackref.h" />
     <ClInclude Include="..\Include\internal\pycore_stats.h" />
     <ClInclude Include="..\Include\internal\pycore_strhex.h" />
     <ClInclude Include="..\Include\refcount.h" />
     <ClInclude Include="..\Include\setobject.h" />
     <ClInclude Include="..\Include\sliceobject.h" />
+    <ClInclude Include="..\Include\slots.h" />
+    <ClInclude Include="..\Include\slots_generated.h" />
     <ClInclude Include="..\Include\structmember.h" />
     <ClInclude Include="..\Include\structseq.h" />
     <ClInclude Include="..\Include\sysmodule.h" />
     <ClCompile Include="..\Python\pythonrun.c" />
     <ClCompile Include="..\Python\specialize.c" />
     <ClCompile Include="..\Python\suggestions.c" />
+    <ClCompile Include="..\Python\slots.c" />
+    <ClCompile Include="..\Python\slots_generated.c" />
     <ClCompile Include="..\Python\structmember.c" />
     <ClCompile Include="..\Python\symtable.c" />
     <ClCompile Include="..\Python\sysmodule.c">
index 04b6641ae30e7f8c06bddbabfd625de56e8327aa..acabcdc8575d4803956e9997827d5534de89d905 100644 (file)
     <ClInclude Include="..\Include\sliceobject.h">
       <Filter>Include</Filter>
     </ClInclude>
+    <ClInclude Include="..\Include\slots.h">
+      <Filter>Include</Filter>
+    </ClInclude>
+    <ClInclude Include="..\Include\slots_generated.h">
+      <Filter>Include</Filter>
+    </ClInclude>
     <ClInclude Include="..\Include\stats.h">
       <Filter>Include</Filter>
     </ClInclude>
     <ClInclude Include="..\Include\internal\pycore_sliceobject.h">
       <Filter>Include\internal</Filter>
     </ClInclude>
+    <ClInclude Include="..\Include\internal\pycore_slots.h">
+      <Filter>Include\internal</Filter>
+    </ClInclude>
+    <ClInclude Include="..\Include\internal\pycore_slots_generated.h">
+      <Filter>Include\internal</Filter>
+    </ClInclude>
     <ClInclude Include="..\Include\internal\pycore_strhex.h">
       <Filter>Include\internal</Filter>
     </ClInclude>
     <ClCompile Include="..\Python\specialize.c">
       <Filter>Python</Filter>
     </ClCompile>
+    <ClCompile Include="..\Objects\slots.c">
+      <Filter>Objects</Filter>
+    </ClCompile>
+    <ClCompile Include="..\Objects\slots_generated.c">
+      <Filter>Objects</Filter>
+    </ClCompile>
     <ClCompile Include="..\Python\structmember.c">
       <Filter>Python</Filter>
     </ClCompile>
index 7aa96196ec1e1055483a24327581a53cc9a3cc10..60a5ee6e770f598a4c5d7ae518f2ac0ef29fbf9e 100644 (file)
@@ -2059,7 +2059,7 @@ import_run_modexport(PyThreadState *tstate, PyModExportFunction ex0,
     /* This is like import_run_extension, but avoids interpreter switching
      * and code for for single-phase modules.
      */
-    PyModuleDef_Slot *slots = ex0();
+    PySlot *slots = ex0();
     if (!slots) {
         if (!PyErr_Occurred()) {
             PyErr_Format(
diff --git a/Python/slots.c b/Python/slots.c
new file mode 100644 (file)
index 0000000..6bc74c7
--- /dev/null
@@ -0,0 +1,404 @@
+/* Common handling of type/module slots
+ */
+
+#include "Python.h"
+
+#include "pycore_slots.h"
+
+#include <stdio.h>
+
+// Iterating through a recursive structure doesn't look great in a debugger.
+// Flip the #if to 1 to get a trace on stderr.
+// (The messages can also serve as code comments.)
+#if 0
+#define MSG(...) { \
+    fprintf(stderr, "slotiter: " __VA_ARGS__); fprintf(stderr, "\n");}
+#else
+#define MSG(...)
+#endif
+
+static char*
+kind_name(_PySlot_KIND kind)
+{
+    switch (kind) {
+        case _PySlot_KIND_TYPE: return "type";
+        case _PySlot_KIND_MOD: return "module";
+        case _PySlot_KIND_COMPAT: return "compat";
+        case _PySlot_KIND_SLOT: return "generic slot";
+    }
+    Py_UNREACHABLE();
+}
+
+static void
+init_with_kind(_PySlotIterator *it, const void *slots,
+               _PySlot_KIND result_kind,
+               _PySlot_KIND slot_struct_kind)
+{
+    MSG("");
+    MSG("init (%s slot iterator)", kind_name(result_kind));
+    it->state = it->states;
+    it->state->any_slot = slots;
+    it->state->slot_struct_kind = slot_struct_kind;
+    it->kind = result_kind;
+    it->name = NULL;
+    it->recursion_level = 0;
+    it->is_at_end = false;
+    it->is_first_run = true;
+    it->current.sl_id = 0;
+    memset(it->seen, 0, sizeof(it->seen));
+}
+
+void
+_PySlotIterator_Init(_PySlotIterator *it, const PySlot *slots,
+                     _PySlot_KIND result_kind)
+{
+    init_with_kind(it, slots, result_kind, _PySlot_KIND_SLOT);
+}
+
+void
+_PySlotIterator_InitLegacy(_PySlotIterator *it, const void *slots,
+                           _PySlot_KIND kind)
+{
+    init_with_kind(it, slots, kind, kind);
+}
+
+void
+_PySlotIterator_Rewind(_PySlotIterator *it, const void *slots)
+{
+    MSG("");
+    MSG("rewind (%s slot iterator)", kind_name(it->kind));
+    assert (it->is_at_end);
+    assert (it->recursion_level == 0);
+    assert (it->state == it->states);
+    it->is_at_end = false;
+    it->state->any_slot = slots;
+    it->is_first_run = false;
+}
+
+static Py_ssize_t
+seen_index(uint16_t id)
+{
+    return id / _PySlot_SEEN_ENTRY_BITS;
+}
+
+static unsigned int
+seen_mask(uint16_t id)
+{
+    return ((unsigned int)1) << (id % _PySlot_SEEN_ENTRY_BITS);
+}
+
+bool
+_PySlotIterator_SawSlot(_PySlotIterator *it, int id)
+{
+    assert (id > 0);
+    assert (id < _Py_slot_COUNT);
+    return it->seen[seen_index(id)] & seen_mask(id);
+}
+
+// Advance `it` to the next entry. Currently cannot fail.
+static void
+advance(_PySlotIterator *it)
+{
+    MSG("advance (at level %d)", (int)it->recursion_level);
+    switch (it->state->slot_struct_kind) {
+        case _PySlot_KIND_SLOT: it->state->slot++; break;
+        case _PySlot_KIND_TYPE: it->state->tp_slot++; break;
+        case _PySlot_KIND_MOD: it->state->mod_slot++; break;
+        default:
+            Py_UNREACHABLE();
+    }
+}
+
+static int handle_first_run(_PySlotIterator *it);
+
+bool
+_PySlotIterator_Next(_PySlotIterator *it)
+{
+    MSG("next");
+    assert(it);
+    assert(!it->is_at_end);
+    assert(!PyErr_Occurred());
+
+    it->current.sl_id = -1;
+
+    while (true) {
+        if (it->state->slot == NULL) {
+            if (it->recursion_level == 0) {
+                MSG("end (initial nesting level done)");
+                it->is_at_end = true;
+                return 0;
+            }
+            MSG("pop nesting level %d", (int)it->recursion_level);
+            it->recursion_level--;
+            it->state = &it->states[it->recursion_level];
+            advance(it);
+            continue;
+        }
+
+        switch (it->state->slot_struct_kind) {
+            case _PySlot_KIND_SLOT: {
+                MSG("copying PySlot structure");
+                it->current = *it->state->slot;
+            } break;
+            case _PySlot_KIND_TYPE: {
+                MSG("converting PyType_Slot structure");
+                memset(&it->current, 0, sizeof(it->current));
+                it->current.sl_id = (uint16_t)it->state->tp_slot->slot;
+                it->current.sl_flags = PySlot_INTPTR;
+                it->current.sl_ptr = (void*)it->state->tp_slot->pfunc;
+            } break;
+            case _PySlot_KIND_MOD: {
+                MSG("converting PyModuleDef_Slot structure");
+                memset(&it->current, 0, sizeof(it->current));
+                it->current.sl_id = (uint16_t)it->state->mod_slot->slot;
+                it->current.sl_flags = PySlot_INTPTR;
+                it->current.sl_ptr = (void*)it->state->mod_slot->value;
+            } break;
+            default: {
+                Py_UNREACHABLE();
+            } break;
+        }
+
+        /* shorter local names */
+        PySlot *const result = &it->current;
+        uint16_t flags = result->sl_flags;
+
+        MSG("slot %d, flags 0x%x, from %p",
+            (int)result->sl_id, (unsigned)flags, it->state->slot);
+
+        uint16_t orig_id = result->sl_id;
+        switch (it->kind) {
+            case _PySlot_KIND_TYPE:
+                result->sl_id = _PySlot_resolve_type_slot(result->sl_id);
+                break;
+            case _PySlot_KIND_MOD:
+                result->sl_id = _PySlot_resolve_mod_slot(result->sl_id);
+                break;
+            default:
+                Py_UNREACHABLE();
+        }
+        MSG("resolved to slot %d (%s)",
+            (int)result->sl_id, _PySlot_GetName(result->sl_id));
+
+        if (result->sl_id == Py_slot_invalid) {
+            MSG("error (unknown/invalid slot)");
+            if (flags & PySlot_OPTIONAL) {
+                advance(it);
+                continue;
+            }
+            _PySlot_err_bad_slot(kind_name(it->kind), orig_id);
+            goto error;
+        }
+        if (result->sl_id == Py_slot_end) {
+            MSG("sentinel slot, flags %x", (unsigned)flags);
+            if (flags & PySlot_OPTIONAL) {
+                MSG("error (bad flags on sentinel)");
+                PyErr_Format(PyExc_SystemError,
+                            "invalid flags for Py_slot_end: 0x%x",
+                             (unsigned int)flags);
+                goto error;
+            }
+            it->state->slot = NULL;
+            continue;
+        }
+
+        if (result->sl_id == Py_slot_subslots
+            || result->sl_id == Py_tp_slots
+            || result->sl_id == Py_mod_slots
+        ) {
+            if (result->sl_ptr == NULL) {
+                MSG("NULL subslots; skipping");
+                advance(it);
+                continue;
+            }
+            if ((it->states[0].slot_struct_kind == _PySlot_KIND_MOD)
+                && (it->state->slot_struct_kind == _PySlot_KIND_SLOT)
+                && !(result->sl_flags & PySlot_STATIC))
+            {
+                PyErr_Format(PyExc_SystemError,
+                             "slots included from PyModuleDef must be static");
+                goto error;
+            }
+            it->recursion_level++;
+            MSG("recursing into level %d", it->recursion_level);
+            if (it->recursion_level >= _PySlot_MAX_NESTING) {
+                MSG("error (too much nesting)");
+                PyErr_Format(PyExc_SystemError,
+                            "%s (slot %d): too many levels of nested slots",
+                            _PySlot_GetName(result->sl_id), orig_id);
+                goto error;
+            }
+            it->state = &it->states[it->recursion_level];
+            memset(it->state, 0, sizeof(_PySlotIterator_state));
+            it->state->slot = result->sl_ptr;
+            switch (result->sl_id) {
+                case Py_slot_subslots:
+                    it->state->slot_struct_kind = _PySlot_KIND_SLOT; break;
+                case Py_tp_slots:
+                    it->state->slot_struct_kind = _PySlot_KIND_TYPE; break;
+                case Py_mod_slots:
+                    it->state->slot_struct_kind = _PySlot_KIND_MOD; break;
+            }
+            continue;
+        }
+
+        if (flags & PySlot_INTPTR) {
+            MSG("casting from intptr");
+            /* this should compile to nothing on common architectures */
+            switch (_PySlot_get_dtype(result->sl_id)) {
+                case _PySlot_DTYPE_SIZE: {
+                    result->sl_size = (Py_ssize_t)(intptr_t)result->sl_ptr;
+                } break;
+                case _PySlot_DTYPE_INT64: {
+                    result->sl_int64 = (int64_t)(intptr_t)result->sl_ptr;
+                } break;
+                case _PySlot_DTYPE_UINT64: {
+                    result->sl_uint64 = (uint64_t)(intptr_t)result->sl_ptr;
+                } break;
+                case _PySlot_DTYPE_PTR:
+                case _PySlot_DTYPE_FUNC:
+                case _PySlot_DTYPE_VOID:
+                    break;
+            }
+        }
+
+        advance(it);
+        switch (_PySlot_get_dtype(result->sl_id)) {
+            case _PySlot_DTYPE_VOID:
+            case _PySlot_DTYPE_PTR:
+                MSG("result: %d (%s): %p",
+                    (int)result->sl_id, _PySlot_GetName(result->sl_id),
+                    (void*)result->sl_ptr);
+                break;
+            case _PySlot_DTYPE_FUNC:
+                MSG("result: %d (%s): %p",
+                    (int)result->sl_id, _PySlot_GetName(result->sl_id),
+                    (void*)result->sl_func);
+                break;
+            case _PySlot_DTYPE_SIZE:
+                MSG("result: %d (%s): %zd",
+                    (int)result->sl_id, _PySlot_GetName(result->sl_id),
+                    (Py_ssize_t)result->sl_size);
+                break;
+            case _PySlot_DTYPE_INT64:
+                MSG("result: %d (%s): %ld",
+                    (int)result->sl_id,  _PySlot_GetName(result->sl_id),
+                    (long)result->sl_int64);
+                break;
+            case _PySlot_DTYPE_UINT64:
+                MSG("result: %d (%s): %lu (0x%lx)",
+                    (int)result->sl_id, _PySlot_GetName(result->sl_id),
+                    (unsigned long)result->sl_int64,
+                    (unsigned long)result->sl_int64);
+                break;
+        }
+        assert (result->sl_id > 0);
+        assert (result->sl_id <= _Py_slot_COUNT);
+        if (it->is_first_run && (handle_first_run(it) < 0)) {
+            goto error;
+        }
+        return result->sl_id != Py_slot_end;
+    }
+    Py_UNREACHABLE();
+
+error:
+    it->current.sl_id = Py_slot_invalid;
+    return true;
+}
+
+/* Validate current slot, and do bookkeeping */
+static int
+handle_first_run(_PySlotIterator *it)
+{
+    int id = it->current.sl_id;
+
+    if (_PySlot_get_must_be_static(id)) {
+        if (!(it->current.sl_flags & PySlot_STATIC)
+            && (it->state->slot_struct_kind == _PySlot_KIND_SLOT))
+        {
+            PyErr_Format(
+                PyExc_SystemError,
+                "%s requires PySlot_STATIC",
+                _PySlot_GetName(id));
+            return -1;
+        }
+    }
+
+    _PySlot_PROBLEM_HANDLING null_handling = _PySlot_get_null_handling(id);
+    if (null_handling != _PySlot_PROBLEM_ALLOW) {
+        bool is_null = false;
+        switch (_PySlot_get_dtype(id)) {
+            case _PySlot_DTYPE_PTR: {
+                is_null = it->current.sl_ptr == NULL;
+            } break;
+            case _PySlot_DTYPE_FUNC: {
+                is_null = it->current.sl_func == NULL;
+            } break;
+            default: {
+                //Py_UNREACHABLE();
+            } break;
+        }
+        if (is_null) {
+            MSG("slot is NULL but shouldn't");
+            if (null_handling == _PySlot_PROBLEM_REJECT) {
+                MSG("error (NULL rejected)");
+                PyErr_Format(PyExc_SystemError,
+                             "NULL not allowed for slot %s",
+                             _PySlot_GetName(id));
+                return -1;
+            }
+            if (it->states[0].slot_struct_kind == _PySlot_KIND_SLOT) {
+                MSG("deprecated NULL");
+                if (PyErr_WarnFormat(
+                    PyExc_DeprecationWarning,
+                    1,
+                    "NULL value in slot %s is deprecated",
+                    _PySlot_GetName(id)) < 0)
+                {
+                    return -1;
+                }
+            }
+            else {
+                MSG("unwanted NULL in legacy struct");
+            }
+        }
+    }
+
+    _PySlot_PROBLEM_HANDLING duplicate_handling = _PySlot_get_duplicate_handling(id);
+    if (duplicate_handling != _PySlot_PROBLEM_ALLOW) {
+        if (_PySlotIterator_SawSlot(it, id)) {
+            MSG("slot was seen before but shouldn't be duplicated");
+            if (duplicate_handling == _PySlot_PROBLEM_REJECT) {
+                MSG("error (duplicate rejected)");
+                PyErr_Format(
+                    PyExc_SystemError,
+                    "%s%s%s has multiple %s (%d) slots",
+                    kind_name(it->kind),
+                    it->name ? " " : "",
+                    it->name ? it->name : "",
+                    _PySlot_GetName(id),
+                    (int)it->current.sl_id);
+                return -1;
+            }
+            if (it->states[0].slot_struct_kind == _PySlot_KIND_SLOT) {
+                MSG("deprecated duplicate");
+                if (PyErr_WarnFormat(
+                        PyExc_DeprecationWarning,
+                        0,
+                        "%s%s%s has multiple %s (%d) slots. This is deprecated.",
+                        kind_name(it->kind),
+                        it->name ? " " : "",
+                        it->name ? it->name : "",
+                        _PySlot_GetName(id),
+                        (int)it->current.sl_id) < 0) {
+                    return -1;
+                }
+            }
+            else {
+                MSG("unwanted duplicate in legacy struct");
+            }
+        }
+    }
+    it->seen[seen_index(id)] |= seen_mask(id);
+    return 0;
+}
diff --git a/Python/slots.toml b/Python/slots.toml
new file mode 100644 (file)
index 0000000..626a44d
--- /dev/null
@@ -0,0 +1,836 @@
+# This file lists all PySlot values
+# This should only be used as input to Tools/build/generate_slots.py,
+# its format can change at any time (e.g. we can switch to slots.csv)
+
+# Entries:
+# name: name of the slot
+# kind:
+# - 'type', 'mod': slots to create a particular kind of object
+# - 'slot': special slots applicable to any kind of object
+# - 'compat': old IDs that need to be resolved
+# dtype: data type (tag for the union of sl_ptr, sl_size, etc.)
+# equivalents: for 'compat' slots; the slots to resolve to
+# is_type_field: slot that corresponds to a field in the type object (or in
+#   an array like PyNumberMethods).
+# functype: C function type, where needed
+# duplicates, nulls: How to handle common "problems" -- duplicate slots with
+#   the same ID, and NULL pointers, respectively
+#   - 'allow': not a problem for this slot
+#   - 'deprecated': issue a deprecation warning. Don't use for new slots.
+#      (typically, the problem was disallowed in docs, but allowed in practice)
+#   - 'reject': raise error
+#   The default for duplicate slots is 'reject'
+#   The default for NULLs is 'reject' for pointer slots; 'allow' for
+#       non-pointer ones
+# must_be_static: true if slot needs the PySlot_STATIC flag (in PySlot struct)
+
+
+[0]
+name = 'Py_slot_end'
+kind = 'slot'
+dtype = 'void'
+
+[1]
+kind = 'compat'
+equivalents = {type='Py_bf_getbuffer', mod='Py_mod_create'}
+
+[2]
+kind = 'compat'
+equivalents = {type='Py_bf_releasebuffer', mod='Py_mod_exec'}
+
+[3]
+kind = 'compat'
+equivalents = {type='Py_mp_ass_subscript', mod='Py_mod_multiple_interpreters'}
+
+[4]
+kind = 'compat'
+equivalents = {type='Py_mp_length', mod='Py_mod_gil'}
+
+[5]
+name = 'Py_mp_subscript'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[6]
+name = 'Py_nb_absolute'
+kind = 'type'
+is_type_field = true
+functype = 'unaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[7]
+name = 'Py_nb_add'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[8]
+name = 'Py_nb_and'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[9]
+name = 'Py_nb_bool'
+kind = 'type'
+is_type_field = true
+functype = 'inquiry'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[10]
+name = 'Py_nb_divmod'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[11]
+name = 'Py_nb_float'
+kind = 'type'
+is_type_field = true
+functype = 'unaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[12]
+name = 'Py_nb_floor_divide'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[13]
+name = 'Py_nb_index'
+kind = 'type'
+is_type_field = true
+functype = 'unaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[14]
+name = 'Py_nb_inplace_add'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[15]
+name = 'Py_nb_inplace_and'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[16]
+name = 'Py_nb_inplace_floor_divide'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[17]
+name = 'Py_nb_inplace_lshift'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[18]
+name = 'Py_nb_inplace_multiply'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[19]
+name = 'Py_nb_inplace_or'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[20]
+name = 'Py_nb_inplace_power'
+kind = 'type'
+is_type_field = true
+functype = 'ternaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[21]
+name = 'Py_nb_inplace_remainder'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[22]
+name = 'Py_nb_inplace_rshift'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[23]
+name = 'Py_nb_inplace_subtract'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[24]
+name = 'Py_nb_inplace_true_divide'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[25]
+name = 'Py_nb_inplace_xor'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[26]
+name = 'Py_nb_int'
+kind = 'type'
+is_type_field = true
+functype = 'unaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[27]
+name = 'Py_nb_invert'
+kind = 'type'
+is_type_field = true
+functype = 'unaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[28]
+name = 'Py_nb_lshift'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[29]
+name = 'Py_nb_multiply'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[30]
+name = 'Py_nb_negative'
+kind = 'type'
+is_type_field = true
+functype = 'unaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[31]
+name = 'Py_nb_or'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[32]
+name = 'Py_nb_positive'
+kind = 'type'
+is_type_field = true
+functype = 'unaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[33]
+name = 'Py_nb_power'
+kind = 'type'
+is_type_field = true
+functype = 'ternaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[34]
+name = 'Py_nb_remainder'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[35]
+name = 'Py_nb_rshift'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[36]
+name = 'Py_nb_subtract'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[37]
+name = 'Py_nb_true_divide'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[38]
+name = 'Py_nb_xor'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[39]
+name = 'Py_sq_ass_item'
+kind = 'type'
+is_type_field = true
+functype = 'ssizeobjargproc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[40]
+name = 'Py_sq_concat'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[41]
+name = 'Py_sq_contains'
+kind = 'type'
+is_type_field = true
+functype = 'objobjproc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[42]
+name = 'Py_sq_inplace_concat'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[43]
+name = 'Py_sq_inplace_repeat'
+kind = 'type'
+is_type_field = true
+functype = 'ssizeargfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[44]
+name = 'Py_sq_item'
+kind = 'type'
+is_type_field = true
+functype = 'ssizeargfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[45]
+name = 'Py_sq_length'
+kind = 'type'
+is_type_field = true
+functype = 'lenfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[46]
+name = 'Py_sq_repeat'
+kind = 'type'
+is_type_field = true
+functype = 'ssizeargfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[47]
+name = 'Py_tp_alloc'
+kind = 'type'
+is_type_field = true
+functype = 'allocfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[48]
+name = 'Py_tp_base'
+kind = 'type'
+is_type_field = true
+dtype = 'ptr'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[49]
+name = 'Py_tp_bases'
+kind = 'type'
+is_type_field = true
+dtype = 'ptr'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[50]
+name = 'Py_tp_call'
+kind = 'type'
+is_type_field = true
+functype = 'ternaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[51]
+name = 'Py_tp_clear'
+kind = 'type'
+is_type_field = true
+functype = 'inquiry'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[52]
+name = 'Py_tp_dealloc'
+kind = 'type'
+is_type_field = true
+functype = 'destructor'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[53]
+name = 'Py_tp_del'
+kind = 'type'
+is_type_field = true
+functype = 'destructor'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[54]
+name = 'Py_tp_descr_get'
+kind = 'type'
+is_type_field = true
+functype = 'descrgetfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[55]
+name = 'Py_tp_descr_set'
+kind = 'type'
+is_type_field = true
+functype = 'descrsetfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[56]
+name = 'Py_tp_doc'
+kind = 'type'
+is_type_field = true
+dtype = 'ptr'
+nulls = 'allow'
+
+[57]
+name = 'Py_tp_getattr'
+kind = 'type'
+is_type_field = true
+functype = 'getattrfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[58]
+name = 'Py_tp_getattro'
+kind = 'type'
+is_type_field = true
+functype = 'getattrofunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[59]
+name = 'Py_tp_hash'
+kind = 'type'
+is_type_field = true
+functype = 'hashfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[60]
+name = 'Py_tp_init'
+kind = 'type'
+is_type_field = true
+functype = 'initproc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[61]
+name = 'Py_tp_is_gc'
+kind = 'type'
+is_type_field = true
+functype = 'inquiry'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[62]
+name = 'Py_tp_iter'
+kind = 'type'
+is_type_field = true
+functype = 'getiterfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[63]
+name = 'Py_tp_iternext'
+kind = 'type'
+is_type_field = true
+functype = 'iternextfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[64]
+name = 'Py_tp_methods'
+kind = 'type'
+is_type_field = true
+dtype = 'ptr'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+must_be_static = true
+
+[65]
+name = 'Py_tp_new'
+kind = 'type'
+is_type_field = true
+functype = 'newfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[66]
+name = 'Py_tp_repr'
+kind = 'type'
+is_type_field = true
+functype = 'reprfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[67]
+name = 'Py_tp_richcompare'
+kind = 'type'
+is_type_field = true
+functype = 'richcmpfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[68]
+name = 'Py_tp_setattr'
+kind = 'type'
+is_type_field = true
+functype = 'setattrfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[69]
+name = 'Py_tp_setattro'
+kind = 'type'
+is_type_field = true
+functype = 'setattrofunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[70]
+name = 'Py_tp_str'
+kind = 'type'
+is_type_field = true
+functype = 'reprfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[71]
+name = 'Py_tp_traverse'
+kind = 'type'
+is_type_field = true
+functype = 'traverseproc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[72]
+name = 'Py_tp_members'
+kind = 'type'
+is_type_field = true
+dtype = 'ptr'
+nulls = 'reject'
+must_be_static = true
+
+[73]
+name = 'Py_tp_getset'
+kind = 'type'
+is_type_field = true
+dtype = 'ptr'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+must_be_static = true
+
+[74]
+name = 'Py_tp_free'
+kind = 'type'
+is_type_field = true
+functype = 'freefunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[75]
+name = 'Py_nb_matrix_multiply'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[76]
+name = 'Py_nb_inplace_matrix_multiply'
+kind = 'type'
+is_type_field = true
+functype = 'binaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[77]
+name = 'Py_am_await'
+kind = 'type'
+is_type_field = true
+functype = 'unaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[78]
+name = 'Py_am_aiter'
+kind = 'type'
+is_type_field = true
+functype = 'unaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[79]
+name = 'Py_am_anext'
+kind = 'type'
+is_type_field = true
+functype = 'unaryfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[80]
+name = 'Py_tp_finalize'
+kind = 'type'
+is_type_field = true
+functype = 'destructor'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[81]
+name = 'Py_am_send'
+kind = 'type'
+is_type_field = true
+functype = 'sendfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[82]
+name = 'Py_tp_vectorcall'
+kind = 'type'
+is_type_field = true
+functype = 'vectorcallfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[83]
+name = 'Py_tp_token'
+kind = 'type'
+is_type_field = true
+dtype = 'ptr'
+field = 'ht_token'
+duplicates = 'deprecated'
+nulls = 'allow'
+
+[84]
+name = 'Py_mod_create'
+kind = 'mod'
+dtype = 'func'
+nulls = 'deprecated'
+
+[85]
+name = 'Py_mod_exec'
+kind = 'mod'
+dtype = 'func'
+duplicates = 'allow'  # only alowed in PyModuleDef.m_slots
+nulls = 'reject'
+
+[86]
+name = 'Py_mod_multiple_interpreters'
+kind = 'mod'
+dtype = 'uint64'
+
+[87]
+name = 'Py_mod_gil'
+kind = 'mod'
+dtype = 'uint64'
+
+[88]
+name = 'Py_bf_getbuffer'
+kind = 'type'
+is_type_field = true
+functype = 'getbufferproc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[89]
+name = 'Py_bf_releasebuffer'
+kind = 'type'
+is_type_field = true
+functype = 'releasebufferproc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[90]
+name = 'Py_mp_ass_subscript'
+kind = 'type'
+is_type_field = true
+functype = 'objobjargproc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[91]
+name = 'Py_mp_length'
+kind = 'type'
+is_type_field = true
+functype = 'lenfunc'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+
+[92]
+name = 'Py_slot_subslots'
+kind = 'slot'
+dtype = 'ptr'
+nulls = 'allow'
+
+[93]
+name = 'Py_tp_slots'
+kind = 'type'
+dtype = 'ptr'
+nulls = 'allow'
+
+[94]
+name = 'Py_mod_slots'
+kind = 'mod'
+dtype = 'ptr'
+nulls = 'allow'
+
+[95]
+name = 'Py_tp_name'
+kind = 'type'
+dtype = 'ptr'
+
+[96]
+name = 'Py_tp_basicsize'
+kind = 'type'
+dtype = 'size'
+
+[97]
+name = 'Py_tp_extra_basicsize'
+kind = 'type'
+dtype = 'size'
+
+[98]
+name = 'Py_tp_itemsize'
+kind = 'type'
+dtype = 'size'
+
+[99]
+name = 'Py_tp_flags'
+kind = 'type'
+dtype = 'uint64'
+
+[100]
+name = 'Py_mod_name'
+kind = 'mod'
+dtype = 'ptr'
+
+[101]
+name = 'Py_mod_doc'
+kind = 'mod'
+dtype = 'ptr'
+
+[102]
+name = 'Py_mod_state_size'
+kind = 'mod'
+dtype = 'size'
+
+[103]
+name = 'Py_mod_methods'
+kind = 'mod'
+dtype = 'ptr'
+must_be_static = true
+
+[104]
+name = 'Py_mod_state_traverse'
+kind = 'mod'
+dtype = 'func'
+
+[105]
+name = 'Py_mod_state_clear'
+kind = 'mod'
+dtype = 'func'
+
+[106]
+name = 'Py_mod_state_free'
+kind = 'mod'
+dtype = 'func'
+
+[107]
+name = 'Py_tp_metaclass'
+kind = 'type'
+dtype = 'ptr'
+
+[108]
+name = 'Py_tp_module'
+kind = 'type'
+dtype = 'ptr'
+
+[109]
+name = 'Py_mod_abi'
+kind = 'mod'
+dtype = 'ptr'
+duplicates = 'allow'
+
+[110]
+name = 'Py_mod_token'
+kind = 'mod'
+dtype = 'ptr'
diff --git a/Python/slots_generated.c b/Python/slots_generated.c
new file mode 100644 (file)
index 0000000..b8da8fb
--- /dev/null
@@ -0,0 +1,119 @@
+/* Generated by Tools/build/generate_slots.py */
+
+#include "Python.h"
+#include "pycore_slots.h"   // _PySlot_names
+
+const char *const _PySlot_names[] = {
+    "Py_slot_end",
+    "Py_bf_getbuffer/Py_mod_create",
+    "Py_bf_releasebuffer/Py_mod_exec",
+    "Py_mp_ass_subscript/Py_mod_multiple_interpreters",
+    "Py_mp_length/Py_mod_gil",
+    "Py_mp_subscript",
+    "Py_nb_absolute",
+    "Py_nb_add",
+    "Py_nb_and",
+    "Py_nb_bool",
+    "Py_nb_divmod",
+    "Py_nb_float",
+    "Py_nb_floor_divide",
+    "Py_nb_index",
+    "Py_nb_inplace_add",
+    "Py_nb_inplace_and",
+    "Py_nb_inplace_floor_divide",
+    "Py_nb_inplace_lshift",
+    "Py_nb_inplace_multiply",
+    "Py_nb_inplace_or",
+    "Py_nb_inplace_power",
+    "Py_nb_inplace_remainder",
+    "Py_nb_inplace_rshift",
+    "Py_nb_inplace_subtract",
+    "Py_nb_inplace_true_divide",
+    "Py_nb_inplace_xor",
+    "Py_nb_int",
+    "Py_nb_invert",
+    "Py_nb_lshift",
+    "Py_nb_multiply",
+    "Py_nb_negative",
+    "Py_nb_or",
+    "Py_nb_positive",
+    "Py_nb_power",
+    "Py_nb_remainder",
+    "Py_nb_rshift",
+    "Py_nb_subtract",
+    "Py_nb_true_divide",
+    "Py_nb_xor",
+    "Py_sq_ass_item",
+    "Py_sq_concat",
+    "Py_sq_contains",
+    "Py_sq_inplace_concat",
+    "Py_sq_inplace_repeat",
+    "Py_sq_item",
+    "Py_sq_length",
+    "Py_sq_repeat",
+    "Py_tp_alloc",
+    "Py_tp_base",
+    "Py_tp_bases",
+    "Py_tp_call",
+    "Py_tp_clear",
+    "Py_tp_dealloc",
+    "Py_tp_del",
+    "Py_tp_descr_get",
+    "Py_tp_descr_set",
+    "Py_tp_doc",
+    "Py_tp_getattr",
+    "Py_tp_getattro",
+    "Py_tp_hash",
+    "Py_tp_init",
+    "Py_tp_is_gc",
+    "Py_tp_iter",
+    "Py_tp_iternext",
+    "Py_tp_methods",
+    "Py_tp_new",
+    "Py_tp_repr",
+    "Py_tp_richcompare",
+    "Py_tp_setattr",
+    "Py_tp_setattro",
+    "Py_tp_str",
+    "Py_tp_traverse",
+    "Py_tp_members",
+    "Py_tp_getset",
+    "Py_tp_free",
+    "Py_nb_matrix_multiply",
+    "Py_nb_inplace_matrix_multiply",
+    "Py_am_await",
+    "Py_am_aiter",
+    "Py_am_anext",
+    "Py_tp_finalize",
+    "Py_am_send",
+    "Py_tp_vectorcall",
+    "Py_tp_token",
+    "Py_mod_create",
+    "Py_mod_exec",
+    "Py_mod_multiple_interpreters",
+    "Py_mod_gil",
+    "Py_bf_getbuffer",
+    "Py_bf_releasebuffer",
+    "Py_mp_ass_subscript",
+    "Py_mp_length",
+    "Py_slot_subslots",
+    "Py_tp_slots",
+    "Py_mod_slots",
+    "Py_tp_name",
+    "Py_tp_basicsize",
+    "Py_tp_extra_basicsize",
+    "Py_tp_itemsize",
+    "Py_tp_flags",
+    "Py_mod_name",
+    "Py_mod_doc",
+    "Py_mod_state_size",
+    "Py_mod_methods",
+    "Py_mod_state_traverse",
+    "Py_mod_state_clear",
+    "Py_mod_state_free",
+    "Py_tp_metaclass",
+    "Py_tp_module",
+    "Py_mod_abi",
+    "Py_mod_token",
+    NULL
+};
index 996f725fdcb9b57069c249cca7c7e932625b9c0a..4a3dd618f6559f83dbf8535f78d36b849abeca3f 100644 (file)
@@ -38,3 +38,7 @@ ignore = [
 "generate_{re_casefix,sre_constants,token}.py" = [
     "UP031",  # Use format specifiers instead of percent format
 ]
+"generate_slots.py" = [
+    "I001",   # Import block is un-sorted
+    "ISC003", # Explicitly concatenated string
+]
diff --git a/Tools/build/generate_slots.py b/Tools/build/generate_slots.py
new file mode 100755 (executable)
index 0000000..1fdec6e
--- /dev/null
@@ -0,0 +1,399 @@
+#!/usr/bin/python
+"""Generate type/module slot files
+"""
+
+# See the input file (Python/slots.toml) for a description of its format.
+
+import io
+import sys
+import json
+import tomllib
+import argparse
+import functools
+import contextlib
+import collections
+from pathlib import Path
+
+GENERATED_BY = 'Generated by Tools/build/generate_slots.py'
+
+REPO_ROOT = Path(__file__).parent.parent.parent
+DEFAULT_INPUT_PATH = REPO_ROOT / 'Python/slots.toml'
+INCLUDE_PATH = REPO_ROOT / 'Include'
+DEFAULT_PUBLIC_HEADER_PATH = INCLUDE_PATH / 'slots_generated.h'
+DEFAULT_PRIVATE_HEADER_PATH = INCLUDE_PATH / 'internal/pycore_slots_generated.h'
+DEFAULT_C_PATH = REPO_ROOT / 'Python/slots_generated.c'
+
+TABLES = {
+    'tp': 'ht_type',
+    'am': 'as_async',
+    'nb': 'as_number',
+    'mp': 'as_mapping',
+    'sq': 'as_sequence',
+    'bf': 'as_buffer',
+}
+
+
+class SlotInfo:
+    def __init__(self, id, data):
+        self.id = id
+        self.kind = data['kind']
+        self._data = data
+        try:
+            self.name = data['name']
+        except KeyError:
+            self.name = '/'.join(data["equivalents"].values())
+        else:
+            assert self.name.isidentifier
+
+    @functools.cached_property
+    def equivalents(self):
+        return self._data['equivalents']
+
+    @functools.cached_property
+    def dtype(self):
+        try:
+            return self._data['dtype']
+        except KeyError:
+            if self.is_type_field:
+                return 'func'
+            raise
+
+    @functools.cached_property
+    def functype(self):
+        return self._data['functype']
+
+    @functools.cached_property
+    def is_type_field(self):
+        return self._data.get('is_type_field')
+
+    @functools.cached_property
+    def type_field(self):
+        assert self.is_type_field
+        return self._data.get('field', self.name.removeprefix('Py_'))
+
+    @functools.cached_property
+    def type_table_ident(self):
+        assert self.is_type_field
+        return self._data.get('table', self.type_field[:2])
+
+    @functools.cached_property
+    def duplicate_handling(self):
+        return self._data.get('duplicates', 'reject')
+
+    @functools.cached_property
+    def null_handling(self):
+        try:
+            return self._data['nulls']
+        except KeyError:
+            if self.kind == 'compat':
+                return 'allow'
+            if self.dtype in {'ptr', 'func'}:
+                return 'reject'
+            return 'allow'
+
+    @functools.cached_property
+    def must_be_static(self):
+        return self._data.get('must_be_static', False)
+
+
+def parse_slots(file):
+    toml_contents = tomllib.load(file)
+    result = [None] * len(toml_contents)
+    for key, data in toml_contents.items():
+        slot_id = int(key)
+        try:
+            if result[slot_id]:
+                raise ValueError(f'slot ID {slot_id} repeated')
+            result[slot_id] = SlotInfo(slot_id, data)
+        except Exception as e:
+            e.add_note(f'handling slot {slot_id}')
+            raise
+    return result
+
+
+class CWriter:
+    """Simple helper for generating C code"""
+
+    def __init__(self, file):
+        self.file = file
+        self.indent = ''
+        self(f'/* {GENERATED_BY} */')
+        self()
+
+    def out(self, *args, **kwargs):
+        """print args to the file, with current indent at the start"""
+        print(self.indent, end='', file=self.file)
+        print(*args, file=self.file, **kwargs)
+
+    __call__ = out
+
+    @contextlib.contextmanager
+    def block(self, header=None, end=''):
+        """Context for a {}-enclosed block of C"""
+        if header is None:
+            self.out('{')
+        else:
+            self.out(header, '{')
+        old_indent = self.indent
+        self.indent += '    '
+        yield
+        self.indent = old_indent
+        self.out('}' + end)
+
+
+def write_public_header(f, slots):
+    out = CWriter(f)
+    out(f'#ifndef _PY_HAVE_SLOTS_GENERATED_H')
+    out(f'#define _PY_HAVE_SLOTS_GENERATED_H')
+    out()
+    out(f'#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15)')
+    out(f'#define _Py_SLOT_COMPAT_VALUE(OLD, NEW) NEW')
+    out(f'#else')
+    out(f'#define _Py_SLOT_COMPAT_VALUE(OLD, NEW) OLD')
+    out(f'#endif')
+    out()
+    compat_ids = {}
+    for slot in slots:
+        if slot.kind == 'compat':
+            for new_name in slot.equivalents.values():
+                  compat_ids[new_name] = slot.id
+    for slot in slots:
+        if slot.kind == 'compat':
+            continue
+        slot_id = slot.id
+        if compat := compat_ids.get(slot.name):
+            slot_id = f'_Py_SLOT_COMPAT_VALUE({compat}, {slot_id})'
+        out(f'#define {slot.name} {slot_id}')
+    out()
+    out(f'#define _Py_slot_COUNT {len(slots)}')
+    out(f'#endif /* _PY_HAVE_SLOTS_GENERATED_H */')
+
+
+def write_private_header(f, slots):
+    out = CWriter(f)
+
+    def add_case(slot):
+        out(out(f'        case {slot.id}:'))
+
+    slots_by_name = {slot.name: slot for slot in slots}
+
+    out(f'#ifndef _PY_HAVE_INTERNAL_SLOTS_GENERATED_H')
+    out(f'#define _PY_HAVE_INTERNAL_SLOTS_GENERATED_H')
+    for kind in 'type', 'mod':
+        out()
+        out(f'static inline uint16_t')
+        out(f'_PySlot_resolve_{kind}_slot(uint16_t slot_id)')
+        with out.block():
+            with out.block('switch (slot_id)'):
+                good_slots = []
+                for slot in slots:
+                    if slot.kind == 'compat':
+                        new_slot = slots_by_name[slot.equivalents[kind]]
+                        out(f'case {slot.id}:')
+                        out(f'    return {new_slot.name};')
+                    elif slot.kind in {kind, 'slot'}:
+                        good_slots.append(f'case {slot.name}:')
+                for case in good_slots:
+                    out(case)
+                out(f'    return slot_id;')
+                out(f'default:')
+                out(f'    return Py_slot_invalid;')
+    out()
+    out(f'static inline void*')
+    out(f'_PySlot_type_getslot(PyTypeObject *tp, uint16_t slot_id)')
+    with out.block():
+        with out.block('switch (slot_id)'):
+            for slot in slots:
+                if slot.is_type_field:
+                    field = slot.type_field
+                    table_ident = slot.type_table_ident
+                    if table_ident == 'tp':
+                        out(f'case {slot.name}:')
+                        out(f'    return (void*)tp->{field};')
+                    else:
+                        if table_ident == 'ht':
+                            cond = 'tp->tp_flags & Py_TPFLAGS_HEAPTYPE'
+                            val = f'((PyHeapTypeObject*)tp)->{field}'
+                        else:
+                            table = TABLES[table_ident]
+                            cond = f'tp->tp_{table}'
+                            val = f'tp->tp_{table}->{field}'
+                        out(f'case {slot.name}:')
+                        out(f'    if (!({cond})) return NULL;')
+                        out(f'    return (void*){val};')
+        out(f'_PySlot_err_bad_slot("PyType_GetSlot", slot_id);')
+        out(f'return NULL;')
+    out()
+    out(f'static inline void')
+    out(f'_PySlot_heaptype_apply_field_slot(PyHeapTypeObject *ht,',
+        f'PySlot slot)')
+    with out.block():
+        with out.block('switch (slot.sl_id)'):
+            for slot in slots:
+                if slot.is_type_field:
+                    field = slot.type_field
+                    table_ident = slot.type_table_ident
+                    if table_ident == 'ht':
+                        continue
+                    table = TABLES[table_ident]
+                    if slot.dtype == 'func':
+                        functype = f'({slot.functype})'
+                    else:
+                        functype = ''
+                    out(f'case {slot.name}:')
+                    out(f'    ht->{table}.{field} = {functype}slot.sl_{slot.dtype};')
+                    out(f'    break;')
+    out()
+    out(f'static inline _PySlot_DTYPE')
+    out(f'_PySlot_get_dtype(uint16_t slot_id)')
+    with out.block():
+        with out.block('switch (slot_id)'):
+            for slot in slots:
+                if slot.kind == 'compat':
+                    continue
+                dtype = slot.dtype
+                name = slot.name
+                out(f'case {name}: return _PySlot_DTYPE_{dtype.upper()};')
+            out(f'default: return _PySlot_DTYPE_VOID;')
+    out()
+    out(f'static inline _PySlot_PROBLEM_HANDLING')
+    out(f'_PySlot_get_duplicate_handling(uint16_t slot_id)')
+    with out.block():
+        with out.block('switch (slot_id)'):
+            results = collections.defaultdict(list)
+            for slot in slots:
+                if slot.kind == 'compat':
+                    continue
+                handling = slot.duplicate_handling
+                results[handling.upper()].append(f'case {slot.name}:')
+            results.pop('REJECT')
+            for handling, cases in results.items():
+                for case in cases:
+                    out(case)
+                out(f'    return _PySlot_PROBLEM_{handling};')
+            out(f'default:')
+            out(f'    return _PySlot_PROBLEM_REJECT;')
+    out()
+    out(f'static inline _PySlot_PROBLEM_HANDLING')
+    out(f'_PySlot_get_null_handling(uint16_t slot_id)')
+    with out.block():
+        with out.block('switch (slot_id)'):
+            results = collections.defaultdict(list)
+            for slot in slots:
+                if slot.kind == 'compat':
+                    continue
+                handling = slot.null_handling
+                if handling is None:
+                    if slot.kind != 'compat' and slot.dtype in {'ptr', 'func'}:
+                        handling = 'reject'
+                    else:
+                        handling = 'allow'
+                results[handling.upper()].append(f'case {slot.name}:')
+            results.pop('REJECT')
+            for handling, cases in results.items():
+                for case in cases:
+                    out(case)
+                out(f'    return _PySlot_PROBLEM_{handling};')
+            out(f'default:')
+            out(f'    return _PySlot_PROBLEM_REJECT;')
+    out()
+    out(f'static inline bool')
+    out(f'_PySlot_get_must_be_static(uint16_t slot_id)')
+    with out.block():
+        with out.block('switch (slot_id)'):
+            cases = []
+            for slot in slots:
+                if slot.must_be_static:
+                    out(f'case {slot.name}: return true;')
+        out(f'return false;')
+    out()
+    out(f'#endif /* _PY_HAVE_INTERNAL_SLOTS_GENERATED_H */')
+
+
+def write_c(f, slots):
+    out = CWriter(f)
+    out('#include "Python.h"')
+    out('#include "pycore_slots.h"   // _PySlot_names')
+    out()
+    with out.block(f'const char *const _PySlot_names[] =', end=';'):
+        for slot in slots:
+            out(f'"{slot.name}",')
+        out('NULL')
+
+
+@contextlib.contextmanager
+def replace_file(filename):
+    file_path = Path(filename)
+    with io.StringIO() as sio:
+        yield sio
+        try:
+            old_text = file_path.read_text()
+        except FileNotFoundError:
+            old_text = None
+        new_text = sio.getvalue()
+        if old_text == new_text:
+            print(f'{filename}: not modified', file=sys.stderr)
+        else:
+            print(f'{filename}: writing new content', file=sys.stderr)
+            file_path.write_text(new_text)
+
+
+def main(argv):
+    if len(argv) == 1:
+        # No sens calling this with no arguments.
+        argv.append('--help')
+
+    parser = argparse.ArgumentParser(prog=argv[0], description=__doc__)
+    parser.add_argument(
+        '-i', '--input', default=DEFAULT_INPUT_PATH,
+        help=f'the input file (default: {DEFAULT_INPUT_PATH})')
+    parser.add_argument(
+        '--generate-all', action=argparse.BooleanOptionalAction,
+        help='write all output files to their default locations')
+    parser.add_argument(
+        '-j', '--jsonl', action=argparse.BooleanOptionalAction,
+        help='write info to stdout in "JSON Lines" format (one JSON per line)')
+    outfile_group = parser.add_argument_group(
+        'output files',
+        description='By default, no files are generated. Use --generate-all '
+            + 'or the options below to generate them.')
+    outfile_group.add_argument(
+        '-H', '--public-header',
+        help='file into which to write the public header')
+    outfile_group.add_argument(
+        '-I', '--private-header',
+        help='file into which to write the private header')
+    outfile_group.add_argument(
+        '-C', '--cfile',
+        help='file into which to write internal C code')
+    args = parser.parse_args(argv[1:])
+
+    if args.generate_all:
+        if args.public_header is None:
+            args.public_header = DEFAULT_PUBLIC_HEADER_PATH
+        if args.private_header is None:
+            args.private_header = DEFAULT_PRIVATE_HEADER_PATH
+        if args.cfile is None:
+            args.cfile = DEFAULT_C_PATH
+
+    with open(args.input, 'rb') as f:
+        slots = parse_slots(f)
+
+    if args.jsonl:
+        for slot in slots:
+            print(json.dumps(slot.to_dict()))
+
+    if args.public_header:
+        with replace_file(args.public_header) as f:
+            write_public_header(f, slots)
+
+    if args.private_header:
+        with replace_file(args.private_header) as f:
+            write_private_header(f, slots)
+
+    if args.cfile:
+        with replace_file(args.cfile) as f:
+            write_c(f, slots)
+
+if __name__ == "__main__":
+    main(sys.argv)
index aa89e312b624825f66ab90de67ef9a9513a9035b..11d58460b3975d86b159d02457f1e99d3db37004 100644 (file)
@@ -462,6 +462,7 @@ Modules/_testcapi/exceptions.c      -       PyRecursingInfinitelyError_Type -
 Modules/_testcapi/heaptype.c   -       _testcapimodule -
 Modules/_testcapi/mem.c        -       FmData  -
 Modules/_testcapi/mem.c        -       FmHook  -
+Modules/_testcapi/module.c     module_from_def_nonstatic_nested        subslots        -
 Modules/_testcapi/object.c     -       MyObject_dealloc_called -
 Modules/_testcapi/object.c     -       MyType  -
 Modules/_testcapi/structmember.c       -       test_structmembersType_OldAPI   -
@@ -576,6 +577,7 @@ Modules/_testinternalcapi.c -       Test_EvalFrame_Resumes  -
 Modules/_testinternalcapi.c    -       Test_EvalFrame_Loads    -
 Modules/_testinternalcapi/interpreter.c        -       Test_EvalFrame_Resumes  -
 Modules/_testinternalcapi/interpreter.c        -       Test_EvalFrame_Loads    -
+Modules/_testlimitedcapi/slots.c       -       TestMethods     -
 Modules/_testmultiphase.c      -       Example_Type_slots      -
 Modules/_testmultiphase.c      -       Example_Type_spec       -
 Modules/_testmultiphase.c      -       Example_methods -
@@ -618,6 +620,13 @@ Modules/_testmultiphase.c  -       slots_exec_unreported_exception -
 Modules/_testmultiphase.c      -       slots_nonmodule_with_exec_slots -
 Modules/_testmultiphase.c      -       testexport_methods      -
 Modules/_testmultiphase.c      -       uninitialized_def       -
+Modules/_testmultiphase.c      PyModExport__test_from_modexport        slots   -
+Modules/_testmultiphase.c      PyModExport__test_from_modexport_gil_used       slots   -
+Modules/_testmultiphase.c      PyModExport__test_from_modexport_create_nonmodule       slots   -
+Modules/_testmultiphase.c      PyModExport__test_from_modexport_create_nonmodule_gil_used      slots   -
+Modules/_testmultiphase.c      PyModExport__test_from_modexport_smoke  slots   -
+Modules/_testmultiphase.c      -       modexport_empty_slots   -
+Modules/_testmultiphase.c      -       modexport_minimal_slots -
 Modules/_testsinglephase.c     -       global_state    -
 Modules/_testsinglephase.c     -       static_module_circular  -
 Modules/_xxtestfuzz/_xxtestfuzz.c      -       _fuzzmodule     -
index 734aa3a6a721d1e400333881bc95b1433a742b51..44ba9a211fefccd6547ea699b893ac92768aaf28 100755 (executable)
--- a/configure
+++ b/configure
@@ -3599,7 +3599,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 if test "$srcdir" != . -a "$srcdir" != "$(pwd)"; then
     # If we're building out-of-tree, we need to make sure the following
     # resources get picked up before their $srcdir counterparts.
-    #   Objects/ -> typeslots.inc
+    #   Objects/ -> slots_generated.c
     #   Include/ -> Python.h
     # (A side effect of this is that these resources will automatically be
     #  regenerated when building out-of-tree, regardless of whether or not
index c8cb1686d55c07452e889d96027485f19c1c0771..b5a32d4b0901751f95a2dd0339d69d665b3fd9ce 100644 (file)
@@ -99,7 +99,7 @@ AC_SUBST([BASECPPFLAGS])
 if test "$srcdir" != . -a "$srcdir" != "$(pwd)"; then
     # If we're building out-of-tree, we need to make sure the following
     # resources get picked up before their $srcdir counterparts.
-    #   Objects/ -> typeslots.inc
+    #   Objects/ -> slots_generated.c
     #   Include/ -> Python.h
     # (A side effect of this is that these resources will automatically be
     #  regenerated when building out-of-tree, regardless of whether or not