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 — [C API] Avoid "const PyObject*" type · Issue #91768 · python/cpython

[C API] Avoid "const PyObject*" type #91768

New issue
Copy link
New issue
Copy link
Closed
Closed
[C API] Avoid "const PyObject*" type#91768
Copy link
Labels
type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@vstinner
vstinner
opened on Apr 20, 2022
Issue body actions

When I converted Py_REFCNT(), Py_TYPE() and Py_SIZE() macros to static inline functions (issue #83754), I chose const PyObject* for their argument because these macros don't modify any member of the PyObject structure. When the Py_IS_TYPE() function was added, it followed the trend (commit 8767ce9). The _PyObject_CAST_CONST() macro was added to easily convert any pointer to const PyObject* for these macros expecting const PyObject*.

The problem is that passing PyObject* to one of these functions emits a compiler warning using gcc -Wcast-qual. The _PyObject_CAST_CONST() doesn't make the warning quiet.

Example explaining the issue:

static inline int value(int *p) { return *p; }
#define value(p) value((int*)(p))

int main()
{
    int x = 1;
    const int *p = &x;
    return value(p);
}

Output:

$ gcc x.c -Wcast-qual -o x && ./x; echo $?
x.c: In function 'main':
x.c:2:24: warning: cast discards 'const' qualifier from pointer target type [-Wcast-qual]
    2 | #define value(p) value((int*)(p))
      |                        ^
x.c:8:12: note: in expansion of macro 'value'
    8 |     return value(p);
      |            ^~~~~
1

In practice, the problem was that building a C extension (which includes <Python.h>) with gcc -Wcast-qual -Werror on Python 3.10 failed with an error on Py_IS_TYPE() implemented as:

static inline int Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) {
    return Py_TYPE(ob) == type;
}
#define Py_IS_TYPE(ob, type) Py_IS_TYPE(_PyObject_CAST_CONST(ob), type)

See the issue #88544 for the compiler error. I removed the Py_TYPE() call in Py_IS_TYPE() to work around the issue:

static inline int Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) {
    // bpo-44378: Don't use Py_TYPE() since Py_TYPE() requires a non-const
    // object.
    return ob->ob_type == type;
}
#define Py_IS_TYPE(ob, type) Py_IS_TYPE(_PyObject_CAST_CONST(ob), type)

But this problem strikes back when I try to convert unicodeobject.h macros to static inline functions for PEP 670 which removes the cast to PyObject* in the limited C API version 3.11: see PR #91696 and PR #91705. For example, _Py_strhex_with_sep() and _Py_strhex_bytes_with_sep() function get a const PyObject* argument and use PyUnicode C functions like PyUnicode_READY(), PyUnicode_KIND() and PyUnicode_READ_CHAR(), but these PyUnicode functions expect PyObject*: the const qualifier is lost. If Python/pystrhex.c is built with gcc -Wcast-qual, gcc emits warnings on PyUnicode_KIND() and PyUnicode_READ_CHAR() calls. That's just an example of problem which can happen in C extensions as well.

To avoid these problems, I propose to avoid const PyObject* in Python: only use PyObject*.

Activity

  1. vstinner
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    on Apr 20, 2022
  2. vstinner commented on Apr 20, 2022

    @vstinner
    vstinner
    on Apr 20, 2022
    MemberAuthor
    More actions

    Using const in C is appealing to help the C compiler to optimize the code. Problem: it's useless.

    The C language is weird because it allows to cast const int* to int* (for example). So the compiler cannot rely on const of a parameter or variable declaration to assume that the code will really not modify these parameters and variables. This is well explained in the article: Why const Doesn't Make C Code Faster.

    See also this question of the C FAQ: Why can't I pass a char ** to a function which expects a const char **?.

  3. vstinner commented on Apr 20, 2022

    @vstinner
    vstinner
    on Apr 20, 2022
    MemberAuthor
    More actions

    This problem will become worse with PEP 670 (convert macros to functions) which removes the implicit cast of function arguments to PyObject* (and other pointer types like void*) in the limited C API version 3.11 and newer. See my PR #91697.

  4. vstinner
    added a commit that references this issue on Apr 21, 2022

    gh-91768: C API no longer use "const PyObject*" type (#91769)

    eaa85cb
  5. vstinner commented on Apr 21, 2022

    @vstinner
    vstinner
    on Apr 21, 2022
    MemberAuthor
    More actions

    Fixed by #91769

  6. vstinner
    closed this as completedon Apr 21, 2022
  7. jamadden
    added 2 commits that reference this issue on Jan 27, 2023

    Stop using 'const PyObject*, per @vstinner

    0981c60

    Stop using 'const PyObject*, per @vstinner

    616df60
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

    type-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.