[Python-Dev] ANSI strict aliasing and Python

Tim Peters tim.one@comcast.net
Thu, 17 Jul 2003 20:54:59 -0400


[Neil Schemenauer]
> Recently the GCC option -fno-strict-aliasing flag got added.  The
> intention was to silence GCC warnings like this:
>
>     ../Objects/object.c: In function `PyObject_IsTrue':
>     ../Objects/object.c:1565: warning: dereferencing type-punned
>     pointer will break strict-aliasing rules

This is strange (according to me).  The real point of adding that option
would be to prevent bad code generation in the presence of pretty common
non-standard C code, but I don't know why a compiler would complain about
PyObject_IsTrue:

int
PyObject_IsTrue(PyObject *v)
{
	int res;
	if (v == Py_True)   THIS IS LINE 1565 FOR ME
		return 1;
	if (v == Py_False)
		return 0;
	if (v == Py_None)
		return 0;
	else if (v->ob_type->tp_as_number != NULL &&
		 v->ob_type->tp_as_number->nb_nonzero != NULL)
		res = (*v->ob_type->tp_as_number->nb_nonzero)(v);
	else if (v->ob_type->tp_as_mapping != NULL &&
		 v->ob_type->tp_as_mapping->mp_length != NULL)
		res = (*v->ob_type->tp_as_mapping->mp_length)(v);
	else if (v->ob_type->tp_as_sequence != NULL &&
		 v->ob_type->tp_as_sequence->sq_length != NULL)
		res = (*v->ob_type->tp_as_sequence->sq_length)(v);
	else
		return 1;
	return (res > 0) ? 1 : res;
}

There are two reasons I'm confused:

1) The indicated line merely compares two addresses -- there's
   no dereferencing there.

2) There's nothing in the function that alters memory -- it
   simply doesn't matter whether non-standard aliasing exists in
   this code.

When a warning makes no sense (as it appears to me in this case), trying to
rewrite code to shut it up is a poke-and-hope waste of time.  If it were
pointing out an actual aliasing problem, fine.

> It looks like most (maybe all) of those warnings are triggered by
> Py_True and Py_False.  Is there some other way of defining Py_True and
> Py_False so that we can avoid those errors?  Maybe it's a lost cause
> anyhow, I don't really understand the ANSI spec on this issue.

> ...

> Does this mean that code like:
>
>     void f (PyObject *a, PyDictObject *b)
>     {
>         a->ob_refcnt += 1;
>         b->ob_refcnt -= 1;
>     }
>     [...]
>         f((PyObject *)somedict, somedict);
>
> is disallowed?

I agree with Martin that it's undefined, but there's nothing "like that" in
PyObject_IsTrue.  C's pointers are typed, and generally speaking a C
compiler is free to assume that the memory areas pointed to by pointers of
different types are disjoint.  An optimizer can get some good out of that,
by reordering loads and stores.  A huge exception is made for char*
pointers, which are assumed to potentially alias all other pointers.  Minor
exceptions are made for closely related types (like int* and unsigned int*
pointers are assumed to potentially point to overlapping memory, unless the
compiler can prove otherwise; but int* and float* pointers are assumed not
to point to overlapping bytes).

The way in which Python fakes inheritance by hand means there are potential
problems all over the place (just about everywhere we cast to or from
PyObject*), but very likely very few real problems.  If the only ones gcc
complains about involve Py_{True,False,None}, it's not being helpful.