GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — PyTuple_SET_ITEM fails to compile in C++ source · Issue #93442 · python/cpython

PyTuple_SET_ITEM fails to compile in C++ source #93442

New issue
Copy link
New issue
Copy link
Closed
#93500
Closed
PyTuple_SET_ITEM fails to compile in C++ source#93442
#93500
Copy link
Assignees
vstinner
Labels
3.11only security fixesonly security fixes3.12only security fixesonly security fixestopic-C-APItype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@nascheme
nascheme
opened on Jun 2, 2022
Issue body actions
  • CPython versions tested on: 3.11b3
  • Operating system and architecture: Debian Linux, GCC 10.2.1-6

The kiwisolver extension doesn't compile with beta 3. The error is:

/usr/local/python3.11.0b3/include/python3.11/pyport.h:47:24: error: invalid ‘static_cast’ from type ‘int’ to type ‘_object*’
   47 |                 return static_cast<type>(const_cast<expr_type &>(expr));
      |                        ^~~~~

This seems to be an issue with the new inline functions for things that used to be macros. E.g. PyTuple_SET_ITEM. The C++ code triggering the issue is:

--- a/py/src/symbolics.h
+++ b/py/src/symbolics.h
@@ -123,7 +123,7 @@ PyObject* BinaryMul::operator()( Expression* first, double second )
                return 0;
        Py_ssize_t end = PyTuple_GET_SIZE( first->terms );
        for( Py_ssize_t i = 0; i < end; ++i )  // memset 0 for safe error return
-               PyTuple_SET_ITEM( terms.get(), i, 0 );
+               PyTuple_SetItem( terms.get(), i, 0 );
        for( Py_ssize_t i = 0; i < end; ++i )
        {
                PyObject* item = PyTuple_GET_ITEM( first->terms, i );

Replacing the macro with the function version seems to allow the extension to compile.

This seems related to #92898

Activity

  1. nascheme
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    on Jun 2, 2022
  2. nascheme
    assigned
    vstinner
    on Jun 2, 2022
  3. AA-Turner
    added
    3.11only security fixesonly security fixes
    topic-C-API
    3.12only security fixesonly security fixes
    on Jun 2, 2022
  4. vstinner commented on Jun 2, 2022

    @vstinner
    vstinner
    on Jun 2, 2022
    Member
    More actions

    What is the result type of terms.get()? Can you write a short C++ code reproducing the issue?

    I guess that the cast which fails is _PyObject_CAST(0): 3rd argument of PyTuple_SET_ITEM(). The function expects a PyObject* and you pass 0 as a pointer.

  5. nascheme commented on Jun 2, 2022

    @nascheme
    nascheme
    on Jun 2, 2022
    MemberAuthor
    More actions

    My understanding is that in C NULL and 0 can be used interchangeably. Maybe in modern C++ versions that's not the case?

    https://stackoverflow.com/questions/176989/do-you-use-null-or-0-zero-for-pointers-in-c

    For the kiwi source code, using NULL instead of 0 doesn't fix the error. Making a local variable for val does work:

    diff --git a/py/src/util.h b/py/src/util.h
    index 4b00fa7..6f51bca 100644
    --- a/py/src/util.h
    +++ b/py/src/util.h
    @@ -116,8 +116,10 @@ make_terms( const std::map<PyObject*, double>& coeffs )
         if( !terms )
             return 0;
         Py_ssize_t size = PyTuple_GET_SIZE( terms.get() );
    -    for( Py_ssize_t i = 0; i < size; ++i ) // zero tuple for safe early return
    -        PyTuple_SET_ITEM( terms.get(), i, 0 );
    +    for( Py_ssize_t i = 0; i < size; ++i ) { // zero tuple for safe early return
    +        PyObject *val = 0;
    +        PyTuple_SET_ITEM( terms.get(), i, val );
    +    }
         Py_ssize_t i = 0;
         iter_t it = coeffs.begin();
         iter_t end = coeffs.end();
    

    My guess is that the macro for PyTuple_GET_SIZE does not treat 0/NULL correctly but I don't know how you would fix it.

  6. vstinner commented on Jun 2, 2022

    @vstinner
    vstinner
    on Jun 2, 2022
    Member
    More actions

    Maybe in modern C++ versions that's not the case?

    In C++, nullptr should be used to pass a "null pointer" (0), no?

    Well, Python should accept 0 as PyObject*.

  7. vstinner commented on Jun 2, 2022

    @vstinner
    vstinner
    on Jun 2, 2022
    Member
    More actions

    The issue can be reproduced by running test_cppext with this change:

    diff --git a/Lib/test/_testcppext.cpp b/Lib/test/_testcppext.cpp
    index eade7ccdaa..70f434e678 100644
    --- a/Lib/test/_testcppext.cpp
    +++ b/Lib/test/_testcppext.cpp
    @@ -74,6 +74,10 @@ test_api_casts(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
         Py_INCREF(strong_ref);
         Py_DECREF(strong_ref);
     
    +    // gh-93442: Pass 0 as NULL for PyObject*
    +    Py_XINCREF(0);
    +    Py_XDECREF(0);
    +
         Py_DECREF(obj);
         Py_RETURN_NONE;
     }
    
  8. nascheme commented on Jun 2, 2022

    @nascheme
    nascheme
    on Jun 2, 2022
    MemberAuthor
    More actions

    My guess is that the _Py_CAST_impl needs to have an overload to handle nullptr_t as the argument. Perhaps then _Py_NULL should not be needed. We would like that API users can still use 0 and NULL as args to these macros.

  9. nascheme commented on Jun 3, 2022

    @nascheme
    nascheme
    on Jun 3, 2022
    MemberAuthor
    More actions

    It looks like _Py_CAST_impl came about as a result of #91959. I wonder though if the cure is worse than the disease. I.e. previously we had warnings when you used -Wold-style-cast. Now you have C++ extensions having compile errors if you don't use nullptr instead of the old style NULL or 0. Can we just go back to:

    #define _PyObject_CAST(op) ((PyObject *)(op))
    

    and similar macros? Maybe the _Py_CAST_impl template magic can be opt-in in some way, for people who want to use -Wold-style-cast and not have errors.

  10. serge-sans-paille commented on Jun 3, 2022

    @serge-sans-paille
    serge-sans-paille
    on Jun 3, 2022
    Contributor
    More actions

    The following overloads seem to be needed to cover all cases

    #include <cstdlib>
    typedef struct PyObject { int x ; } PyObject;
    
    extern "C++" {
        namespace {
            template <typename type>
            inline type _Py_CAST_impl(long int ptr) {
                return reinterpret_cast<type>(ptr);
            }
            template <typename type>
            inline type _Py_CAST_impl(int ptr) {
                return reinterpret_cast<type>(ptr);
            }
            template <typename type>
            inline type _Py_CAST_impl(std::nullptr_t) {
                return static_cast<type>(nullptr);
            }
    
            template <typename type, typename expr_type>
                inline type _Py_CAST_impl(expr_type *expr) {
                    return reinterpret_cast<type>(expr);
                }
    
            template <typename type, typename expr_type>
                inline type _Py_CAST_impl(expr_type const *expr) {
                    return reinterpret_cast<type>(const_cast<expr_type *>(expr));
                }
    
            template <typename type, typename expr_type>
                inline type _Py_CAST_impl(expr_type &expr) {
                    return static_cast<type>(expr);
                }
    
            template <typename type, typename expr_type>
                inline type _Py_CAST_impl(expr_type const &expr) {
                    return static_cast<type>(
                        const_cast<expr_type &>(expr)
                    );
                }
        }
    }
    #  define _Py_CAST(type, expr) _Py_CAST_impl<type>(expr)
    
    int main()
    {
        PyObject *obj = _Py_CAST(PyObject*, nullptr);
        PyObject *obj2 = _Py_CAST(PyObject*, 0);
        PyObject *obj3 = _Py_CAST(PyObject*, NULL);
        return (obj == nullptr);
    }
    
  11. nascheme commented on Jun 3, 2022

    @nascheme
    nascheme
    on Jun 3, 2022
    MemberAuthor
    More actions

    I can confirm this change makes kiwi compile without having to make source code changes. 🎉

  12. corona10 commented on Jun 4, 2022

    @corona10
    corona10
    on Jun 4, 2022
    Member
    More actions

    @serge-sans-paille would you like to submit the PR for this issue? cc @nascheme @vstinner

  13. nascheme
    linked a pull request that will close this issuegh-93442: Make C++ version of _Py_CAST work with 0/NULL. #93500on Jun 4, 2022
  14. nascheme
    closed this as completedin #93500on Jun 5, 2022
  15. nascheme
    added a commit that references this issue on Jun 5, 2022

    gh-93442: Make C++ version of _Py_CAST work with 0/NULL. (#93500)

    8bcc3fa
  16. corona10
    added a commit that references this issue on Jun 5, 2022

    gh-93442: Add test for _Py_CAST(nullptr). (gh-93505)

    713eb18
  17. miss-islington
    added a commit that references this issue on Jun 5, 2022

    pythongh-93442: Make C++ version of _Py_CAST work with 0/NULL. (pytho…

    3c64fa3
  18. corona10
    added a commit that references this issue on Jun 5, 2022

    gh-93442: Make C++ version of _Py_CAST work with 0/NULL. (GH-93500) (g…

    d97e2c5
  19. miss-islington
    added a commit that references this issue on Jun 5, 2022

    pythongh-93442: Add test for _Py_CAST(nullptr). (pythongh-93505)

    e6ec695
  20. corona10
    added a commit that references this issue on Jun 5, 2022

    gh-93442: Add test for _Py_CAST(nullptr). (gh-93505) (gh-93509)

    c21f2bd
  21. serge-sans-paille
    added a commit that references this issue on Jun 5, 2022

    Add support for casting nullptr in _Py_CAST_impl

    This issue will close once commit 5cf29ce is merged into the 'master' branch.
    5cf29ce
  22. hroncok
    added a commit that references this issue on Jun 21, 2022

    00383: pythongh-93442: Make C++ version of _Py_CAST work with 0/NULL

    752148b
  23. hrnciar
    added a commit that references this issue on Jun 24, 2022

    00383: pythongh-93442: Make C++ version of _Py_CAST work with 0/NULL

    e6a1ac7
  24. serhiy-storchaka
    added a commit that references this issue on Jun 26, 2022

    Revert 30935 defer bpo45162 to 312 (#94285)

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

Metadata

Metadata

Assignees

  • @vstinner
    vstinner

Labels

3.11only security fixesonly security fixes3.12only security fixesonly security fixestopic-C-APItype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    You are visitor 01402503 — GeezerVison does not store the pages you look up.
    Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.