I already explained the rationale behind the decision to have
the built-in accept expressions: to be able to easily query for
type attributes in expressions such as:
typedef __attribute__ ((may_alias)) int* BadInt;
void f (BadInt *p)
{
_Static_assert (__builtin_has_attribute (*p, may_alias));
}
or
struct S {
int a[1] __attribute__ ((packed));
};
void f (struct S *p)
{
_Static_assert (__builtin_has_attribute (p->a, packed));
}
or even
_Static_assert (__builtin_has_attribute (p->a[0], packed));
If the built-in rejects expressions, I don't see how one would
query for these properties.
So how about __builtin_has_attribute (typeof (p->a), packed)?
typeof (p->a) is int, not 'packed int', so the built-in returns
false in this case, while true in the expression I wrote. This
applies to any attribute that's attached to a member as opposed
to its type. I know of no way to refer to a struct member in C
without using some non-trivial expression (in C++, S::a could
be used instead).
Trying to query/set attributes on things like "1 + 1" just doesn't make any
sense. So why don't we handle TYPE_P/DECL_P and give an error for the rest?
Mainly because of the above. But also because there is no harm
in accepting arbitrary expressions and querying their type, no
problem that I'm aware of that would justify giving an error.
This is analogous to __alignof__. If __alignof__ (1 + 1) makes
enough sense to accept then I see no reason why
__builtin_has_attribute (1 + 1, aligned) should not be.
Martin
PS __alignof__ is documented to determine the alignment requirement
of a function, object, or a type, but it accepts expressions as well.
For lvalues, like p->a above, __alignof__ is further documented to
determine the alignment of the lvalue's type, but that's not what
it actually does, at least not in the sense of
__alignof__ (__typeof__ (p->a)), for over- or underaligned struct
members declared either with attribute aligned or packed. What
it does is return the alignment of the member subobject, which is,
I'm sure, what most users expect.