Skip to content

C API: copying a PyStatus emits -Wdeprecated-declarations in C++ with clang since gh-158080 #160436

Open

Description

@davidnich

Bug description:

Since gh-158080 (GH-158085), PyStatus.exitcode is declared with Py_DEPRECATED(3.16) in Include/cpython/initconfig.h. Because the C API passes and returns PyStatus by value (PyStatus_Exception(), PyStatus_IsError(), Py_InitializeFromConfig(), ...), C++ code that uses the API in the documented way makes clang generate the implicit copy constructor / move assignment operator of PyStatus. These use the deprecated member, so clang emits -Wdeprecated-declarations even though the code never touches exitcode. With -Werror, the build fails.

The usage pattern of the Doc/c-api/init_config.rst examples (ex: init_python()), compiled as C++, is enough to reproduce:

#include <Python.h>

int main(int argc, char** argv) {
    PyStatus status;
    PyConfig config;
    PyConfig_InitPythonConfig(&config);
    status = PyConfig_SetBytesString(&config, &config.program_name, argv[0]);
    if (PyStatus_Exception(status)) {
        goto exception;
    }
    status = Py_InitializeFromConfig(&config);
    if (PyStatus_Exception(status)) {
        goto exception;
    }
    PyConfig_Clear(&config);
    return Py_RunMain();
exception:
    PyConfig_Clear(&config);
    Py_ExitStatusException(status);
}
$ clang++ -std=c++17 -Wall -Werror -I<cpython>/Include -I<build> -c embed.cpp
Include/cpython/initconfig.h:10:9: error: 'exitcode' is deprecated [-Werror,-Wdeprecated-declarations]
embed.cpp:7:12: note: in implicit move assignment operator for 'PyStatus' first required here
Include/cpython/initconfig.h:10:9: error: 'exitcode' is deprecated [-Werror,-Wdeprecated-declarations]
Include/cpython/initconfig.h:10:9: error: 'exitcode' is deprecated [-Werror,-Wdeprecated-declarations]
embed.cpp:8:28: note: in implicit copy constructor for 'PyStatus' first required here
  • Reproduced with 3.16.0a1, with Ubuntu clang 21.1.8 (Linux aarch64) and Apple clang 21.0.0 (macOS arm64); the PyStatus definition is unchanged on current main.
  • g++ 15, and clang compiling the same file as C, emit no warning.
  • The diagnostic is reported at the definition of the structure in initconfig.h, so users cannot silence it locally with a pragma around their own calls; they have to wrap the #include <Python.h> itself.

Possible fix

Suppress the warning around the definition of the structure, with the existing _Py_COMP_DIAG_* macros of pyport.h:

 /* --- PyStatus ----------------------------------------------- */

+_Py_COMP_DIAG_PUSH
+_Py_COMP_DIAG_IGNORE_DEPR_DECLS
 typedef struct {
     enum {
         _PyStatus_TYPE_OK=0,
         _PyStatus_TYPE_ERROR=1,
         _PyStatus_TYPE_EXIT=2  // deprecated
     } _type;
     const char *func;
     const char *err_msg;
     Py_DEPRECATED(3.16) int exitcode;
 } PyStatus;
+_Py_COMP_DIAG_POP

With this change the example above compiles cleanly with clang++ -Werror, while real uses (status.exitcode, PyStatus_Exit(), PyStatus_IsExit()) still emit the deprecation warning. (Alternatively, only the functions could be marked deprecated and the member documented as deprecated.)

A C++ test in Lib/test/test_cppext that copies a PyStatus would catch regressions of this kind.

CPython versions tested on:

3.16, CPython main branch

Operating systems tested on:

Linux, macOS

Linked PRs

Activity

  1. added
    buildThe build process and cross-build
    3.16new features, bugs and security fixes
    on Oct 23, 2026
  2. NekoAsakura commented on Oct 27, 2026

    @NekoAsakura
    Member

    Reproduced with clang 5 to 21. It looks like we already do the pragma option for deprecated code elsewhere.

    _Py_COMP_DIAG_PUSH
    _Py_COMP_DIAG_IGNORE_DEPR_DECLS
    #ifdef Py_GIL_DISABLED
    return _Py_atomic_load_ssize_relaxed(&self->ob_shash);
    #else
    return self->ob_shash;
    #endif
    _Py_COMP_DIAG_POP

    @vstinner shall I go with that?

  3. vstinner commented on Oct 27, 2026

    @vstinner
    Member

    I cannot reproduce the C++ warning on Fedora 44 with g++ 16.2.1 or clang++ 22.1.8 using your example.

    Building this C++ code with g++ -std=c++17 x.cpp -o -x -Wall -Wextra or clang++ -std=c++17 x.cpp -o -x -Wall -Wextra doesn't emit compiler warnings neither:

    struct PyStatus {
        int status;
        __attribute__((__deprecated__)) int exitcode;
    };
    
    PyStatus func(void)
    {
    #if defined(__clang__)
    _Pragma("clang diagnostic push")
    _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")
    #else
    _Pragma("GCC diagnostic push")
    _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
    #endif
        PyStatus status = {1, 0};
    #if defined(__clang__)
    _Pragma("clang diagnostic pop")
    #else
    _Pragma("GCC diagnostic pop")
    #endif
        return status;
    }
    
    int main(void)
    {
        PyStatus copy = func();
        return copy.status == 0;
    }
  4. vstinner commented on Oct 27, 2026

    @vstinner
    Member

    Can you please try this patch?

    diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h
    index 846e05fb26a..1dcb7807761 100644
    --- a/Include/cpython/initconfig.h
    +++ b/Include/cpython/initconfig.h
    @@ -15,7 +15,13 @@ typedef struct {
         } _type;
         const char *func;
         const char *err_msg;
    +#ifndef __cplusplus
         Py_DEPRECATED(3.16) int exitcode;
    +#else
    +    // Avoid Py_DEPRECATED() on C++ to avoid a compile warning on an (implicit)
    +    // PyStatus move assignment operator.
    +    int exitcode;
    +#endif
     } PyStatus;
     
     PyAPI_FUNC(PyStatus) PyStatus_Ok(void);
  5. vstinner commented on Oct 27, 2026

    @vstinner
    Member

    @NekoAsakura: _Py_COMP_DIAG_IGNORE_DEPR_DECLS is designed to ignore a deprecation warning when using a deprecated function/API. I don't think that it should be used when marking an API as deprecated.

  6. NekoAsakura commented on Oct 28, 2026

    @NekoAsakura
    Member

    I cannot reproduce the C++ warning on Fedora 44 with g++ 16.2.1 or clang++ 22.1.8 using your example.

    @vstinner That's because it was fixed in clang >= 22 (#147400). I can confirm your example also warns on older versions.

    I also tried your patch. It fixes the issue, but it causes a regression where using status.exitcode in C++ no longer warns.

    _Py_COMP_DIAG_IGNORE_DEPR_DECLS is designed to ignore a deprecation warning when using a deprecated function/API. I don't think that it should be used when marking an API as deprecated.

    It is usually used around code that uses a deprecated API, but here the warning is reported inside Python.h, so users can't silence it in their own code.

  7. davidnich commented on Oct 28, 2026

    @davidnich
    Author

    Tested with Apple clang 21.0.0 (clang-2100.3.34.2, current Xcode, arm64), using a copy of the 3.14 headers with PyStatus.exitcode declared as in 3.16, with -std=c++17 -Wall:

    initconfig.h example from the report s.exitcode in C++ s.exitcode in C
    current (3.16) 3 warnings warning warning
    #ifndef __cplusplus patch no warning no warning warning
    _Py_COMP_DIAG_PUSH / _Py_COMP_DIAG_IGNORE_DEPR_DECLS around the struct definition no warning warning warning

    So, as @NekoAsakura says, the #ifndef __cplusplus patch fixes the false positive but drops the deprecation for real C++ uses. Suppressing the warning only around the definition keeps it. Clang reports the warning for a use in the user's own code, outside the push/pop region, so only the implicit copy/move members generated from the definition are affected. Apple's current clang 21 still has the behavior fixed in clang 22 (llvm/llvm-project#147400), so macOS embedders building with Xcode will hit this until Xcode ships clang 22+.

  8. vstinner commented on Nov 4, 2026

    @vstinner
    Member

    @vstinner That's because it was fixed in clang >= 22 (llvm/llvm-project#147400). I can confirm your example also warns on older versions.

    Ah. In that case, I suggest to skip Py_DEPRECATED() on clang++ 21 and older. It's not a big deal, PyStatus.exitcode deprecation is not very important :-)

    @NekoAsakura: Do you want to propose a PR for that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.16new features, bugs and security fixesbuildThe build process and cross-buildinterpreter-core(Objects, Python, Grammar, and Parser dirs)topic-C-API

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions