| Document | D1703R0 |
|---|---|
| Audience | EWG |
| Authors | Boris Kolpackov (Code Synthesis) |
| Reply-To | boris@codesynthesis.com |
| Date | 2019-06-14 |
Abstract
Currently, recognizing header unit imports requires full preprocessing
which is problematic for dependency scanning and partial preprocessing. This
paper proposes changes that will allow handling such imports with the same
degree of preprocessing as #include directives.
Contents
1 Before/After Tables ("Tony Tables")
1.1 Affected Use Cases
| before | after |
|---|---|
int x; import <map>; int y; | int x; import <map>; int y; |
| before | after |
|---|---|
import <map>; import <set>; | import <map>; import <set>; |
| before | after |
|---|---|
export import <map>; | export import <map>; |
| before | after |
|---|---|
#ifdef MAYBE_EXPORT export #endif import <map>; | #ifdef MAYBE_EXPORT export import <map>; #else import <map>; #endif |
| before | after |
|---|---|
#define MAYBE_EXPORT export MAYBE_EXPORT import <map>; | #define MAYBE_EXPORT #ifdef MAYBE_EXPORT export import <map>; #else import <map>; #endif |
1.2 Unaffected Use Cases
Header unit names are still macro-expanded (similar to
#include).
#define MYMODULE <map> import MYMODULE;
Normal module imports are unaffected:
import std.set; using int_set = std::set<int>;
1.3 Unsupported Use Cases
With the proposed change the following will no longer be possible:
#define MYIMPORT(x) import x MYIMPORT(<set>);
Note also that the following is already impossible (because neither
#include nor import's closing ; can
be the result of macro replacement):
#define IMPORT_OR_INCLUDE(x) ??? IMPORT_OR_INCLUDE(<set>)
2 Background
With the current wording, recognizing a header unit import
declaration requires performing macros replacement and tokenization of every
line in a translation unit. As a representative example, consider the
following line:
MYDECL import <int>;
Whether this is a header unit importation or something else depends on
what MYDECL expands to. Compare:
#define MYDECL int x; MYDECL import <int>;
And:
template <typename> class import; #define MYDECL using x = MYDECL import <int>;
While the second example is contrived, it is valid (again, according to
the current wording) because import is a context-sensitive
keyword.
Requiring such full macro replacement is at a minimum wasteful for header dependency scanning but also may not be something that tools other than compilers can easily and correctly do.
Additionally, several implementations provide support for partial
preprocessing (GCC's -fdirectives-only and Clang's
-frewrite-includes) and this requirement is in conflict with
the essence of that functionality.
More specifically, GCC is currently unable to support header unit imports
in its -M (dependency scanning) and
-fdirectives-only (partial preprocessing) modes because in
these modes it does not perform macro replacement in non-directive
lines.
While Clang currently performs full preprocessing in its -M
and -frewrite-includes modes, there is agreement that it's not
ideal for it to be impossible to correctly extract dependencies without full
preprocessing.
Finally, consulting with the developers of clang-scan-deps
(a Clang-based tool for fast dependency extraction) revealed that this
requirement would be problematic for their implementation.
3 Proposal
We propose to further restrict header unit import declarations so that
they can be recognized and handled with the same degree of preprocessing as
#include directives.
Specifically, we propose recognizing a declaration as a header unit import if, additionally to restrictions in [cpp.module.1]:
- It starts with the
importtoken orexport importtoken sequence that have not been produced by macro replacement. - Followed, after macro replacement, by header-name-tokens.
- The entire, single declaration is on one line.
We believe this should not detract much from usability because header
imports are replacing #include directives where we have the
same restrictions.
4 Questions and Answers
4.1 Who will be in Cologne to present this paper?
Boris Kolpackov
4.2 Is there implementation experience?
Yes, an implementation is available in the boris/c++-modules-ex
GCC branch. This includes working -fdirectives-only mode.
One encouraging result of implementing the proposed change was the
relative ease of generalizing the #include directive handling
code in the GCC preprocessor (libcpp) and module mapper to also
handle header unit imports.
4.3 Is there usage experience?
Yes, the build2 build
system implements support for header unit importation relying on this
functionality.
4.4 What shipping vehicle do you target with this proposal?
The same as C++ Modules, presumably C++20.
5 Acknowledgments
To our knowledge this issue was first discovered and documented (in the GCC manual) by Nathan Sidwell.
Thanks to Nathan Sidwell, Richard Smith, Gabriel Dos Reis, Alex Lorenz, Michael Spencer, and Ben Boeckel for discussions regarding this issue and for feedback on earlier drafts of this paper.