[PATCH]: C++ decl classes for the AST

Hi,

The attached patch introduces new Decl subclasses that accomodate C++ class members (there are changes only to the AST library):

-'CXXRecordDecl' (inherits from RecordDecl) is for C++ struct/union/classes that are not simple C structs (i.e. they contain methods, nested types etc.)
-'ClassMember' serves as a base class for members of a CXXRecord. It provides the access specifier and the parent CXXRecord. Decls that inherit ClassMember:
    CXXField - for instance fields (inherits FieldDecl)
    CXXMethod - for static and instance methods (inherits FunctionDecl)
    NestedTypedef - for nested typedefs (inherits TypedefDecl)
    NestedRecordDecl - for nested struct/union/classes (inherits CXXRecordDecl)
    ClassVar - for static data members (inherits VarDecl)

-I also moved the 'Decl' implementation to a separate 'DeclBase.cpp' file

The instance fields of CXXRecord are stored in the members array of RecordDecl, thus the data layout of CXXRecord is calculated through the Record.
All the other members (including the static fields), are ScopedDecls with the CXXRecord as declaration context, so they can be iterated through a general DeclContext member iterator (not implemented yet).
Name lookup for class members will be efficient through the use of the IdentifierResolver.

-Argiris

ast-cxxdecl.patch (54.3 KB)

Hi,

The attached patch introduces new Decl subclasses that accomodate C++ class members (there are changes only to the AST library):

Nice!

-'CXXRecordDecl' (inherits from RecordDecl) is for C++ struct/union/classes that are not simple C structs (i.e. they contain methods, nested types etc.)

Great.

-'ClassMember' serves as a base class for members of a CXXRecord. It provides the access specifier and the parent CXXRecord. Decls that inherit ClassMember:
  CXXField - for instance fields (inherits FieldDecl)
  CXXMethod - for static and instance methods (inherits FunctionDecl)
  NestedTypedef - for nested typedefs (inherits TypedefDecl)
  NestedRecordDecl - for nested struct/union/classes (inherits CXXRecordDecl)
  ClassVar - for static data members (inherits VarDecl)

I'm less enthused about using multiple inheritance for this :). Is there more common between members than just access specifiers? Would it be horrible to just give each of those classes their own AccessSpecifiers member?

It doesn't seem very useful to pass around generic ClassMember*'s. If we need a generic "give me the access specifiers for this member", it could be written as a top-level method on decl or something.

Alternatively, maybe AccessSpecifiers should be pushed up the class hierarchy, say to NamedDecl? Though it would not be used by every subclass, it would make things simpler that way. This would eliminate the need for classes like 'NestedTypedef' and 'NestedRecordDecl' which is strange. A typedef shouldn't be represented with a different class based on where it is defined.

Some minor things: I don't think it makes sense for CXXMethod to derive from FunctionDecl, a CXXMethod doesn't have the "isa" property for FunctionDecl.

Should ClassVarDecl -> CXXClassVarDecl? Does it make sense to distinguish these from CXXField in the class hierarchy?

-I also moved the 'Decl' implementation to a separate 'DeclBase.cpp' file

Ok.

The instance fields of CXXRecord are stored in the members array of RecordDecl, thus the data layout of CXXRecord is calculated through the Record.
All the other members (including the static fields), are ScopedDecls with the CXXRecord as declaration context, so they can be iterated through a general DeclContext member iterator (not implemented yet).
Name lookup for class members will be efficient through the use of the IdentifierResolver.

Nice!

-Chris

Chris Lattner wrote:

-'ClassMember' serves as a base class for members of a CXXRecord. It provides the access specifier and the parent CXXRecord. Decls that inherit ClassMember:
  CXXField - for instance fields (inherits FieldDecl)
  CXXMethod - for static and instance methods (inherits FunctionDecl)
  NestedTypedef - for nested typedefs (inherits TypedefDecl)
  NestedRecordDecl - for nested struct/union/classes (inherits CXXRecordDecl)
  ClassVar - for static data members (inherits VarDecl)

I'm less enthused about using multiple inheritance for this :). Is there more common between members than just access specifiers? Would it be horrible to just give each of those classes their own AccessSpecifiers member?

It doesn't seem very useful to pass around generic ClassMember*'s. If we need a generic "give me the access specifiers for this member", it could be written as a top-level method on decl or something.

Alternatively, maybe AccessSpecifiers should be pushed up the class hierarchy, say to NamedDecl? Though it would not be used by every subclass, it would make things simpler that way. This would eliminate the need for classes like 'NestedTypedef' and 'NestedRecordDecl' which is strange. A typedef shouldn't be represented with a different class based on where it is defined.

All the extra classes were due to the "keep to a decl only the absolutely necessary" philosophy so that a typedef in C doesn't carry a redundant member, but I actually agree with you and would prefer that things were simpler.
I'd also suggest to *not* use a CXXRecord. The only difference from a Record is that CXXRecord can be used as a DeclContext, and it adds this kind of complexity:

-create CXXRecord
-parse the struct definition
-determine that Record is sufficient
-create new Record with new Fields
-Replace CXXRecord with new Record in scope

But this is not so bad as this:

-struct Foo; // should create a Record #1
-Foo *x; #2
-struct Foo { typedef int Bar; } // should create a CXXRecord #3

Now merging #1 with #3 is not so simple anymore. x's type is of pointer of #1 but Bar belongs to #3. If there was only a Record class, we would easily say that Bar belongs to #1.

To sum up, I suggest:

-Not using a separate CXXRecord
-Set Tag as DeclContext so that both Enum and Record can serve as DeclContext
-Add AccessSpecifier at TypeDecl; Typedef, Record, Enum can use it (I had forgotten about Enum)
-Add AccessSpecifier at CXXMethod, CXXClassVar. (Could be in ValueDecl but EnumConstant can get it from Enum and ParmVar don't use it)
-Add 'isCXXClassMember()' and 'getCXXClassParent()' to NamedDecl.

Some minor things: I don't think it makes sense for CXXMethod to derive from FunctionDecl, a CXXMethod doesn't have the "isa" property for FunctionDecl.

I'd regard a CXXMethod as a Function with an implicit 'this' parameter; everything from FunctionDecl can be reused.
What is the benefit of CXXMethodDecl as a different class from FunctionDecl ?

Should ClassVarDecl -> CXXClassVarDecl?

Ok.

Does it make sense to distinguish these from CXXField in the class hierarchy?

-The logic is that Field/CXXFields are instance vars that contribute to the data layout of the Record. Everything that refers to the layout need only refer to the members array of the Record without having to check whether a member from the array is static or not; all C struct layout code can work on a C++ struct with no modifications.
-A static field have no use for Field's BitWidth, and as far as codegen is concerned, a static field has more in common with a VarDecl than with a FieldDecl.

-Argiris

Argiris Kirtzidis wrote:

I'd also suggest to *not* use a CXXRecord. The only difference from a Record is that CXXRecord can be used as a DeclContext, and it adds this kind of complexity:

-create CXXRecord
-parse the struct definition
-determine that Record is sufficient
-create new Record with new Fields
-Replace CXXRecord with new Record in scope

I wasn't clear enough here; I didn't mean that there shouldn't be a check for simple structs and treating them differently, it's just that there is a minor complexity in Parser/Sema about starting with a CXXRecord but changing it to a Record later, but, as I mentioned later, the merging of Records is way more problematic.

There is going to be a parsing of struct definitions like this (in C++):

-parse the struct definition and create CXXFields
-if the struct/class contains only public CXXFields, replace them with Fields.

-Argiris

Argiris Kirtzidis wrote:

-struct Foo; // should create a Record #1
-Foo *x; #2
-struct Foo { typedef int Bar; } // should create a CXXRecord #3

Now merging #1 with #3 is not so simple anymore. x's type is of pointer of #1 but Bar belongs to #3. If there was only a Record class, we would easily say that Bar belongs to #1.

To sum up, I suggest:

-Not using a separate CXXRecord

Another idea is to use Record for C and CXXRecord for C++, but CXXRecord will be used everywhere, Records won't be mixed with CXXRecords.

-Argiris