mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-27 15:41:46 +00:00
[C++20][Modules][HU 1/5] Introduce header units as a module type.
This is the first in a series of patches that introduce C++20 importable header units. These differ from clang header modules in that: (a) they are identifiable by an internal name (b) they represent the top level source for a single header - although that might include or import other headers. We name importable header units with the path by which they are specified (although that need not be the absolute path for the file). So "foo/bar.h" would have a name "foo/bar.h". Header units are made a separate module type so that we can deal with diagnosing places where they are permitted but a named module is not. Differential Revision: https://reviews.llvm.org/D121095
This commit is contained in:
parent
c48b4641c7
commit
6c0e60e884
@ -166,7 +166,7 @@ BENIGN_LANGOPT(HeinousExtensions , 1, 0, "extensions that we really don't like a
|
||||
LANGOPT(Modules , 1, 0, "modules semantics")
|
||||
COMPATIBLE_LANGOPT(ModulesTS , 1, 0, "C++ Modules TS syntax")
|
||||
COMPATIBLE_LANGOPT(CPlusPlusModules, 1, 0, "C++ modules syntax")
|
||||
BENIGN_ENUM_LANGOPT(CompilingModule, CompilingModuleKind, 2, CMK_None,
|
||||
BENIGN_ENUM_LANGOPT(CompilingModule, CompilingModuleKind, 3, CMK_None,
|
||||
"compiling a module interface")
|
||||
BENIGN_LANGOPT(CompilingPCH, 1, 0, "building a pch")
|
||||
BENIGN_LANGOPT(BuildingPCHWithObjectFile, 1, 0, "building a pch which has a corresponding object file")
|
||||
|
@ -90,6 +90,9 @@ public:
|
||||
/// Compiling a module from a list of header files.
|
||||
CMK_HeaderModule,
|
||||
|
||||
/// Compiling a module header unit.
|
||||
CMK_HeaderUnit,
|
||||
|
||||
/// Compiling a C++ modules TS module interface unit.
|
||||
CMK_ModuleInterface,
|
||||
};
|
||||
|
@ -109,6 +109,9 @@ public:
|
||||
/// This is a C++20 module interface unit.
|
||||
ModuleInterfaceUnit,
|
||||
|
||||
/// This is a C++ 20 header unit.
|
||||
ModuleHeaderUnit,
|
||||
|
||||
/// This is a C++ 20 module partition interface.
|
||||
ModulePartitionInterface,
|
||||
|
||||
|
@ -5640,6 +5640,8 @@ def emit_module_interface : Flag<["-"], "emit-module-interface">,
|
||||
HelpText<"Generate pre-compiled module file from a C++ module interface">;
|
||||
def emit_header_module : Flag<["-"], "emit-header-module">,
|
||||
HelpText<"Generate pre-compiled module file from a set of header files">;
|
||||
def emit_header_unit : Flag<["-"], "emit-header-unit">,
|
||||
HelpText<"Generate C++20 header units from header files">;
|
||||
def emit_pch : Flag<["-"], "emit-pch">,
|
||||
HelpText<"Generate pre-compiled header file">;
|
||||
def emit_llvm_bc : Flag<["-"], "emit-llvm-bc">,
|
||||
|
@ -168,6 +168,15 @@ private:
|
||||
CreateOutputFile(CompilerInstance &CI, StringRef InFile) override;
|
||||
};
|
||||
|
||||
class GenerateHeaderUnitAction : public GenerateModuleAction {
|
||||
|
||||
private:
|
||||
bool BeginSourceFileAction(CompilerInstance &CI) override;
|
||||
|
||||
std::unique_ptr<raw_pwrite_stream>
|
||||
CreateOutputFile(CompilerInstance &CI, StringRef InFile) override;
|
||||
};
|
||||
|
||||
class SyntaxOnlyAction : public ASTFrontendAction {
|
||||
protected:
|
||||
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
|
||||
|
@ -90,6 +90,9 @@ enum ActionKind {
|
||||
/// Generate pre-compiled module from a set of header files.
|
||||
GenerateHeaderModule,
|
||||
|
||||
/// Generate a C++20 header unit module from a header file.
|
||||
GenerateHeaderUnit,
|
||||
|
||||
/// Generate pre-compiled header.
|
||||
GeneratePCH,
|
||||
|
||||
|
@ -564,6 +564,10 @@ public:
|
||||
/// Create a header module from the specified list of headers.
|
||||
Module *createHeaderModule(StringRef Name, ArrayRef<Module::Header> Headers);
|
||||
|
||||
/// Create a C++20 header unit.
|
||||
Module *createHeaderUnit(SourceLocation Loc, StringRef Name,
|
||||
Module::Header H);
|
||||
|
||||
/// Infer the contents of a framework module map from the given
|
||||
/// framework directory.
|
||||
Module *inferFrameworkModule(const DirectoryEntry *FrameworkDir,
|
||||
|
@ -2977,6 +2977,12 @@ public:
|
||||
NotACXX20Module ///< Not a C++20 TU, or an invalid state was found.
|
||||
};
|
||||
|
||||
private:
|
||||
/// The parser has begun a translation unit to be compiled as a C++20
|
||||
/// Header Unit, helper for ActOnStartOfTranslationUnit() only.
|
||||
void HandleStartOfHeaderUnit();
|
||||
|
||||
public:
|
||||
/// The parser has processed a module-declaration that begins the definition
|
||||
/// of a module interface or implementation.
|
||||
DeclGroupPtrTy ActOnModuleDecl(SourceLocation StartLoc,
|
||||
|
@ -1561,6 +1561,7 @@ Module *Decl::getOwningModuleForLinkage(bool IgnoreLinkage) const {
|
||||
case Module::ModulePartitionImplementation:
|
||||
return M;
|
||||
|
||||
case Module::ModuleHeaderUnit:
|
||||
case Module::GlobalModuleFragment: {
|
||||
// External linkage declarations in the global module have no owning module
|
||||
// for linkage purposes. But internal linkage declarations in the global
|
||||
@ -1576,7 +1577,8 @@ Module *Decl::getOwningModuleForLinkage(bool IgnoreLinkage) const {
|
||||
InternalLinkage = !ND->hasExternalFormalLinkage();
|
||||
else
|
||||
InternalLinkage = isInAnonymousNamespace();
|
||||
return InternalLinkage ? M->Parent : nullptr;
|
||||
return InternalLinkage ? M->Kind == Module::ModuleHeaderUnit ? M : M->Parent
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
case Module::PrivateModuleFragment:
|
||||
|
@ -2420,6 +2420,7 @@ static const auto &getFrontendActionTable() {
|
||||
{frontend::GenerateModule, OPT_emit_module},
|
||||
{frontend::GenerateModuleInterface, OPT_emit_module_interface},
|
||||
{frontend::GenerateHeaderModule, OPT_emit_header_module},
|
||||
{frontend::GenerateHeaderUnit, OPT_emit_header_unit},
|
||||
{frontend::GeneratePCH, OPT_emit_pch},
|
||||
{frontend::GenerateInterfaceStubs, OPT_emit_interface_stubs},
|
||||
{frontend::InitOnly, OPT_init_only},
|
||||
@ -2436,7 +2437,7 @@ static const auto &getFrontendActionTable() {
|
||||
{frontend::MigrateSource, OPT_migrate},
|
||||
{frontend::RunPreprocessorOnly, OPT_Eonly},
|
||||
{frontend::PrintDependencyDirectivesSourceMinimizerOutput,
|
||||
OPT_print_dependency_directives_minimized_source},
|
||||
OPT_print_dependency_directives_minimized_source},
|
||||
};
|
||||
|
||||
return Table;
|
||||
@ -4160,6 +4161,7 @@ static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) {
|
||||
case frontend::GenerateModule:
|
||||
case frontend::GenerateModuleInterface:
|
||||
case frontend::GenerateHeaderModule:
|
||||
case frontend::GenerateHeaderUnit:
|
||||
case frontend::GeneratePCH:
|
||||
case frontend::GenerateInterfaceStubs:
|
||||
case frontend::ParseSyntaxOnly:
|
||||
|
@ -336,6 +336,21 @@ GenerateHeaderModuleAction::CreateOutputFile(CompilerInstance &CI,
|
||||
return CI.createDefaultOutputFile(/*Binary=*/true, InFile, "pcm");
|
||||
}
|
||||
|
||||
bool GenerateHeaderUnitAction::BeginSourceFileAction(CompilerInstance &CI) {
|
||||
if (!CI.getLangOpts().CPlusPlusModules) {
|
||||
CI.getDiagnostics().Report(diag::err_module_interface_requires_cpp_modules);
|
||||
return false;
|
||||
}
|
||||
CI.getLangOpts().setCompilingModule(LangOptions::CMK_HeaderUnit);
|
||||
return GenerateModuleAction::BeginSourceFileAction(CI);
|
||||
}
|
||||
|
||||
std::unique_ptr<raw_pwrite_stream>
|
||||
GenerateHeaderUnitAction::CreateOutputFile(CompilerInstance &CI,
|
||||
StringRef InFile) {
|
||||
return CI.createDefaultOutputFile(/*Binary=*/true, InFile, "pcm");
|
||||
}
|
||||
|
||||
SyntaxOnlyAction::~SyntaxOnlyAction() {
|
||||
}
|
||||
|
||||
@ -818,6 +833,8 @@ static StringRef ModuleKindName(Module::ModuleKind MK) {
|
||||
return "Partition Interface";
|
||||
case Module::ModulePartitionImplementation:
|
||||
return "Partition Implementation";
|
||||
case Module::ModuleHeaderUnit:
|
||||
return "Header Unit";
|
||||
case Module::GlobalModuleFragment:
|
||||
return "Global Module Fragment";
|
||||
case Module::PrivateModuleFragment:
|
||||
|
@ -67,6 +67,8 @@ CreateFrontendBaseAction(CompilerInstance &CI) {
|
||||
return std::make_unique<GenerateModuleInterfaceAction>();
|
||||
case GenerateHeaderModule:
|
||||
return std::make_unique<GenerateHeaderModuleAction>();
|
||||
case GenerateHeaderUnit:
|
||||
return std::make_unique<GenerateHeaderUnitAction>();
|
||||
case GeneratePCH: return std::make_unique<GeneratePCHAction>();
|
||||
case GenerateInterfaceStubs:
|
||||
return std::make_unique<GenerateInterfaceStubsAction>();
|
||||
|
@ -905,6 +905,19 @@ Module *ModuleMap::createHeaderModule(StringRef Name,
|
||||
return Result;
|
||||
}
|
||||
|
||||
Module *ModuleMap::createHeaderUnit(SourceLocation Loc, StringRef Name,
|
||||
Module::Header H) {
|
||||
assert(LangOpts.CurrentModule == Name && "module name mismatch");
|
||||
assert(!Modules[Name] && "redefining existing module");
|
||||
|
||||
auto *Result = new Module(Name, Loc, nullptr, /*IsFramework*/ false,
|
||||
/*IsExplicit*/ false, NumCreatedModules++);
|
||||
Result->Kind = Module::ModuleHeaderUnit;
|
||||
Modules[Name] = SourceModule = Result;
|
||||
addHeader(Result, H, NormalHeader);
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// For a framework module, infer the framework against which we
|
||||
/// should link.
|
||||
static void inferFrameworkLink(Module *Mod, const DirectoryEntry *FrameworkDir,
|
||||
|
@ -2468,8 +2468,9 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
|
||||
break;
|
||||
case Sema::ModuleImportState::GlobalFragment:
|
||||
// We can only have pre-processor directives in the global module
|
||||
// fragment. We can, however have a header unit import here.
|
||||
if (!HeaderUnit)
|
||||
// fragment. We cannot import a named modules here, however we have a
|
||||
// header unit import.
|
||||
if (!HeaderUnit || HeaderUnit->Kind != Module::ModuleKind::ModuleHeaderUnit)
|
||||
Diag(ImportLoc, diag::err_import_in_wrong_fragment) << IsPartition << 0;
|
||||
else
|
||||
SeenError = false;
|
||||
|
@ -1030,9 +1030,13 @@ void Sema::emitAndClearUnusedLocalTypedefWarnings() {
|
||||
/// is parsed. Note that the ASTContext may have already injected some
|
||||
/// declarations.
|
||||
void Sema::ActOnStartOfTranslationUnit() {
|
||||
if (getLangOpts().ModulesTS &&
|
||||
(getLangOpts().getCompilingModule() == LangOptions::CMK_ModuleInterface ||
|
||||
getLangOpts().getCompilingModule() == LangOptions::CMK_None)) {
|
||||
if (getLangOpts().CPlusPlusModules &&
|
||||
getLangOpts().getCompilingModule() == LangOptions::CMK_HeaderUnit)
|
||||
HandleStartOfHeaderUnit();
|
||||
else if (getLangOpts().ModulesTS &&
|
||||
(getLangOpts().getCompilingModule() ==
|
||||
LangOptions::CMK_ModuleInterface ||
|
||||
getLangOpts().getCompilingModule() == LangOptions::CMK_None)) {
|
||||
// We start in an implied global module fragment.
|
||||
SourceLocation StartOfTU =
|
||||
SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID());
|
||||
|
@ -97,6 +97,38 @@ Sema::ActOnGlobalModuleFragmentDecl(SourceLocation ModuleLoc) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Sema::HandleStartOfHeaderUnit() {
|
||||
assert(getLangOpts().CPlusPlusModules &&
|
||||
"Header units are only valid for C++20 modules");
|
||||
SourceLocation StartOfTU =
|
||||
SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID());
|
||||
|
||||
StringRef HUName = getLangOpts().CurrentModule;
|
||||
if (HUName.empty()) {
|
||||
HUName = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID())->getName();
|
||||
const_cast<LangOptions &>(getLangOpts()).CurrentModule = HUName.str();
|
||||
}
|
||||
|
||||
auto &Map = PP.getHeaderSearchInfo().getModuleMap();
|
||||
// TODO: Make the C++20 header lookup independent.
|
||||
Module::Header H{getLangOpts().CurrentModule, getLangOpts().CurrentModule,
|
||||
SourceMgr.getFileEntryForID(SourceMgr.getMainFileID())};
|
||||
Module *Mod = Map.createHeaderUnit(StartOfTU, HUName, H);
|
||||
assert(Mod && "module creation should not fail");
|
||||
ModuleScopes.push_back({}); // No GMF
|
||||
ModuleScopes.back().BeginLoc = StartOfTU;
|
||||
ModuleScopes.back().Module = Mod;
|
||||
ModuleScopes.back().ModuleInterface = true;
|
||||
ModuleScopes.back().IsPartition = false;
|
||||
VisibleModules.setVisible(Mod, StartOfTU);
|
||||
|
||||
// From now on, we have an owning module for all declarations we see.
|
||||
// All of these are implicitly exported.
|
||||
auto *TU = Context.getTranslationUnitDecl();
|
||||
TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::Visible);
|
||||
TU->setLocalOwningModule(Mod);
|
||||
}
|
||||
|
||||
Sema::DeclGroupPtrTy
|
||||
Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
|
||||
ModuleDeclKind MDK, ModuleIdPath Path,
|
||||
@ -149,6 +181,7 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
|
||||
return nullptr;
|
||||
|
||||
case LangOptions::CMK_HeaderModule:
|
||||
case LangOptions::CMK_HeaderUnit:
|
||||
Diag(ModuleLoc, diag::err_module_decl_in_header_module);
|
||||
return nullptr;
|
||||
}
|
||||
@ -310,6 +343,7 @@ Sema::ActOnPrivateModuleFragmentDecl(SourceLocation ModuleLoc,
|
||||
case Module::GlobalModuleFragment:
|
||||
case Module::ModulePartitionImplementation:
|
||||
case Module::ModulePartitionInterface:
|
||||
case Module::ModuleHeaderUnit:
|
||||
Diag(PrivateLoc, diag::err_private_module_fragment_not_module);
|
||||
return nullptr;
|
||||
|
||||
@ -480,7 +514,13 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
|
||||
Mod->Kind == Module::ModuleKind::ModulePartitionImplementation) {
|
||||
Diag(ExportLoc, diag::err_export_partition_impl)
|
||||
<< SourceRange(ExportLoc, Path.back().second);
|
||||
} else if (!ModuleScopes.empty() && ModuleScopes.back().ModuleInterface) {
|
||||
} else if (!ModuleScopes.empty() &&
|
||||
(ModuleScopes.back().ModuleInterface ||
|
||||
(getLangOpts().CPlusPlusModules &&
|
||||
ModuleScopes.back().Module->isGlobalModule()))) {
|
||||
assert((!ModuleScopes.back().Module->isGlobalModule() ||
|
||||
Mod->Kind == Module::ModuleKind::ModuleHeaderUnit) &&
|
||||
"should only be importing a header unit into the GMF");
|
||||
// Re-export the module if the imported module is exported.
|
||||
// Note that we don't need to add re-exported module to Imports field
|
||||
// since `Exports` implies the module is imported already.
|
||||
|
104
clang/test/Modules/cxx20-hu-01.cpp
Normal file
104
clang/test/Modules/cxx20-hu-01.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
// Test generation and import of simple C++20 Header Units.
|
||||
|
||||
// RUN: rm -rf %t
|
||||
// RUN: mkdir -p %t
|
||||
// RUN: split-file %s %t
|
||||
|
||||
// RUN: %clang_cc1 -std=c++20 -emit-header-unit -xc++-header %t/hu-01.h \
|
||||
// RUN: -o %t/hu-01.pcm
|
||||
|
||||
// RUN: %clang_cc1 -std=c++20 -module-file-info %t/hu-01.pcm | \
|
||||
// RUN: FileCheck --check-prefix=CHECK-HU %s -DTDIR=%t
|
||||
|
||||
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/imp-hu-01.cpp \
|
||||
// RUN: -fmodule-file=%t/hu-01.pcm -o %t/B.pcm -Rmodule-import 2>&1 | \
|
||||
// RUN: FileCheck --check-prefix=CHECK-IMP %s -DTDIR=%t
|
||||
|
||||
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/imp-hu-02.cpp \
|
||||
// RUN: -fmodule-file=%t/hu-01.pcm -o %t/C.pcm -Rmodule-import 2>&1 | \
|
||||
// RUN: FileCheck --check-prefix=CHECK-GMF-IMP %s -DTDIR=%t
|
||||
|
||||
// RUN: %clang_cc1 -std=c++20 -emit-header-unit -xc++-header %t/hu-02.h \
|
||||
// RUN: -o %t/hu-02.pcm
|
||||
|
||||
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/imp-hu-03.cpp \
|
||||
// RUN: -fmodule-file=%t/hu-01.pcm -fmodule-file=%t/hu-02.pcm -o %t/D.pcm \
|
||||
// RUN: -Rmodule-import 2>&1 | \
|
||||
// RUN: FileCheck --check-prefix=CHECK-BOTH %s -DTDIR=%t
|
||||
|
||||
// RUN: %clang_cc1 -std=c++20 -emit-header-unit -xc++-header %t/hu-03.h \
|
||||
// RUN: -fmodule-file=%t/hu-01.pcm -o %t/hu-03.pcm
|
||||
|
||||
// RUN: %clang_cc1 -std=c++20 -module-file-info %t/hu-03.pcm | \
|
||||
// RUN: FileCheck --check-prefix=CHECK-HU-HU %s -DTDIR=%t
|
||||
|
||||
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/imp-hu-04.cpp \
|
||||
// RUN: -fmodule-file=%t/hu-03.pcm -o %t/E.pcm -Rmodule-import 2>&1 | \
|
||||
// RUN: FileCheck --check-prefix=CHECK-NESTED %s -DTDIR=%t
|
||||
|
||||
//--- hu-01.h
|
||||
int foo(int);
|
||||
|
||||
// CHECK-HU: ====== C++20 Module structure ======
|
||||
// CHECK-HU-NEXT: Header Unit '[[TDIR]]/hu-01.h' is the Primary Module at index #1
|
||||
|
||||
//--- imp-hu-01.cpp
|
||||
export module B;
|
||||
import "hu-01.h";
|
||||
|
||||
int bar(int x) {
|
||||
return foo(x);
|
||||
}
|
||||
// CHECK-IMP: remark: importing module '[[TDIR]]/hu-01.h' from '[[TDIR]]/hu-01.pcm'
|
||||
// expected-no-diagnostics
|
||||
|
||||
//--- imp-hu-02.cpp
|
||||
module;
|
||||
import "hu-01.h";
|
||||
|
||||
export module C;
|
||||
|
||||
int bar(int x) {
|
||||
return foo(x);
|
||||
}
|
||||
// CHECK-GMF-IMP: remark: importing module '[[TDIR]]/hu-01.h' from '[[TDIR]]/hu-01.pcm'
|
||||
// expected-no-diagnostics
|
||||
|
||||
//--- hu-02.h
|
||||
int baz(int);
|
||||
|
||||
//--- imp-hu-03.cpp
|
||||
module;
|
||||
export import "hu-01.h";
|
||||
|
||||
export module D;
|
||||
import "hu-02.h";
|
||||
|
||||
int bar(int x) {
|
||||
return foo(x) + baz(x);
|
||||
}
|
||||
// CHECK-BOTH: remark: importing module '[[TDIR]]/hu-01.h' from '[[TDIR]]/hu-01.pcm'
|
||||
// CHECK-BOTH: remark: importing module '[[TDIR]]/hu-02.h' from '[[TDIR]]/hu-02.pcm'
|
||||
// expected-no-diagnostics
|
||||
|
||||
//--- hu-03.h
|
||||
export import "hu-01.h";
|
||||
int baz(int);
|
||||
// CHECK-HU-HU: ====== C++20 Module structure ======
|
||||
// CHECK-HU-HU-NEXT: Header Unit '[[TDIR]]/hu-03.h' is the Primary Module at index #2
|
||||
// CHECK-HU-HU-NEXT: Exports:
|
||||
// CHECK-HU-HU-NEXT: Header Unit '[[TDIR]]/hu-01.h' is at index #1
|
||||
|
||||
// expected-no-diagnostics
|
||||
|
||||
//--- imp-hu-04.cpp
|
||||
module;
|
||||
import "hu-03.h";
|
||||
|
||||
export module E;
|
||||
|
||||
int bar(int x) {
|
||||
return foo(x) + baz(x);
|
||||
}
|
||||
// CHECK-NESTED: remark: importing module '[[TDIR]]/hu-03.h' from '[[TDIR]]/hu-03.pcm'
|
||||
// expected-no-diagnostics
|
Loading…
Reference in New Issue
Block a user