mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-28 10:16:06 +00:00

This reverts commit 82dc2d403066a84ef0051b06f1d179e00331f319. The fix has been reverted in f63e8ed16ef1fd2deb80cd88b5ca9d5b631b1c36
2075 lines
69 KiB
ReStructuredText
2075 lines
69 KiB
ReStructuredText
====================
|
|
Standard C++ Modules
|
|
====================
|
|
|
|
.. contents::
|
|
:local:
|
|
|
|
Introduction
|
|
============
|
|
|
|
The term ``module`` is ambiguous, as it is used to mean multiple things in
|
|
Clang. For Clang users, a module may refer to an ``Objective-C Module``,
|
|
`Clang Module <Modules.html>`_ (also called a ``Clang Header Module``) or a
|
|
``C++20 Module`` (or a ``Standard C++ Module``). The implementation of all
|
|
these kinds of modules in Clang shares a lot of code, but from the perspective
|
|
of users their semantics and command line interfaces are very different. This
|
|
document is an introduction to the use of C++20 modules in Clang. In the
|
|
remainder of this document, the term ``module`` will refer to Standard C++20
|
|
modules and the term ``Clang module`` will refer to the Clang Modules
|
|
extension.
|
|
|
|
In terms of the C++ Standard, modules consist of two components: "Named
|
|
Modules" or "Header Units". This document covers both.
|
|
|
|
Standard C++ Named modules
|
|
==========================
|
|
|
|
In order to better understand the compiler's behavior, it is helpful to
|
|
understand some terms and definitions for readers who are not familiar with the
|
|
C++ feature. This document is not a tutorial on C++; it only introduces
|
|
necessary concepts to better understand use of modules in a project.
|
|
|
|
Background and terminology
|
|
--------------------------
|
|
|
|
Module and module unit
|
|
~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
A module consists of one or more module units. A module unit is a special kind
|
|
of translation unit. A module unit should almost always start with a module
|
|
declaration. The syntax of the module declaration is:
|
|
|
|
.. code-block:: c++
|
|
|
|
[export] module module_name[:partition_name];
|
|
|
|
Terms enclosed in ``[]`` are optional. ``module_name`` and ``partition_name``
|
|
follow the rules for a C++ identifier, except that they may contain one or more
|
|
period (``.``) characters. Note that a ``.`` in the name has no semantic
|
|
meaning and does not imply any hierarchy.
|
|
|
|
In this document, module units are classified as:
|
|
|
|
* Primary module interface unit
|
|
* Module implementation unit
|
|
* Module partition interface unit
|
|
* Internal module partition unit
|
|
|
|
A primary module interface unit is a module unit whose module declaration is
|
|
``export module module_name;`` where ``module_name`` denotes the name of the
|
|
module. A module should have one and only one primary module interface unit.
|
|
|
|
A module implementation unit is a module unit whose module declaration is
|
|
``module module_name;``. Multiple module implementation units can be declared
|
|
in the same module.
|
|
|
|
A module partition interface unit is a module unit whose module declaration is
|
|
``export module module_name:partition_name;``. The ``partition_name`` should be
|
|
unique within any given module.
|
|
|
|
An internal module partition unit is a module unit whose module
|
|
declaration is ``module module_name:partition_name;``. The ``partition_name``
|
|
should be unique within any given module.
|
|
|
|
In this document, we use the following terms:
|
|
|
|
* A ``module interface unit`` refers to either a ``primary module interface unit``
|
|
or a ``module partition interface unit``.
|
|
|
|
* An ``importable module unit`` refers to either a ``module interface unit`` or
|
|
an ``internal module partition unit``.
|
|
|
|
* A ``module partition unit`` refers to either a ``module partition interface unit``
|
|
or an ``internal module partition unit``.
|
|
|
|
Built Module Interface
|
|
~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
A ``Built Module Interface`` (or ``BMI``) is the precompiled result of an
|
|
importable module unit.
|
|
|
|
Global module fragment
|
|
~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The ``global module fragment`` (or ``GMF``) is the code between the ``module;``
|
|
and the module declaration within a module unit.
|
|
|
|
|
|
How to build projects using modules
|
|
-----------------------------------
|
|
|
|
Quick Start
|
|
~~~~~~~~~~~
|
|
|
|
Let's see a "hello world" example that uses modules.
|
|
|
|
.. code-block:: c++
|
|
|
|
// Hello.cppm
|
|
module;
|
|
#include <iostream>
|
|
export module Hello;
|
|
export void hello() {
|
|
std::cout << "Hello World!\n";
|
|
}
|
|
|
|
// use.cpp
|
|
import Hello;
|
|
int main() {
|
|
hello();
|
|
return 0;
|
|
}
|
|
|
|
Then, on the command line, invoke Clang like:
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang++ -std=c++20 Hello.cppm --precompile -o Hello.pcm
|
|
$ clang++ -std=c++20 use.cpp -fmodule-file=Hello=Hello.pcm Hello.pcm -o Hello.out
|
|
$ ./Hello.out
|
|
Hello World!
|
|
|
|
In this example, we make and use a simple module ``Hello`` which contains only a
|
|
primary module interface unit named ``Hello.cppm``.
|
|
|
|
A more complex "hello world" example which uses the 4 kinds of module units is:
|
|
|
|
.. code-block:: c++
|
|
|
|
// M.cppm
|
|
export module M;
|
|
export import :interface_part;
|
|
import :impl_part;
|
|
export void Hello();
|
|
|
|
// interface_part.cppm
|
|
export module M:interface_part;
|
|
export void World();
|
|
|
|
// impl_part.cppm
|
|
module;
|
|
#include <iostream>
|
|
#include <string>
|
|
module M:impl_part;
|
|
import :interface_part;
|
|
|
|
std::string W = "World.";
|
|
void World() {
|
|
std::cout << W << std::endl;
|
|
}
|
|
|
|
// Impl.cpp
|
|
module;
|
|
#include <iostream>
|
|
module M;
|
|
void Hello() {
|
|
std::cout << "Hello ";
|
|
}
|
|
|
|
// User.cpp
|
|
import M;
|
|
int main() {
|
|
Hello();
|
|
World();
|
|
return 0;
|
|
}
|
|
|
|
Then, back on the command line, invoke Clang with:
|
|
|
|
.. code-block:: console
|
|
|
|
# Precompiling the module
|
|
$ clang++ -std=c++20 interface_part.cppm --precompile -o M-interface_part.pcm
|
|
$ clang++ -std=c++20 impl_part.cppm --precompile -fprebuilt-module-path=. -o M-impl_part.pcm
|
|
$ clang++ -std=c++20 M.cppm --precompile -fprebuilt-module-path=. -o M.pcm
|
|
$ clang++ -std=c++20 Impl.cpp -fprebuilt-module-path=. -c -o Impl.o
|
|
|
|
# Compiling the user
|
|
$ clang++ -std=c++20 User.cpp -fprebuilt-module-path=. -c -o User.o
|
|
|
|
# Compiling the module and linking it together
|
|
$ clang++ -std=c++20 M-interface_part.pcm -fprebuilt-module-path=. -c -o M-interface_part.o
|
|
$ clang++ -std=c++20 M-impl_part.pcm -fprebuilt-module-path=. -c -o M-impl_part.o
|
|
$ clang++ -std=c++20 M.pcm -fprebuilt-module-path=. -c -o M.o
|
|
$ clang++ User.o M-interface_part.o M-impl_part.o M.o Impl.o -o a.out
|
|
|
|
We explain the options in the following sections.
|
|
|
|
How to enable standard C++ modules
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Standard C++ modules are enabled automatically when the language standard mode
|
|
is ``-std=c++20`` or newer.
|
|
|
|
How to produce a BMI
|
|
~~~~~~~~~~~~~~~~~~~~
|
|
|
|
To generate a BMI for an importable module unit, use either the ``--precompile``
|
|
or ``-fmodule-output`` command line options.
|
|
|
|
The ``--precompile`` option generates the BMI as the output of the compilation
|
|
with the output path specified using the ``-o`` option.
|
|
|
|
The ``-fmodule-output`` option generates the BMI as a by-product of the
|
|
compilation. If ``-fmodule-output=`` is specified, the BMI will be emitted to
|
|
the specified location. If ``-fmodule-output`` and ``-c`` are specified, the
|
|
BMI will be emitted in the directory of the output file with the name of the
|
|
input file with the extension ``.pcm``. Otherwise, the BMI will be emitted in
|
|
the working directory with the name of the input file with the extension
|
|
``.pcm``.
|
|
|
|
Generating BMIs with ``--precompile`` is referred to as two-phase compilation
|
|
because it takes two steps to compile a source file to an object file.
|
|
Generating BMIs with ``-fmodule-output`` is called one-phase compilation. The
|
|
one-phase compilation model is simpler for build systems to implement while the
|
|
two-phase compilation has the potential to compile faster due to higher
|
|
parallelism. As an example, if there are two module units ``A`` and ``B``, and
|
|
``B`` depends on ``A``, the one-phase compilation model needs to compile them
|
|
serially, whereas the two-phase compilation model is able to be compiled as
|
|
soon as ``A.pcm`` is available, and thus can be compiled simultaneously as the
|
|
``A.pcm`` to ``A.o`` compilation step.
|
|
|
|
File name requirements
|
|
~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
By convention, ``importable module unit`` files should use ``.cppm`` (or
|
|
``.ccm``, ``.cxxm``, or ``.c++m``) as a file extension.
|
|
``Module implementation unit`` files should use ``.cpp`` (or ``.cc``, ``.cxx``,
|
|
or ``.c++``) as a file extension.
|
|
|
|
A BMI should use ``.pcm`` as a file extension. The file name of the BMI for a
|
|
``primary module interface unit`` should be ``module_name.pcm``. The file name
|
|
of a BMI for a ``module partition unit`` should be
|
|
``module_name-partition_name.pcm``.
|
|
|
|
Clang may fail to build the module if different extensions are used. For
|
|
example, if the filename of an ``importable module unit`` ends with ``.cpp``
|
|
instead of ``.cppm``, then Clang cannot generate a BMI for the
|
|
``importable module unit`` with the ``--precompile`` option because the
|
|
``--precompile`` option would only run the preprocessor (``-E``). If using a
|
|
different extension than the conventional one for an ``importable module unit``
|
|
you can specify ``-x c++-module`` before the file. For example,
|
|
|
|
.. code-block:: c++
|
|
|
|
// Hello.cpp
|
|
module;
|
|
#include <iostream>
|
|
export module Hello;
|
|
export void hello() {
|
|
std::cout << "Hello World!\n";
|
|
}
|
|
|
|
// use.cpp
|
|
import Hello;
|
|
int main() {
|
|
hello();
|
|
return 0;
|
|
}
|
|
|
|
In this example, the extension used by the ``module interface`` is ``.cpp``
|
|
instead of ``.cppm``, so it cannot be compiled like the previous example, but
|
|
it can be compiled with:
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang++ -std=c++20 -x c++-module Hello.cpp --precompile -o Hello.pcm
|
|
$ clang++ -std=c++20 use.cpp -fprebuilt-module-path=. Hello.pcm -o Hello.out
|
|
$ ./Hello.out
|
|
Hello World!
|
|
|
|
Module name requirements
|
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
..
|
|
|
|
[module.unit]p1:
|
|
|
|
All module-names either beginning with an identifier consisting of std followed by zero
|
|
or more digits or containing a reserved identifier ([lex.name]) are reserved and shall not
|
|
be specified in a module-declaration; no diagnostic is required. If any identifier in a reserved
|
|
module-name is a reserved identifier, the module name is reserved for use by C++ implementations;
|
|
otherwise it is reserved for future standardization.
|
|
|
|
Therefore, none of the following names are valid by default:
|
|
|
|
.. code-block:: text
|
|
|
|
std
|
|
std1
|
|
std.foo
|
|
__test
|
|
// and so on ...
|
|
|
|
Using a reserved module name is strongly discouraged, but
|
|
``-Wno-reserved-module-identifier`` can be used to suppress the warning.
|
|
|
|
Specifying dependent BMIs
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
There are 3 ways to specify a dependent BMI:
|
|
|
|
1. ``-fprebuilt-module-path=<path/to/directory>``.
|
|
2. ``-fmodule-file=<path/to/BMI>`` (Deprecated).
|
|
3. ``-fmodule-file=<module-name>=<path/to/BMI>``.
|
|
|
|
The ``-fprebuilt-module-path`` option specifies the path to search for
|
|
dependent BMIs. Multiple paths may be specified, similar to using ``-I`` to
|
|
specify a search path for header files. When importing a module ``M``, the
|
|
compiler looks for ``M.pcm`` in the directories specified by
|
|
``-fprebuilt-module-path``. Similarly, when importing a partition module unit
|
|
``M:P``, the compiler looks for ``M-P.pcm`` in the directories specified by
|
|
``-fprebuilt-module-path``.
|
|
|
|
The ``-fmodule-file=<path/to/BMI>`` option causes the compiler to load the
|
|
specified BMI directly. The ``-fmodule-file=<module-name>=<path/to/BMI>``
|
|
option causes the compiler to load the specified BMI for the module specified
|
|
by ``<module-name>`` when necessary. The main difference is that
|
|
``-fmodule-file=<path/to/BMI>`` will load the BMI eagerly, whereas
|
|
``-fmodule-file=<module-name>=<path/to/BMI>`` will only load the BMI lazily,
|
|
as will ``-fprebuilt-module-path``. The ``-fmodule-file=<path/to/BMI>`` option
|
|
for named modules is deprecated and will be removed in a future version of
|
|
Clang.
|
|
|
|
When these options are specified in the same invocation of the compiler, the
|
|
``-fmodule-file=<path/to/BMI>`` option takes precedence over
|
|
``-fmodule-file=<module-name>=<path/to/BMI>``, which takes precedence over
|
|
``-fprebuilt-module-path=<path/to/directory>``.
|
|
|
|
Note: all dependant BMIs must be specified explicitly, either directly or
|
|
indirectly dependent BMIs explicitly. See
|
|
https://github.com/llvm/llvm-project/issues/62707 for details.
|
|
|
|
When compiling a ``module implementation unit``, the BMI of the corresponding
|
|
``primary module interface unit`` must be specified because a module
|
|
implementation unit implicitly imports the primary module interface unit.
|
|
|
|
[module.unit]p8
|
|
|
|
A module-declaration that contains neither an export-keyword nor a module-partition implicitly
|
|
imports the primary module interface unit of the module as if by a module-import-declaration.
|
|
|
|
The ``-fprebuilt-module-path=<path/to/directory>``, ``-fmodule-file=<path/to/BMI>``,
|
|
and ``-fmodule-file=<module-name>=<path/to/BMI>`` options may be specified
|
|
multiple times. For example, the command line to compile ``M.cppm`` in
|
|
the previous example could be rewritten as:
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang++ -std=c++20 M.cppm --precompile -fmodule-file=M:interface_part=M-interface_part.pcm -fmodule-file=M:impl_part=M-impl_part.pcm -o M.pcm
|
|
|
|
When there are multiple ``-fmodule-file=<module-name>=`` options for the same
|
|
``<module-name>``, the last ``-fmodule-file=<module-name>=`` overrides the
|
|
previous ``-fmodule-file=<module-name>=`` option.
|
|
|
|
Remember that module units still have an object counterpart to the BMI
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
While module interfaces resemble traditional header files, they still require
|
|
compilation. Module units are translation units, and need to be compiled to
|
|
object files, which then need to be linked together as the following examples
|
|
show.
|
|
|
|
For example, the traditional compilation processes for headers are like:
|
|
|
|
.. code-block:: text
|
|
|
|
src1.cpp -+> clang++ src1.cpp --> src1.o ---,
|
|
hdr1.h --' +-> clang++ src1.o src2.o -> executable
|
|
hdr2.h --, |
|
|
src2.cpp -+> clang++ src2.cpp --> src2.o ---'
|
|
|
|
And the compilation process for module units are like:
|
|
|
|
.. code-block:: text
|
|
|
|
src1.cpp ----------------------------------------+> clang++ src1.cpp -------> src1.o -,
|
|
(header unit) hdr1.h -> clang++ hdr1.h ... -> hdr1.pcm --' +-> clang++ src1.o mod1.o src2.o -> executable
|
|
mod1.cppm -> clang++ mod1.cppm ... -> mod1.pcm --,--> clang++ mod1.pcm ... -> mod1.o -+
|
|
src2.cpp ----------------------------------------+> clang++ src2.cpp -------> src2.o -'
|
|
|
|
As the diagrams show, we need to compile the BMI from module units to object
|
|
files and then link the object files. (However, this cannot be done for the BMI
|
|
from header units. See the section on :ref:`header units <header-units>` for
|
|
more details.
|
|
|
|
BMIs cannot be shipped in an archive to create a module library. Instead, the
|
|
BMIs(``*.pcm``) are compiled into object files(``*.o``) and those object files
|
|
are added to the archive instead.
|
|
|
|
clang-cl
|
|
~~~~~~~~
|
|
|
|
``clang-cl`` supports the same options as ``clang++`` for modules as detailed above;
|
|
there is no need to prefix these options with ``/clang:``. Note that ``cl.exe``
|
|
`options to emit/consume IFC files <https://devblogs.microsoft.com/cppblog/using-cpp-modules-in-msvc-from-the-command-line-part-1/>` are *not* supported.
|
|
The resultant precompiled modules are also not compatible for use with ``cl.exe``.
|
|
|
|
We recommend that build system authors use the above-mentioned ``clang++`` options with ``clang-cl`` to build modules.
|
|
|
|
Consistency Requirements
|
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Modules can be viewed as a kind of cache to speed up compilation. Thus, like
|
|
other caching techniques, it is important to maintain cache consistency which
|
|
is why Clang does very strict checking for consistency.
|
|
|
|
Options consistency
|
|
^^^^^^^^^^^^^^^^^^^
|
|
|
|
Compiler options related to the language dialect for a module unit and its
|
|
non-module-unit uses need to be consistent. Consider the following example:
|
|
|
|
.. code-block:: c++
|
|
|
|
// M.cppm
|
|
export module M;
|
|
|
|
// Use.cpp
|
|
import M;
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang++ -std=c++20 M.cppm --precompile -o M.pcm
|
|
$ clang++ -std=c++23 Use.cpp -fprebuilt-module-path=.
|
|
|
|
Clang rejects the example due to the inconsistent language standard modes. Not
|
|
all compiler options are language dialect options, though. For example:
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang++ -std=c++20 M.cppm --precompile -o M.pcm
|
|
# Inconsistent optimization level.
|
|
$ clang++ -std=c++20 -O3 Use.cpp -fprebuilt-module-path=.
|
|
# Inconsistent debugging level.
|
|
$ clang++ -std=c++20 -g Use.cpp -fprebuilt-module-path=.
|
|
|
|
Although the optimization and debugging levels are inconsistent, these
|
|
compilations are accepted because the compiler options do not impact the
|
|
language dialect.
|
|
|
|
Note that the compiler **currently** doesn't reject inconsistent macro
|
|
definitions (this may change in the future). For example:
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang++ -std=c++20 M.cppm --precompile -o M.pcm
|
|
# Inconsistent optimization level.
|
|
$ clang++ -std=c++20 -O3 -DNDEBUG Use.cpp -fprebuilt-module-path=.
|
|
|
|
Currently, Clang accepts the above example, though it may produce surprising
|
|
results if the debugging code depends on consistent use of ``NDEBUG`` in other
|
|
translation units.
|
|
|
|
Source Files Consistency
|
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Clang may open the input files\ :sup:`1`` of a BMI during the compilation. This implies that
|
|
when Clang consumes a BMI, all the input files need to be present in the original path
|
|
and with the original contents.
|
|
|
|
To overcome these requirements and simplify cases like distributed builds and sandboxed
|
|
builds, users can use the ``-fmodules-embed-all-files`` flag to embed all input files
|
|
into the BMI so that Clang does not need to open the corresponding file on disk.
|
|
|
|
When the ``-fmodules-embed-all-files`` flag are enabled, Clang explicitly emits the source
|
|
code into the BMI file, the contents of the BMI file contain a sufficiently verbose
|
|
representation to reproduce the original source file.
|
|
|
|
:sup:`1`` Input files: The source files which took part in the compilation of the BMI.
|
|
For example:
|
|
|
|
.. code-block:: c++
|
|
|
|
// M.cppm
|
|
module;
|
|
#include "foo.h"
|
|
export module M;
|
|
|
|
// foo.h
|
|
#pragma once
|
|
#include "bar.h"
|
|
|
|
The ``M.cppm``, ``foo.h`` and ``bar.h`` are input files for the BMI of ``M.cppm``.
|
|
|
|
Object definition consistency
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
The C++ language requires that declarations of the same entity in different
|
|
translation units have the same definition, which is known as the One
|
|
Definition Rule (ODR). Without modules, the compiler cannot perform strong ODR
|
|
violation checking because it only sees one translation unit at a time. With
|
|
the use of modules, the compiler can perform checks for ODR violations across
|
|
translation units.
|
|
|
|
However, the current ODR checking mechanisms are not perfect. There are a
|
|
significant number of false positive ODR violation diagnostics, where the
|
|
compiler incorrectly diagnoses two identical declarations as having different
|
|
definitions. Further, true positive ODR violations are not always reported.
|
|
|
|
To give a better user experience, improve compilation performance, and for
|
|
consistency with MSVC, ODR checking of declarations in the global module
|
|
fragment is disabled by default. These checks can be enabled by specifying
|
|
``-Xclang -fno-skip-odr-check-in-gmf`` when compiling. If the check is enabled
|
|
and you encounter incorrect or missing diagnostics, please report them via the
|
|
`community issue tracker <https://github.com/llvm/llvm-project/issues/>`_.
|
|
|
|
Privacy Issue
|
|
-------------
|
|
|
|
BMIs are not and should not be treated as an information hiding mechanism.
|
|
They should always be assumed to contain all the information that was used to
|
|
create them, in a recoverable form.
|
|
|
|
ABI Impacts
|
|
-----------
|
|
|
|
This section describes the new ABI changes brought by modules. Only changes to
|
|
the Itanium C++ ABI are covered.
|
|
|
|
Name Mangling
|
|
~~~~~~~~~~~~~
|
|
|
|
The declarations in a module unit which are not in the global module fragment
|
|
have new linkage names.
|
|
|
|
For example,
|
|
|
|
.. code-block:: c++
|
|
|
|
export module M;
|
|
namespace NS {
|
|
export int foo();
|
|
}
|
|
|
|
The linkage name of ``NS::foo()`` is ``_ZN2NSW1M3fooEv``. This couldn't be
|
|
demangled by previous versions of the debugger or demangler. As of LLVM 15.x,
|
|
``llvm-cxxfilt`` can be used to demangle this:
|
|
|
|
.. code-block:: console
|
|
|
|
$ llvm-cxxfilt _ZN2NSW1M3fooEv
|
|
NS::foo@M()
|
|
|
|
The result should be read as ``NS::foo()`` in module ``M``.
|
|
|
|
The ABI implies that something cannot be declared in a module unit and defined
|
|
in a non-module unit (or vice-versa), as this would result in linking errors.
|
|
|
|
Despite this, it is possible to implement declarations with a compatible ABI in
|
|
a module unit by using a language linkage specifier because the declarations in
|
|
the language linkage specifier are attached to the global module fragment. For
|
|
example:
|
|
|
|
.. code-block:: c++
|
|
|
|
export module M;
|
|
namespace NS {
|
|
export extern "C++" int foo();
|
|
}
|
|
|
|
Now the linkage name of ``NS::foo()`` will be ``_ZN2NS3fooEv``.
|
|
|
|
Module Initializers
|
|
~~~~~~~~~~~~~~~~~~~
|
|
|
|
All importable module units are required to emit an initializer function to
|
|
handle the dynamic initialization of non-inline variables in the module unit.
|
|
The importable module unit has to emit the initializer even if there is no
|
|
dynamic initialization; otherwise, the importer may call a nonexistent
|
|
function. The initializer function emits calls to imported modules first
|
|
followed by calls to all to of the dynamic initializers in the current module
|
|
unit.
|
|
|
|
Translation units that explicitly or implicitly import a named module must call
|
|
the initializer functions of the imported named module within the sequence of
|
|
the dynamic initializers in the translation unit. Initializations of entities
|
|
at namespace scope are appearance-ordered. This (recursively) extends to
|
|
imported modules at the point of appearance of the import declaration.
|
|
|
|
If the imported module is known to be empty, the call to its initializer may be
|
|
omitted. Additionally, if the imported module is known to have already been
|
|
imported, the call to its initializer may be omitted.
|
|
|
|
Reduced BMI
|
|
-----------
|
|
|
|
To support the two-phase compilation model, Clang puts everything needed to
|
|
produce an object into the BMI. However, other consumers of the BMI generally
|
|
don't need that information. This makes the BMI larger and may introduce
|
|
unnecessary dependencies for the BMI. To mitigate the problem, Clang has a
|
|
compiler option to reduce the information contained in the BMI. These two
|
|
formats are known as Full BMI and Reduced BMI, respectively.
|
|
|
|
Users can use the ``-fmodules-reduced-bmi`` option to produce a
|
|
Reduced BMI.
|
|
|
|
For the one-phase compilation model (CMake implements this model), with
|
|
``-fmodules-reduced-bmi``, the generated BMI will be a Reduced
|
|
BMI automatically. (The output path of the BMI is specified by
|
|
``-fmodule-output=`` as usual with the one-phase compilation model).
|
|
|
|
It is also possible to produce a Reduced BMI with the two-phase compilation
|
|
model. When ``-fmodules-reduced-bmi``, ``--precompile``, and
|
|
``-fmodule-output=`` are specified, the generated BMI specified by ``-o`` will
|
|
be a full BMI and the BMI specified by ``-fmodule-output=`` will be a Reduced
|
|
BMI. The dependency graph in this case would look like:
|
|
|
|
.. code-block:: none
|
|
|
|
module-unit.cppm --> module-unit.full.pcm -> module-unit.o
|
|
|
|
|
-> module-unit.reduced.pcm -> consumer1.cpp
|
|
-> consumer2.cpp
|
|
-> ...
|
|
-> consumer_n.cpp
|
|
|
|
Clang does not emit diagnostics when ``-fmodules-reduced-bmi`` is
|
|
used with a non-module unit. This design permits users of the one-phase
|
|
compilation model to try using reduced BMIs without needing to modify the build
|
|
system. The two-phase compilation module requires build system support.
|
|
|
|
In a Reduced BMI, Clang does not emit unreachable entities from the global
|
|
module fragment, or definitions of non-inline functions and non-inline
|
|
variables. This may not be a transparent change.
|
|
|
|
Consider the following example:
|
|
|
|
.. code-block:: c++
|
|
|
|
// foo.h
|
|
namespace N {
|
|
struct X {};
|
|
int d();
|
|
int e();
|
|
inline int f(X, int = d()) { return e(); }
|
|
int g(X);
|
|
int h(X);
|
|
}
|
|
|
|
// M.cppm
|
|
module;
|
|
#include "foo.h"
|
|
export module M;
|
|
template<typename T> int use_f() {
|
|
N::X x; // N::X, N, and :: are decl-reachable from use_f
|
|
return f(x, 123); // N::f is decl-reachable from use_f,
|
|
// N::e is indirectly decl-reachable from use_f
|
|
// because it is decl-reachable from N::f, and
|
|
// N::d is decl-reachable from use_f
|
|
// because it is decl-reachable from N::f
|
|
// even though it is not used in this call
|
|
}
|
|
template<typename T> int use_g() {
|
|
N::X x; // N::X, N, and :: are decl-reachable from use_g
|
|
return g((T(), x)); // N::g is not decl-reachable from use_g
|
|
}
|
|
template<typename T> int use_h() {
|
|
N::X x; // N::X, N, and :: are decl-reachable from use_h
|
|
return h((T(), x)); // N::h is not decl-reachable from use_h, but
|
|
// N::h is decl-reachable from use_h<int>
|
|
}
|
|
int k = use_h<int>();
|
|
// use_h<int> is decl-reachable from k, so
|
|
// N::h is decl-reachable from k
|
|
|
|
// M-impl.cpp
|
|
module M;
|
|
int a = use_f<int>(); // OK
|
|
int b = use_g<int>(); // error: no viable function for call to g;
|
|
// g is not decl-reachable from purview of
|
|
// module M's interface, so is discarded
|
|
int c = use_h<int>(); // OK
|
|
|
|
In the above example, the function definition of ``N::g`` is elided from the
|
|
Reduced BMI of ``M.cppm``. Then the use of ``use_g<int>`` in ``M-impl.cpp``
|
|
fails to instantiate. For such issues, users can add references to ``N::g`` in
|
|
the `module purview <https://eel.is/c++draft/module.unit#5>`_ of ``M.cppm`` to
|
|
ensure it is reachable, e.g. ``using N::g;``.
|
|
|
|
Support for Reduced BMIs is still experimental, but it may become the default
|
|
in the future. The expected roadmap for Reduced BMIs as of Clang 19.x is:
|
|
|
|
1. ``-fexperimental-modules-reduced-bmi`` was introduced in v19.x
|
|
2. For v20.x, ``-fmodules-reduced-bmi`` is introduced as an equivalent non-experimental
|
|
option. It is expected to stay opt-in for 1~2 releases, though the period depends
|
|
on user feedback and may be extended.
|
|
3. Finally, ``-fmodules-reduced-bmi`` will be the default. When that time
|
|
comes, the term BMI will refer to the Reduced BMI and the Full BMI will only
|
|
be meaningful to build systems which elect to support two-phase compilation.
|
|
|
|
Experimental Non-Cascading Changes
|
|
----------------------------------
|
|
|
|
This section is primarily for build system vendors. For end compiler users,
|
|
if you don't want to read it all, this is helpful to reduce recompilations.
|
|
We encourage build system vendors and end users try this out and bring feedback.
|
|
|
|
Before Clang 19, a change in BMI of any (transitive) dependency would cause the
|
|
outputs of the BMI to change. Starting with Clang 19, changes to non-direct
|
|
dependencies should not directly affect the output BMI, unless they affect the
|
|
results of the compilations. We expect that there are many more opportunities
|
|
for this optimization than we currently have realized and would appreaciate
|
|
feedback about missed optimization opportunities. For example,
|
|
|
|
.. code-block:: c++
|
|
|
|
// m-partA.cppm
|
|
export module m:partA;
|
|
|
|
// m-partB.cppm
|
|
export module m:partB;
|
|
export int getB() { return 44; }
|
|
|
|
// m.cppm
|
|
export module m;
|
|
export import :partA;
|
|
export import :partB;
|
|
|
|
// useBOnly.cppm
|
|
export module useBOnly;
|
|
import m;
|
|
export int B() {
|
|
return getB();
|
|
}
|
|
|
|
// Use.cc
|
|
import useBOnly;
|
|
int get() {
|
|
return B();
|
|
}
|
|
|
|
To compile the project (for brevity, some commands are omitted.):
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang++ -std=c++20 m-partA.cppm --precompile -o m-partA.pcm
|
|
$ clang++ -std=c++20 m-partB.cppm --precompile -o m-partB.pcm
|
|
$ clang++ -std=c++20 m.cppm --precompile -o m.pcm -fprebuilt-module-path=.
|
|
$ clang++ -std=c++20 useBOnly.cppm --precompile -o useBOnly.pcm -fprebuilt-module-path=.
|
|
$ md5sum useBOnly.pcm
|
|
07656bf4a6908626795729295f9608da useBOnly.pcm
|
|
|
|
If the interface of ``m-partA.cppm`` is changed to:
|
|
|
|
.. code-block:: c++
|
|
|
|
// m-partA.v1.cppm
|
|
export module m:partA;
|
|
export int getA() { return 43; }
|
|
|
|
and the BMI for ``useBOnly`` is recompiled as in:
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang++ -std=c++20 m-partA.cppm --precompile -o m-partA.pcm
|
|
$ clang++ -std=c++20 m-partB.cppm --precompile -o m-partB.pcm
|
|
$ clang++ -std=c++20 m.cppm --precompile -o m.pcm -fprebuilt-module-path=.
|
|
$ clang++ -std=c++20 useBOnly.cppm --precompile -o useBOnly.pcm -fprebuilt-module-path=.
|
|
$ md5sum useBOnly.pcm
|
|
07656bf4a6908626795729295f9608da useBOnly.pcm
|
|
|
|
then the contents of ``useBOnly.pcm`` remain unchanged.
|
|
Consequently, if the build system only bases recompilation decisions on directly imported modules,
|
|
it becomes possible to skip the recompilation of ``Use.cc``.
|
|
It should be fine because the altered interfaces do not affect ``Use.cc`` in any way;
|
|
the changes do not cascade.
|
|
|
|
When ``Clang`` generates a BMI, it records the hash values of all potentially contributory BMIs
|
|
for the BMI being produced. This ensures that build systems are not required to consider
|
|
transitively imported modules when deciding whether to recompile.
|
|
|
|
What is considered to be a potential contributory BMIs is currently unspecified.
|
|
However, it is a severe bug for a BMI to remain unchanged following an observable change
|
|
that affects its consumers.
|
|
|
|
Build systems may utilize this optimization by doing an update-if-changed operation to the BMI
|
|
that is consumed from the BMI that is output by the compiler.
|
|
|
|
We encourage build systems to add an experimental mode that
|
|
reuses the cached BMI when **direct** dependencies did not change,
|
|
even if **transitive** dependencies did change.
|
|
|
|
Given there are potential compiler bugs, we recommend that build systems
|
|
support this feature as a configurable option so that users
|
|
can go back to the transitive change mode safely at any time.
|
|
|
|
Interactions with Reduced BMI
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
With reduced BMI, non-cascading changes can be more powerful. For example,
|
|
|
|
.. code-block:: c++
|
|
|
|
// A.cppm
|
|
export module A;
|
|
export int a() { return 44; }
|
|
|
|
// B.cppm
|
|
export module B;
|
|
import A;
|
|
export int b() { return a(); }
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang++ -std=c++20 A.cppm -c -fmodule-output=A.pcm -fmodules-reduced-bmi -o A.o
|
|
$ clang++ -std=c++20 B.cppm -c -fmodule-output=B.pcm -fmodules-reduced-bmi -o B.o -fmodule-file=A=A.pcm
|
|
$ md5sum B.pcm
|
|
6c2bd452ca32ab418bf35cd141b060b9 B.pcm
|
|
|
|
And let's change the implementation for ``A.cppm`` into:
|
|
|
|
.. code-block:: c++
|
|
|
|
export module A;
|
|
int a_impl() { return 99; }
|
|
export int a() { return a_impl(); }
|
|
|
|
and recompile the example:
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang++ -std=c++20 A.cppm -c -fmodule-output=A.pcm -fmodules-reduced-bmi -o A.o
|
|
$ clang++ -std=c++20 B.cppm -c -fmodule-output=B.pcm -fmodules-reduced-bmi -o B.o -fmodule-file=A=A.pcm
|
|
$ md5sum B.pcm
|
|
6c2bd452ca32ab418bf35cd141b060b9 B.pcm
|
|
|
|
We should find the contents of ``B.pcm`` remains the same. In this case, the build system is
|
|
allowed to skip recompilations of TUs which solely and directly depend on module ``B``.
|
|
|
|
This only happens with a reduced BMI. With reduced BMIs, we won't record the function body
|
|
of ``int b()`` in the BMI for ``B`` so that the module ``A`` doesn't contribute to the BMI of ``B``
|
|
and we have less dependencies.
|
|
|
|
Performance Tips
|
|
----------------
|
|
|
|
Reduce duplications
|
|
~~~~~~~~~~~~~~~~~~~
|
|
|
|
While it is valid to have duplicated declarations in the global module fragments
|
|
of different module units, it is not free for Clang to deal with the duplicated
|
|
declarations. A translation unit will compile more slowly if there is a lot of
|
|
duplicated declarations between the translation unit and modules it imports.
|
|
For example:
|
|
|
|
.. code-block:: c++
|
|
|
|
// M-partA.cppm
|
|
module;
|
|
#include "big.header.h"
|
|
export module M:partA;
|
|
...
|
|
|
|
// M-partB.cppm
|
|
module;
|
|
#include "big.header.h"
|
|
export module M:partB;
|
|
...
|
|
|
|
// other partitions
|
|
...
|
|
|
|
// M-partZ.cppm
|
|
module;
|
|
#include "big.header.h"
|
|
export module M:partZ;
|
|
...
|
|
|
|
// M.cppm
|
|
export module M;
|
|
export import :partA;
|
|
export import :partB;
|
|
...
|
|
export import :partZ;
|
|
|
|
// use.cpp
|
|
import M;
|
|
... // use declarations from module M.
|
|
|
|
When ``big.header.h`` is big enough and there are a lot of partitions, the
|
|
compilation of ``use.cpp`` may be significantly slower than the following
|
|
approach:
|
|
|
|
.. code-block:: c++
|
|
|
|
module;
|
|
#include "big.header.h"
|
|
export module m:big.header.wrapper;
|
|
export ... // export the needed declarations
|
|
|
|
// M-partA.cppm
|
|
export module M:partA;
|
|
import :big.header.wrapper;
|
|
...
|
|
|
|
// M-partB.cppm
|
|
export module M:partB;
|
|
import :big.header.wrapper;
|
|
...
|
|
|
|
// other partitions
|
|
...
|
|
|
|
// M-partZ.cppm
|
|
export module M:partZ;
|
|
import :big.header.wrapper;
|
|
...
|
|
|
|
// M.cppm
|
|
export module M;
|
|
export import :partA;
|
|
export import :partB;
|
|
...
|
|
export import :partZ;
|
|
|
|
// use.cpp
|
|
import M;
|
|
... // use declarations from module M.
|
|
|
|
Reducing the duplication from textual includes is what improves compile-time
|
|
performance.
|
|
|
|
To help users to identify such issues, we add a warning ``-Wdecls-in-multiple-modules``.
|
|
This warning is disabled by default and it needs to be explicitly enabled or by ``-Weverything``.
|
|
|
|
Transitioning to modules
|
|
------------------------
|
|
|
|
It is best for new code and libraries to use modules from the start if
|
|
possible. However, it may be a breaking change for existing code or libraries
|
|
to switch to modules. As a result, many existing libraries need to provide
|
|
both headers and module interfaces for a while to not break existing users.
|
|
|
|
This section suggests some suggestions on how to ease the transition process
|
|
for existing libraries. **Note that this information is only intended as
|
|
guidance, rather than as requirements to use modules in Clang.** It presumes
|
|
the project is starting with no module-based dependencies.
|
|
|
|
ABI non-breaking styles
|
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
export-using style
|
|
^^^^^^^^^^^^^^^^^^
|
|
|
|
.. code-block:: c++
|
|
|
|
module;
|
|
#include "header_1.h"
|
|
#include "header_2.h"
|
|
...
|
|
#include "header_n.h"
|
|
export module your_library;
|
|
export namespace your_namespace {
|
|
using decl_1;
|
|
using decl_2;
|
|
...
|
|
using decl_n;
|
|
}
|
|
|
|
This example shows how to include all the headers containing declarations which
|
|
need to be exported, and uses `using` declarations in an `export` block to
|
|
produce the module interface.
|
|
|
|
export extern-C++ style
|
|
^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
.. code-block:: c++
|
|
|
|
module;
|
|
#include "third_party/A/headers.h"
|
|
#include "third_party/B/headers.h"
|
|
...
|
|
#include "third_party/Z/headers.h"
|
|
export module your_library;
|
|
#define IN_MODULE_INTERFACE
|
|
extern "C++" {
|
|
#include "header_1.h"
|
|
#include "header_2.h"
|
|
...
|
|
#include "header_n.h"
|
|
}
|
|
|
|
Headers (from ``header_1.h`` to ``header_n.h``) need to define the macro:
|
|
|
|
.. code-block:: c++
|
|
|
|
#ifdef IN_MODULE_INTERFACE
|
|
#define EXPORT export
|
|
#else
|
|
#define EXPORT
|
|
#endif
|
|
|
|
and put ``EXPORT`` on the declarations you want to export.
|
|
|
|
Also, it is recommended to refactor headers to include third-party headers
|
|
conditionally:
|
|
|
|
.. code-block:: c++
|
|
|
|
#ifndef IN_MODULE_INTERFACE
|
|
#include "third_party/A/headers.h"
|
|
#endif
|
|
|
|
#include "header_x.h"
|
|
|
|
...
|
|
|
|
This can be helpful because it gives better diagnostic messages if the module
|
|
interface unit is not properly updated when modifying code.
|
|
|
|
This approach works because the declarations with language linkage are attached
|
|
to the global module. Thus, the ABI of the modular form of the library does not
|
|
change.
|
|
|
|
While this style is more involved than the export-using style, it makes it
|
|
easier to further refactor the library to other styles.
|
|
|
|
ABI breaking style
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
The term ``ABI breaking`` may sound like a bad approach. However, this style
|
|
forces consumers of the library use it in a consistent way. e.g., either always
|
|
include headers for the library or always import modules. The style prevents
|
|
the ability to mix includes and imports for the library.
|
|
|
|
The pattern for ABI breaking style is similar to the export extern-C++ style.
|
|
|
|
.. code-block:: c++
|
|
|
|
module;
|
|
#include "third_party/A/headers.h"
|
|
#include "third_party/B/headers.h"
|
|
...
|
|
#include "third_party/Z/headers.h"
|
|
export module your_library;
|
|
#define IN_MODULE_INTERFACE
|
|
#include "header_1.h"
|
|
#include "header_2.h"
|
|
...
|
|
#include "header_n.h"
|
|
|
|
#if the number of .cpp files in your project are small
|
|
module :private;
|
|
#include "source_1.cpp"
|
|
#include "source_2.cpp"
|
|
...
|
|
#include "source_n.cpp"
|
|
#else // the number of .cpp files in your project are a lot
|
|
// Using all the declarations from third-party libraries which are
|
|
// used in the .cpp files.
|
|
namespace third_party_namespace {
|
|
using third_party_decl_used_in_cpp_1;
|
|
using third_party_decl_used_in_cpp_2;
|
|
...
|
|
using third_party_decl_used_in_cpp_n;
|
|
}
|
|
#endif
|
|
|
|
(And add `EXPORT` and conditional include to the headers as suggested in the
|
|
export extern-C++ style section.)
|
|
|
|
The ABI with modules is different and thus we need to compile the source files
|
|
into the new ABI. This is done by an additional part of the interface unit:
|
|
|
|
.. code-block:: c++
|
|
|
|
#if the number of .cpp files in your project are small
|
|
module :private;
|
|
#include "source_1.cpp"
|
|
#include "source_2.cpp"
|
|
...
|
|
#include "source_n.cpp"
|
|
#else // the number of .cpp files in your project are a lot
|
|
// Using all the declarations from third-party libraries which are
|
|
// used in the .cpp files.
|
|
namespace third_party_namespace {
|
|
using third_party_decl_used_in_cpp_1;
|
|
using third_party_decl_used_in_cpp_2;
|
|
...
|
|
using third_party_decl_used_in_cpp_n;
|
|
}
|
|
#endif
|
|
|
|
If the number of source files is small, everything can be put in the private
|
|
module fragment directly (it is recommended to add conditional includes to the
|
|
source files as well). However, compile time performance will be bad if there
|
|
are a lot of source files to compile.
|
|
|
|
**Note that the private module fragment can only be in the primary module
|
|
interface unit and the primary module interface unit containing the private
|
|
module fragment should be the only module unit of the corresponding module.**
|
|
|
|
In this case, source files (.cpp files) must be converted to module
|
|
implementation units:
|
|
|
|
.. code-block:: c++
|
|
|
|
#ifndef IN_MODULE_INTERFACE
|
|
// List all the includes here.
|
|
#include "third_party/A/headers.h"
|
|
...
|
|
#include "header.h"
|
|
#endif
|
|
|
|
module your_library;
|
|
|
|
// Following off should be unchanged.
|
|
...
|
|
|
|
The module implementation unit will import the primary module implicitly. Do
|
|
not include any headers in the module implementation units as it avoids
|
|
duplicated declarations between translation units. This is why non-exported
|
|
using declarations should be added from third-party libraries in the primary
|
|
module interface unit.
|
|
|
|
If the library is provided as ``libyour_library.so``, a modular library (e.g.,
|
|
``libyour_library_modules.so``) may also need to be provided for ABI
|
|
compatibility.
|
|
|
|
What if there are headers only included by the source files
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
The above practice may be problematic if there are headers only included by the
|
|
source files. When using a private module fragment, this issue may be solved by
|
|
including those headers in the private module fragment. While it is OK to solve
|
|
it by including the implementation headers in the module purview when using
|
|
implementation module units, it may be suboptimal because the primary module
|
|
interface units now contain entities that do not belong to the interface.
|
|
|
|
This can potentially be improved by introducing a module partition
|
|
implementation unit. An internal module partition unit is an importable
|
|
module unit which is internal to the module itself.
|
|
|
|
Providing a header to skip parsing redundant headers
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Many redeclarations shared between translation units causes Clang to have
|
|
slower compile-time performance. Further, there are known issues with
|
|
`include after import <https://github.com/llvm/llvm-project/issues/61465>`_.
|
|
Even when that issue is resolved, users may still get slower compilation speed
|
|
and larger BMIs. For these reasons, it is recommended to not include headers
|
|
after importing the corresponding module. However, it is not always easy if the
|
|
library is included by other dependencies, as in:
|
|
|
|
.. code-block:: c++
|
|
|
|
#include "third_party/A.h" // #include "your_library/a_header.h"
|
|
import your_library;
|
|
|
|
or
|
|
|
|
.. code-block:: c++
|
|
|
|
import your_library;
|
|
#include "third_party/A.h" // #include "your_library/a_header.h"
|
|
|
|
For such cases, it is best if the library providing both module and header
|
|
interfaces also provides a header which skips parsing so that the library can
|
|
be imported with the following approach that skips redundant redeclarations:
|
|
|
|
.. code-block:: c++
|
|
|
|
import your_library;
|
|
#include "your_library_imported.h"
|
|
#include "third_party/A.h" // #include "your_library/a_header.h" but got skipped
|
|
|
|
The implementation of ``your_library_imported.h`` can be a set of controlling
|
|
macros or an overall controlling macro if using `#pragma once`. Then headers
|
|
can be refactored to:
|
|
|
|
.. code-block:: c++
|
|
|
|
#pragma once
|
|
#ifndef YOUR_LIBRARY_IMPORTED
|
|
...
|
|
#endif
|
|
|
|
If the modules imported by the library provide such headers, remember to add
|
|
them to ``your_library_imported.h`` too.
|
|
|
|
Importing modules
|
|
~~~~~~~~~~~~~~~~~
|
|
|
|
When there are dependent libraries providing modules, they should be imported
|
|
in your module as well. Many existing libraries will fall into this category
|
|
once the ``std`` module is more widely available.
|
|
|
|
All dependent libraries providing modules
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Of course, most of the complexity disappears if all the dependent libraries
|
|
provide modules.
|
|
|
|
Headers need to be converted to include third-party headers conditionally. Then,
|
|
for the export-using style:
|
|
|
|
.. code-block:: c++
|
|
|
|
module;
|
|
import modules_from_third_party;
|
|
#define IN_MODULE_INTERFACE
|
|
#include "header_1.h"
|
|
#include "header_2.h"
|
|
...
|
|
#include "header_n.h"
|
|
export module your_library;
|
|
export namespace your_namespace {
|
|
using decl_1;
|
|
using decl_2;
|
|
...
|
|
using decl_n;
|
|
}
|
|
|
|
or, for the export extern-C++ style:
|
|
|
|
.. code-block:: c++
|
|
|
|
export module your_library;
|
|
import modules_from_third_party;
|
|
#define IN_MODULE_INTERFACE
|
|
extern "C++" {
|
|
#include "header_1.h"
|
|
#include "header_2.h"
|
|
...
|
|
#include "header_n.h"
|
|
}
|
|
|
|
or, for the ABI-breaking style,
|
|
|
|
.. code-block:: c++
|
|
|
|
export module your_library;
|
|
import modules_from_third_party;
|
|
#define IN_MODULE_INTERFACE
|
|
#include "header_1.h"
|
|
#include "header_2.h"
|
|
...
|
|
#include "header_n.h"
|
|
|
|
#if the number of .cpp files in your project are small
|
|
module :private;
|
|
#include "source_1.cpp"
|
|
#include "source_2.cpp"
|
|
...
|
|
#include "source_n.cpp"
|
|
#endif
|
|
|
|
Non-exported ``using`` declarations are unnecessary if using implementation
|
|
module units. Instead, third-party modules can be imported directly in
|
|
implementation module units.
|
|
|
|
Partial dependent libraries providing modules
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
If the library has to mix the use of ``include`` and ``import`` in its module,
|
|
the primary goal is still the removal of duplicated declarations in translation
|
|
units as much as possible. If the imported modules provide headers to skip
|
|
parsing their headers, those should be included after the import. If the
|
|
imported modules don't provide such a header, one can be made manually for
|
|
improved compile time performance.
|
|
|
|
Reachability of internal partition units
|
|
----------------------------------------
|
|
|
|
The internal partition units are sometimes called implementation partition units in other documentation.
|
|
However, the name may be confusing since implementation partition units are not implementation
|
|
units.
|
|
|
|
According to `[module.reach]p1 <https://eel.is/c++draft/module.reach#1>`_ and
|
|
`[module.reach]p2 <https://eel.is/c++draft/module.reach#2>`_ (from N4986):
|
|
|
|
A translation unit U is necessarily reachable from a point P if U is a module
|
|
interface unit on which the translation unit containing P has an interface
|
|
dependency, or the translation unit containing P imports U, in either case
|
|
prior to P.
|
|
|
|
All translation units that are necessarily reachable are reachable. Additional
|
|
translation units on which the point within the program has an interface
|
|
dependency may be considered reachable, but it is unspecified which are and
|
|
under what circumstances.
|
|
|
|
For example,
|
|
|
|
.. code-block:: c++
|
|
|
|
// a.cpp
|
|
import B;
|
|
int main()
|
|
{
|
|
g<void>();
|
|
}
|
|
|
|
// b.cppm
|
|
export module B;
|
|
import :C;
|
|
export template <typename T> inline void g() noexcept
|
|
{
|
|
return f<T>();
|
|
}
|
|
|
|
// c.cppm
|
|
module B:C;
|
|
template<typename> inline void f() noexcept {}
|
|
|
|
The internal partition unit ``c.cppm`` is not necessarily reachable by
|
|
``a.cpp`` because ``c.cppm`` is not a module interface unit and ``a.cpp``
|
|
doesn't import ``c.cppm``. This leaves it up to the compiler to decide if
|
|
``c.cppm`` is reachable by ``a.cpp`` or not. Clang's behavior is that
|
|
indirectly imported internal partition units are not reachable.
|
|
|
|
The suggested approach for using an internal partition unit in Clang is
|
|
to only import them in the implementation unit.
|
|
|
|
Known Issues
|
|
------------
|
|
|
|
The following describes issues in the current implementation of modules. Please
|
|
see
|
|
`the issues list for modules <https://github.com/llvm/llvm-project/labels/clang%3Amodules>`_
|
|
for a list of issues or to file a new issue if you don't find an existing one.
|
|
When creating a new issue for standard C++ modules, please start the title with
|
|
``[C++20] [Modules]`` (or ``[C++23] [Modules]``, etc) and add the label
|
|
``clang:modules`` if possible.
|
|
|
|
A high-level overview of support for standards features, including modules, can
|
|
be found on the `C++ Feature Status <https://clang.llvm.org/cxx_status.html>`_
|
|
page.
|
|
|
|
Including headers after import is not well-supported
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The following example is accepted:
|
|
|
|
.. code-block:: c++
|
|
|
|
#include <iostream>
|
|
import foo; // assume module 'foo' contain the declarations from `<iostream>`
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
std::cout << "Test\n";
|
|
return 0;
|
|
}
|
|
|
|
but if the order of ``#include <iostream>`` and ``import foo;`` is reversed,
|
|
then the code is currently rejected:
|
|
|
|
.. code-block:: c++
|
|
|
|
import foo; // assume module 'foo' contain the declarations from `<iostream>`
|
|
#include <iostream>
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
std::cout << "Test\n";
|
|
return 0;
|
|
}
|
|
|
|
Both of the above examples should be accepted.
|
|
|
|
This is a limitation of the implementation. In the first example, the compiler
|
|
will see and parse ``<iostream>`` first then it will see the ``import``. In
|
|
this case, ODR checking and declaration merging will happen in the
|
|
deserializer. In the second example, the compiler will see the ``import`` first
|
|
and the ``#include`` second which results in ODR checking and declarations
|
|
merging happening in the semantic analyzer. This is due to a divergence in the
|
|
implementation path. This is tracked by
|
|
`#61465 <https://github.com/llvm/llvm-project/issues/61465>`_.
|
|
|
|
Ignored ``preferred_name`` Attribute
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
When Clang writes BMIs, it will ignore the ``preferred_name`` attribute on
|
|
declarations which use it. Thus, the preferred name will not be displayed in
|
|
the debugger as expected. This is tracked by
|
|
`#56490 <https://github.com/llvm/llvm-project/issues/56490>`_.
|
|
|
|
Don't emit macros about module declaration
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
This is covered by `P1857R3 <https://wg21.link/P1857R3>`_. It is mentioned here
|
|
because we want users to be aware that we don't yet implement it.
|
|
|
|
A direct approach to write code that can be compiled by both modules and
|
|
non-module builds may look like:
|
|
|
|
.. code-block:: c++
|
|
|
|
MODULE
|
|
IMPORT header_name
|
|
EXPORT_MODULE MODULE_NAME;
|
|
IMPORT header_name
|
|
EXPORT ...
|
|
|
|
The intent of this is that this file can be compiled like a module unit or a
|
|
non-module unit depending on the definition of some macros. However, this usage
|
|
is forbidden by P1857R3 which is not yet implemented in Clang. This means that
|
|
is possible to write invalid modules which will no longer be accepted once
|
|
P1857R3 is implemented. This is tracked by
|
|
`#54047 <https://github.com/llvm/llvm-project/issues/54047>`_.
|
|
|
|
Until then, it is recommended not to mix macros with module declarations.
|
|
|
|
|
|
In consistent filename suffix requirement for importable module units
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Currently, Clang requires the file name of an ``importable module unit`` to
|
|
have ``.cppm`` (or ``.ccm``, ``.cxxm``, ``.c++m``) as the file extension.
|
|
However, the behavior is inconsistent with other compilers. This is tracked by
|
|
`#57416 <https://github.com/llvm/llvm-project/issues/57416>`_.
|
|
|
|
Incorrect ODR violation diagnostics
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
ODR violations are a common issue when using modules. Clang sometimes produces
|
|
false-positive diagnostics or fails to produce true-positive diagnostics of the
|
|
One Definition Rule. One often-reported example is:
|
|
|
|
.. code-block:: c++
|
|
|
|
// part.cc
|
|
module;
|
|
typedef long T;
|
|
namespace ns {
|
|
inline void fun() {
|
|
(void)(T)0;
|
|
}
|
|
}
|
|
export module repro:part;
|
|
|
|
// repro.cc
|
|
module;
|
|
typedef long T;
|
|
namespace ns {
|
|
using ::T;
|
|
}
|
|
namespace ns {
|
|
inline void fun() {
|
|
(void)(T)0;
|
|
}
|
|
}
|
|
export module repro;
|
|
export import :part;
|
|
|
|
Currently the compiler incorrectly diagnoses the inconsistent definition of
|
|
``fun()`` in two module units. Because both definitions of ``fun()`` have the
|
|
same spelling and ``T`` refers to the same type entity, there is no ODR
|
|
violation. This is tracked by
|
|
`#78850 <https://github.com/llvm/llvm-project/issues/78850>`_.
|
|
|
|
Using TU-local entity in other units
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Module units are translation units, so the entities which should be local to
|
|
the module unit itself should never be used by other units.
|
|
|
|
The C++ standard defines the concept of ``TU-local`` and ``exposure`` in
|
|
`basic.link/p14 <https://eel.is/c++draft/basic.link#14>`_,
|
|
`basic.link/p15 <https://eel.is/c++draft/basic.link#15>`_,
|
|
`basic.link/p16 <https://eel.is/c++draft/basic.link#16>`_,
|
|
`basic.link/p17 <https://eel.is/c++draft/basic.link#17>`_, and
|
|
`basic.link/p18 <https://eel.is/c++draft/basic.link#18>`_.
|
|
|
|
However, Clang doesn't formally support these two concepts. This results in
|
|
unclear or confusing diagnostic messages. Further, Clang may import
|
|
``TU-local`` entities to other units without any diagnostics. This is tracked
|
|
by `#78173 <https://github.com/llvm/llvm-project/issues/78173>`_.
|
|
|
|
.. _header-units:
|
|
|
|
Header Units
|
|
============
|
|
|
|
How to build projects using header units
|
|
----------------------------------------
|
|
|
|
.. warning::
|
|
|
|
The support for header units, including related command line options, is
|
|
experimental. There are still many unanswered question about how tools
|
|
should interact with header units. The details described here may change in
|
|
the future.
|
|
|
|
Quick Start
|
|
~~~~~~~~~~~
|
|
|
|
The following example:
|
|
|
|
.. code-block:: c++
|
|
|
|
import <iostream>;
|
|
int main() {
|
|
std::cout << "Hello World.\n";
|
|
}
|
|
|
|
could be compiled with:
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang++ -std=c++20 -xc++-system-header --precompile iostream -o iostream.pcm
|
|
$ clang++ -std=c++20 -fmodule-file=iostream.pcm main.cpp
|
|
|
|
How to produce BMIs
|
|
~~~~~~~~~~~~~~~~~~~
|
|
|
|
Similar to named modules, ``--precompile`` can be used to produce a BMI.
|
|
However, that requires specifying that the input file is a header by using
|
|
``-xc++-system-header`` or ``-xc++-user-header``.
|
|
|
|
The ``-fmodule-header={user,system}`` option can also be used to produce a BMI
|
|
for header units which have a file extension like `.h` or `.hh`. The argument to
|
|
``-fmodule-header`` specifies either the user search path or the system search
|
|
path. The default value for ``-fmodule-header`` is ``user``. For example:
|
|
|
|
.. code-block:: c++
|
|
|
|
// foo.h
|
|
#include <iostream>
|
|
void Hello() {
|
|
std::cout << "Hello World.\n";
|
|
}
|
|
|
|
// use.cpp
|
|
import "foo.h";
|
|
int main() {
|
|
Hello();
|
|
}
|
|
|
|
could be compiled with:
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang++ -std=c++20 -fmodule-header foo.h -o foo.pcm
|
|
$ clang++ -std=c++20 -fmodule-file=foo.pcm use.cpp
|
|
|
|
For headers which do not have a file extension, ``-xc++-header`` (or
|
|
``-xc++-system-header``, ``-xc++-user-header``) must be used to specify the
|
|
file as a header. For example:
|
|
|
|
.. code-block:: c++
|
|
|
|
// use.cpp
|
|
import "foo.h";
|
|
int main() {
|
|
Hello();
|
|
}
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang++ -std=c++20 -fmodule-header=system -xc++-header iostream -o iostream.pcm
|
|
$ clang++ -std=c++20 -fmodule-file=iostream.pcm use.cpp
|
|
|
|
How to specify dependent BMIs
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
``-fmodule-file`` can be used to specify a dependent BMI (or multiple times for
|
|
more than one dependent BMI).
|
|
|
|
With the existing implementation, ``-fprebuilt-module-path`` cannot be used for
|
|
header units (because they are nominally anonymous). For header units, use
|
|
``-fmodule-file`` to include the relevant PCM file for each header unit.
|
|
|
|
This is expect to be solved in a future version of Clang either by the compiler
|
|
finding and specifying ``-fmodule-file`` automatically, or by the use of a
|
|
module-mapper that understands how to map the header name to their PCMs.
|
|
|
|
Compiling a header unit to an object file
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
A header unit cannot be compiled to an object file due to the semantics of
|
|
header units. For example:
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang++ -std=c++20 -xc++-system-header --precompile iostream -o iostream.pcm
|
|
# This is not allowed!
|
|
$ clang++ iostream.pcm -c -o iostream.o
|
|
|
|
Include translation
|
|
~~~~~~~~~~~~~~~~~~~
|
|
|
|
The C++ standard allows vendors to convert ``#include header-name`` to
|
|
``import header-name;`` when possible. Currently, Clang does this translation
|
|
for the ``#include`` in the global module fragment. For example, the following
|
|
example:
|
|
|
|
.. code-block:: c++
|
|
|
|
module;
|
|
import <iostream>;
|
|
export module M;
|
|
export void Hello() {
|
|
std::cout << "Hello.\n";
|
|
}
|
|
|
|
is the same as this example:
|
|
|
|
.. code-block:: c++
|
|
|
|
module;
|
|
#include <iostream>
|
|
export module M;
|
|
export void Hello() {
|
|
std::cout << "Hello.\n";
|
|
}
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang++ -std=c++20 -xc++-system-header --precompile iostream -o iostream.pcm
|
|
$ clang++ -std=c++20 -fmodule-file=iostream.pcm --precompile M.cppm -o M.cpp
|
|
|
|
In the latter example, Clang can find the BMI for ``<iostream>`` and so it
|
|
tries to replace the ``#include <iostream>`` with ``import <iostream>;``
|
|
automatically.
|
|
|
|
|
|
Differences between Clang modules and header units
|
|
--------------------------------------------------
|
|
|
|
Header units have similar semantics to Clang modules. The semantics of both are
|
|
like headers. Therefore, header units can be mimicked by Clang modules as in
|
|
the following example:
|
|
|
|
.. code-block:: c++
|
|
|
|
module "iostream" {
|
|
export *
|
|
header "/path/to/libstdcxx/iostream"
|
|
}
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang++ -std=c++20 -fimplicit-modules -fmodule-map-file=.modulemap main.cpp
|
|
|
|
This example is simplified when using libc++:
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang++ -std=c++20 main.cpp -fimplicit-modules -fimplicit-module-maps
|
|
|
|
because libc++ already supplies a
|
|
`module map <https://github.com/llvm/llvm-project/blob/main/libcxx/include/module.modulemap.in>`_.
|
|
|
|
This raises the question: why are header units not implemented through Clang
|
|
modules?
|
|
|
|
This is primarily because Clang modules have more hierarchical semantics when
|
|
wrapping multiple headers together as one module, which is not supported by
|
|
Standard C++ Header units. We want to avoid the impression that these
|
|
additional semantics get interpreted as Standard C++ behavior.
|
|
|
|
Another reason is that there are proposals to introduce module mappers to the
|
|
C++ standard (for example, https://wg21.link/p1184r2). Reusing Clang's
|
|
``modulemap`` may be more difficult if we need to introduce another module
|
|
mapper.
|
|
|
|
Discovering Dependencies
|
|
========================
|
|
|
|
Without use of modules, all the translation units in a project can be compiled
|
|
in parallel. However, the presence of module units requires compiling the
|
|
translation units in a topological order.
|
|
|
|
The ``clang-scan-deps`` tool can extract dependency information and produce a
|
|
JSON file conforming to the specification described in
|
|
`P1689 <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1689r5.html>`_.
|
|
Only named modules are supported currently.
|
|
|
|
A compilation database is needed when using ``clang-scan-deps``. See
|
|
`JSON Compilation Database Format Specification <JSONCompilationDatabase.html>`_
|
|
for more information about compilation databases. Note that the ``output``
|
|
JSON attribute is necessary for ``clang-scan-deps`` to scan using the P1689
|
|
format. For example:
|
|
|
|
.. code-block:: c++
|
|
|
|
//--- M.cppm
|
|
export module M;
|
|
export import :interface_part;
|
|
import :impl_part;
|
|
export int Hello();
|
|
|
|
//--- interface_part.cppm
|
|
export module M:interface_part;
|
|
export void World();
|
|
|
|
//--- Impl.cpp
|
|
module;
|
|
#include <iostream>
|
|
module M;
|
|
void Hello() {
|
|
std::cout << "Hello ";
|
|
}
|
|
|
|
//--- impl_part.cppm
|
|
module;
|
|
#include <string>
|
|
#include <iostream>
|
|
module M:impl_part;
|
|
import :interface_part;
|
|
|
|
std::string W = "World.";
|
|
void World() {
|
|
std::cout << W << std::endl;
|
|
}
|
|
|
|
//--- User.cpp
|
|
import M;
|
|
import third_party_module;
|
|
int main() {
|
|
Hello();
|
|
World();
|
|
return 0;
|
|
}
|
|
|
|
And here is the compilation database:
|
|
|
|
.. code-block:: text
|
|
|
|
[
|
|
{
|
|
"directory": ".",
|
|
"command": "<path-to-compiler-executable>/clang++ -std=c++20 M.cppm -c -o M.o",
|
|
"file": "M.cppm",
|
|
"output": "M.o"
|
|
},
|
|
{
|
|
"directory": ".",
|
|
"command": "<path-to-compiler-executable>/clang++ -std=c++20 Impl.cpp -c -o Impl.o",
|
|
"file": "Impl.cpp",
|
|
"output": "Impl.o"
|
|
},
|
|
{
|
|
"directory": ".",
|
|
"command": "<path-to-compiler-executable>/clang++ -std=c++20 impl_part.cppm -c -o impl_part.o",
|
|
"file": "impl_part.cppm",
|
|
"output": "impl_part.o"
|
|
},
|
|
{
|
|
"directory": ".",
|
|
"command": "<path-to-compiler-executable>/clang++ -std=c++20 interface_part.cppm -c -o interface_part.o",
|
|
"file": "interface_part.cppm",
|
|
"output": "interface_part.o"
|
|
},
|
|
{
|
|
"directory": ".",
|
|
"command": "<path-to-compiler-executable>/clang++ -std=c++20 User.cpp -c -o User.o",
|
|
"file": "User.cpp",
|
|
"output": "User.o"
|
|
}
|
|
]
|
|
|
|
To get the dependency information in P1689 format, use:
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang-scan-deps -format=p1689 -compilation-database P1689.json
|
|
|
|
to get:
|
|
|
|
.. code-block:: text
|
|
|
|
{
|
|
"revision": 0,
|
|
"rules": [
|
|
{
|
|
"primary-output": "Impl.o",
|
|
"requires": [
|
|
{
|
|
"logical-name": "M",
|
|
"source-path": "M.cppm"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"primary-output": "M.o",
|
|
"provides": [
|
|
{
|
|
"is-interface": true,
|
|
"logical-name": "M",
|
|
"source-path": "M.cppm"
|
|
}
|
|
],
|
|
"requires": [
|
|
{
|
|
"logical-name": "M:interface_part",
|
|
"source-path": "interface_part.cppm"
|
|
},
|
|
{
|
|
"logical-name": "M:impl_part",
|
|
"source-path": "impl_part.cppm"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"primary-output": "User.o",
|
|
"requires": [
|
|
{
|
|
"logical-name": "M",
|
|
"source-path": "M.cppm"
|
|
},
|
|
{
|
|
"logical-name": "third_party_module"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"primary-output": "impl_part.o",
|
|
"provides": [
|
|
{
|
|
"is-interface": false,
|
|
"logical-name": "M:impl_part",
|
|
"source-path": "impl_part.cppm"
|
|
}
|
|
],
|
|
"requires": [
|
|
{
|
|
"logical-name": "M:interface_part",
|
|
"source-path": "interface_part.cppm"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"primary-output": "interface_part.o",
|
|
"provides": [
|
|
{
|
|
"is-interface": true,
|
|
"logical-name": "M:interface_part",
|
|
"source-path": "interface_part.cppm"
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"version": 1
|
|
}
|
|
|
|
See the P1689 paper for the meaning of the fields.
|
|
|
|
Getting dependency information per file with finer-grained control (such as
|
|
scanning generated source files) is possible. For example:
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang-scan-deps -format=p1689 -- <path-to-compiler-executable>/clang++ -std=c++20 impl_part.cppm -c -o impl_part.o
|
|
|
|
will produce:
|
|
|
|
.. code-block:: text
|
|
|
|
{
|
|
"revision": 0,
|
|
"rules": [
|
|
{
|
|
"primary-output": "impl_part.o",
|
|
"provides": [
|
|
{
|
|
"is-interface": false,
|
|
"logical-name": "M:impl_part",
|
|
"source-path": "impl_part.cppm"
|
|
}
|
|
],
|
|
"requires": [
|
|
{
|
|
"logical-name": "M:interface_part"
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"version": 1
|
|
}
|
|
|
|
Individual command line options can be specified after ``--``.
|
|
``clang-scan-deps`` will extract the necessary information from the specified
|
|
options. Note that the path to the compiler executable needs to be specified
|
|
explicitly instead of using ``clang++`` directly.
|
|
|
|
Users may want the scanner to get the transitional dependency information for
|
|
headers. Otherwise, the project has to be scanned twice, once for headers and
|
|
once for modules. To address this, ``clang-scan-deps`` will recognize the
|
|
specified preprocessor options in the given command line and generate the
|
|
corresponding dependency information. For example:
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang-scan-deps -format=p1689 -- ../bin/clang++ -std=c++20 impl_part.cppm -c -o impl_part.o -MD -MT impl_part.ddi -MF impl_part.dep
|
|
$ cat impl_part.dep
|
|
|
|
will produce:
|
|
|
|
.. code-block:: text
|
|
|
|
impl_part.ddi: \
|
|
/usr/include/bits/wchar.h /usr/include/bits/types/wint_t.h \
|
|
/usr/include/bits/types/mbstate_t.h \
|
|
/usr/include/bits/types/__mbstate_t.h /usr/include/bits/types/__FILE.h \
|
|
/usr/include/bits/types/FILE.h /usr/include/bits/types/locale_t.h \
|
|
/usr/include/bits/types/__locale_t.h \
|
|
...
|
|
|
|
When ``clang-scan-deps`` detects the ``-MF`` option, it will try to write the
|
|
dependency information for headers to the file specified by ``-MF``.
|
|
|
|
Possible Issues: Failed to find system headers
|
|
----------------------------------------------
|
|
|
|
If encountering an error like ``fatal error: 'stddef.h' file not found``,
|
|
the specified ``<path-to-compiler-executable>/clang++`` probably refers to a
|
|
symlink instead a real binary. There are four potential solutions to the
|
|
problem:
|
|
|
|
1. Point the specified compiler executable to the real binary instead of the
|
|
symlink.
|
|
2. Invoke ``<path-to-compiler-executable>/clang++ -print-resource-dir`` to get
|
|
the corresponding resource directory for your compiler and add that
|
|
directory to the include search paths manually in the build scripts.
|
|
3. For build systems that use a compilation database as the input for
|
|
``clang-scan-deps``, the build system can add the
|
|
``--resource-dir-recipe invoke-compiler`` option when executing
|
|
``clang-scan-deps`` to calculate the resource directory dynamically.
|
|
The calculation happens only once for a unique ``<path-to-compiler-executable>/clang++``.
|
|
4. For build systems that invoke ``clang-scan-deps`` per file, repeatedly
|
|
calculating the resource directory may be inefficient. In such cases, the
|
|
build system can cache the resource directory and specify
|
|
``-resource-dir <resource-dir>`` explicitly, as in:
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang-scan-deps -format=p1689 -- <path-to-compiler-executable>/clang++ -std=c++20 -resource-dir <resource-dir> mod.cppm -c -o mod.o
|
|
|
|
|
|
Import modules with clang-repl
|
|
==============================
|
|
|
|
``clang-repl`` supports importing C++20 named modules. For example:
|
|
|
|
.. code-block:: c++
|
|
|
|
// M.cppm
|
|
export module M;
|
|
export const char* Hello() {
|
|
return "Hello Interpreter for Modules!";
|
|
}
|
|
|
|
The named module still needs to be compiled ahead of time.
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang++ -std=c++20 M.cppm --precompile -o M.pcm
|
|
$ clang++ M.pcm -c -o M.o
|
|
$ clang++ -shared M.o -o libM.so
|
|
|
|
Note that the module unit needs to be compiled as a dynamic library so that
|
|
``clang-repl`` can load the object files of the module units. Then it is
|
|
possible to import module ``M`` in clang-repl.
|
|
|
|
.. code-block:: console
|
|
|
|
$ clang-repl -Xcc=-std=c++20 -Xcc=-fprebuilt-module-path=.
|
|
# We need to load the dynamic library first before importing the modules.
|
|
clang-repl> %lib libM.so
|
|
clang-repl> import M;
|
|
clang-repl> extern "C" int printf(const char *, ...);
|
|
clang-repl> printf("%s\n", Hello());
|
|
Hello Interpreter for Modules!
|
|
clang-repl> %quit
|
|
|
|
Possible Questions
|
|
==================
|
|
|
|
How modules speed up compilation
|
|
--------------------------------
|
|
|
|
A classic theory for the reason why modules speed up the compilation is: if
|
|
there are ``n`` headers and ``m`` source files and each header is included by
|
|
each source file, then the complexity of the compilation is ``O(n*m)``.
|
|
However, if there are ``n`` module interfaces and ``m`` source files, the
|
|
complexity of the compilation is ``O(n+m)``. Therefore, using modules would be
|
|
a significant improvement at scale. More simply, use of modules causes many of
|
|
the redundant compilations to no longer be necessary.
|
|
|
|
While this is accurate at a high level, this depends greatly on the
|
|
optimization level, as illustrated below.
|
|
|
|
First is ``-O0``. The compilation process is described in the following graph.
|
|
|
|
.. code-block:: none
|
|
|
|
├-------------frontend----------┼-------------middle end----------------┼----backend----┤
|
|
│ │ │ │
|
|
└---parsing----sema----codegen--┴----- transformations ---- codegen ----┴---- codegen --┘
|
|
|
|
├---------------------------------------------------------------------------------------┐
|
|
| │
|
|
| source file │
|
|
| │
|
|
└---------------------------------------------------------------------------------------┘
|
|
|
|
├--------┐
|
|
│ │
|
|
│imported│
|
|
│ │
|
|
│ code │
|
|
│ │
|
|
└--------┘
|
|
|
|
In this case, the source file (which could be a non-module unit or a module
|
|
unit) would get processed by the entire pipeline. However, the imported code
|
|
would only get involved in semantic analysis, which, for the most part, is name
|
|
lookup, overload resolution, and template instantiation. All of these processes
|
|
are fast relative to the whole compilation process. More importantly, the
|
|
imported code only needs to be processed once during frontend code generation,
|
|
as well as the whole middle end and backend. So we could get a big win for the
|
|
compilation time in ``-O0``.
|
|
|
|
But with optimizations, things are different (the ``code generation`` part for
|
|
each end is omitted due to limited space):
|
|
|
|
.. code-block:: none
|
|
|
|
├-------- frontend ---------┼--------------- middle end --------------------┼------ backend ----┤
|
|
│ │ │ │
|
|
└--- parsing ---- sema -----┴--- optimizations --- IPO ---- optimizations---┴--- optimizations -┘
|
|
|
|
├-----------------------------------------------------------------------------------------------┐
|
|
│ │
|
|
│ source file │
|
|
│ │
|
|
└-----------------------------------------------------------------------------------------------┘
|
|
├---------------------------------------┐
|
|
│ │
|
|
│ │
|
|
│ imported code │
|
|
│ │
|
|
│ │
|
|
└---------------------------------------┘
|
|
|
|
It would be very unfortunate if we end up with worse performance when using
|
|
modules. The main concern is that when a source file is compiled, the compiler
|
|
needs to see the body of imported module units so that it can perform IPO
|
|
(InterProcedural Optimization, primarily inlining in practice) to optimize
|
|
functions in the current source file with the help of the information provided
|
|
by the imported module units. In other words, the imported code would be
|
|
processed again and again in importee units by optimizations (including IPO
|
|
itself). The optimizations before IPO and IPO itself are the most time-consuming
|
|
part in whole compilation process. So from this perspective, it might not be
|
|
possible to get the compile time improvements described, but there could be
|
|
time savings for optimizations after IPO and the whole backend.
|
|
|
|
Overall, at ``-O0`` the implementations of functions defined in a module will
|
|
not impact module users, but at higher optimization levels the definitions of
|
|
such functions are provided to user compilations for the purposes of
|
|
optimization (but definitions of these functions are still not included in the
|
|
use's object file). This means the build speedup at higher optimization levels
|
|
may be lower than expected given ``-O0`` experience, but does provide more
|
|
optimization opportunities.
|
|
|
|
Interoperability with Clang Modules
|
|
-----------------------------------
|
|
|
|
We **wish** to support Clang modules and standard C++ modules at the same time,
|
|
but the mixing them together is not well used/tested yet. Please file new
|
|
GitHub issues as you find interoperability problems.
|