The C++20 Modules Chicken-and-Egg Problem

A new version of C++ can introduce dozens of new language features. But publication of a new language standard does not mean that every feature is immediately ready to be used with confidence.

There is a complicated three-way dependency between specifying a new language feature, implementing that feature in a compiler, and verifying that the implementation is correct.

It is a bit of a chicken-and-egg problem.

Before a new feature is fully defined, we want to know how complex it will be to implement and what it feels like to use, both of which require an implementation in a compiler. Developing the implementation requires tests, and writing comprehensive tests requires both a sufficiently mature specification and an implementation against which those tests can be developed.

In practice, this circularity is broken by stepwise refinement. The specification develops, compiler implementations mature, testing improves, and the cycle repeats. For relatively simple language features, this can happen quite quickly. For more complicated features, it can take much, much longer.

The modules addition to C++20 is a good example of a more complicated feature. Modules were introduced as a new way of organizing and encapsulating code. They are also complex to implement. Until quite recently, no mainstream compiler had sufficiently mature support for modules for us to create the comprehensive test suite we wanted. Part of the reason is that modules brought together ideas that had already been implemented differently in MSVC, Clang and eventually GCC. Existing implementations therefore had to be adapted to the standardized approach.

There is a Dutch expression for this: the law of the handicap of a head start (it sounds cooler in Dutch: de wet van de remmende voorsprong). Sometimes having an existing implementation makes adapting to a new standard harder than starting from scratch.

Compiler support has now matured to the point where we have been able to complete our coverage of the C++20 language in SuperTest Van Gogh 6.0, including 173 tests specifically for modules.

And the results are interesting. Of those 173 tests, each of the three mainstream compiler platforms we tested – GCC, Clang and MSVC – currently fails around 20%.

Is that a bad thing? Not at all.

Rome was not built in a day. New language features need time to mature. Comprehensive testing is part of that process. Testing helps compiler developers identify where implementations still need refinement. At the same time, it helps compiler users to understand which features they can reliably use and where portability between platforms may still be an issue.

The chicken-and-egg problem eventually gets solved. It just takes a few iterations.