mypyc icon indicating copy to clipboard operation
mypyc copied to clipboard

GCC on Linux fails to compile tuple assignment with somewhat complex type annotation with "array subscript 1 is above array bounds"

Open ichard26 opened this issue 4 years ago • 4 comments

from typing import Optional, Tuple

Context = Tuple[str, Tuple[int, int]]
RawNode = Tuple[int, Optional[str], Optional[Context]]

def setup() -> None:
    # This is what ticks off gcc.
    newnode: RawNode = (7, None, None)
(bm-venv) ichard26@acer-ubuntu:~/programming/oss/black$ mypy --version ; python --version ; lsb_release -d
mypy 0.920+dev.68a67aedb072dc811424636140a87aa76f1b59bb
Python 3.8.5
Description:	Ubuntu 20.04.2 LTS
(bm-venv) ichard26@acer-ubuntu:~/programming/oss/black$ export CC=gcc-10
(bm-venv) ichard26@acer-ubuntu:~/programming/oss/black$ mypyc sorrygcc.py
running build_ext
building 'sorrygcc' extension
creating build/temp.linux-x86_64-3.8
creating build/temp.linux-x86_64-3.8/build
gcc-10 -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -g0 -fPIC -I/home/ichard26/programming/oss/black/bm-venv/lib/python3.8/site-packages/mypyc/lib-rt -I/home/ichard26/programming/oss/black/bm-venv/include -I/opt/python3.8.5/include/python3.8 -c build/__native.c -o build/temp.linux-x86_64-3.8/build/__native.o -O3 -Werror -Wno-unused-function -Wno-unused-label -Wno-unreachable-code -Wno-unused-variable -Wno-unused-command-line-argument -Wno-unknown-warning-option -Wno-unused-but-set-variable
In file included from /home/ichard26/programming/oss/black/bm-venv/lib/python3.8/site-packages/mypyc/lib-rt/pythonsupport.h:13,
                 from /home/ichard26/programming/oss/black/bm-venv/lib/python3.8/site-packages/mypyc/lib-rt/CPy.h:12,
                 from /home/ichard26/programming/oss/black/bm-venv/lib/python3.8/site-packages/mypyc/lib-rt/init.c:2,
                 from build/__native.c:1:
build/__native.c: In function ‘CPyDef_setup’:
/opt/python3.8.5/include/python3.8/cpython/tupleobject.h:27:60: error: array subscript 1 is above array bounds of ‘PyObject *[1]’ {aka ‘struct _object *[1]’} [-Werror=array-bounds]
   27 | #define PyTuple_GET_ITEM(op, i) (_PyTuple_CAST(op)->ob_item[i])
/home/ichard26/programming/oss/black/bm-venv/lib/python3.8/site-packages/mypyc/lib-rt/mypyc_util.h:10:43: note: in definition of macro ‘unlikely’
   10 | #define unlikely(x)     __builtin_expect((x),0)
      |                                           ^
/opt/python3.8.5/include/python3.8/object.h:351:35: note: in expansion of macro ‘PyType_HasFeature’
  351 | #define PyType_FastSubclass(t,f)  PyType_HasFeature(t,f)
      |                                   ^~~~~~~~~~~~~~~~~
/opt/python3.8.5/include/python3.8/tupleobject.h:27:18: note: in expansion of macro ‘PyType_FastSubclass’
   27 |                  PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_TUPLE_SUBCLASS)
      |                  ^~~~~~~~~~~~~~~~~~~
/opt/python3.8.5/include/python3.8/object.h:122:34: note: in expansion of macro ‘_PyObject_CAST’
  122 | #define Py_TYPE(ob)             (_PyObject_CAST(ob)->ob_type)
      |                                  ^~~~~~~~~~~~~~
/opt/python3.8.5/include/python3.8/tupleobject.h:27:38: note: in expansion of macro ‘Py_TYPE’
   27 |                  PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_TUPLE_SUBCLASS)
      |                                      ^~~~~~~
build/__native.c:115:20: note: in expansion of macro ‘PyTuple_Check’
  115 |     if (unlikely(!(PyTuple_Check(PyTuple_GET_ITEM(PyTuple_GET_ITEM(cpy_r_r1, 2), 1)) && PyTuple_GET_SIZE(PyTuple_GET_ITEM(PyTuple_GET_ITEM(cpy_r_r1, 2), 1)) == 2))) {
      |                    ^~~~~~~~~~~~~
build/__native.c:115:34: note: in expansion of macro ‘PyTuple_GET_ITEM’
  115 |     if (unlikely(!(PyTuple_Check(PyTuple_GET_ITEM(PyTuple_GET_ITEM(cpy_r_r1, 2), 1)) && PyTuple_GET_SIZE(PyTuple_GET_ITEM(PyTuple_GET_ITEM(cpy_r_r1, 2), 1)) == 2))) {
      |                                  ^~~~~~~~~~~~~~~~
In file included from /opt/python3.8.5/include/python3.8/tupleobject.h:41,
                 from /opt/python3.8.5/include/python3.8/Python.h:105,
                 from /home/ichard26/programming/oss/black/bm-venv/lib/python3.8/site-packages/mypyc/lib-rt/init.c:1,
                 from build/__native.c:1:
/opt/python3.8.5/include/python3.8/cpython/tupleobject.h:14:15: note: while referencing ‘ob_item’
   14 |     PyObject *ob_item[1];
      |               ^~~~~~~
build/__native.c: At top level:
cc1: note: unrecognized command-line option ‘-Wno-unknown-warning-option’ may have been intended to silence earlier diagnostics
cc1: note: unrecognized command-line option ‘-Wno-unused-command-line-argument’ may have been intended to silence earlier diagnostics
cc1: all warnings being treated as errors
error: command 'gcc-10' failed with exit status 1

Relevant generated IR:

def setup():
    r0 :: tuple[int, None, None]
    r1 :: object
    r2, newnode :: tuple[int, union[str, None], union[tuple[str, tuple[int, int]], None]]
    r3 :: None
L0:
    r0 = (14, 1, 1)
    r1 = box(tuple[int, None, None], r0)
    r2 = unbox(tuple[int, union[str, None], union[tuple[str, tuple[int, int]], None]], r1)
    dec_ref r1
    if is_error(r2) goto L2 (error at setup:9) else goto L1
L1:
    newnode = r2
    dec_ref newnode
    return 1
L2:
    r3 = <error> :: None
    return r3

Relevant generated C:

char CPyDef_setup(void) {
    tuple_T3ICC cpy_r_r0;
    PyObject *cpy_r_r1;
    tuple_T3IOO cpy_r_r2;
    tuple_T3IOO cpy_r_newnode;
    char cpy_r_r3;
CPyL0: ;
    cpy_r_r0.f0 = 14;
    cpy_r_r0.f1 = 1;
    cpy_r_r0.f2 = 1;
    CPyTagged_IncRef(cpy_r_r0.f0);
    cpy_r_r1 = PyTuple_New(3);
    if (unlikely(cpy_r_r1 == NULL))
        CPyError_OutOfMemory();
    PyObject *__tmp1 = CPyTagged_StealAsObject(cpy_r_r0.f0);
    PyTuple_SET_ITEM(cpy_r_r1, 0, __tmp1);
    PyObject *__tmp2 = Py_None;
    CPy_INCREF(__tmp2);
    PyTuple_SET_ITEM(cpy_r_r1, 1, __tmp2);
    PyObject *__tmp3 = Py_None;
    CPy_INCREF(__tmp3);
    PyTuple_SET_ITEM(cpy_r_r1, 2, __tmp3);
    PyObject *__tmp4;
    if (unlikely(!(PyTuple_Check(cpy_r_r1) && PyTuple_GET_SIZE(cpy_r_r1) == 3))) {
        __tmp4 = NULL;
        goto __LL5;
    }
    if (likely(PyLong_Check(PyTuple_GET_ITEM(cpy_r_r1, 0))))
        __tmp4 = PyTuple_GET_ITEM(cpy_r_r1, 0);
    else {
        __tmp4 = NULL;
    }
    if (__tmp4 == NULL) goto __LL5;
    if (PyUnicode_Check(PyTuple_GET_ITEM(cpy_r_r1, 1)))
        __tmp4 = PyTuple_GET_ITEM(cpy_r_r1, 1);
    else {
        __tmp4 = NULL;
    }
    if (__tmp4 != NULL) goto __LL6;
    if (PyTuple_GET_ITEM(cpy_r_r1, 1) == Py_None)
        __tmp4 = PyTuple_GET_ITEM(cpy_r_r1, 1);
    else {
        __tmp4 = NULL;
    }
    if (__tmp4 != NULL) goto __LL6;
    __tmp4 = NULL;
__LL6: ;
    if (__tmp4 == NULL) goto __LL5;
    if (unlikely(!(PyTuple_Check(PyTuple_GET_ITEM(cpy_r_r1, 2)) && PyTuple_GET_SIZE(PyTuple_GET_ITEM(cpy_r_r1, 2)) == 2))) {
        __tmp4 = NULL;
        goto __LL8;
    }
    if (likely(PyUnicode_Check(PyTuple_GET_ITEM(PyTuple_GET_ITEM(cpy_r_r1, 2), 0))))
        __tmp4 = PyTuple_GET_ITEM(PyTuple_GET_ITEM(cpy_r_r1, 2), 0);
    else {
        __tmp4 = NULL;
    }
    if (__tmp4 == NULL) goto __LL8;
    if (unlikely(!(PyTuple_Check(PyTuple_GET_ITEM(PyTuple_GET_ITEM(cpy_r_r1, 2), 1)) && PyTuple_GET_SIZE(PyTuple_GET_ITEM(PyTuple_GET_ITEM(cpy_r_r1, 2), 1)) == 2))) {
        __tmp4 = NULL;
        goto __LL9;
    }
    if (likely(PyLong_Check(PyTuple_GET_ITEM(PyTuple_GET_ITEM(PyTuple_GET_ITEM(cpy_r_r1, 2), 1), 0))))
        __tmp4 = PyTuple_GET_ITEM(PyTuple_GET_ITEM(PyTuple_GET_ITEM(cpy_r_r1, 2), 1), 0);
    else {
        __tmp4 = NULL;
    }
    if (__tmp4 == NULL) goto __LL9;
    if (likely(PyLong_Check(PyTuple_GET_ITEM(PyTuple_GET_ITEM(PyTuple_GET_ITEM(cpy_r_r1, 2), 1), 1))))
        __tmp4 = PyTuple_GET_ITEM(PyTuple_GET_ITEM(PyTuple_GET_ITEM(cpy_r_r1, 2), 1), 1);
    else {
        __tmp4 = NULL;
    }
    if (__tmp4 == NULL) goto __LL9;
    __tmp4 = PyTuple_GET_ITEM(PyTuple_GET_ITEM(cpy_r_r1, 2), 1);
__LL9: ;
    if (__tmp4 == NULL) goto __LL8;
    __tmp4 = PyTuple_GET_ITEM(cpy_r_r1, 2);
__LL8: ;
    if (__tmp4 != NULL) goto __LL7;
    if (PyTuple_GET_ITEM(cpy_r_r1, 2) == Py_None)
        __tmp4 = PyTuple_GET_ITEM(cpy_r_r1, 2);
    else {
        __tmp4 = NULL;
    }
    if (__tmp4 != NULL) goto __LL7;
    __tmp4 = NULL;
__LL7: ;
    if (__tmp4 == NULL) goto __LL5;
    __tmp4 = cpy_r_r1;
__LL5: ;
    if (unlikely(__tmp4 == NULL)) {
        CPy_TypeError("tuple[int, union[str, None], union[tuple[str, tuple[int, int]], None]]", cpy_r_r1); cpy_r_r2 = tuple_undefined_T3IOO;
    } else {
        PyObject *__tmp10 = PyTuple_GET_ITEM(cpy_r_r1, 0);
        CPyTagged __tmp11;
        if (likely(PyLong_Check(__tmp10)))
            __tmp11 = CPyTagged_FromObject(__tmp10);
        else {
            CPy_TypeError("int", __tmp10); __tmp11 = CPY_INT_TAG;
        }
        cpy_r_r2.f0 = __tmp11;
        PyObject *__tmp12 = PyTuple_GET_ITEM(cpy_r_r1, 1);
        CPy_INCREF(__tmp12);
        PyObject *__tmp13;
        if (PyUnicode_Check(__tmp12))
            __tmp13 = __tmp12;
        else {
            __tmp13 = NULL;
        }
        if (__tmp13 != NULL) goto __LL14;
        if (__tmp12 == Py_None)
            __tmp13 = __tmp12;
        else {
            __tmp13 = NULL;
        }
        if (__tmp13 != NULL) goto __LL14;
        CPy_TypeError("str or None", __tmp12); __tmp13 = NULL;
__LL14: ;
        cpy_r_r2.f1 = __tmp13;
        PyObject *__tmp15 = PyTuple_GET_ITEM(cpy_r_r1, 2);
        CPy_INCREF(__tmp15);
        PyObject *__tmp16;
        if (unlikely(!(PyTuple_Check(__tmp15) && PyTuple_GET_SIZE(__tmp15) == 2))) {
            __tmp16 = NULL;
            goto __LL18;
        }
        if (likely(PyUnicode_Check(PyTuple_GET_ITEM(__tmp15, 0))))
            __tmp16 = PyTuple_GET_ITEM(__tmp15, 0);
        else {
            __tmp16 = NULL;
        }
        if (__tmp16 == NULL) goto __LL18;
        if (unlikely(!(PyTuple_Check(PyTuple_GET_ITEM(__tmp15, 1)) && PyTuple_GET_SIZE(PyTuple_GET_ITEM(__tmp15, 1)) == 2))) {
            __tmp16 = NULL;
            goto __LL19;
        }
        if (likely(PyLong_Check(PyTuple_GET_ITEM(PyTuple_GET_ITEM(__tmp15, 1), 0))))
            __tmp16 = PyTuple_GET_ITEM(PyTuple_GET_ITEM(__tmp15, 1), 0);
        else {
            __tmp16 = NULL;
        }
        if (__tmp16 == NULL) goto __LL19;
        if (likely(PyLong_Check(PyTuple_GET_ITEM(PyTuple_GET_ITEM(__tmp15, 1), 1))))
            __tmp16 = PyTuple_GET_ITEM(PyTuple_GET_ITEM(__tmp15, 1), 1);
        else {
            __tmp16 = NULL;
        }
        if (__tmp16 == NULL) goto __LL19;
        __tmp16 = PyTuple_GET_ITEM(__tmp15, 1);
__LL19: ;
        if (__tmp16 == NULL) goto __LL18;
        __tmp16 = __tmp15;
__LL18: ;
        if (__tmp16 != NULL) goto __LL17;
        if (__tmp15 == Py_None)
            __tmp16 = __tmp15;
        else {
            __tmp16 = NULL;
        }
        if (__tmp16 != NULL) goto __LL17;
        CPy_TypeError("tuple[str, tuple[int, int]] or None", __tmp15); __tmp16 = NULL;
__LL17: ;
        cpy_r_r2.f2 = __tmp16;
    }
    CPy_DecRef(cpy_r_r1);
    if (unlikely(cpy_r_r2.f0 == CPY_INT_TAG)) {
        CPy_AddTraceback("sorrygcc.py", "setup", 9, CPyStatic_globals);
        goto CPyL2;
    }
CPyL1: ;
    cpy_r_newnode = cpy_r_r2;
    CPyTagged_DecRef(cpy_r_newnode.f0);
    CPy_DecRef(cpy_r_newnode.f1);
    CPy_DecRef(cpy_r_newnode.f2);
    return 1;
CPyL2: ;
    cpy_r_r3 = 2;
    return cpy_r_r3;
}

This is a heavily minimized case from blib2to3.pgen2.parse.Parser.setup. Interestingly this doesn't show up with clang.

ichard26 avatar Aug 06 '21 02:08 ichard26

Thanks for the detailed bug report! I can confirm that this fails to compile with gcc on Linux.

JukkaL avatar Aug 06 '21 10:08 JukkaL

FWIW, this won't be blocking me from integrating mypyc with psf/black. I've decided against GCC on Linux for our project for several unrelated reasons. While this bug does look pretty bad, it's not hindering Black at all so please don't worry on that front :)

It's also pretty easy to workaround, just inlining the tuple into the next assignment avoids the issue.

ichard26 avatar Aug 10 '21 22:08 ichard26

This seems like a GCC version added new checks that are somehow getting tripped up.

jhance avatar Sep 22 '21 16:09 jhance

from typing import Optional, Tuple

def setup():
    # This is what ticks off gcc.
    newnode: Tuple[int, Optional[Tuple[int, int]]] = (1, None)

Here's an even simpler reproducer.

ichard26 avatar Jul 02 '22 22:07 ichard26

I can't reproduce this anymore. The original cases were "fixed" by https://github.com/python/mypy/pull/14899 and using a Any tuple to force a tuple unbox doesn't cause the error to appear either.

ichard26 avatar Jul 20 '23 14:07 ichard26