So you want to be a
cOOmpiler writer? - part V

by Sean A. Corfield


In part IV I looked at the type system and said I would examine some of the implications of converting the inheritance hierarchy to use mixins for templates. First of all, I'm going to take a brief diversion to look at what can be considered good and bad in abstract base classes.

The ABC of ABCs

When I first started designing the analyser, I didn't have much experience with OOD although I had spent about a decade designing and building compilers, interpreters, optimisers and so on. Many of the base classes within my original type hierarchy were concrete classes - I was deriving StructType from ClassType to start with! During the maintenance cycle, extra classes were added into the hierarchy and base classes were generally made into abstract classes.

One of the ongoing problems this caused - indicative of how heavily poor design is punished in OOP - was that base class constructors tended to have quite a few arguments. In particular, NamedScope (see part IV) ended up with four or five constructor arguments. Since there were several layers of classes below that, all those arguments had to be supplied to the most derived class constructor and then passed back up the inheritance chain in the mem-initialisers. Sometimes this will be unavoidable but quite often it is simply due to having inappropriate state information in an abstract base class.

If a base class is truly abstract then there will be no member data - state - within it. Does this sound extreme? Well, consider what member data actually means: it implements state. That means that an implementation decision has to be made as to how to represent that state. Sometimes the representation is easily determined by the operations on the base class (e.g., void set(int); int get() const;) but mostly the protocol represented by an ABC is more complex and does not directly suggest an implementation. Such state information can be provided by an implementation class which is either referenced from the base class (using a pointer or reference) or derived from the base class (forming one side of a mixin diamond).

A stateless base class will usually only need a default constructor. This brings notational convenience because the constructor call can be omitted from mem-initialisers. Is he suggesting implicit initialisations? Yes, for this particular case. Let me explain why...

Virtual base classes

A virtual base class must be initialised by every class derived from it - or, more accurately, its constructor is called from the mem-initialiser list of the most-derived class (i.e., effectively it must appear in every derived class's mem-initialiser list). If the virtual base class constructor requires arguments, the constructor call must be made explicit with all the arguments supplied within every derived class's mem-initialiser list.

If virtual base classes have only default constructors, then you cannot forget to initialise them because the compiler default behaviour does the right thing!

Some guidelines

  1. abstract classes should be stateless
  2. virtual base classes should have default constructors (only)
  3. don't explicitly initialise virtual base classes

I think I can justify those based on the observations made above. I'd like to go further but what follows is slightly harder to back up with hard experience. As a corollary to (1), I think it follows that stateless classes should have default constructors (only). Although it doesn't follow logically, I'd argue that virtual base classes should also be abstract classes and many C++ "experts" agree.

Back to my problems

That's hindsight, however, and would have been useful in easing the transition from a non-virtual inheritance hierarchy to a mixin-based hierarchy. The first step was to change inheritance from the base class Type, which was abstract, to be virtual. That was easy. However, during the design of the template argument deduction mechanism, it was decided to abstract out some aspects of the various class types' matching algorithms (mainly so that A<T> could be deduced regardless of whether A was a class, struct or union). This introduced another mixin diamond - see figure 1. This meant more virtual inheritance (from AbsClass and TemplateType) and that's where the problems really started!

At the bottom of the hierarchy, the classes representing instantiated template classes, structs and unions have three direct base classes, one of which is virtual, and two indirect virtual base classes. The first problem was simply changing all the relevant mem-initialiser lists and discovering that, because some of the virtual base classes had state, not all the necessary data was available in the most-derived constructor. Having sorted that out, the next problem was harder to solve: a compiler bug! The combination of virtual inheritance and multiple base classes proved too much for Sun's SPARCcompiler and it generated code that crashed during execution of the mem-initialisers. I tried the code on several other compilers which generated correct code.

I could either back out the changes and redesign things or change compilers. I chose the latter and rebuilt everything with the GNU compiler, g++ 2.6.3, which uncovered a new compiler bug!


Given a combination of virtual and non-virtual inheritance, g++ seemed to lose accessibility of protected base class members. The obvious solution was to make the members public but this bothered me so I experimented further with g++. I discovered that if all the inheritance was virtual the problem went away. Given that some people advocate public virtual inheritance as the true expression of the "is-a" relationship, this seemed a reasonable approach.

Of course, changing several inheritance relationships to use virtual meant more classes to be initialised by the most-derived classes - classes that had non-default constructors unfortunately.

Next time

Having battled with some basic aspects of the type system, I shall turn my attention next to representing statements and parse trees.

Sean A. Corfield
Object Consultancy Services