Bug 84531

Summary: c/c++: bogus warning for functions with different argument lengths but compatible arguments
Product: gcc Reporter: Siddhesh Poyarekar <siddhesh>
Component: cAssignee: Not yet assigned to anyone <unassigned>
Status: RESOLVED INVALID    
Severity: normal    
Priority: P3    
Version: unknown   
Target Milestone: ---   
Host: Target:
Build: Known to work:
Known to fail: Last reconfirmed:

Description Siddhesh Poyarekar 2018-02-23 16:11:16 UTC
gcc trunk throws a bogus warning about incompatible function cast when the cast target and the function have different argument lengths but the same arguments for the common subset.  An example of this is the PyCFunction <-> PyCFunctionWithKeywords cast in python.

Patch posted along with test case:

https://gcc.gnu.org/ml/gcc-patches/2018-02/msg01315.html
Comment 1 Bernd Edlinger 2018-02-24 10:41:17 UTC
The suggested wild-card function type for functions taking 2 or more parameters
would be something like this:

PyObject * (*) (PyObject *, PyObject *, ...);

It is still necessary to cast it back to the correct type before calling.
Comment 2 Siddhesh Poyarekar 2018-02-25 03:38:43 UTC
There's also a PyCFunctionWithoutArgs that takes just one parameter so it'll have to be one arg onwards but I don't know the impact on it's API. I'm going to test that when I get back home tonight and propose a fix in Python. I kinda understand the rationale behind not diluting the warning, but I'd like to keep this open pending conclusion of the discussion on the ML.
Comment 3 Bernd Edlinger 2018-02-25 08:20:57 UTC
Actually the warning on PyCFunctionWithoutArgs
is _not_ a false positive:

I am looking at Python-3.6.1 right now.
What I see is that functions with no arguments
have the signature "PyObject * (*)(PyObject *)"
but on the call side the Call passes two arguments
see Python-3.6.1/Objects/methodobject.c:

        case METH_NOARGS:
            size = PyTuple_GET_SIZE(args);
            if (size != 0) {
                PyErr_Format(PyExc_TypeError,
                    "%.200s() takes no arguments (%zd given)",
                    f->m_ml->ml_name, size);
                return NULL;
            }

            res = (*meth)(self, NULL);
Comment 4 Bernd Edlinger 2018-02-25 11:34:48 UTC
Also these look like more like invalid casts than bogus warnings:

../Python-3.6.1/Objects/frameobject.c:586:5: Warnung: cast between incompatible function types from »void (*)(PyFrameObject *)« {alias »void (*)(struct _fra
me *)«} to »int (*)(PyObject *)« {alias »int (*)(struct _object *)«} [-Wcast-function-type]
     (inquiry)frame_tp_clear,                    /* tp_clear */
     ^
../Python-3.6.1/Objects/funcobject.c:408:18: Warnung: cast between incompatible function types from »PyObject * (*)(PyFunctionObject *)« {alias »struct _obj
ect * (*)(struct <anonym> *)«} to »PyObject * (*)(PyObject *, void *)« {alias »struct _object * (*)(struct _object *, void *)«} [-Wcast-function-type]
     {"__code__", (getter)func_get_code, (setter)func_set_code},
                  ^
../Python-3.6.1/Objects/funcobject.c:408:41: Warnung: cast between incompatible function types from »int (*)(PyFunctionObject *, PyObject *)« {alias »int (*
)(struct <anonym> *, struct _object *)«} to »int (*)(PyObject *, PyObject *, void *)« {alias »int (*)(struct _object *, struct _object *, void *)«} [-Wcast-
function-type]
     {"__code__", (getter)func_get_code, (setter)func_set_code},
                                         ^

And unfortunately the last two would be suppressed by the proposed patch.
Comment 5 Siddhesh Poyarekar 2018-02-26 06:56:43 UTC
Yeah that doesn't look good.  I had not done a cpython build; I had started with a gem5 build (which builds python modules) and only superficially looked at cpython.

I thought about this over the weekend and agree that silencing the warning will only hold us back indefinitely.  I'll look at the cpython build a bit closer and try to fix it there when I have time for it.  Thanks for looking into this.