diff --git a/Include/cpython/funcobject.h b/Include/cpython/funcobject.h
index 60b702218a1f40..bd508ebfb7df00 100644
--- a/Include/cpython/funcobject.h
+++ b/Include/cpython/funcobject.h
@@ -7,56 +7,7 @@
extern "C" {
#endif
-
-#define COMMON_FIELDS(PREFIX) \
- PyObject *PREFIX ## globals; \
- PyObject *PREFIX ## builtins; \
- PyObject *PREFIX ## name; \
- PyObject *PREFIX ## qualname; \
- PyObject *PREFIX ## code; /* A code object, the __code__ attribute */ \
- PyObject *PREFIX ## defaults; /* NULL or a tuple */ \
- PyObject *PREFIX ## kwdefaults; /* NULL or a dict */ \
- PyObject *PREFIX ## closure; /* NULL or a tuple of cell objects */
-
-typedef struct {
- COMMON_FIELDS(fc_)
-} PyFrameConstructor;
-
-/* Function objects and code objects should not be confused with each other:
- *
- * Function objects are created by the execution of the 'def' statement.
- * They reference a code object in their __code__ attribute, which is a
- * purely syntactic object, i.e. nothing more than a compiled version of some
- * source code lines. There is one code object per source code "fragment",
- * but each code object can be referenced by zero or many function objects
- * depending only on how many times the 'def' statement in the source was
- * executed so far.
- */
-
-typedef struct {
- PyObject_HEAD
- COMMON_FIELDS(func_)
- PyObject *func_doc; /* The __doc__ attribute, can be anything */
- PyObject *func_dict; /* The __dict__ attribute, a dict or NULL */
- PyObject *func_weakreflist; /* List of weak references */
- PyObject *func_module; /* The __module__ attribute, can be anything */
- PyObject *func_annotations; /* Annotations, a dict or NULL */
- vectorcallfunc vectorcall;
- /* Version number for use by specializer.
- * Can set to non-zero when we want to specialize.
- * Will be set to zero if any of these change:
- * defaults
- * kwdefaults (only if the object changes, not the contents of the dict)
- * code
- * annotations */
- uint32_t func_version;
-
- /* Invariant:
- * func_closure contains the bindings for func_code->co_freevars, so
- * PyTuple_Size(func_closure) == PyCode_GetNumFree(func_code)
- * (func_closure may be NULL if PyCode_GetNumFree(func_code) == 0).
- */
-} PyFunctionObject;
+typedef struct _functionobject PyFunctionObject;
PyAPI_DATA(PyTypeObject) PyFunction_Type;
@@ -82,27 +33,14 @@ PyAPI_FUNC(PyObject *) _PyFunction_Vectorcall(
size_t nargsf,
PyObject *kwnames);
-uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func);
-
-/* Macros for direct access to these values. Type checks are *not*
- done, so use with care. */
-#define PyFunction_GET_CODE(func) \
- (((PyFunctionObject *)func) -> func_code)
-#define PyFunction_GET_GLOBALS(func) \
- (((PyFunctionObject *)func) -> func_globals)
-#define PyFunction_GET_MODULE(func) \
- (((PyFunctionObject *)func) -> func_module)
-#define PyFunction_GET_DEFAULTS(func) \
- (((PyFunctionObject *)func) -> func_defaults)
-#define PyFunction_GET_KW_DEFAULTS(func) \
- (((PyFunctionObject *)func) -> func_kwdefaults)
-#define PyFunction_GET_CLOSURE(func) \
- (((PyFunctionObject *)func) -> func_closure)
-#define PyFunction_GET_ANNOTATIONS(func) \
- (((PyFunctionObject *)func) -> func_annotations)
-
-#define PyFunction_AS_FRAME_CONSTRUCTOR(func) \
- ((PyFrameConstructor *)&((PyFunctionObject *)(func))->func_globals)
+/* Macros for backwards compatibility. */
+#define PyFunction_GET_CODE(func) PyFunction_GetCode(func)
+#define PyFunction_GET_GLOBALS(func) PyFunction_GetGlobals(func)
+#define PyFunction_GET_MODULE(func) PyFunction_GetModule(func)
+#define PyFunction_GET_DEFAULTS(func) PyFunction_GetDefaults(func)
+#define PyFunction_GET_KW_DEFAULTS(func) PyFunction_GetKwDefaults(func)
+#define PyFunction_GET_CLOSURE(func) PyFunction_GetClosure(func)
+#define PyFunction_GET_ANNOTATIONS(func) PyFunction_GetAnnotations(func)
/* The classmethod and staticmethod types lives here, too */
PyAPI_DATA(PyTypeObject) PyClassMethod_Type;
diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h
index 53580b99d33ac6..d5c15222ff3d0f 100644
--- a/Include/internal/pycore_ceval.h
+++ b/Include/internal/pycore_ceval.h
@@ -12,6 +12,7 @@ extern "C" {
struct pyruntimestate;
struct _ceval_runtime_state;
+#include "pycore_function.h" // PyFrameConstructor
#include "pycore_interp.h" // PyInterpreterState.eval_frame
#include "pycore_pystate.h" // _PyThreadState_GET()
diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h
index b025ca0b8d766c..a7b061a01e5adb 100644
--- a/Include/internal/pycore_frame.h
+++ b/Include/internal/pycore_frame.h
@@ -4,6 +4,8 @@
extern "C" {
#endif
+#include "pycore_function.h" // PyFrameConstructor
+
/* These values are chosen so that the inline functions below all
* compare f_state to zero.
*/
diff --git a/Include/internal/pycore_function.h b/Include/internal/pycore_function.h
new file mode 100644
index 00000000000000..9d8c61f32c9039
--- /dev/null
+++ b/Include/internal/pycore_function.h
@@ -0,0 +1,63 @@
+#ifndef PYCORE_FUNCTION_H
+#define PYCORE_FUNCTION_H
+
+#define COMMON_FIELDS(PREFIX) \
+ PyObject *PREFIX ## globals; \
+ PyObject *PREFIX ## builtins; \
+ PyObject *PREFIX ## name; \
+ PyObject *PREFIX ## qualname; \
+ PyObject *PREFIX ## code; /* A code object, the __code__ attribute */ \
+ PyObject *PREFIX ## defaults; /* NULL or a tuple */ \
+ PyObject *PREFIX ## kwdefaults; /* NULL or a dict */ \
+ PyObject *PREFIX ## closure; /* NULL or a tuple of cell objects */
+
+typedef struct {
+ COMMON_FIELDS(fc_)
+} PyFrameConstructor;
+
+/* Function objects and code objects should not be confused with each other:
+ *
+ * Function objects are created by the execution of the 'def' statement.
+ * They reference a code object in their __code__ attribute, which is a
+ * purely syntactic object, i.e. nothing more than a compiled version of some
+ * source code lines. There is one code object per source code "fragment",
+ * but each code object can be referenced by zero or many function objects
+ * depending only on how many times the 'def' statement in the source was
+ * executed so far.
+ */
+
+typedef struct _functionobject {
+ PyObject_HEAD
+ COMMON_FIELDS(func_)
+ PyObject *func_doc; /* The __doc__ attribute, can be anything */
+ PyObject *func_dict; /* The __dict__ attribute, a dict or NULL */
+ PyObject *func_weakreflist; /* List of weak references */
+ PyObject *func_module; /* The __module__ attribute, can be anything */
+ PyObject *func_annotations; /* Annotations, a dict or NULL */
+ vectorcallfunc vectorcall;
+ /* Version number for use by specializer.
+ * Can set to non-zero when we want to specialize.
+ * Will be set to zero if any of these change:
+ * defaults
+ * kwdefaults (only if the object changes, not the contents of the dict)
+ * code
+ * annotations */
+ uint32_t func_version;
+
+ /* Invariant:
+ * func_closure contains the bindings for func_code->co_freevars, so
+ * PyTuple_Size(func_closure) == PyCode_GetNumFree(func_code)
+ * (func_closure may be NULL if PyCode_GetNumFree(func_code) == 0).
+ */
+} PyFunctionObject;
+
+#define PyFunction_AS_FRAME_CONSTRUCTOR(func) \
+ ((PyFrameConstructor *)&((PyFunctionObject *)(func))->func_globals)
+
+uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif // PYCORE_FUNCTION_H
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 7e959b01906bc8..29f5f8a448e99c 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -1256,6 +1256,8 @@ PYTHON_HEADERS= \
$(srcdir)/Include/internal/pycore_dtoa.h \
$(srcdir)/Include/internal/pycore_fileutils.h \
$(srcdir)/Include/internal/pycore_floatobject.h \
+ $(srcdir)/Include/internal/pycore_frame.h \
+ $(srcdir)/Include/internal/pycore_function.h \
$(srcdir)/Include/internal/pycore_format.h \
$(srcdir)/Include/internal/pycore_getopt.h \
$(srcdir)/Include/internal/pycore_gil.h \
diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-11-10-13-08-41.bpo-45753.WEDzYA.rst b/Misc/NEWS.d/next/Core and Builtins/2021-11-10-13-08-41.bpo-45753.WEDzYA.rst
new file mode 100644
index 00000000000000..89bf2afe53e6fa
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2021-11-10-13-08-41.bpo-45753.WEDzYA.rst
@@ -0,0 +1,4 @@
+Move the PyFunctionObject struct to an internal header.
+
+The meaning to the internal fields of the function was never defined and
+could result in subtle bugs if misused. This move makes that clearer.
diff --git a/Objects/call.c b/Objects/call.c
index 5e55518b04cbfe..7e0c1e4bb876db 100644
--- a/Objects/call.c
+++ b/Objects/call.c
@@ -1,6 +1,7 @@
#include "Python.h"
#include "pycore_call.h" // _PyObject_CallNoArgsTstate()
#include "pycore_ceval.h" // _PyEval_EvalFrame()
+#include "pycore_function.h" // PyFrameConstructor
#include "pycore_object.h" // _PyObject_GC_TRACK()
#include "pycore_pyerrors.h" // _PyErr_Occurred()
#include "pycore_pystate.h" // _PyThreadState_GET()
diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj
index b65998186927b6..943e93d6f79e06 100644
--- a/PCbuild/pythoncore.vcxproj
+++ b/PCbuild/pythoncore.vcxproj
@@ -192,6 +192,8 @@
+
+
diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters
index 62aab5bccf9ef2..e5e7825cb75ac6 100644
--- a/PCbuild/pythoncore.vcxproj.filters
+++ b/PCbuild/pythoncore.vcxproj.filters
@@ -525,6 +525,12 @@
Include\internal
+
+ Include\internal
+
+
+ Include\internal
+
Include\internal
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index 9e3b25c59a5505..c4938761b99de3 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -5,6 +5,7 @@
#include "pycore_ast.h" // _PyAST_Validate()
#include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_compile.h" // _PyAST_Compile()
+#include "pycore_function.h" // PyFrameConstructor
#include "pycore_object.h" // _Py_AddToAllObjects()
#include "pycore_pyerrors.h" // _PyErr_NoMemory()
#include "pycore_pystate.h" // _PyThreadState_GET()
diff --git a/Python/ceval.c b/Python/ceval.c
index c9f6638afa0e46..4e6dbb43a674d7 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -14,6 +14,7 @@
#include "pycore_call.h" // _PyObject_FastCallDictTstate()
#include "pycore_ceval.h" // _PyEval_SignalAsyncExc()
#include "pycore_code.h"
+#include "pycore_function.h"
#include "pycore_initconfig.h" // _PyStatus_OK()
#include "pycore_long.h" // _PyLong_GetZero()
#include "pycore_object.h" // _PyObject_GC_TRACK()
@@ -4625,7 +4626,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
}
// Check if the call can be inlined or not
if (Py_TYPE(function) == &PyFunction_Type && tstate->interp->eval_frame == NULL) {
- int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(function))->co_flags;
+ PyFunctionObject *func = (PyFunctionObject *)function;
+ int code_flags = ((PyCodeObject*)func->func_code)->co_flags;
int is_generator = code_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR);
if (!is_generator) {
PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : PyFunction_GET_GLOBALS(function);
diff --git a/Python/specialize.c b/Python/specialize.c
index 162728314e1003..f180b786e914ca 100644
--- a/Python/specialize.c
+++ b/Python/specialize.c
@@ -1,5 +1,6 @@
#include "Python.h"
#include "pycore_code.h"
+#include "pycore_function.h"
#include "pycore_dict.h"
#include "pycore_long.h"
#include "pycore_moduleobject.h"