From: Richard Smith Date: Sun, 2 Mar 2014 05:58:18 +0000 (+0000) Subject: Add [extern_c] attribute for modules, allowing a C module to be imported within an... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=1cafcfd06bb10505a7e570eb62148906c185879e;p=clang Add [extern_c] attribute for modules, allowing a C module to be imported within an extern "C" block in C++ code. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@202615 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/docs/Modules.rst b/docs/Modules.rst index 037add7074..7545bf0cc0 100644 --- a/docs/Modules.rst +++ b/docs/Modules.rst @@ -245,7 +245,7 @@ As an example, the module map file for the C standard library might look a bit l .. parsed-literal:: - module std [system] { + module std [system] [extern_c] { module complex { header "complex.h" export * @@ -325,7 +325,9 @@ The ``framework`` qualifier specifies that this module corresponds to a Darwin-s 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: diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index bf6f33c13e..15c7a53a9b 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -6903,6 +6903,14 @@ def err_module_private_declaration : Error< "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 { diff --git a/include/clang/Basic/Module.h b/include/clang/Basic/Module.h index a880671bdd..3e5f05cfed 100644 --- a/include/clang/Basic/Module.h +++ b/include/clang/Basic/Module.h @@ -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. /// diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 430ae716d4..137e1b7f4c 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -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 diff --git a/lib/Basic/Module.cpp b/lib/Basic/Module.cpp index c6a1d602a9..24c480a766 100644 --- a/lib/Basic/Module.cpp +++ b/lib/Basic/Module.cpp @@ -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; diff --git a/lib/Frontend/FrontendActions.cpp b/lib/Frontend/FrontendActions.cpp index eda21c5f3f..f587d829b3 100644 --- a/lib/Frontend/FrontendActions.cpp +++ b/lib/Frontend/FrontendActions.cpp @@ -130,19 +130,25 @@ operator+=(SmallVectorImpl &Includes, StringRef RHS) { static void addHeaderInclude(StringRef HeaderName, SmallVectorImpl &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 &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(const LangOptions &LangOpts, 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(const LangOptions &LangOpts, 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(const LangOptions &LangOpts, } // 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::BeginSourceFileAction(CompilerInstance &CI, // 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); diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index 05238278ba..0d9cd10953 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -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::parseOptionalAttributes(Attributes &Attrs) { AttributeKind Attribute = llvm::StringSwitch(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::parseOptionalAttributes(Attributes &Attrs) { Attrs.IsSystem = true; break; + case AT_extern_c: + Attrs.IsExternC = true; + break; + case AT_exhaustive: Attrs.IsExhaustive = true; break; diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 7d198efa2e..33d1040759 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -313,11 +313,42 @@ Decl *Parser::ParseLinkage(ParsingDeclSpec &DS, unsigned Context) { 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(); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 69dd4905cb..abcd886da6 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -12957,6 +12957,36 @@ Decl *Sema::ActOnFileScopeAsmDecl(Expr *expr, return New; } +static void checkModuleImportContext(Sema &S, Module *M, + SourceLocation ImportLoc, + DeclContext *DC) { + if (auto *LSD = dyn_cast(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(DC)) + DC = DC->getParent(); + if (!isa(DC)) { + S.Diag(ImportLoc, diag::err_module_import_not_at_top_level) + << M->getFullModuleName() << DC; + S.Diag(cast(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(SourceLocation AtLoc, /*IsIncludeDirective=*/false); if (!Mod) return true; - + + checkModuleImportContext(*this, Mod, ImportLoc, CurContext); + SmallVector IdentifierLocs; Module *ModCheck = Mod; for (unsigned I = 0, N = Path.size(); I != N; ++I) { @@ -12987,6 +13019,8 @@ DeclResult Sema::ActOnModuleImport(SourceLocation AtLoc, } 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); diff --git a/test/Modules/Inputs/c-header-bad.h b/test/Modules/Inputs/c-header-bad.h new file mode 100644 index 0000000000..7f7cd8701e --- /dev/null +++ b/test/Modules/Inputs/c-header-bad.h @@ -0,0 +1,4 @@ +} // expected-error {{extraneous closing brace ('}')}} +int not_in_extern_c; +extern "C" { // expected-note {{to match this '{'}} +// expected-error {{expected '}'}} diff --git a/test/Modules/Inputs/c-header.h b/test/Modules/Inputs/c-header.h new file mode 100644 index 0000000000..e12f644672 --- /dev/null +++ b/test/Modules/Inputs/c-header.h @@ -0,0 +1 @@ +int f(void); diff --git a/test/Modules/Inputs/cxx-header.h b/test/Modules/Inputs/cxx-header.h new file mode 100644 index 0000000000..7ed7775122 --- /dev/null +++ b/test/Modules/Inputs/cxx-header.h @@ -0,0 +1 @@ +int f(); diff --git a/test/Modules/Inputs/module.map b/test/Modules/Inputs/module.map index 0ce16fbd3e..362a175f60 100644 --- a/test/Modules/Inputs/module.map +++ b/test/Modules/Inputs/module.map @@ -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" diff --git a/test/Modules/extern_c.cpp b/test/Modules/extern_c.cpp new file mode 100644 index 0000000000..7ef4709812 --- /dev/null +++ b/test/Modules/extern_c.cpp @@ -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@-3 {{import of C++ module 'cxx_library' appears within extern "C" language linkage specification}} +// expected-note@-17 {{extern "C" language linkage specification begins here}} +#elif defined(NAMESPACE) +// expected-error-re@-6 {{import of module '{{c_library|cxx_library}}' appears within namespace 'M'}} +// expected-note@-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@-2 {{redefinition of 'f' as different kind of symbol}} + // expected-note@c-header.h:1 {{previous}} +#endif + } +} + +suppress_expected_no_diagnostics_error; // expected-error {{}} diff --git a/test/Modules/extern_c_bad.cpp b/test/Modules/extern_c_bad.cpp new file mode 100644 index 0000000000..bafdc046ce --- /dev/null +++ b/test/Modules/extern_c_bad.cpp @@ -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