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.
