summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/Modules.rst29
-rw-r--r--include/clang/Basic/DiagnosticLexKinds.td7
-rw-r--r--include/clang/Basic/Module.h4
-rw-r--r--include/clang/Serialization/ASTBitCodes.h3
-rw-r--r--lib/Basic/Module.cpp5
-rw-r--r--lib/Lex/ModuleMap.cpp44
-rw-r--r--lib/Serialization/ASTReader.cpp7
-rw-r--r--lib/Serialization/ASTWriter.cpp13
-rw-r--r--test/Modules/Inputs/export_as_test.modulemap9
-rw-r--r--test/Modules/export_as_test.c9
10 files changed, 128 insertions, 2 deletions
diff --git a/docs/Modules.rst b/docs/Modules.rst
index 9650279261..6dde22c237 100644
--- a/docs/Modules.rst
+++ b/docs/Modules.rst
@@ -323,11 +323,12 @@ Module map files use a simplified form of the C99 lexer, with the same rules for
.. parsed-literal::
- ``config_macros`` ``export`` ``private``
+ ``config_macros`` ``export_as`` ``private``
``conflict`` ``framework`` ``requires``
``exclude`` ``header`` ``textual``
``explicit`` ``link`` ``umbrella``
``extern`` ``module`` ``use``
+ ``export``
Module map file
---------------
@@ -387,6 +388,7 @@ Modules can have a number of different kinds of members, each of which is descri
*umbrella-dir-declaration*
*submodule-declaration*
*export-declaration*
+ *export-as-declaration*
*use-declaration*
*link-declaration*
*config-macros-declaration*
@@ -666,6 +668,31 @@ Note that, if ``Derived.h`` includes ``Base.h``, one can simply use a wildcard e
compatibility for programs that rely on transitive inclusion (i.e.,
all of them).
+Re-export Declaration
+~~~~~~~~~~~~~~~~~~
+An *export-as-declaration* specifies that the current module is a private
+module whose interface will be re-exported by the named public module.
+
+.. parsed-literal::
+
+ *export-as-declaration*:
+ ``export_as`` *identifier*
+
+The *export-as-declaration* names the public module that the current
+(private) module will be re-exported through. Only top-level modules
+can be re-exported, and any given module may only be re-exported
+through a single public module.
+
+**Example:** In the following example, the (private) module
+``MyFrameworkCore`` will be re-exported via the public module
+``MyFramework``:
+
+.. parsed-literal::
+
+ module MyFrameworkCore {
+ export_as MyFramework
+ }
+
Use declaration
~~~~~~~~~~~~~~~
A *use-declaration* specifies another module that the current top-level module
diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td
index 9f35ed5762..ec95dca6ac 100644
--- a/include/clang/Basic/DiagnosticLexKinds.td
+++ b/include/clang/Basic/DiagnosticLexKinds.td
@@ -674,6 +674,13 @@ def err_mmap_invalid_header_attribute_value : Error<
"expected integer literal as value for header attribute '%0'">;
def err_mmap_expected_header_attribute : Error<
"expected a header attribute name ('size' or 'mtime')">;
+def err_mmap_conflicting_export_as : Error<
+ "conflicting re-export of module '%0' as '%1' or '%2'">;
+def warn_mmap_redundant_export_as : Warning<
+ "module '%0' already re-exported as '%1'">,
+ InGroup<PrivateModule>;
+def err_mmap_submodule_export_as : Error<
+ "only top-level modules can be re-exported as public">;
def warn_auto_module_import : Warning<
"treating #%select{include|import|include_next|__include_macros}0 as an "
diff --git a/include/clang/Basic/Module.h b/include/clang/Basic/Module.h
index edc2f8e723..0b2a665f05 100644
--- a/include/clang/Basic/Module.h
+++ b/include/clang/Basic/Module.h
@@ -99,6 +99,10 @@ public:
/// \brief The name of the umbrella entry, as written in the module map.
std::string UmbrellaAsWritten;
+
+ /// \brief The module through which entities defined in this module will
+ /// eventually be exposed, for use in "private" modules.
+ std::string ExportAsModule;
private:
/// \brief The submodules of this module, indexed by name.
diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h
index ab4b82e5d3..5ecba87aa3 100644
--- a/include/clang/Serialization/ASTBitCodes.h
+++ b/include/clang/Serialization/ASTBitCodes.h
@@ -716,6 +716,9 @@ namespace clang {
/// \brief Specifies some declarations with initializers that must be
/// emitted to initialize the module.
SUBMODULE_INITIALIZERS = 16,
+ /// \brief Specifies the name of the module that will eventually
+ /// re-export the entities in this module.
+ SUBMODULE_EXPORT_AS = 17,
};
/// \brief Record types used within a comments block.
diff --git a/lib/Basic/Module.cpp b/lib/Basic/Module.cpp
index 1d96afd476..621b1b23d7 100644
--- a/lib/Basic/Module.cpp
+++ b/lib/Basic/Module.cpp
@@ -440,6 +440,11 @@ void Module::print(raw_ostream &OS, unsigned Indent) const {
}
}
+ if (!ExportAsModule.empty()) {
+ OS.indent(Indent + 2);
+ OS << "export_as" << ExportAsModule << "\n";
+ }
+
for (submodule_const_iterator MI = submodule_begin(), MIEnd = submodule_end();
MI != MIEnd; ++MI)
// Print inferred subframework modules so that we don't need to re-infer
diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp
index b01080e55a..fc7fe138c5 100644
--- a/lib/Lex/ModuleMap.cpp
+++ b/lib/Lex/ModuleMap.cpp
@@ -1201,6 +1201,7 @@ namespace clang {
ExcludeKeyword,
ExplicitKeyword,
ExportKeyword,
+ ExportAsKeyword,
ExternKeyword,
FrameworkKeyword,
LinkKeyword,
@@ -1312,6 +1313,7 @@ namespace clang {
SourceLocation LeadingLoc);
void parseUmbrellaDirDecl(SourceLocation UmbrellaLoc);
void parseExportDecl();
+ void parseExportAsDecl();
void parseUseDecl();
void parseLinkDecl();
void parseConfigMacros();
@@ -1363,6 +1365,7 @@ retry:
.Case("exclude", MMToken::ExcludeKeyword)
.Case("explicit", MMToken::ExplicitKeyword)
.Case("export", MMToken::ExportKeyword)
+ .Case("export_as", MMToken::ExportAsKeyword)
.Case("extern", MMToken::ExternKeyword)
.Case("framework", MMToken::FrameworkKeyword)
.Case("header", MMToken::HeaderKeyword)
@@ -1590,6 +1593,7 @@ namespace {
/// header-declaration
/// submodule-declaration
/// export-declaration
+/// export-as-declaration
/// link-declaration
///
/// submodule-declaration:
@@ -1824,6 +1828,10 @@ void ModuleMapParser::parseModuleDecl() {
parseExportDecl();
break;
+ case MMToken::ExportAsKeyword:
+ parseExportAsDecl();
+ break;
+
case MMToken::UseKeyword:
parseUseDecl();
break;
@@ -2284,6 +2292,41 @@ void ModuleMapParser::parseExportDecl() {
ActiveModule->UnresolvedExports.push_back(Unresolved);
}
+/// \brief Parse a module export_as declaration.
+///
+/// export-as-declaration:
+/// 'export_as' identifier
+void ModuleMapParser::parseExportAsDecl() {
+ assert(Tok.is(MMToken::ExportAsKeyword));
+ consumeToken();
+
+ if (!Tok.is(MMToken::Identifier)) {
+ Diags.Report(Tok.getLocation(), diag::err_mmap_module_id);
+ HadError = true;
+ return;
+ }
+
+ if (ActiveModule->Parent) {
+ Diags.Report(Tok.getLocation(), diag::err_mmap_submodule_export_as);
+ consumeToken();
+ return;
+ }
+
+ if (!ActiveModule->ExportAsModule.empty()) {
+ if (ActiveModule->ExportAsModule == Tok.getString()) {
+ Diags.Report(Tok.getLocation(), diag::warn_mmap_redundant_export_as)
+ << ActiveModule->Name << Tok.getString();
+ } else {
+ Diags.Report(Tok.getLocation(), diag::err_mmap_conflicting_export_as)
+ << ActiveModule->Name << ActiveModule->ExportAsModule
+ << Tok.getString();
+ }
+ }
+
+ ActiveModule->ExportAsModule = Tok.getString();
+ consumeToken();
+}
+
/// \brief Parse a module use declaration.
///
/// use-declaration:
@@ -2689,6 +2732,7 @@ bool ModuleMapParser::parseModuleMapFile() {
case MMToken::Exclaim:
case MMToken::ExcludeKeyword:
case MMToken::ExportKeyword:
+ case MMToken::ExportAsKeyword:
case MMToken::HeaderKeyword:
case MMToken::Identifier:
case MMToken::LBrace:
diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp
index 3dba299790..46823030d3 100644
--- a/lib/Serialization/ASTReader.cpp
+++ b/lib/Serialization/ASTReader.cpp
@@ -5123,7 +5123,7 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) {
break;
}
- case SUBMODULE_INITIALIZERS:
+ case SUBMODULE_INITIALIZERS: {
if (!ContextObj)
break;
SmallVector<uint32_t, 16> Inits;
@@ -5132,6 +5132,11 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) {
ContextObj->addLazyModuleInitializers(CurrentModule, Inits);
break;
}
+
+ case SUBMODULE_EXPORT_AS:
+ CurrentModule->ExportAsModule = Blob.str();
+ break;
+ }
}
}
diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp
index 8f4a908f54..063d6f46e3 100644
--- a/lib/Serialization/ASTWriter.cpp
+++ b/lib/Serialization/ASTWriter.cpp
@@ -1130,6 +1130,7 @@ void ASTWriter::WriteBlockInfoBlock() {
RECORD(SUBMODULE_TEXTUAL_HEADER);
RECORD(SUBMODULE_PRIVATE_TEXTUAL_HEADER);
RECORD(SUBMODULE_INITIALIZERS);
+ RECORD(SUBMODULE_EXPORT_AS);
// Comments Block.
BLOCK(COMMENTS_BLOCK);
@@ -2791,6 +2792,12 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) {
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Message
unsigned ConflictAbbrev = Stream.EmitAbbrev(std::move(Abbrev));
+ Abbrev = std::make_shared<BitCodeAbbrev>();
+ Abbrev->Add(BitCodeAbbrevOp(SUBMODULE_EXPORT_AS));
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Macro name
+ unsigned ExportAsAbbrev = Stream.EmitAbbrev(std::move(Abbrev));
+
+
// Write the submodule metadata block.
RecordData::value_type Record[] = {
getNumberOfModules(WritingModule),
@@ -2925,6 +2932,12 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) {
if (!Inits.empty())
Stream.EmitRecord(SUBMODULE_INITIALIZERS, Inits);
+ // Emit the name of the re-exported module, if any.
+ if (!Mod->ExportAsModule.empty()) {
+ RecordData::value_type Record[] = {SUBMODULE_EXPORT_AS};
+ Stream.EmitRecordWithBlob(ExportAsAbbrev, Record, Mod->ExportAsModule);
+ }
+
// Queue up the submodules of this module.
for (auto *M : Mod->submodules())
Q.push(M);
diff --git a/test/Modules/Inputs/export_as_test.modulemap b/test/Modules/Inputs/export_as_test.modulemap
new file mode 100644
index 0000000000..4aaec4194e
--- /dev/null
+++ b/test/Modules/Inputs/export_as_test.modulemap
@@ -0,0 +1,9 @@
+module PrivateFoo {
+ export_as Foo
+ export_as Bar
+ export_as Bar
+
+ module Sub {
+ export_as Wibble
+ }
+}
diff --git a/test/Modules/export_as_test.c b/test/Modules/export_as_test.c
new file mode 100644
index 0000000000..a73d6bfc46
--- /dev/null
+++ b/test/Modules/export_as_test.c
@@ -0,0 +1,9 @@
+// RUN: rm -rf %t
+// RUN: not %clang_cc1 -fmodules -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/export_as_test.modulemap %s 2> %t.err
+// RUN: FileCheck %s < %t.err
+
+// CHECK: export_as_test.modulemap:3:13: error: conflicting re-export of module 'PrivateFoo' as 'Foo' or 'Bar'
+// CHECK: export_as_test.modulemap:4:13: warning: module 'PrivateFoo' already re-exported as 'Bar'
+// CHECK: export_as_test.modulemap:7:15: error: only top-level modules can be re-exported as public
+
+