GCC on Linux fails to compile tuple assignment with somewhat complex type annotation with "array subscript 1 is above array bounds"
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.
Thanks for the detailed bug report! I can confirm that this fails to compile with gcc on Linux.
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.
This seems like a GCC version added new checks that are somehow getting tripped up.
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.
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.