Skip to content

Commit fec3ce8

Browse files
committed
[clang][modules-driver] Add initial support for driver-managed module builds
With -fmodules-driver enabled, the Clang driver provides native support for: - Named module imports defined in other source files on the command line - Standard library imports (import std; and import std.compat;) - Clang modules discovered via module map files Regular translation units can import both Clang modules and C++20 named modules. Importing a Clang module into a C++20 named module interface unit, or vice versa, is not supported by this patch.
1 parent 9760106 commit fec3ce8

File tree

11 files changed

+1796
-58
lines changed

11 files changed

+1796
-58
lines changed

clang/include/clang/Basic/DiagnosticDriverKinds.td

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,22 @@ def remark_found_cxx20_module_usage : Remark<
587587
def remark_performing_driver_managed_module_build : Remark<
588588
"performing driver managed module build">,
589589
InGroup<ModulesDriver>;
590+
def remark_std_module_manifest_path : Remark<
591+
"using std modules manifest: '%0'">, InGroup<ModulesDriver>;
592+
def err_failed_parse_modules_manifest_json: Error<
593+
"failed to parse the std modules manifest">;
594+
def err_failed_depdendency_scan : Error<
595+
"failed to perform dependency scan">;
596+
def remark_failed_dependency_scan_for_input : Remark<
597+
"dependency scan failed for source input '%0'">,
598+
InGroup<ModulesDriver>;
599+
def err_mod_graph_named_module_redefinition : Error<
600+
"duplicate definitions of C++20 named module '%0' in '%1' and '%2'">;
601+
def err_building_depdendency_graph : Error<
602+
"failed to construct the module dependency graph">;
603+
def remark_printing_module_graph : Remark<
604+
"printing module dependency graph">,
605+
InGroup<ModulesDriver>;
590606

591607
def warn_drv_delayed_template_parsing_after_cxx20 : Warning<
592608
"-fdelayed-template-parsing is deprecated after C++20">,

clang/include/clang/Driver/Driver.h

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ class Driver {
135135
/// interpretation.
136136
bool ModulesModeCXX20;
137137

138+
/// Set if the dirver should plan the compilation after scanning module
139+
/// dependencies, using the scan results (set by -f(no-)modules-driver.)
140+
bool DriverManagedModulesBuild;
141+
138142
/// LTO mode selected via -f(no-)?lto(=.*)? options.
139143
LTOKind LTOMode;
140144

@@ -796,13 +800,6 @@ class Driver {
796800
/// compilation based on which -f(no-)?lto(=.*)? option occurs last.
797801
void setLTOMode(const llvm::opt::ArgList &Args);
798802

799-
/// Scans the leading lines of the C++ source inputs to detect C++20 module
800-
/// usage.
801-
///
802-
/// \returns True if module usage is detected, false otherwise, or an error on
803-
/// read failure.
804-
llvm::ErrorOr<bool>
805-
ScanInputsForCXX20ModulesUsage(const InputList &Inputs) const;
806803
/// Retrieves a ToolChain for a particular \p Target triple.
807804
///
808805
/// Will cache ToolChains for the life of the driver object, and create them

clang/include/clang/Driver/Job.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,8 @@ class Command {
221221

222222
const char *getExecutable() const { return Executable; }
223223

224+
llvm::opt::ArgStringList &getArguments() { return Arguments; }
225+
224226
const llvm::opt::ArgStringList &getArguments() const { return Arguments; }
225227

226228
const std::vector<InputInfo> &getInputInfos() const { return InputInfoList; }
@@ -277,6 +279,7 @@ class JobList {
277279
/// Clear the job list.
278280
void clear();
279281

282+
list_type &getJobs() { return Jobs; }
280283
const list_type &getJobs() const { return Jobs; }
281284

282285
bool empty() const { return Jobs.empty(); }
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//===- DependencyScanner.h - Module dependency discovery --------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
///
9+
/// \file
10+
/// This file defines the module dependency graph and dependency-scanning
11+
/// functionality.
12+
///
13+
//===----------------------------------------------------------------------===//
14+
15+
#ifndef LLVM_CLANG_DRIVER_DEPENDENCYSCANNER_H
16+
#define LLVM_CLANG_DRIVER_DEPENDENCYSCANNER_H
17+
18+
#include "clang/Driver/Types.h"
19+
#include "llvm/ADT/SmallString.h"
20+
#include "llvm/Support/VirtualFileSystem.h"
21+
22+
namespace clang {
23+
class DiagnosticsEngine;
24+
namespace driver {
25+
class Compilation;
26+
} // namespace driver
27+
} // namespace clang
28+
29+
namespace clang::driver::modules {
30+
31+
using InputTy = std::pair<types::ID, const llvm::opt::Arg *>;
32+
33+
using InputList = llvm::SmallVector<InputTy, 16>;
34+
35+
/// Checks whether the -fmodules-driver feature should be implicitly enabled.
36+
///
37+
/// When -fmodules-driver is no longer experimental, it should be enabled by
38+
/// default iff both conditions are met:
39+
/// (1) there are two or more C++ source inputs; and
40+
/// (2) at least one input uses C++20 named modules.
41+
bool shouldEnableModulesDriver(const InputList &Inputs,
42+
llvm::vfs::FileSystem &VFS,
43+
DiagnosticsEngine &Diags);
44+
45+
/// Appends the std and std.compat module inputs.
46+
bool ensureNamedModuleStdLibraryInputs(Compilation &C, InputList &Inputs);
47+
48+
bool performDriverModuleBuild(Compilation &C, DiagnosticsEngine &Diags);
49+
50+
} // namespace clang::driver::modules
51+
52+
#endif

clang/include/clang/Driver/Options.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3302,6 +3302,12 @@ def fmodules_driver : Flag<["-"], "fmodules-driver">,
33023302
def fno_modules_driver : Flag<["-"], "fno-modules-driver">,
33033303
Group<f_Group>, Visibility<[ClangOption]>,
33043304
HelpText<"Disable support for driver managed module builds (experimental)">;
3305+
def fimplicit_import_std : Flag<["-"], "fimplicit-import-std">,
3306+
Group<f_Group>, Visibility<[ClangOption]>,
3307+
HelpText<"Implicitly add the std module when discovered in driver managed module builds">;
3308+
def fno_implicit_import_std : Flag<["-"], "fno-implicit-import-std">,
3309+
Group<f_Group>, Visibility<[ClangOption]>,
3310+
HelpText<"Don't implicitly add the std module when discovered in driver managed module builds">;
33053311

33063312
def experimental_modules_reduced_bmi : Flag<["-"], "fexperimental-modules-reduced-bmi">,
33073313
Group<f_Group>, Visibility<[ClangOption, CC1Option]>, Alias<fmodules_reduced_bmi>;

clang/lib/Driver/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ add_clang_library(clangDriver
2121
Driver.cpp
2222
DriverOptions.cpp
2323
Job.cpp
24+
ModulesDriver.cpp
2425
Multilib.cpp
2526
MultilibBuilder.cpp
2627
OffloadBundler.cpp
@@ -98,6 +99,7 @@ add_clang_library(clangDriver
9899

99100
LINK_LIBS
100101
clangBasic
102+
clangDependencyScanning
101103
clangLex
102104
${system_libs}
103105
)

clang/lib/Driver/Driver.cpp

Lines changed: 33 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
#include "clang/Driver/Compilation.h"
6161
#include "clang/Driver/InputInfo.h"
6262
#include "clang/Driver/Job.h"
63+
#include "clang/Driver/ModulesDriver.h"
6364
#include "clang/Driver/Options.h"
6465
#include "clang/Driver/Phases.h"
6566
#include "clang/Driver/SanitizerArgs.h"
@@ -1473,33 +1474,6 @@ bool Driver::loadDefaultConfigFiles(llvm::cl::ExpansionContext &ExpCtx) {
14731474
return false;
14741475
}
14751476

1476-
static bool hasCXXModuleInputType(const Driver::InputList &Inputs) {
1477-
const auto IsTypeCXXModule = [](const auto &Input) -> bool {
1478-
const auto TypeID = Input.first;
1479-
return (TypeID == types::TY_CXXModule);
1480-
};
1481-
return llvm::any_of(Inputs, IsTypeCXXModule);
1482-
}
1483-
1484-
llvm::ErrorOr<bool>
1485-
Driver::ScanInputsForCXX20ModulesUsage(const InputList &Inputs) const {
1486-
const auto CXXInputs = llvm::make_filter_range(
1487-
Inputs, [](const auto &Input) { return types::isCXX(Input.first); });
1488-
for (const auto &Input : CXXInputs) {
1489-
StringRef Filename = Input.second->getSpelling();
1490-
auto ErrOrBuffer = VFS->getBufferForFile(Filename);
1491-
if (!ErrOrBuffer)
1492-
return ErrOrBuffer.getError();
1493-
const auto Buffer = std::move(*ErrOrBuffer);
1494-
1495-
if (scanInputForCXX20ModulesUsage(Buffer->getBuffer())) {
1496-
Diags.Report(diag::remark_found_cxx20_module_usage) << Filename;
1497-
return true;
1498-
}
1499-
}
1500-
return false;
1501-
}
1502-
15031477
Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
15041478
llvm::PrettyStackTraceString CrashInfo("Compilation construction");
15051479

@@ -1853,6 +1827,18 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
18531827
}
18541828
}
18551829

1830+
if (C->getArgs().hasFlag(options::OPT_fmodules_driver,
1831+
options::OPT_fno_modules_driver, false)) {
1832+
// The detection logic for this is kept here only for diagnostics until
1833+
// is enabled by default.
1834+
modules::shouldEnableModulesDriver(Inputs, getVFS(), Diags);
1835+
Diags.Report(diag::remark_performing_driver_managed_module_build);
1836+
if (C->getArgs().hasFlag(options::OPT_fimplicit_import_std,
1837+
options::OPT_fno_implicit_import_std, true)) {
1838+
modules::ensureNamedModuleStdLibraryInputs(*C, Inputs);
1839+
}
1840+
}
1841+
18561842
// Populate the tool chains for the offloading devices, if any.
18571843
CreateOffloadingDeviceToolChains(*C, Inputs);
18581844

@@ -1863,26 +1849,6 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
18631849
else
18641850
BuildActions(*C, C->getArgs(), Inputs, C->getActions());
18651851

1866-
if (C->getArgs().hasFlag(options::OPT_fmodules_driver,
1867-
options::OPT_fno_modules_driver, false)) {
1868-
// TODO: When -fmodules-driver is no longer experimental, it should be
1869-
// enabled by default only if both conditions are met: (1) there are two or
1870-
// more C++ source inputs; and (2) at least one input uses C++20 named
1871-
// modules.
1872-
// The detection logic for this is kept here only for diagnostics until
1873-
// is enabled by default.
1874-
bool UsesCXXModules = hasCXXModuleInputType(Inputs);
1875-
if (!UsesCXXModules) {
1876-
const auto ErrOrScanResult = ScanInputsForCXX20ModulesUsage(Inputs);
1877-
if (!ErrOrScanResult) {
1878-
Diags.Report(diag::err_cannot_open_file)
1879-
<< ErrOrScanResult.getError().message();
1880-
}
1881-
UsesCXXModules = *ErrOrScanResult;
1882-
}
1883-
Diags.Report(diag::remark_performing_driver_managed_module_build);
1884-
}
1885-
18861852
if (CCCPrintPhases) {
18871853
PrintActions(*C);
18881854
return C;
@@ -4236,10 +4202,20 @@ void Driver::handleArguments(Compilation &C, DerivedArgList &Args,
42364202
YcArg = nullptr;
42374203
}
42384204

4239-
if (Args.hasArgNoClaim(options::OPT_fmodules_driver))
4240-
// TODO: Check against all incompatible -fmodules-driver arguments
4241-
if (!ModulesModeCXX20 && !Args.hasArgNoClaim(options::OPT_fmodules))
4242-
Args.eraseArg(options::OPT_fmodules_driver);
4205+
if (Args.hasArgNoClaim(options::OPT_fmodules_driver)) {
4206+
// HACK: This should be only added for the Standard library jobs, explicitly
4207+
// created by the modules driver.
4208+
MakeInputArg(Args, getOpts(),
4209+
Args.MakeArgString("-Wno-reserved-module-identifier"));
4210+
if (Args.hasArg(options::OPT_fmodules)) {
4211+
Args.eraseArg(options::OPT_fmodules);
4212+
Arg *Arg = Args.MakeSeparateArg(
4213+
nullptr, getOpts().getOption(options::OPT_fimplicit_modules),
4214+
Args.MakeArgString(("-fimplicit-modules")));
4215+
Arg->claim();
4216+
Args.append(Arg);
4217+
}
4218+
}
42434219

42444220
Arg *FinalPhaseArg;
42454221
phases::ID FinalPhase = getFinalPhase(Args, &FinalPhaseArg);
@@ -5427,6 +5403,12 @@ void Driver::BuildJobs(Compilation &C) const {
54275403
}
54285404
}
54295405
}
5406+
if (C.getArgs().hasFlag(options::OPT_fmodules_driver,
5407+
options::OPT_fno_modules_driver, false)) {
5408+
auto Success = modules::performDriverModuleBuild(C, C.getDriver().Diags);
5409+
if (!Success)
5410+
return;
5411+
}
54305412
}
54315413

54325414
namespace {

0 commit comments

Comments
 (0)