We were reconsidering the design of C++ AST nodes
ElaboratedType
QualifiedNameType
DependentNameType
In our opinion, the design of class DependentNameType, bringing
together the elaborated keyword and the name qualification, is actually
good. In contrast, the distinction between ElaboratedType and
QualifiedNameType seems somewhat artificial.
Would it be acceptable to collapse these two nodes in a single one,
bringing together the corresponding info, similarly to DependentNameType?
Hmm. This muddies the waters a little — we wouldn't have a single type representing an tagged type name — but it's probably the right thing to do. The new node would need to represent types written with 'typename' as well as types written without a keyword at all, which is to say, the new node would need to use the ElaboratedTypeKeyword enum. But this would be great and would magically improve source fidelity anyway.
Done naively, the change will increase the memory requirements of unqualified elaborated types by a pointer. This wouldn't be a big deal, since we only create ElaboratedTypes in C++, where they're fairly rare, and the uniquing process would likely hold down the overhead.
Done naively, the change will increase the memory requirements of qualified types by a word. This is a somewhat larger problem because QualifiedNameTypes are quite common in C++.
Fortunately, we don't have to do this naively.
There are spare bits in Type that are currently going unexploited because the second base class of ElaboratedType inhibits the relevant layout optimization in the standard ABI. We can take advantage of these bits by introducing an intermediate super class like so:
class TypeWithKeyword : public Type {
unsigned Keyword : 3;
virtual ~TypeWithKeyword(); // pin vtable to Type.cpp
// ...
ElaboratedTypeKeyword getKeyword() const {
return static_cast<ElaboratedTypeKeyword>(Keyword);
}
class CannotCastToThisType {};
static CannotCastToThisType classof(const Type *);
};
class ElaboratedType : public TypeWithKeyword, public llvm::FoldingSetNode {
// ...
};
This would make this refactor neutral in terms of its storage impact. In fact, we can use the same TypeWithKeyword superclass for DependentNameType and thus slash a word of storage from them, too.
After that is put straight, we would like to consider the addition of
appropriate typeloc info to the corresponding classes
ElaboartedTypeLoc
QualifiedNameTypeLoc
DependentNameTypeLoc
This would be great, thanks!
Again, we conjecture that collapsing Elaborated and QualifiedName would
allow for a cleaner design. However, in this case we have another doubt.
An elaborated typeloc should contain syntactic info for the underlying
tag type node. Should we add a TypeSourceInfo* to the corresponding
LocInfo class (as done for TypeOfTypeLoc), or should we have the
ElaboratedTypeLoc have an _inner_ typeloc (as done for pointer and array
typelocs)?
It should be an inner type loc. You might have to teach TypeLoc::getFullSourceRange() that inner type locs aren't always written to the left of the outer type loc.
John.