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 — `Py_MIN()`, `Py_MAX()` and `Py_ABS()` cause C compatibility regressions · Issue #158942 · python/cpython

Py_MIN(), Py_MAX() and Py_ABS() cause C compatibility regressions #158942

New issue
Copy link
New issue
Copy link
Closed
Closed
Py_MIN(), Py_MAX() and Py_ABS() cause C compatibility regressions#158942
Copy link
Labels
3.16new features, bugs and security fixesnew features, bugs and security fixesbuildThe build process and cross-buildThe build process and cross-buildinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)topic-C-APItype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@lpyu001
lpyu001
opened on Oct 6, 2026
Issue body actions

Bug report

Summary

On GCC and clang, when compiling C , Py_MIN(), Py_MAX() and Py_ABS() are now GNU statement expressions that copy each argument into local variables named _x and _y. Code that used these public macros in an integer constant expression no longer compiles: file-scope array sizes, struct member array sizes, enum values and case labels all fail. Code that passes a bit-field also no longer compiles. When the caller passes a variable named _x or _y, the code compiles and the result is silently wrong: Py_MAX(a, _x) returns a, and Py_MIN(b, _y) and Py_ABS(_x) read an uninitialized local. Nested calls such as Py_MAX(Py_MIN(a, b), c) now emit -Wshadow warnings, and mixed-sign arguments such as Py_MIN(size_t_var, INT_MAX) emit -Wsign-compare warnings under GCC.

Reproduction Code

Requires GCC or clang compiling C. MSVC and C++ use the previous ternary definitions. The commands below use GCC 13.3.0 and clang 18.1.3 on x86-64 Linux, with -I pointing at a CPython source tree that includes the new Include/pymacro.h.

Wrong results:

/* wrong_result.c */
#include <Python.h>
#include <stdio.h>

int main(void)
{
    int a = 1, _x = 5;
    printf("Py_MAX(a, _x) = %d (expected 5)\n", Py_MAX(a, _x));

    int b = 9, _y = 2;
    printf("Py_MIN(b, _y) = %d (expected 2)\n", Py_MIN(b, _y));

    int c = -7;
    {
        int _x = c;
        printf("Py_ABS(_x) = %d (expected 7)\n", Py_ABS(_x));
    }
    return 0;
}
gcc -std=c11 -O0 -I Include -I . wrong_result.c -o t && ./t
gcc -std=c11 -O2 -I Include -I . wrong_result.c -o t && ./t
clang -std=c11 -O0 -I Include -I . wrong_result.c -o t && ./t

Compile failures and new warnings (each block is a separate file):

/* file_scope.c: compile with -c */
#include <Python.h>

static char buf[Py_MAX(sizeof(long), 16)];
struct S { char data[Py_MIN(8, 32)]; };
enum { E = Py_MAX(3, 7) };
/* bitfield.c: compile with -c */
#include <Python.h>

struct S { unsigned bf : 4; };
int f(struct S *s, int k) { return Py_MAX(s->bf, k); }
/* case_label.c: compile with -c */
#include <Python.h>

int f(int v)
{
    switch (v) {
    case Py_MAX(1, 2): return 1;
    default: return 0;
    }
}
/* shadow.c: compile with -c -Wall -Wextra -Wshadow */
#include <Python.h>

int f(int a, int b, int c) { return Py_MAX(Py_MIN(a, b), c); }
/* signcmp.c: compile with -c -Wall -Wextra */
#include <Python.h>
#include <limits.h>

int f(size_t n) { return (int)Py_MIN(n, INT_MAX); }

Actual Behavior

wrong_result.c output:

--- gcc -O0
Py_MAX(a, _x) = 1 (expected 5)
Py_MIN(b, _y) = -219883104 (expected 2)
Py_ABS(_x) = 32766 (expected 7)
--- gcc -O2
Py_MAX(a, _x) = 1 (expected 5)
Py_MIN(b, _y) = 0 (expected 2)
Py_ABS(_x) = 0 (expected 7)
--- clang -O0
Py_MAX(a, _x) = 1 (expected 5)
Py_MIN(b, _y) = 0 (expected 2)
Py_ABS(_x) = 0 (expected 7)

GCC compiles wrong_result.c without any warning at the default warning level. Under -Wall, clang warns variable '_y' is uninitialized when used within its own initialization [-Wuninitialized] and the same for _x. GCC gives no such warning.

Compile failures with GCC:

=== gcc -std=c11 -c file_scope.c
Include/pymacro.h:127:8: error: braced-group within expression allowed only inside a function
Include/pymacro.h:121:8: error: braced-group within expression allowed only inside a function
Include/pymacro.h:127:8: error: braced-group within expression allowed only inside a function
=== gcc -std=c11 -c bitfield.c
bitfield.c:4:43: error: ‘typeof’ applied to a bit-field
=== gcc -std=c11 -c case_label.c
case_label.c:6:5: error: case label does not reduce to an integer constant

Compile failures with clang:

=== clang -std=c11 -c file_scope.c
file_scope.c:3:17: error: statement expression not allowed at file scope
file_scope.c:4:22: error: statement expression not allowed at file scope
file_scope.c:5:12: error: statement expression not allowed at file scope
=== clang -std=c11 -c bitfield.c
bitfield.c:4:36: error: invalid application of 'typeof' to bit-field
=== clang -std=c11 -c case_label.c
case_label.c:6:10: error: expression is not an integer constant expression

New warnings:

=== gcc -std=c11 -Wall -Wextra -Wshadow -c shadow.c
Include/pymacro.h:121:26: warning: declaration of ‘_x’ shadows a previous local [-Wshadow]
=== gcc -std=c11 -Wall -Wextra -Wshadow -c signcmp.c
Include/pymacro.h:123:14: warning: comparison of integer expressions of different signedness: ‘size_t’ {aka ‘long unsigned int’} and ‘int’ [-Wsign-compare]
Include/pymacro.h:123:26: warning: operand of ‘?:’ changes signedness from ‘int’ to ‘size_t’ {aka ‘long unsigned int’} due to unsignedness of other operand [-Wsign-compare]
=== clang -std=c11 -Wall -Wextra -Wshadow -c shadow.c
shadow.c:3:44: warning: declaration shadows a local variable [-Wshadow]

clang emits no -Wsign-compare warning for signcmp.c.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs

  • gh-158942: restore constant-expression support in Py_MIN, Py_MAX, and Py_ABS #158943
  • gh-158942: Use more unique variable names in Py_MIN/MAX/ABS() #158969

Activity

  1. lpyu001
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    on Oct 6, 2026
  2. ZeroIntensity commented on Oct 7, 2026

    @ZeroIntensity
    ZeroIntensity
    on Oct 7, 2026
    Member
    More actions

    Has this caused problems in practice anywhere, or is this just conceptual? Some of these examples look very artificial (it is not particularly useful to use Py_MAX in switch cases, for example).

    cc @vstinner

    Reacted by Victor Stinner
  3. ZeroIntensity
    added
    topic-C-API
    on Oct 7, 2026
  4. picnixz
    added
    interpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)
    on Oct 7, 2026
  5. picnixz commented on Oct 7, 2026

    @picnixz
    picnixz
    on Oct 7, 2026
    Member
    More actions

    All the examples except the switch one look important to fix. The one with file_scope is however too fragile IMO as Py_MIN/MAX could be considered a function call (in which case it shouldn't be used in arrays static lengths).

  6. picnixz commented on Oct 7, 2026

    @picnixz
    picnixz
    on Oct 7, 2026
    Member
    More actions

    IIRC @vstinner you recently altered those macros

  7. picnixz
    added
    release-blocker
    on Oct 7, 2026
  8. github-project-automation
    added this to Release and Deferred blockers 🚫on Oct 7, 2026
  9. github-project-automation
    moved this to Todo in Release and Deferred blockers 🚫on Oct 7, 2026
  10. picnixz
    added
    3.16new features, bugs and security fixesnew features, bugs and security fixes
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    interpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)
    buildThe build process and cross-buildThe build process and cross-build
    topic-C-API
    and removed
    release-blocker
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    interpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)
    topic-C-API
    on Oct 7, 2026
  11. picnixz
    removed this from Release and Deferred blockers 🚫on Oct 7, 2026
  12. vstinner
    added 2 commits that reference this issue on Oct 7, 2026

    pythongh-158942: Use more unique variable names in Py_MIN/MAX/ABS

    5981e4e

    pythongh-158942: Use more unique variable names in Py_MIN/MAX/ABS()

    c67985d
  13. vstinner commented on Oct 7, 2026

    @vstinner
    vstinner
    on Oct 7, 2026
    Member
    More actions

    I created PR #158969 to fix most issues reported here. I didn't fix file_scope.c: you need a workaround in your code for this one. The case_label.c case is not fixed, but just: don't do that :-)

    /* file_scope.c: compile with -c */
    #include <Python.h>
    static char buf[Py_MAX(sizeof(long), 16)];
    struct S { char data[Py_MIN(8, 32)]; };
    enum { E = Py_MAX(3, 7) };

    It's unfortunate that this use case is broken by the new macros implementation. If you need the macros at file scope, you might have to bring your own implementation of these macros (copy Python 3.15 macros to your C file and rename them).

    /* shadow.c: compile with -c -Wall -Wextra -Wshadow */
    int f(int a, int b, int c) { return Py_MAX(Py_MIN(a, b), c); }

    This one is fixed by my PR.

    /* signcmp.c: compile with -c -Wall -Wextra */
    int f(size_t n) { return (int)Py_MIN(n, INT_MAX); }

    That's a legit bug in your bug. It's a nice and deliberate side effect of the new implementation: detect such sign comparison bug!

    The correct size is: int f(size_t n) { return (int)Py_MIN(n, (size_t)INT_MAX); }.

    /* case_label.c: compile with -c */
    case Py_MAX(1, 2): return 1;

    I don't think that's a realistic use case. Just don't do that :-) Did you use a LLM to generate these C files?

  14. lpyu001 commented on Oct 8, 2026

    @lpyu001
    lpyu001
    on Oct 8, 2026
    ContributorAuthor
    More actions

    Yes, I first noticed the issue where variables named _x and _y could be shadowed. I then used an LLM to further evaluate #157496 and generated these test cases.

    Reacted by Victor Stinner
  15. vstinner
    added a commit that references this issue on Oct 8, 2026

    gh-158942: Use more unique variable names in Py_MIN/MAX/ABS() (#158969)

    e2c3c7e
  16. lpyu001
    closed this as completedon Oct 9, 2026
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 fixesnew features, bugs and security fixesbuildThe build process and cross-buildThe build process and cross-buildinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)topic-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 01402520 — 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.