We have a number of C++ PRs open around problems with code like this:
struct S {
void f();
virtual void g();
};
typedef __attribute__((...)) struct S T;
If the attribute makes any substantive change to S (e.g., changes its
size, alignment, etc.) then bad things happen. For example, the member
functions of "S" have expectations about the layout of "S" that are not
satisfied if they are called with a "T". Depending on the attribute and
circumstances, we do all manner of bad things, including ICE, generate
wrong code, etc.
For a while now, I've been promising to propose semantics for these
constructs. Here is a sketch of the semantics that I think we should
have. (I say a sketch because I have not attempted to write standardese.)
All attributes must be classified as either "semantic" or "non-semantic"
attributes. A "semantic" attribute is one which might affect
code-generation in any way. A "non-semantic" attribute cannot affect
code-generation. For example, "used" and "deprecated" are non-semantic
attributes; there is no way to observe, by looking at an object file,
whether or not a class has been marked with one of these attributes. In
contrast, "packed" is a semantic attribute; the size of a class is
different depending on whether or not it is "packed".
Any attribute may be applied at the point of definition of a class.
These attributes (whether semantic or non-semantic) apply to the class.
For example, if the class is packed, then the member functions expect
the "this" pointer to point to the packed class.
A typedef declaration which adds only non-semantic attributes is always
valid. As with other typedefs, the typedef declaration creates a new
name for an existing type. The type referred to by that name is the
same type as the original type. However, the *name* has additional
properties, implied by the (non-semantic) attributes. For example,
using a "deprecated" name for a type results in a deprecation warning.
But, a function declared to take a parameter with the non-deprecated
name may be passed a parameter with the "deprecated" name.
A typedef declaration which adds semantic attributes to a class type,
other than POD classes with no explicitly declared members other than
data members, to arrays of such classes, to arrays of such arrays, etc.,
is invalid. (POD-ness alone is not satisfactory, as PODs may contain
function members, and I think dealing with static data members and
typedef members is not worth the trouble.)
A typedef declaration which adds semantic attributes to a POD class type
with no function members is valid, but creates an entirely new type,
different from all other types except others formed by adding the same
combination of semantic attributes to the same original class type. In
the example above, if the typedef adds a semantic attribute, you may not
pass an "S" to a function expecting a "T" or vice versa. Neither may
you pass an "S*" to a function expecting a "T*", without an explicit
reinterpret_cast. The name of "T", for linkage purposes, is "T", and
there is no implicit "T::S" type; instead, however, there is a "T::T"
type. (Various consequences follow; for example, typeid(T) gives you a
type_info object that indicates that the name of the type is "T".)
References to the original type from within the types of the members of
the class still refer to the original class. For example, in:
struct S {
char c;
S* next;
};
typedef __attribute__((packed)) S T;
the data member T::next has type "S*", not "T*".
A typedef declaration which adds semantic attributes to a non-class type
is valid, but again creates an entirely new type. (We might want a
special exception to the "entirely new type" rule for the "mode"
attribute, declaring that "typedef __attribute__((mode(DI))) int LL" is
equivalent to "typedef long long LL;" on platforms where "long long" has
DImode.) So,
typedef S* P;
typedef __attribute__((...)) P Q;
creates a type "Q" that is incompatible with "S*" if the attribute is
semantic. However, the type of "*Q" is still "S". It is invalid to do
anything that would require either type_info or a mangled name for "Q",
including using it as an argument to typeid, thowing an exception of a
type involving "Q", or declaring a template to take a parameter of a
type involving "Q". (We could relax some of these restrictions in
future, if we add mangling support for attributes.)
A variable declaration involving attributes, like:
__attribute__((...)) S v;
is treated as syntactic sugar for:
typedef __attribute__((...)) S T;
T v;
where T is some invented type name different from all others in the program.
For example given:
__attribute__((packed)) S v;
the type of "&v" is "__attribute__((packed)) S *", and cannot be passed
to a function expecting an "S*", but can of course be passed to a
function expecting an "__attribute__((packed)) S *", or a typedef for
such a type.
Thoughts?
--
Mark Mitchell
CodeSourcery
[EMAIL PROTECTED]
(650) 331-3385 x713