r202615 - Add [extern_c] attribute for modules, allowing a C module to be imported within an extern "C" block in C++ code.

Richard Smith richard-llvm at metafoo.co.uk
Sat Mar 1 21:58:19 PST 2014


Author: rsmith
Date: Sat Mar  1 23:58:18 2014
New Revision: 202615

URL: http://llvm.org/viewvc/llvm-project?rev=202615&view=rev
Log:
Add [extern_c] attribute for modules, allowing a C module to be imported within an extern "C" block in C++ code.

Added:
    cfe/trunk/test/Modules/Inputs/c-header-bad.h
    cfe/trunk/test/Modules/Inputs/c-header.h
    cfe/trunk/test/Modules/Inputs/cxx-header.h
    cfe/trunk/test/Modules/extern_c.cpp
    cfe/trunk/test/Modules/extern_c_bad.cpp
Modified:
    cfe/trunk/docs/Modules.rst
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Basic/Module.h
    cfe/trunk/include/clang/Parse/Parser.h
    cfe/trunk/lib/Basic/Module.cpp
    cfe/trunk/lib/Frontend/FrontendActions.cpp
    cfe/trunk/lib/Lex/ModuleMap.cpp
    cfe/trunk/lib/Parse/ParseDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/test/Modules/Inputs/module.map

Modified: cfe/trunk/docs/Modules.rst
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/Modules.rst?rev=202615&r1=202614&r2=202615&view=diff
==============================================================================
--- cfe/trunk/docs/Modules.rst (original)
+++ cfe/trunk/docs/Modules.rst Sat Mar  1 23:58:18 2014
@@ -245,7 +245,7 @@ As an example, the module map file for t
 
 .. parsed-literal::
 
-  module std [system] {
+  module std [system] [extern_c] {
     module complex {
       header "complex.h"
       export *
@@ -325,7 +325,9 @@ The ``framework`` qualifier specifies th
     Resources/                Subdirectory containing additional resources
     Name                      Symbolic link to the shared library for the framework
 
-The ``system`` attribute specifies that the module is a system module. When a system module is rebuilt, all of the module's header will be considered system headers, which suppresses warnings. This is equivalent to placing ``#pragma GCC system_header`` in each of the module's headers. The form of attributes is described in the section Attributes_, below.
+The ``system`` attribute specifies that the module is a system module. When a system module is rebuilt, all of the module's headers will be considered system headers, which suppresses warnings. This is equivalent to placing ``#pragma GCC system_header`` in each of the module's headers. The form of attributes is described in the section Attributes_, below.
+
+The ``extern_c`` attribute specifies that the module contains C code that can be used from within C++. When such a module is built for use in C++ code, all of the module's headers will be treated as if they were contained within an implicit ``extern "C"`` block. An import for a module with this attribute can appear within an ``extern "C"`` block. No other restrictions are lifted, however: the module currently cannot be imported within an ``extern "C"`` block in a namespace.
 
 Modules can have a number of different kinds of members, each of which is described below:
 

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=202615&r1=202614&r2=202615&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Sat Mar  1 23:58:18 2014
@@ -6903,6 +6903,14 @@ def err_module_private_declaration : Err
   "declaration of %0 must be imported from module '%1' before it is required">;
 def err_module_private_definition : Error<
   "definition of %0 must be imported from module '%1' before it is required">;
+def err_module_import_in_extern_c : Error<
+  "import of C++ module '%0' appears within extern \"C\" language linkage "
+  "specification">;
+def note_module_import_in_extern_c : Note<
+  "extern \"C\" language linkage specification begins here">;
+def err_module_import_not_at_top_level : Error<
+  "import of module '%0' appears within %1">;
+def note_module_import_not_at_top_level : Note<"%0 begins here">;
 }
 
 let CategoryName = "Documentation Issue" in {

Modified: cfe/trunk/include/clang/Basic/Module.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Module.h?rev=202615&r1=202614&r2=202615&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Module.h (original)
+++ cfe/trunk/include/clang/Basic/Module.h Sat Mar  1 23:58:18 2014
@@ -128,7 +128,12 @@ public:
   /// \brief Whether this is a "system" module (which assumes that all
   /// headers in it are system headers).
   unsigned IsSystem : 1;
-  
+
+  /// \brief Whether this is an 'extern "C"' module (which implicitly puts all
+  /// headers in it within an 'extern "C"' block, and allows the module to be
+  /// imported within such a block).
+  unsigned IsExternC : 1;
+
   /// \brief Whether we should infer submodules for this module based on 
   /// the headers.
   ///

Modified: cfe/trunk/include/clang/Parse/Parser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=202615&r1=202614&r2=202615&view=diff
==============================================================================
--- cfe/trunk/include/clang/Parse/Parser.h (original)
+++ cfe/trunk/include/clang/Parse/Parser.h Sat Mar  1 23:58:18 2014
@@ -272,6 +272,10 @@ public:
   /// ParseTopLevelDecl - Parse one top-level declaration. Returns true if
   /// the EOF was encountered.
   bool ParseTopLevelDecl(DeclGroupPtrTy &Result);
+  bool ParseTopLevelDecl() {
+    DeclGroupPtrTy Result;
+    return ParseTopLevelDecl(Result);
+  }
 
   /// ConsumeToken - Consume the current 'peek token' and lex the next one.
   /// This does not work with special tokens: string literals, code completion

Modified: cfe/trunk/lib/Basic/Module.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/Module.cpp?rev=202615&r1=202614&r2=202615&view=diff
==============================================================================
--- cfe/trunk/lib/Basic/Module.cpp (original)
+++ cfe/trunk/lib/Basic/Module.cpp Sat Mar  1 23:58:18 2014
@@ -24,15 +24,14 @@
 
 using namespace clang;
 
-Module::Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent, 
+Module::Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent,
                bool IsFramework, bool IsExplicit)
   : Name(Name), DefinitionLoc(DefinitionLoc), Parent(Parent),
     Umbrella(), ASTFile(0), IsAvailable(true), IsFromModuleFile(false),
     IsFramework(IsFramework), IsExplicit(IsExplicit), IsSystem(false),
-    InferSubmodules(false), InferExplicitSubmodules(false), 
+    IsExternC(false), InferSubmodules(false), InferExplicitSubmodules(false),
     InferExportWildcard(false), ConfigMacrosExhaustive(false),
-    NameVisibility(Hidden)
-{ 
+    NameVisibility(Hidden) {
   if (Parent) {
     if (!Parent->isAvailable())
       IsAvailable = false;

Modified: cfe/trunk/lib/Frontend/FrontendActions.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/FrontendActions.cpp?rev=202615&r1=202614&r2=202615&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/FrontendActions.cpp (original)
+++ cfe/trunk/lib/Frontend/FrontendActions.cpp Sat Mar  1 23:58:18 2014
@@ -130,19 +130,25 @@ operator+=(SmallVectorImpl<char> &Includ
 
 static void addHeaderInclude(StringRef HeaderName,
                              SmallVectorImpl<char> &Includes,
-                             const LangOptions &LangOpts) {
+                             const LangOptions &LangOpts,
+                             bool IsExternC) {
+  if (IsExternC)
+    Includes += "extern \"C\" {\n";
   if (LangOpts.ObjC1)
     Includes += "#import \"";
   else
     Includes += "#include \"";
   Includes += HeaderName;
   Includes += "\"\n";
+  if (IsExternC)
+    Includes += "}\n";
 }
 
 static void addHeaderInclude(const FileEntry *Header,
                              SmallVectorImpl<char> &Includes,
-                             const LangOptions &LangOpts) {
-  addHeaderInclude(Header->getName(), Includes, LangOpts);
+                             const LangOptions &LangOpts,
+                             bool IsExternC) {
+  addHeaderInclude(Header->getName(), Includes, LangOpts, IsExternC);
 }
 
 /// \brief Collect the set of header includes needed to construct the given 
@@ -165,7 +171,7 @@ static void collectModuleHeaderIncludes(
   for (unsigned I = 0, N = Module->NormalHeaders.size(); I != N; ++I) {
     const FileEntry *Header = Module->NormalHeaders[I];
     Module->addTopHeader(Header);
-    addHeaderInclude(Header, Includes, LangOpts);
+    addHeaderInclude(Header, Includes, LangOpts, Module->IsExternC);
   }
   // Note that Module->PrivateHeaders will not be a TopHeader.
 
@@ -173,7 +179,7 @@ static void collectModuleHeaderIncludes(
     Module->addTopHeader(UmbrellaHeader);
     if (Module->Parent) {
       // Include the umbrella header for submodules.
-      addHeaderInclude(UmbrellaHeader, Includes, LangOpts);
+      addHeaderInclude(UmbrellaHeader, Includes, LangOpts, Module->IsExternC);
     }
   } else if (const DirectoryEntry *UmbrellaDir = Module->getUmbrellaDir()) {
     // Add all of the headers we find in this subdirectory.
@@ -199,7 +205,7 @@ static void collectModuleHeaderIncludes(
       }
       
       // Include this header umbrella header for submodules.
-      addHeaderInclude(Dir->path(), Includes, LangOpts);
+      addHeaderInclude(Dir->path(), Includes, LangOpts, Module->IsExternC);
     }
   }
   
@@ -267,7 +273,8 @@ bool GenerateModuleAction::BeginSourceFi
   // Collect the set of #includes we need to build the module.
   SmallString<256> HeaderContents;
   if (const FileEntry *UmbrellaHeader = Module->getUmbrellaHeader())
-    addHeaderInclude(UmbrellaHeader, HeaderContents, CI.getLangOpts());
+    addHeaderInclude(UmbrellaHeader, HeaderContents, CI.getLangOpts(),
+                     Module->IsExternC);
   collectModuleHeaderIncludes(CI.getLangOpts(), FileMgr,
     CI.getPreprocessor().getHeaderSearchInfo().getModuleMap(),
     Module, HeaderContents);

Modified: cfe/trunk/lib/Lex/ModuleMap.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/ModuleMap.cpp?rev=202615&r1=202614&r2=202615&view=diff
==============================================================================
--- cfe/trunk/lib/Lex/ModuleMap.cpp (original)
+++ cfe/trunk/lib/Lex/ModuleMap.cpp Sat Mar  1 23:58:18 2014
@@ -906,11 +906,14 @@ namespace clang {
 
   /// \brief The set of attributes that can be attached to a module.
   struct Attributes {
-    Attributes() : IsSystem(), IsExhaustive() { }
+    Attributes() : IsSystem(), IsExternC(), IsExhaustive() { }
 
     /// \brief Whether this is a system module.
     unsigned IsSystem : 1;
 
+    /// \brief Whether this is an extern "C" module.
+    unsigned IsExternC : 1;
+
     /// \brief Whether this is an exhaustive set of configuration macros.
     unsigned IsExhaustive : 1;
   };
@@ -1178,6 +1181,8 @@ namespace {
     AT_unknown,
     /// \brief The 'system' attribute.
     AT_system,
+    /// \brief The 'extern_c' attribute.
+    AT_extern_c,
     /// \brief The 'exhaustive' attribute.
     AT_exhaustive
   };
@@ -1334,7 +1339,9 @@ void ModuleMapParser::parseModuleDecl()
   ActiveModule->DefinitionLoc = ModuleNameLoc;
   if (Attrs.IsSystem || IsSystem)
     ActiveModule->IsSystem = true;
-  
+  if (Attrs.IsExternC)
+    ActiveModule->IsExternC = true;
+
   bool Done = false;
   do {
     switch (Tok.Kind) {
@@ -2097,6 +2104,7 @@ bool ModuleMapParser::parseOptionalAttri
     AttributeKind Attribute
       = llvm::StringSwitch<AttributeKind>(Tok.getString())
           .Case("exhaustive", AT_exhaustive)
+          .Case("extern_c", AT_extern_c)
           .Case("system", AT_system)
           .Default(AT_unknown);
     switch (Attribute) {
@@ -2109,6 +2117,10 @@ bool ModuleMapParser::parseOptionalAttri
       Attrs.IsSystem = true;
       break;
 
+    case AT_extern_c:
+      Attrs.IsExternC = true;
+      break;
+
     case AT_exhaustive:
       Attrs.IsExhaustive = true;
       break;

Modified: cfe/trunk/lib/Parse/ParseDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDeclCXX.cpp?rev=202615&r1=202614&r2=202615&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Sat Mar  1 23:58:18 2014
@@ -313,11 +313,42 @@ Decl *Parser::ParseLinkage(ParsingDeclSp
 
   BalancedDelimiterTracker T(*this, tok::l_brace);
   T.consumeOpen();
-  while (Tok.isNot(tok::r_brace) && !isEofOrEom()) {
-    ParsedAttributesWithRange attrs(AttrFactory);
-    MaybeParseCXX11Attributes(attrs);
-    MaybeParseMicrosoftAttributes(attrs);
-    ParseExternalDeclaration(attrs);
+
+  unsigned NestedModules = 0;
+  while (true) {
+    switch (Tok.getKind()) {
+    case tok::annot_module_begin:
+      ++NestedModules;
+      ParseTopLevelDecl();
+      continue;
+
+    case tok::annot_module_end:
+      if (!NestedModules)
+        break;
+      --NestedModules;
+      ParseTopLevelDecl();
+      continue;
+
+    case tok::annot_module_include:
+      ParseTopLevelDecl();
+      continue;
+
+    case tok::eof:
+      break;
+
+    case tok::r_brace:
+      if (!NestedModules)
+        break;
+      // Fall through.
+    default:
+      ParsedAttributesWithRange attrs(AttrFactory);
+      MaybeParseCXX11Attributes(attrs);
+      MaybeParseMicrosoftAttributes(attrs);
+      ParseExternalDeclaration(attrs);
+      continue;
+    }
+
+    break;
   }
 
   T.consumeClose();

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=202615&r1=202614&r2=202615&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Sat Mar  1 23:58:18 2014
@@ -12957,6 +12957,36 @@ Decl *Sema::ActOnFileScopeAsmDecl(Expr *
   return New;
 }
 
+static void checkModuleImportContext(Sema &S, Module *M,
+                                     SourceLocation ImportLoc,
+                                     DeclContext *DC) {
+  if (auto *LSD = dyn_cast<LinkageSpecDecl>(DC)) {
+    switch (LSD->getLanguage()) {
+    case LinkageSpecDecl::lang_c:
+      if (!M->IsExternC) {
+        S.Diag(ImportLoc, diag::err_module_import_in_extern_c)
+          << M->getFullModuleName();
+        S.Diag(LSD->getLocStart(), diag::note_module_import_in_extern_c);
+        return;
+      }
+      break;
+    case LinkageSpecDecl::lang_cxx:
+      break;
+    }
+    DC = LSD->getParent();
+  }
+
+  while (isa<LinkageSpecDecl>(DC))
+    DC = DC->getParent();
+  if (!isa<TranslationUnitDecl>(DC)) {
+    S.Diag(ImportLoc, diag::err_module_import_not_at_top_level)
+      << M->getFullModuleName() << DC;
+    S.Diag(cast<Decl>(DC)->getLocStart(),
+           diag::note_module_import_not_at_top_level)
+      << DC;
+  }
+}
+
 DeclResult Sema::ActOnModuleImport(SourceLocation AtLoc, 
                                    SourceLocation ImportLoc, 
                                    ModuleIdPath Path) {
@@ -12965,7 +12995,9 @@ DeclResult Sema::ActOnModuleImport(Sourc
                                                 /*IsIncludeDirective=*/false);
   if (!Mod)
     return true;
-  
+
+  checkModuleImportContext(*this, Mod, ImportLoc, CurContext);
+
   SmallVector<SourceLocation, 2> IdentifierLocs;
   Module *ModCheck = Mod;
   for (unsigned I = 0, N = Path.size(); I != N; ++I) {
@@ -12987,6 +13019,8 @@ DeclResult Sema::ActOnModuleImport(Sourc
 }
 
 void Sema::ActOnModuleInclude(SourceLocation DirectiveLoc, Module *Mod) {
+  checkModuleImportContext(*this, Mod, DirectiveLoc, CurContext);
+
   // FIXME: Should we synthesize an ImportDecl here?
   PP.getModuleLoader().makeModuleVisible(Mod, Module::AllVisible, DirectiveLoc,
                                          /*Complain=*/true);

Added: cfe/trunk/test/Modules/Inputs/c-header-bad.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/c-header-bad.h?rev=202615&view=auto
==============================================================================
--- cfe/trunk/test/Modules/Inputs/c-header-bad.h (added)
+++ cfe/trunk/test/Modules/Inputs/c-header-bad.h Sat Mar  1 23:58:18 2014
@@ -0,0 +1,4 @@
+} // expected-error {{extraneous closing brace ('}')}}
+int not_in_extern_c;
+extern "C" { // expected-note {{to match this '{'}}
+// expected-error {{expected '}'}}

Added: cfe/trunk/test/Modules/Inputs/c-header.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/c-header.h?rev=202615&view=auto
==============================================================================
--- cfe/trunk/test/Modules/Inputs/c-header.h (added)
+++ cfe/trunk/test/Modules/Inputs/c-header.h Sat Mar  1 23:58:18 2014
@@ -0,0 +1 @@
+int f(void);

Added: cfe/trunk/test/Modules/Inputs/cxx-header.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/cxx-header.h?rev=202615&view=auto
==============================================================================
--- cfe/trunk/test/Modules/Inputs/cxx-header.h (added)
+++ cfe/trunk/test/Modules/Inputs/cxx-header.h Sat Mar  1 23:58:18 2014
@@ -0,0 +1 @@
+int f();

Modified: cfe/trunk/test/Modules/Inputs/module.map
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/module.map?rev=202615&r1=202614&r2=202615&view=diff
==============================================================================
--- cfe/trunk/test/Modules/Inputs/module.map (original)
+++ cfe/trunk/test/Modules/Inputs/module.map Sat Mar  1 23:58:18 2014
@@ -1,3 +1,6 @@
+module c_library [extern_c] { header "c-header.h" }
+module cxx_library { header "cxx-header.h" requires cplusplus }
+module c_library_bad [extern_c] { header "c-header-bad.h" }
 module diamond_top { header "diamond_top.h" }
 module diamond_left { 
   header "diamond_left.h" 

Added: cfe/trunk/test/Modules/extern_c.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/extern_c.cpp?rev=202615&view=auto
==============================================================================
--- cfe/trunk/test/Modules/extern_c.cpp (added)
+++ cfe/trunk/test/Modules/extern_c.cpp Sat Mar  1 23:58:18 2014
@@ -0,0 +1,66 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -fmodules -verify -fmodules-cache-path=%t -I %S/Inputs %s
+// RUN: %clang_cc1 -fmodules -verify -fmodules-cache-path=%t -I %S/Inputs %s -DEXTERN_C
+// RUN: %clang_cc1 -fmodules -verify -fmodules-cache-path=%t -I %S/Inputs %s -DEXTERN_CXX
+// RUN: %clang_cc1 -fmodules -verify -fmodules-cache-path=%t -I %S/Inputs %s -DEXTERN_C -DEXTERN_CXX
+// RUN: %clang_cc1 -fmodules -verify -fmodules-cache-path=%t -I %S/Inputs %s -DEXTERN_C -DNAMESPACE
+// RUN: %clang_cc1 -fmodules -verify -fmodules-cache-path=%t -I %S/Inputs %s -DCXX_HEADER
+// RUN: %clang_cc1 -fmodules -verify -fmodules-cache-path=%t -I %S/Inputs %s -DCXX_HEADER -DEXTERN_C
+// RUN: %clang_cc1 -fmodules -verify -fmodules-cache-path=%t -I %S/Inputs %s -DCXX_HEADER -DEXTERN_CXX
+// RUN: %clang_cc1 -fmodules -verify -fmodules-cache-path=%t -I %S/Inputs %s -DCXX_HEADER -DEXTERN_C -DEXTERN_CXX
+// RUN: %clang_cc1 -fmodules -verify -fmodules-cache-path=%t -I %S/Inputs %s -DCXX_HEADER -DEXTERN_C -DNAMESPACE
+
+#ifdef NAMESPACE
+namespace M {
+#endif
+
+#ifdef EXTERN_C
+extern "C" {
+#endif
+
+#ifdef EXTERN_CXX
+extern "C++" {
+#endif
+
+#ifdef CXX_HEADER
+#define HEADER "cxx-header.h"
+#else
+#define HEADER "c-header.h"
+#endif
+
+#include HEADER
+
+#if defined(EXTERN_C) && !defined(EXTERN_CXX) && defined(CXX_HEADER)
+// expected-error at -3 {{import of C++ module 'cxx_library' appears within extern "C" language linkage specification}}
+// expected-note at -17 {{extern "C" language linkage specification begins here}}
+#elif defined(NAMESPACE)
+// expected-error-re at -6 {{import of module '{{c_library|cxx_library}}' appears within namespace 'M'}}
+// expected-note at -24 {{namespace 'M' begins here}}
+#endif
+
+#ifdef EXTERN_CXX
+}
+#endif
+
+#ifdef EXTERN_C
+}
+#endif
+
+#ifdef NAMESPACE
+}
+using namespace M;
+#endif
+
+namespace N {
+  int k = f();
+
+  extern "C" {
+    int f;
+#if !defined(CXX_HEADER)
+    // expected-error at -2 {{redefinition of 'f' as different kind of symbol}}
+    // expected-note at c-header.h:1 {{previous}}
+#endif
+  }
+}
+
+suppress_expected_no_diagnostics_error; // expected-error {{}}

Added: cfe/trunk/test/Modules/extern_c_bad.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/extern_c_bad.cpp?rev=202615&view=auto
==============================================================================
--- cfe/trunk/test/Modules/extern_c_bad.cpp (added)
+++ cfe/trunk/test/Modules/extern_c_bad.cpp Sat Mar  1 23:58:18 2014
@@ -0,0 +1,2 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -verify -fmodules -x c++ -emit-module -fmodules-cache-path=%t -fmodule-name=c_library_bad %S/Inputs/module.map





More information about the cfe-commits mailing list