Skip to content

Commit 4c4ff00

Browse files
committed
[C++20][Modules][Driver][HU 2/N] Add fmodule-header, fmodule-header=
These command-line flags are alternates to providing the -x c++-*-header indicators that we are building a header unit. Act on fmodule-header= for headers on the c/l: If we have x.hh -fmodule-header, then we should treat that header as a header unit input (equivalent to -xc++-header-unit-header x.hh). Likewise, for fmodule-header={user,system} the source should be now recognised as a header unit input (since this can affect the job list that we need). It's not practical to recognise a header without any suffix so -fmodule-header=system foo isn't going to happen. Although -fmodule-header=system foo.hh will work OK. However we can make it work if the user indicates that the item without a suffix is a valid header. (so -fmodule-header=system -xc++-header vector) Differential Revision: https://reviews.llvm.org/D121589
1 parent fc760c0 commit 4c4ff00

File tree

5 files changed

+143
-11
lines changed

5 files changed

+143
-11
lines changed

clang/docs/ClangCommandLineReference.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,6 +1176,14 @@ Validate the system headers that a module depends on when loading the module
11761176

11771177
Specify the prebuilt module path
11781178

1179+
.. option:: -fmodule-header
1180+
1181+
Build a C++20 header unit from a header specified.
1182+
1183+
.. option:: -fmodule-header=\[user,system\]
1184+
1185+
Build a C++20 header unit, but search for the header in the user or system header search paths respectively.
1186+
11791187
.. option:: --hip-path=<arg>
11801188

11811189
HIP runtime installation path, used for finding HIP version and adding HIP include path.

clang/include/clang/Driver/Driver.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,16 @@ enum LTOKind {
5656
LTOK_Unknown
5757
};
5858

59+
/// Whether headers used to construct C++20 module units should be looked
60+
/// up by the path supplied on the command line, or in the user or system
61+
/// search paths.
62+
enum ModuleHeaderMode {
63+
HeaderMode_None,
64+
HeaderMode_Default,
65+
HeaderMode_User,
66+
HeaderMode_System
67+
};
68+
5969
/// Driver - Encapsulate logic for constructing compilation processes
6070
/// from a set of gcc-driver-like command line arguments.
6171
class Driver {
@@ -84,6 +94,13 @@ class Driver {
8494
EmbedBitcode
8595
} BitcodeEmbed;
8696

97+
/// Header unit mode set by -fmodule-header={user,system}.
98+
ModuleHeaderMode CXX20HeaderType;
99+
100+
/// Set if we should process inputs and jobs with C++20 module
101+
/// interpretation.
102+
bool ModulesModeCXX20;
103+
87104
/// LTO mode selected via -f(no-)?lto(=.*)? options.
88105
LTOKind LTOMode;
89106

@@ -574,6 +591,12 @@ class Driver {
574591
/// ShouldEmitStaticLibrary - Should the linker emit a static library.
575592
bool ShouldEmitStaticLibrary(const llvm::opt::ArgList &Args) const;
576593

594+
/// Returns true if the user has indicated a C++20 header unit mode.
595+
bool hasHeaderMode() const { return CXX20HeaderType != HeaderMode_None; }
596+
597+
/// Get the mode for handling headers as set by fmodule-header{=}.
598+
ModuleHeaderMode getModuleHeaderMode() const { return CXX20HeaderType; }
599+
577600
/// Returns true if we are performing any kind of LTO.
578601
bool isUsingLTO(bool IsOffload = false) const {
579602
return getLTOMode(IsOffload) != LTOK_None;

clang/include/clang/Driver/Options.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2296,6 +2296,11 @@ defm implicit_modules : BoolFOption<"implicit-modules",
22962296
NegFlag<SetFalse, [CC1Option]>, PosFlag<SetTrue>, BothFlags<[NoXarchOption,CoreOption]>>;
22972297
def fretain_comments_from_system_headers : Flag<["-"], "fretain-comments-from-system-headers">, Group<f_Group>, Flags<[CC1Option]>,
22982298
MarshallingInfoFlag<LangOpts<"RetainCommentsFromSystemHeaders">>;
2299+
def fmodule_header : Flag <["-"], "fmodule-header">, Group<f_Group>,
2300+
Flags<[NoXarchOption]>, HelpText<"Build a C++20 Header Unit from a header.">;
2301+
def fmodule_header_EQ : Joined<["-"], "fmodule-header=">, Group<f_Group>,
2302+
Flags<[NoXarchOption]>, MetaVarName<"<kind>">,
2303+
HelpText<"Build a C++20 Header Unit from a header that should be found in the user (fmodule-header=user) or system (fmodule-header=system) search path.">;
22992304

23002305
def fno_knr_functions : Flag<["-"], "fno-knr-functions">, Group<f_Group>,
23012306
MarshallingInfoFlag<LangOpts<"DisableKNRFunctions">>,

clang/lib/Driver/Driver.cpp

Lines changed: 75 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -191,13 +191,14 @@ Driver::Driver(StringRef ClangExecutable, StringRef TargetTriple,
191191
DiagnosticsEngine &Diags, std::string Title,
192192
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS)
193193
: Diags(Diags), VFS(std::move(VFS)), Mode(GCCMode),
194-
SaveTemps(SaveTempsNone), BitcodeEmbed(EmbedNone), LTOMode(LTOK_None),
195-
ClangExecutable(ClangExecutable), SysRoot(DEFAULT_SYSROOT),
196-
DriverTitle(Title), CCCPrintBindings(false), CCPrintOptions(false),
197-
CCPrintHeaders(false), CCLogDiagnostics(false), CCGenDiagnostics(false),
198-
CCPrintProcessStats(false), TargetTriple(TargetTriple), Saver(Alloc),
199-
CheckInputsExist(true), GenReproducer(false),
200-
SuppressMissingInputWarning(false) {
194+
SaveTemps(SaveTempsNone), BitcodeEmbed(EmbedNone),
195+
CXX20HeaderType(HeaderMode_None), ModulesModeCXX20(false),
196+
LTOMode(LTOK_None), ClangExecutable(ClangExecutable),
197+
SysRoot(DEFAULT_SYSROOT), DriverTitle(Title), CCCPrintBindings(false),
198+
CCPrintOptions(false), CCPrintHeaders(false), CCLogDiagnostics(false),
199+
CCGenDiagnostics(false), CCPrintProcessStats(false),
200+
TargetTriple(TargetTriple), Saver(Alloc), CheckInputsExist(true),
201+
GenReproducer(false), SuppressMissingInputWarning(false) {
201202
// Provide a sane fallback if no VFS is specified.
202203
if (!this->VFS)
203204
this->VFS = llvm::vfs::getRealFileSystem();
@@ -337,9 +338,13 @@ phases::ID Driver::getFinalPhase(const DerivedArgList &DAL,
337338
CCGenDiagnostics) {
338339
FinalPhase = phases::Preprocess;
339340

340-
// --precompile only runs up to precompilation.
341+
// --precompile only runs up to precompilation.
342+
// Options that cause the output of C++20 compiled module interfaces or
343+
// header units have the same effect.
341344
} else if ((PhaseArg = DAL.getLastArg(options::OPT__precompile)) ||
342-
(PhaseArg = DAL.getLastArg(options::OPT_extract_api))) {
345+
(PhaseArg = DAL.getLastArg(options::OPT_extract_api)) ||
346+
(PhaseArg = DAL.getLastArg(options::OPT_fmodule_header,
347+
options::OPT_fmodule_header_EQ))) {
343348
FinalPhase = phases::Precompile;
344349
// -{fsyntax-only,-analyze,emit-ast} only run up to the compiler.
345350
} else if ((PhaseArg = DAL.getLastArg(options::OPT_fsyntax_only)) ||
@@ -1251,6 +1256,37 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
12511256
BitcodeEmbed = static_cast<BitcodeEmbedMode>(Model);
12521257
}
12531258

1259+
// Setting up the jobs for some precompile cases depends on whether we are
1260+
// treating them as PCH, implicit modules or C++20 ones.
1261+
// TODO: inferring the mode like this seems fragile (it meets the objective
1262+
// of not requiring anything new for operation, however).
1263+
const Arg *Std = Args.getLastArg(options::OPT_std_EQ);
1264+
ModulesModeCXX20 =
1265+
!Args.hasArg(options::OPT_fmodules) && Std &&
1266+
(Std->containsValue("c++20") || Std->containsValue("c++2b") ||
1267+
Std->containsValue("c++2a") || Std->containsValue("c++latest"));
1268+
1269+
// Process -fmodule-header{=} flags.
1270+
if (Arg *A = Args.getLastArg(options::OPT_fmodule_header_EQ,
1271+
options::OPT_fmodule_header)) {
1272+
// These flags force C++20 handling of headers.
1273+
ModulesModeCXX20 = true;
1274+
if (A->getOption().matches(options::OPT_fmodule_header))
1275+
CXX20HeaderType = HeaderMode_Default;
1276+
else {
1277+
StringRef ArgName = A->getValue();
1278+
unsigned Kind = llvm::StringSwitch<unsigned>(ArgName)
1279+
.Case("user", HeaderMode_User)
1280+
.Case("system", HeaderMode_System)
1281+
.Default(~0U);
1282+
if (Kind == ~0U) {
1283+
Diags.Report(diag::err_drv_invalid_value)
1284+
<< A->getAsString(Args) << ArgName;
1285+
} else
1286+
CXX20HeaderType = static_cast<ModuleHeaderMode>(Kind);
1287+
}
1288+
}
1289+
12541290
std::unique_ptr<llvm::opt::InputArgList> UArgs =
12551291
std::make_unique<InputArgList>(std::move(Args));
12561292

@@ -2220,8 +2256,11 @@ bool Driver::DiagnoseInputExistence(const DerivedArgList &Args, StringRef Value,
22202256
return true;
22212257

22222258
// If it's a header to be found in the system or user search path, then defer
2223-
// complaints about its absence until those searches can be done.
2224-
if (Ty == types::TY_CXXSHeader || Ty == types::TY_CXXUHeader)
2259+
// complaints about its absence until those searches can be done. When we
2260+
// are definitely processing headers for C++20 header units, extend this to
2261+
// allow the user to put "-fmodule-header -xc++-header vector" for example.
2262+
if (Ty == types::TY_CXXSHeader || Ty == types::TY_CXXUHeader ||
2263+
(ModulesModeCXX20 && Ty == types::TY_CXXHeader))
22252264
return true;
22262265

22272266
if (getVFS().exists(Value))
@@ -2287,6 +2326,21 @@ bool Driver::DiagnoseInputExistence(const DerivedArgList &Args, StringRef Value,
22872326
return false;
22882327
}
22892328

2329+
// Get the C++20 Header Unit type corresponding to the input type.
2330+
static types::ID CXXHeaderUnitType(ModuleHeaderMode HM) {
2331+
switch (HM) {
2332+
case HeaderMode_User:
2333+
return types::TY_CXXUHeader;
2334+
case HeaderMode_System:
2335+
return types::TY_CXXSHeader;
2336+
case HeaderMode_Default:
2337+
break;
2338+
case HeaderMode_None:
2339+
llvm_unreachable("should not be called in this case");
2340+
}
2341+
return types::TY_CXXHUHeader;
2342+
}
2343+
22902344
// Construct a the list of inputs and their types.
22912345
void Driver::BuildInputs(const ToolChain &TC, DerivedArgList &Args,
22922346
InputList &Inputs) const {
@@ -2406,6 +2460,11 @@ void Driver::BuildInputs(const ToolChain &TC, DerivedArgList &Args,
24062460
else if (Args.hasArg(options::OPT_ObjCXX))
24072461
Ty = types::TY_ObjCXX;
24082462
}
2463+
2464+
// Disambiguate headers that are meant to be header units from those
2465+
// intended to be PCH.
2466+
if (Ty == types::TY_CXXHeader && hasHeaderMode())
2467+
Ty = CXXHeaderUnitType(CXX20HeaderType);
24092468
} else {
24102469
assert(InputTypeArg && "InputType set w/o InputTypeArg");
24112470
if (!InputTypeArg->getOption().matches(options::OPT_x)) {
@@ -2457,6 +2516,11 @@ void Driver::BuildInputs(const ToolChain &TC, DerivedArgList &Args,
24572516
Diag(clang::diag::err_drv_unknown_language) << A->getValue();
24582517
InputType = types::TY_Object;
24592518
}
2519+
2520+
// If the user has put -fmodule-header{,=} then we treat C++ headers as
2521+
// header unit inputs. So we 'promote' -xc++-header appropriately.
2522+
if (InputType == types::TY_CXXHeader && hasHeaderMode())
2523+
InputType = CXXHeaderUnitType(CXX20HeaderType);
24602524
} else if (A->getOption().getID() == options::OPT_U) {
24612525
assert(A->getNumValues() == 1 && "The /U option has one value.");
24622526
StringRef Val = A->getValue(0);
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Test user-facing command line options to generate C++20 header units.
2+
3+
// RUN: %clang -### -std=c++20 -fmodule-header=user foo.hh 2>&1 | \
4+
// RUN: FileCheck -check-prefix=CHECK-USER %s
5+
6+
// RUN: %clang -### -std=c++20 -fmodule-header=system foo.hh 2>&1 | \
7+
// RUN: FileCheck -check-prefix=CHECK-SYS1 %s
8+
9+
// RUN: %clang -### -std=c++20 -fmodule-header=system \
10+
// RUN: -xc++-system-header vector 2>&1 | FileCheck -check-prefix=CHECK-SYS2 %s
11+
12+
// RUN: %clang -### -std=c++20 -fmodule-header=system \
13+
// RUN: -xc++-header vector 2>&1 | FileCheck -check-prefix=CHECK-SYS2 %s
14+
15+
// RUN: %clang -### -std=c++20 -fmodule-header %/S/Inputs/header-unit-01.hh \
16+
// RUN: 2>&1 | FileCheck -check-prefix=CHECK-ABS %s -DTDIR=%/S/Inputs
17+
18+
// CHECK-USER: "-emit-header-unit"
19+
// CHECK-USER-SAME: "-o" "foo.pcm"
20+
// CHECK-USER-SAME: "-x" "c++-user-header" "foo.hh"
21+
22+
// CHECK-SYS1: "-emit-header-unit"
23+
// CHECK-SYS1-SAME: "-o" "foo.pcm"
24+
// CHECK-SYS1-SAME: "-x" "c++-system-header" "foo.hh"
25+
26+
// CHECK-SYS2: "-emit-header-unit"
27+
// CHECK-SYS2-SAME: "-o" "vector.pcm"
28+
// CHECK-SYS2-SAME: "-x" "c++-system-header" "vector"
29+
30+
// CHECK-ABS: "-emit-header-unit"
31+
// CHECK-ABS-SAME: "-o" "header-unit-01.pcm"
32+
// CHECK-ABS-SAME: "-x" "c++-header-unit-header" "[[TDIR]]/header-unit-01.hh"

0 commit comments

Comments
 (0)