r220448 - [modules] Add support for 'textual header' directives.

Richard Smith richard-llvm at metafoo.co.uk
Wed Oct 22 16:50:57 PDT 2014


Author: rsmith
Date: Wed Oct 22 18:50:56 2014
New Revision: 220448

URL: http://llvm.org/viewvc/llvm-project?rev=220448&view=rev
Log:
[modules] Add support for 'textual header' directives.

This allows a module to specify that it logically contains a file, but that
said file is non-modular and intended for textual inclusion. This allows
layering checks to work properly in the presence of such files.

Modified:
    cfe/trunk/docs/Modules.rst
    cfe/trunk/include/clang/Basic/Module.h
    cfe/trunk/include/clang/Lex/ModuleMap.h
    cfe/trunk/include/clang/Serialization/ASTBitCodes.h
    cfe/trunk/lib/Basic/Module.cpp
    cfe/trunk/lib/Lex/ModuleMap.cpp
    cfe/trunk/lib/Lex/PPLexerChange.cpp
    cfe/trunk/lib/Serialization/ASTWriter.cpp
    cfe/trunk/test/Modules/Inputs/declare-use/module.map

Modified: cfe/trunk/docs/Modules.rst
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/Modules.rst?rev=220448&r1=220447&r2=220448&view=diff
==============================================================================
--- cfe/trunk/docs/Modules.rst (original)
+++ cfe/trunk/docs/Modules.rst Wed Oct 22 18:50:56 2014
@@ -267,6 +267,12 @@ As an example, the module map file for t
 .. parsed-literal::
 
   module std [system] [extern_c] {
+    module assert {
+      textual header "assert.h"
+      header "bits/assert-decls.h"
+      export *
+    }
+
     module complex {
       header "complex.h"
       export *
@@ -299,11 +305,11 @@ Module map files use a simplified form o
 
 .. parsed-literal::
 
-  ``config_macros`` ``export``     ``module``
+  ``config_macros`` ``export``     ``private``
   ``conflict``      ``framework``  ``requires``
-  ``exclude``       ``header``     ``private``
+  ``exclude``       ``header``     ``textual``
   ``explicit``      ``link``       ``umbrella``
-  ``extern``        ``use``
+  ``extern``        ``module``     ``use``
 
 Module map file
 ---------------
@@ -331,7 +337,7 @@ A module declaration describes a module,
     ``explicit``:sub:`opt` ``framework``:sub:`opt` ``module`` *module-id* *attributes*:sub:`opt` '{' *module-member** '}'
     ``extern`` ``module`` *module-id* *string-literal*
 
-The *module-id* should consist of only a single *identifier*, which provides the name of the module being defined. Each module shall have a single definition. 
+The *module-id* should consist of only a single *identifier*, which provides the name of the module being defined. Each module shall have a single definition.
 
 The ``explicit`` qualifier can only be applied to a submodule, i.e., a module that is nested within another module. The contents of explicit submodules are only made available when the submodule itself was explicitly named in an import declaration or was re-exported from an imported module.
 
@@ -441,9 +447,10 @@ A header declaration specifies that a pa
   *header-declaration*:
     ``umbrella``:sub:`opt` ``header`` *string-literal*
     ``private`` ``header`` *string-literal*
+    ``textual`` ``header`` *string-literal*
     ``exclude`` ``header`` *string-literal*
 
-A header declaration that does not contain ``exclude`` specifies a header that contributes to the enclosing module. Specifically, when the module is built, the named header will be parsed and its declarations will be (logically) placed into the enclosing submodule.
+A header declaration that does not contain ``exclude`` nor ``textual`` specifies a header that contributes to the enclosing module. Specifically, when the module is built, the named header will be parsed and its declarations will be (logically) placed into the enclosing submodule.
 
 A header with the ``umbrella`` specifier is called an umbrella header. An umbrella header includes all of the headers within its directory (and any subdirectories), and is typically used (in the ``#include`` world) to easily access the full API provided by a particular library. With modules, an umbrella header is a convenient shortcut that eliminates the need to write out ``header`` declarations for every library header. A given directory can only contain a single umbrella header.
 
@@ -455,6 +462,8 @@ A header with the ``umbrella`` specifier
 
 A header with the ``private`` specifier may not be included from outside the module itself.
 
+A header with the ``textual`` specifier will not be included when the module is built, and will be textually included if it is named by a ``#include`` directive. However, it is considered to be part of the module for the purpose of checking *use-declaration*\s.
+
 A header with the ``exclude`` specifier is excluded from the module. It will not be included when the module is built, nor will it be considered to be part of the module.
 
 **Example**: The C header ``assert.h`` is an excellent candidate for an excluded header, because it is meant to be included multiple times (possibly with different ``NDEBUG`` settings).

Modified: cfe/trunk/include/clang/Basic/Module.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Module.h?rev=220448&r1=220447&r2=220448&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Module.h (original)
+++ cfe/trunk/include/clang/Basic/Module.h Wed Oct 22 18:50:56 2014
@@ -87,6 +87,10 @@ public:
   /// \brief The headers that are explicitly excluded from this module.
   SmallVector<const FileEntry *, 2> ExcludedHeaders;
 
+  /// \brief The headers that are logically part of this module but
+  /// must be textually included.
+  SmallVector<const FileEntry *, 2> TextualHeaders;
+
   /// \brief The headers that are private to this module.
   SmallVector<const FileEntry *, 2> PrivateHeaders;
 

Modified: cfe/trunk/include/clang/Lex/ModuleMap.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Lex/ModuleMap.h?rev=220448&r1=220447&r2=220448&view=diff
==============================================================================
--- cfe/trunk/include/clang/Lex/ModuleMap.h (original)
+++ cfe/trunk/include/clang/Lex/ModuleMap.h Wed Oct 22 18:50:56 2014
@@ -71,6 +71,9 @@ public:
     NormalHeader,
     /// \brief This header is included but private.
     PrivateHeader,
+    /// \brief This header is part of the module (for layering purposes) but
+    /// should be textually included.
+    TextualHeader,
     /// \brief This header is explicitly excluded from the module.
     ExcludedHeader
     // Caution: Adding an enumerator needs other changes.
@@ -249,11 +252,16 @@ public:
   /// used from.  Used to disambiguate if a header is present in multiple
   /// modules.
   ///
+  /// \param IncludeTextualHeaders If \c true, also find textual headers. By
+  /// default, these are treated like excluded headers and result in no known
+  /// header being found.
+  ///
   /// \returns The module KnownHeader, which provides the module that owns the
   /// given header file.  The KnownHeader is default constructed to indicate
   /// that no module owns this header file.
   KnownHeader findModuleForHeader(const FileEntry *File,
-                                  Module *RequestingModule = nullptr);
+                                  Module *RequestingModule = nullptr,
+                                  bool IncludeTextualHeaders = false);
 
   /// \brief Reports errors if a module must not include a specific file.
   ///

Modified: cfe/trunk/include/clang/Serialization/ASTBitCodes.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Serialization/ASTBitCodes.h?rev=220448&r1=220447&r2=220448&view=diff
==============================================================================
--- cfe/trunk/include/clang/Serialization/ASTBitCodes.h (original)
+++ cfe/trunk/include/clang/Serialization/ASTBitCodes.h Wed Oct 22 18:50:56 2014
@@ -638,7 +638,10 @@ namespace clang {
       /// \brief Specifies a conflict with another module.
       SUBMODULE_CONFLICT = 12,
       /// \brief Specifies a header that is private to this submodule.
-      SUBMODULE_PRIVATE_HEADER = 13
+      SUBMODULE_PRIVATE_HEADER = 13,
+      /// \brief Specifies a header that is part of the module but must be
+      /// textually included.
+      SUBMODULE_TEXTUAL_HEADER = 14,
     };
 
     /// \brief Record types used within a comments block.

Modified: cfe/trunk/lib/Basic/Module.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/Module.cpp?rev=220448&r1=220447&r2=220448&view=diff
==============================================================================
--- cfe/trunk/lib/Basic/Module.cpp (original)
+++ cfe/trunk/lib/Basic/Module.cpp Wed Oct 22 18:50:56 2014
@@ -338,27 +338,23 @@ void Module::print(raw_ostream &OS, unsi
     OS << "\n";
   }
 
-  for (unsigned I = 0, N = NormalHeaders.size(); I != N; ++I) {
-    OS.indent(Indent + 2);
-    OS << "header \"";
-    OS.write_escaped(NormalHeaders[I]->getName());
-    OS << "\"\n";
-  }
+  struct HeaderKind {
+    StringRef Prefix;
+    const SmallVectorImpl<const FileEntry *> &Headers;
+  } Kinds[] = {{"", NormalHeaders},
+               {"exclude ", ExcludedHeaders},
+               {"textual ", TextualHeaders},
+               {"private ", PrivateHeaders}};
 
-  for (unsigned I = 0, N = ExcludedHeaders.size(); I != N; ++I) {
-    OS.indent(Indent + 2);
-    OS << "exclude header \"";
-    OS.write_escaped(ExcludedHeaders[I]->getName());
-    OS << "\"\n";
+  for (auto &K : Kinds) {
+    for (auto *H : K.Headers) {
+      OS.indent(Indent + 2);
+      OS << K.Prefix << "header \"";
+      OS.write_escaped(H->getName());
+      OS << "\"\n";
+    }
   }
 
-  for (unsigned I = 0, N = PrivateHeaders.size(); I != N; ++I) {
-    OS.indent(Indent + 2);
-    OS << "private header \"";
-    OS.write_escaped(PrivateHeaders[I]->getName());
-    OS << "\"\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

Modified: cfe/trunk/lib/Lex/ModuleMap.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/ModuleMap.cpp?rev=220448&r1=220447&r2=220448&view=diff
==============================================================================
--- cfe/trunk/lib/Lex/ModuleMap.cpp (original)
+++ cfe/trunk/lib/Lex/ModuleMap.cpp Wed Oct 22 18:50:56 2014
@@ -315,9 +315,16 @@ void ModuleMap::diagnoseHeaderInclusion(
 
 ModuleMap::KnownHeader
 ModuleMap::findModuleForHeader(const FileEntry *File,
-                               Module *RequestingModule) {
+                               Module *RequestingModule,
+                               bool IncludeTextualHeaders) {
   HeadersMap::iterator Known = findKnownHeader(File);
 
+  auto MakeResult = [&](ModuleMap::KnownHeader R) -> ModuleMap::KnownHeader {
+    if (!IncludeTextualHeaders && R.getRole() == ModuleMap::TextualHeader)
+      return ModuleMap::KnownHeader();
+    return R;
+  };
+
   if (Known != Headers.end()) {
     ModuleMap::KnownHeader Result = KnownHeader();
 
@@ -336,7 +343,7 @@ ModuleMap::findModuleForHeader(const Fil
       // If 'File' is part of 'RequestingModule', 'RequestingModule' is the
       // module we are looking for.
       if (I->getModule() == RequestingModule)
-        return *I;
+        return MakeResult(*I);
 
       // If uses need to be specified explicitly, we are only allowed to return
       // modules that are explicitly used by the requesting module.
@@ -349,10 +356,11 @@ ModuleMap::findModuleForHeader(const Fil
       // are going to get.
       // FIXME: If we have a RequestingModule, we should prefer the header from
       // that module.
-      if (I->getRole() == ModuleMap::NormalHeader)
+      if (I->getRole() == ModuleMap::NormalHeader ||
+          I->getRole() == ModuleMap::TextualHeader)
         break;
     }
-    return Result;
+    return MakeResult(Result);
   }
 
   SmallVector<const DirectoryEntry *, 2> SkippedDirs;
@@ -422,9 +430,9 @@ ModuleMap::findModuleForHeader(const Fil
     if (!Result->isAvailable())
       return KnownHeader();
 
-    return Headers[File].back();
+    return MakeResult(Headers[File].back());
   }
-  
+
   return KnownHeader();
 }
 
@@ -785,6 +793,8 @@ void ModuleMap::addHeader(Module *Mod, c
                           ModuleHeaderRole Role) {
   if (Role == ExcludedHeader) {
     Mod->ExcludedHeaders.push_back(Header);
+  } else if (Role == TextualHeader) {
+    Mod->TextualHeaders.push_back(Header);
   } else {
     if (Role == PrivateHeader)
       Mod->PrivateHeaders.push_back(Header);
@@ -946,6 +956,7 @@ namespace clang {
       RequiresKeyword,
       Star,
       StringLiteral,
+      TextualKeyword,
       LBrace,
       RBrace,
       LSquare,
@@ -1096,6 +1107,7 @@ retry:
                  .Case("module", MMToken::ModuleKeyword)
                  .Case("private", MMToken::PrivateKeyword)
                  .Case("requires", MMToken::RequiresKeyword)
+                 .Case("textual", MMToken::TextualKeyword)
                  .Case("umbrella", MMToken::UmbrellaKeyword)
                  .Case("use", MMToken::UseKeyword)
                  .Default(MMToken::Identifier);
@@ -1463,6 +1475,17 @@ void ModuleMapParser::parseModuleDecl()
       parseRequiresDecl();
       break;
 
+    case MMToken::TextualKeyword: {
+      SourceLocation TextualLoc = consumeToken();
+      if (Tok.is(MMToken::HeaderKeyword)) {
+        parseHeaderDecl(MMToken::TextualKeyword, TextualLoc);
+      } else {
+        Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header)
+          << "textual";
+      }
+      break;
+    }
+
     case MMToken::UmbrellaKeyword: {
       SourceLocation UmbrellaLoc = consumeToken();
       if (Tok.is(MMToken::HeaderKeyword))
@@ -1650,8 +1673,12 @@ static void appendSubframeworkPaths(Modu
 /// \brief Parse a header declaration.
 ///
 ///   header-declaration:
-///     'umbrella'[opt] 'header' string-literal
 ///     'exclude'[opt] 'header' string-literal
+///     'private'[opt] 'header' string-literal
+///     'textual'[opt] 'header' string-literal
+///     'umbrella'[opt] 'header' string-literal
+///
+/// FIXME: Support 'private textual header'.
 void ModuleMapParser::parseHeaderDecl(MMToken::TokenKind LeadingToken,
                                       SourceLocation LeadingLoc) {
   assert(Tok.is(MMToken::HeaderKeyword));
@@ -1747,6 +1774,8 @@ void ModuleMapParser::parseHeaderDecl(MM
         Role = ModuleMap::ExcludedHeader;
       else if (LeadingToken == MMToken::PrivateKeyword)
         Role = ModuleMap::PrivateHeader;
+      else if (LeadingToken == MMToken::TextualKeyword)
+        Role = ModuleMap::TextualHeader;
       else
         assert(LeadingToken == MMToken::HeaderKeyword);
 
@@ -1839,6 +1868,7 @@ void ModuleMapParser::parseExportDecl()
   ModuleId ParsedModuleId;
   bool Wildcard = false;
   do {
+    // FIXME: Support string-literal module names here.
     if (Tok.is(MMToken::Identifier)) {
       ParsedModuleId.push_back(std::make_pair(Tok.getString(), 
                                               Tok.getLocation()));
@@ -1936,6 +1966,7 @@ void ModuleMapParser::parseConfigMacros(
   }
 
   // If we don't have an identifier, we're done.
+  // FIXME: Support macros with the same name as a keyword here.
   if (!Tok.is(MMToken::Identifier))
     return;
 
@@ -1952,6 +1983,7 @@ void ModuleMapParser::parseConfigMacros(
     consumeToken();
 
     // We expect to see a macro name here.
+    // FIXME: Support macros with the same name as a keyword here.
     if (!Tok.is(MMToken::Identifier)) {
       Diags.Report(Tok.getLocation(), diag::err_mmap_expected_config_macro);
       break;
@@ -2117,6 +2149,7 @@ void ModuleMapParser::parseInferredModul
       }
 
       consumeToken();
+      // FIXME: Support string-literal module names here.
       if (!Tok.is(MMToken::Identifier)) {
         Diags.Report(Tok.getLocation(), diag::err_mmap_missing_exclude_name);
         break;

Modified: cfe/trunk/lib/Lex/PPLexerChange.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/PPLexerChange.cpp?rev=220448&r1=220447&r2=220448&view=diff
==============================================================================
--- cfe/trunk/lib/Lex/PPLexerChange.cpp (original)
+++ cfe/trunk/lib/Lex/PPLexerChange.cpp Wed Oct 22 18:50:56 2014
@@ -519,7 +519,8 @@ bool Preprocessor::HandleEndOfFile(Token
           continue;
 
         // If it's not part of a module and not unknown, complain.
-        if (!ModMap.findModuleForHeader(File) &&
+        if (!ModMap.findModuleForHeader(File, nullptr,
+                                        /*IncludeTextualHeaders*/true) &&
             !ModMap.isHeaderInUnavailableModule(File)) {
           Diag(StartLoc, diag::warn_forgotten_module_header)
             << File->getName() << Mod->getFullModuleName();

Modified: cfe/trunk/lib/Serialization/ASTWriter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriter.cpp?rev=220448&r1=220447&r2=220448&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTWriter.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTWriter.cpp Wed Oct 22 18:50:56 2014
@@ -2405,6 +2405,11 @@ void ASTWriter::WriteSubmodules(Module *
   unsigned ExcludedHeaderAbbrev = Stream.EmitAbbrev(Abbrev);
 
   Abbrev = new BitCodeAbbrev();
+  Abbrev->Add(BitCodeAbbrevOp(SUBMODULE_TEXTUAL_HEADER));
+  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Name
+  unsigned TextualHeaderAbbrev = Stream.EmitAbbrev(Abbrev);
+
+  Abbrev = new BitCodeAbbrev();
   Abbrev->Add(BitCodeAbbrevOp(SUBMODULE_PRIVATE_HEADER));
   Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Name
   unsigned PrivateHeaderAbbrev = Stream.EmitAbbrev(Abbrev);
@@ -2481,7 +2486,7 @@ void ASTWriter::WriteSubmodules(Module *
       Stream.EmitRecordWithBlob(UmbrellaDirAbbrev, Record, 
                                 UmbrellaDir->getName());      
     }
-    
+
     // Emit the headers.
     for (unsigned I = 0, N = Mod->NormalHeaders.size(); I != N; ++I) {
       Record.clear();
@@ -2496,6 +2501,13 @@ void ASTWriter::WriteSubmodules(Module *
       Stream.EmitRecordWithBlob(ExcludedHeaderAbbrev, Record, 
                                 Mod->ExcludedHeaders[I]->getName());
     }
+    // Emit the textual headers.
+    for (unsigned I = 0, N = Mod->TextualHeaders.size(); I != N; ++I) {
+      Record.clear();
+      Record.push_back(SUBMODULE_TEXTUAL_HEADER);
+      Stream.EmitRecordWithBlob(TextualHeaderAbbrev, Record, 
+                                Mod->TextualHeaders[I]->getName());
+    }
     // Emit the private headers.
     for (unsigned I = 0, N = Mod->PrivateHeaders.size(); I != N; ++I) {
       Record.clear();

Modified: cfe/trunk/test/Modules/Inputs/declare-use/module.map
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/declare-use/module.map?rev=220448&r1=220447&r2=220448&view=diff
==============================================================================
--- cfe/trunk/test/Modules/Inputs/declare-use/module.map (original)
+++ cfe/trunk/test/Modules/Inputs/declare-use/module.map Wed Oct 22 18:50:56 2014
@@ -38,6 +38,7 @@ module XG {
   use XC
   use XE
   use XJ
+  use XK
 }
 
 module XH {
@@ -52,5 +53,13 @@ module XJ {
   header "j.h"
 }
 
+module XK {
+  textual header "k.h"
+}
+
+module XL {
+  textual header "l.h"
+}
+
 module XS {
 }





More information about the cfe-commits mailing list