What’s this about @import?

By: Jeremy W. Sherman. Published: . Categories: clang obj-c sourcedive.

So today in the company IRC channel my illustrious colleague Mark Dalrymple (of Advanced Mac OS X Programming fame) mentioned this new-fangled @import compiler directive. News of this compiler directive appears to be spreading through the Objective-C developer community mostly by way of Twitter-pigeon.

As it happened, I had not heard of @import. But then the inimitable Mikey Ward (alias: Wookiee) asked me about it. Two persons independently inquiring? Now I had to look into it.

It appears modules are filtering into Objective-C by way of C++, the same way Objective-C is rumored to be inheriting you-pick-the-base-type enums from C++TNG. Only this time the feature isn’t part of any standard.

I get this idea from an exchange on the cfe-dev mailing list in late December, which I have condensed into a single apocryphal message:

If you check recent (the last 6 months or so) commits to clang by Doug Gregor, you’ll find some work to implement C++ modules is already underway.

I’m not sure how much it’s based on any specific proposal.

To misquote Doug [Gregor] (can’t find the email, I think it might’ve been on IRC): “The semantics are obvious enough, so I’m implementing those. After that we can haggle over the syntax”

(In case it’s driving you crazy, “cfe” is short for “c/clang front-end”, which is all the clang tool you use from the commandline is: a driver for a whole mess of surprisingly unmessy library code.)

At this point, Doug was kind enough to chime in:

Most of the work I’m doing is in three places. The Serialization module, which takes care of serializing/deserializing an already-parsed AST, is the hardest part: it’s the infrastructure that allows one to compile a module on its own, storing the serialized AST to disk, and then load that module into another translation unit later on. This part is likely to be the same regardless of how modules behave. [Clang will produce and cache module AST files on the fly. Authors and build systems will remain ignorant of these AST files.]

The module map part of the Lex module handles the mapping between headers and modules. It’s mainly a transitional a little sub-language that allows one to describe the relationships between headers (which are used everywhere today) and modules.

The easy part is the parsing of module imports, labeling what is exported/hidden, and name-lookup semantics. It’s also the part that people will want to discuss endlessly, so for now the various keywords are uglified so that we don’t commit to any one syntax.

Once you chase on down through the code, you find yourself at the abstract syntax tree level staring at the ImportDecl class. What does it do? Well,

[it] describes a module import declaration, which makes the contents of the named module visible in the current translation unit. An import declaration imports the named module (or submodule). For example: @import std.vector; Import declarations can also be implicitly generated from #include/#import directives.

That’s right: #include/#import are going to become legacy syntax for this nifty new modules system. Then, instead of playing “find the header that includes the symbol you want to use”, you will be able to basically just import the functionality directly.

The actual mapping from module name to file is handled by the ModuleLoader. Judging by the tests, there’s going to be a way to explicitly manage this mapping using module stanzas in a ModuleMap:

module category_left {
  header "category_right.h"
  export category_top
}

You also get another way to manage symbol visibility via export directives in the module stanzas, as you can see there.

Possibly the awesomest future visibility control is that over those pesky preprocessor macros. That’s right: the proposed syntax is

#define MODULE_H_MACRO 1
#define MODULE_H_PUBMACRO 2
#__private_macro MODULE_H_MACRO
#__public_macro MODULE_H_PUBMACRO

The private/public macro preprocessor directives update the visibility of the named macro. If you have multiple macros, you have to issue multiple directives – there’s no support for privatizing multiple macros something like:

#__private_macro XYZZY PLUGH PLOVER  /* <-- THIS DOES NOT WORK */

I haven’t the faintest notion when we’ll see this live and slaving away under Xcode, but I am looking forward to the coming sleeker, faster import process.


References galore, from top to bottom: