From c1d22393628a145e54396c0ac66e9625d13a7658 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Wed, 13 Mar 2013 21:13:43 +0000 Subject: [Modules] Resolve top-headers of modules lazily. This allows resolving top-header filenames of modules to FileEntries when we need them, not eagerly. Note that that this breaks ABI for libclang functions clang_Module_getTopLevelHeader / clang_Module_getNumTopLevelHeaders but this is fine because they are experimental and not widely used yet. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@176975 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Basic/Module.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'lib/Basic/Module.cpp') diff --git a/lib/Basic/Module.cpp b/lib/Basic/Module.cpp index 65deac1b4a..f074391098 100644 --- a/lib/Basic/Module.cpp +++ b/lib/Basic/Module.cpp @@ -15,6 +15,7 @@ #include "clang/Basic/FileManager.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/TargetInfo.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ErrorHandling.h" @@ -129,6 +130,19 @@ const DirectoryEntry *Module::getUmbrellaDir() const { return Umbrella.dyn_cast(); } +ArrayRef Module::getTopHeaders(FileManager &FileMgr) { + if (!TopHeaderNames.empty()) { + for (std::vector::iterator + I = TopHeaderNames.begin(), E = TopHeaderNames.end(); I != E; ++I) { + if (const FileEntry *FE = FileMgr.getFile(*I)) + TopHeaders.insert(FE); + } + TopHeaderNames.clear(); + } + + return llvm::makeArrayRef(TopHeaders.begin(), TopHeaders.end()); +} + void Module::addRequirement(StringRef Feature, const LangOptions &LangOpts, const TargetInfo &Target) { Requires.push_back(Feature); -- cgit v1.2.3-70-g09d2 From 63a726870b486e0470c3a4b11cf62bab8be00b73 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 20 Mar 2013 00:22:05 +0000 Subject: Introduce configuration macros into module maps. Configuration macros are macros that are intended to alter how a module works, such that we need to build different module variants for different values of these macros. A module can declare its configuration macros, in which case we will complain if the definition of a configation macro on the command line (or lack thereof) differs from the current preprocessor state at the point where the module is imported. This should eliminate some surprises when enabling modules, because "#define CONFIG_MACRO ..." followed by "#include " would silently ignore the CONFIG_MACRO setting. At least it will no longer be silent about it. Configuration macros are eventually intended to help reduce the number of module variants that need to be built. When the list of configuration macros for a module is exhaustive, we only need to consider the settings for those macros when building/finding the module, which can help isolate modules for various project-specific -D flags that should never affect how modules are build (but currently do). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@177466 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticFrontendKinds.td | 8 ++- include/clang/Basic/DiagnosticGroups.td | 1 + include/clang/Basic/DiagnosticLexKinds.td | 4 ++ include/clang/Basic/Module.h | 18 ++++- include/clang/Serialization/ASTBitCodes.h | 4 +- lib/Basic/Module.cpp | 14 +++- lib/Frontend/CompilerInstance.cpp | 99 +++++++++++++++++++++++++- lib/Lex/ModuleMap.cpp | 79 ++++++++++++++++++-- lib/Serialization/ASTReader.cpp | 12 ++++ lib/Serialization/ASTWriter.cpp | 15 ++++ test/Modules/Inputs/config.h | 7 ++ test/Modules/Inputs/module.map | 5 ++ test/Modules/config_macros.m | 28 ++++++++ 13 files changed, 283 insertions(+), 11 deletions(-) create mode 100644 test/Modules/Inputs/config.h create mode 100644 test/Modules/config_macros.m (limited to 'lib/Basic/Module.cpp') diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td index 230a6d3dcc..111622e0fe 100644 --- a/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/include/clang/Basic/DiagnosticFrontendKinds.td @@ -137,5 +137,11 @@ def warn_missing_submodule : Warning<"missing submodule '%0'">, def err_module_map_temp_file : Error< "unable to write temporary module map file '%0'">, DefaultFatal; def err_module_unavailable : Error<"module '%0' requires feature '%1'">; - +def warn_module_config_macro_undef : Warning< + "%select{definition|#undef}0 of configuration macro '%1' has no effect on " + "the import of '%2'; pass '%select{-D%1=...|-U%1}0' on the command line " + "to configure the module">, + InGroup; +def note_module_def_undef_here : Note< + "macro was %select{defined|#undef'd}0 here">; } diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index 0f127bf2e2..9af83b10a7 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -47,6 +47,7 @@ def CastAlign : DiagGroup<"cast-align">; def : DiagGroup<"cast-qual">; def : DiagGroup<"char-align">; def Comment : DiagGroup<"comment">; +def ConfigMacros : DiagGroup<"config-macros">; def : DiagGroup<"ctor-dtor-privacy">; def GNUDesignator : DiagGroup<"gnu-designator">; diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td index 7f66a87d61..80c52492f4 100644 --- a/include/clang/Basic/DiagnosticLexKinds.td +++ b/include/clang/Basic/DiagnosticLexKinds.td @@ -532,6 +532,10 @@ def err_mmap_export_module_id : Error< "expected an exported module name or '*'">; def err_mmap_expected_library_name : Error< "expected %select{library|framework}0 name as a string">; +def err_mmap_config_macro_submodule : Error< + "configuration macros are only allowed on top-level modules">; +def err_mmap_expected_config_macro : Error< + "expected configuration macro name after ','">; def err_mmap_missing_module_unqualified : Error< "no module named '%0' visible from '%1'">; def err_mmap_missing_module_qualified : Error< diff --git a/include/clang/Basic/Module.h b/include/clang/Basic/Module.h index 5f081e0f56..2add58019f 100644 --- a/include/clang/Basic/Module.h +++ b/include/clang/Basic/Module.h @@ -119,7 +119,14 @@ public: /// \brief Whether, when inferring submodules, the inferr submodules should /// export all modules they import (e.g., the equivalent of "export *"). unsigned InferExportWildcard : 1; - + + /// \brief Whether the set of configuration macros is exhaustive. + /// + /// When the set of configuration macros is exhaustive, meaning + /// that no identifier not in this list should affect how the module is + /// built. + unsigned ConfigMacrosExhaustive : 1; + /// \brief Describes the visibility of the various names within a /// particular module. enum NameVisibilityKind { @@ -190,6 +197,10 @@ public: /// an entity from this module is used. llvm::SmallVector LinkLibraries; + /// \brief The set of "configuration macros", which are macros that + /// (intentionally) change how this module is built. + std::vector ConfigMacros; + /// \brief Construct a top-level module. explicit Module(StringRef Name, SourceLocation DefinitionLoc, bool IsFramework) @@ -197,7 +208,8 @@ public: IsAvailable(true), IsFromModuleFile(false), IsFramework(IsFramework), IsExplicit(false), IsSystem(false), InferSubmodules(false), InferExplicitSubmodules(false), - InferExportWildcard(false), NameVisibility(Hidden) { } + InferExportWildcard(false), ConfigMacrosExhaustive(false), + NameVisibility(Hidden) { } /// \brief Construct a new module or submodule. Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent, @@ -328,7 +340,7 @@ public: /// /// \returns The submodule if found, or NULL otherwise. Module *findSubmodule(StringRef Name) const; - + typedef std::vector::iterator submodule_iterator; typedef std::vector::const_iterator submodule_const_iterator; diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 93c5c073d5..2669d8e6d7 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -609,7 +609,9 @@ namespace clang { /// from this submodule. SUBMODULE_EXCLUDED_HEADER = 9, /// \brief Specifies a library or framework to link against. - SUBMODULE_LINK_LIBRARY = 10 + SUBMODULE_LINK_LIBRARY = 10, + /// \brief Specifies a configuration macro for this module. + SUBMODULE_CONFIG_MACRO = 11 }; /// \brief Record types used within a comments block. diff --git a/lib/Basic/Module.cpp b/lib/Basic/Module.cpp index f074391098..019d04732b 100644 --- a/lib/Basic/Module.cpp +++ b/lib/Basic/Module.cpp @@ -279,7 +279,19 @@ void Module::print(raw_ostream &OS, unsigned Indent) const { OS.write_escaped(UmbrellaDir->getName()); OS << "\"\n"; } - + + if (!ConfigMacros.empty() || ConfigMacrosExhaustive) { + OS.indent(Indent + 2); + OS << "config_macros "; + if (ConfigMacrosExhaustive) + OS << "[exhausive]"; + for (unsigned I = 0, N = ConfigMacros.size(); I != N; ++I) { + if (I) + OS << ", "; + OS << ConfigMacros[I]; + } + } + for (unsigned I = 0, N = Headers.size(); I != N; ++I) { OS.indent(Indent + 2); OS << "header \""; diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index d5c1a27ec8..a20e7d7ed0 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -905,6 +905,96 @@ static void compileModule(CompilerInstance &ImportingInstance, } } +/// \brief Diagnose differences between the current definition of the given +/// configuration macro and the definition provided on the command line. +static void checkConfigMacro(Preprocessor &PP, StringRef ConfigMacro, + Module *Mod, SourceLocation ImportLoc) { + IdentifierInfo *Id = PP.getIdentifierInfo(ConfigMacro); + SourceManager &SourceMgr = PP.getSourceManager(); + + // If this identifier has never had a macro definition, then it could + // not have changed. + if (!Id->hadMacroDefinition()) + return; + + // If this identifier does not currently have a macro definition, + // check whether it had one on the command line. + if (!Id->hasMacroDefinition()) { + MacroDirective *UndefMD = PP.getMacroDirectiveHistory(Id); + for (MacroDirective *MD = UndefMD; MD; MD = MD->getPrevious()) { + + FileID FID = SourceMgr.getFileID(MD->getLocation()); + if (FID.isInvalid()) + continue; + + const llvm::MemoryBuffer *Buffer = SourceMgr.getBuffer(FID); + if (!Buffer) + continue; + + // We only care about the predefines buffer. + if (!StringRef(Buffer->getBufferIdentifier()).equals("")) + continue; + + // This macro was defined on the command line, then #undef'd later. + // Complain. + PP.Diag(ImportLoc, diag::warn_module_config_macro_undef) + << true << ConfigMacro << Mod->getFullModuleName(); + if (UndefMD->getUndefLoc().isValid()) + PP.Diag(UndefMD->getUndefLoc(), diag::note_module_def_undef_here) + << true; + return; + } + + // Okay: no definition in the predefines buffer. + return; + } + + // This identifier has a macro definition. Check whether we had a definition + // on the command line. + MacroDirective *DefMD = PP.getMacroDirective(Id); + MacroDirective *PredefinedMD = 0; + for (MacroDirective *MD = DefMD; MD; MD = MD->getPrevious()) { + FileID FID = SourceMgr.getFileID(MD->getLocation()); + if (FID.isInvalid()) + continue; + + const llvm::MemoryBuffer *Buffer = SourceMgr.getBuffer(FID); + if (!Buffer) + continue; + + // We only care about the predefines buffer. + if (!StringRef(Buffer->getBufferIdentifier()).equals("")) + continue; + + PredefinedMD = MD; + break; + } + + // If there was no definition for this macro in the predefines buffer, + // complain. + if (!PredefinedMD || + (!PredefinedMD->getLocation().isValid() && + PredefinedMD->getUndefLoc().isValid())) { + PP.Diag(ImportLoc, diag::warn_module_config_macro_undef) + << false << ConfigMacro << Mod->getFullModuleName(); + PP.Diag(DefMD->getLocation(), diag::note_module_def_undef_here) + << false; + return; + } + + // If the current macro definition is the same as the predefined macro + // definition, it's okay. + if (DefMD == PredefinedMD || + DefMD->getInfo()->isIdenticalTo(*PredefinedMD->getInfo(), PP)) + return; + + // The macro definitions differ. + PP.Diag(ImportLoc, diag::warn_module_config_macro_undef) + << false << ConfigMacro << Mod->getFullModuleName(); + PP.Diag(DefMD->getLocation(), diag::note_module_def_undef_here) + << false; +} + ModuleLoadResult CompilerInstance::loadModule(SourceLocation ImportLoc, ModuleIdPath Path, @@ -1177,7 +1267,14 @@ CompilerInstance::loadModule(SourceLocation ImportLoc, ModuleManager->makeModuleVisible(Module, Visibility, ImportLoc); } - + + // Check for any configuration macros that have changed. + clang::Module *TopModule = Module->getTopLevelModule(); + for (unsigned I = 0, N = TopModule->ConfigMacros.size(); I != N; ++I) { + checkConfigMacro(getPreprocessor(), TopModule->ConfigMacros[I], + Module, ImportLoc); + } + // If this module import was due to an inclusion directive, create an // implicit import declaration to capture it in the AST. if (IsInclusionDirective && hasASTContext()) { diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index 71a98e2152..9cea5aace7 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -643,6 +643,7 @@ namespace clang { struct MMToken { enum TokenKind { Comma, + ConfigMacros, EndOfFile, HeaderKeyword, Identifier, @@ -687,10 +688,13 @@ namespace clang { /// \brief The set of attributes that can be attached to a module. struct Attributes { - Attributes() : IsSystem() { } + Attributes() : IsSystem(), IsExhaustive() { } /// \brief Whether this is a system module. unsigned IsSystem : 1; + + /// \brief Whether this is an exhaustive set of configuration macros. + unsigned IsExhaustive : 1; }; @@ -739,6 +743,7 @@ namespace clang { void parseUmbrellaDirDecl(SourceLocation UmbrellaLoc); void parseExportDecl(); void parseLinkDecl(); + void parseConfigMacros(); void parseInferredModuleDecl(bool Framework, bool Explicit); bool parseOptionalAttributes(Attributes &Attrs); @@ -776,11 +781,12 @@ retry: Tok.StringData = LToken.getRawIdentifierData(); Tok.StringLength = LToken.getLength(); Tok.Kind = llvm::StringSwitch(Tok.getString()) - .Case("header", MMToken::HeaderKeyword) + .Case("config_macros", MMToken::ConfigMacros) .Case("exclude", MMToken::ExcludeKeyword) .Case("explicit", MMToken::ExplicitKeyword) .Case("export", MMToken::ExportKeyword) .Case("framework", MMToken::FrameworkKeyword) + .Case("header", MMToken::HeaderKeyword) .Case("link", MMToken::LinkKeyword) .Case("module", MMToken::ModuleKeyword) .Case("requires", MMToken::RequiresKeyword) @@ -937,7 +943,9 @@ namespace { /// \brief An unknown attribute. AT_unknown, /// \brief The 'system' attribute. - AT_system + AT_system, + /// \brief The 'exhaustive' attribute. + AT_exhaustive }; } @@ -1094,7 +1102,11 @@ void ModuleMapParser::parseModuleDecl() { case MMToken::RBrace: Done = true; break; - + + case MMToken::ConfigMacros: + parseConfigMacros(); + break; + case MMToken::ExplicitKeyword: case MMToken::FrameworkKeyword: case MMToken::ModuleKeyword: @@ -1489,6 +1501,59 @@ void ModuleMapParser::parseLinkDecl() { IsFramework)); } +/// \brief Parse a configuration macro declaration. +/// +/// module-declaration: +/// 'config_macros' attributes[opt] config-macro-list? +/// +/// config-macro-list: +/// identifier (',' identifier)? +void ModuleMapParser::parseConfigMacros() { + assert(Tok.is(MMToken::ConfigMacros)); + SourceLocation ConfigMacrosLoc = consumeToken(); + + // Only top-level modules can have configuration macros. + if (ActiveModule->Parent) { + Diags.Report(ConfigMacrosLoc, diag::err_mmap_config_macro_submodule); + } + + // Parse the optional attributes. + Attributes Attrs; + parseOptionalAttributes(Attrs); + if (Attrs.IsExhaustive && !ActiveModule->Parent) { + ActiveModule->ConfigMacrosExhaustive = true; + } + + // If we don't have an identifier, we're done. + if (!Tok.is(MMToken::Identifier)) + return; + + // Consume the first identifier. + if (!ActiveModule->Parent) { + ActiveModule->ConfigMacros.push_back(Tok.getString().str()); + } + consumeToken(); + + do { + // If there's a comma, consume it. + if (!Tok.is(MMToken::Comma)) + break; + consumeToken(); + + // We expect to see a macro name here. + if (!Tok.is(MMToken::Identifier)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_config_macro); + break; + } + + // Consume the macro name. + if (!ActiveModule->Parent) { + ActiveModule->ConfigMacros.push_back(Tok.getString().str()); + } + consumeToken(); + } while (true); +} + /// \brief Parse an inferred module declaration (wildcard modules). /// /// module-declaration: @@ -1668,6 +1733,7 @@ bool ModuleMapParser::parseOptionalAttributes(Attributes &Attrs) { // Decode the attribute name. AttributeKind Attribute = llvm::StringSwitch(Tok.getString()) + .Case("exhaustive", AT_exhaustive) .Case("system", AT_system) .Default(AT_unknown); switch (Attribute) { @@ -1679,6 +1745,10 @@ bool ModuleMapParser::parseOptionalAttributes(Attributes &Attrs) { case AT_system: Attrs.IsSystem = true; break; + + case AT_exhaustive: + Attrs.IsExhaustive = true; + break; } consumeToken(); @@ -1730,6 +1800,7 @@ bool ModuleMapParser::parseModuleMapFile() { break; case MMToken::Comma: + case MMToken::ConfigMacros: case MMToken::ExcludeKeyword: case MMToken::ExportKeyword: case MMToken::HeaderKeyword: diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 42b5038ae9..00e5ce910b 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -3718,6 +3718,18 @@ bool ASTReader::ReadSubmoduleBlock(ModuleFile &F) { CurrentModule->LinkLibraries.push_back( Module::LinkLibrary(Blob, Record[0])); break; + + case SUBMODULE_CONFIG_MACRO: + if (First) { + Error("missing submodule metadata record at beginning of block"); + return true; + } + + if (!CurrentModule) + break; + + CurrentModule->ConfigMacros.push_back(Blob.str()); + break; } } } diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 724c8cf825..08e8afdd0a 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -2135,6 +2135,7 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferSubmodules... Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferExplicit... Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferExportWild... + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // ConfigMacrosExh... Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Name unsigned DefinitionAbbrev = Stream.EmitAbbrev(Abbrev); @@ -2174,6 +2175,11 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Name unsigned LinkLibraryAbbrev = Stream.EmitAbbrev(Abbrev); + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(SUBMODULE_CONFIG_MACRO)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Macro name + unsigned ConfigMacroAbbrev = Stream.EmitAbbrev(Abbrev); + // Write the submodule metadata block. RecordData Record; Record.push_back(getNumberOfModules(WritingModule)); @@ -2204,6 +2210,7 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { Record.push_back(Mod->InferSubmodules); Record.push_back(Mod->InferExplicitSubmodules); Record.push_back(Mod->InferExportWildcard); + Record.push_back(Mod->ConfigMacrosExhaustive); Stream.EmitRecordWithBlob(DefinitionAbbrev, Record, Mod->Name); // Emit the requirements. @@ -2288,6 +2295,14 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { Mod->LinkLibraries[I].Library); } + // Emit the configuration macros. + for (unsigned I = 0, N = Mod->ConfigMacros.size(); I != N; ++I) { + Record.clear(); + Record.push_back(SUBMODULE_CONFIG_MACRO); + Stream.EmitRecordWithBlob(ConfigMacroAbbrev, Record, + Mod->ConfigMacros[I]); + } + // Queue up the submodules of this module. for (Module::submodule_iterator Sub = Mod->submodule_begin(), SubEnd = Mod->submodule_end(); diff --git a/test/Modules/Inputs/config.h b/test/Modules/Inputs/config.h new file mode 100644 index 0000000000..f2dfda64c3 --- /dev/null +++ b/test/Modules/Inputs/config.h @@ -0,0 +1,7 @@ +#ifdef WANT_FOO +int* foo(); +#endif + +#ifdef WANT_BAR +char *bar(); +#endif diff --git a/test/Modules/Inputs/module.map b/test/Modules/Inputs/module.map index 53f2fd65d3..479bfcfa7f 100644 --- a/test/Modules/Inputs/module.map +++ b/test/Modules/Inputs/module.map @@ -183,3 +183,8 @@ module cxx_inline_namespace { module cxx_linkage_cache { header "cxx-linkage-cache.h" } + +module config { + header "config.h" + config_macros [exhaustive] WANT_FOO, WANT_BAR +} diff --git a/test/Modules/config_macros.m b/test/Modules/config_macros.m new file mode 100644 index 0000000000..200744d614 --- /dev/null +++ b/test/Modules/config_macros.m @@ -0,0 +1,28 @@ +@import config; + +int *test_foo() { + return foo(); +} + +char *test_bar() { + return bar(); // expected-warning{{implicit declaration of function 'bar' is invalid in C99}} \ + // expected-warning{{incompatible integer to pointer conversion}} +} + +#undef WANT_FOO // expected-note{{macro was #undef'd here}} +@import config; // expected-warning{{#undef of configuration macro 'WANT_FOO' has no effect on the import of 'config'; pass '-UWANT_FOO' on the command line to configure the module}} + +#define WANT_FOO 2 // expected-note{{macro was defined here}} +@import config; // expected-warning{{definition of configuration macro 'WANT_FOO' has no effect on the import of 'config'; pass '-DWANT_FOO=...' on the command line to configure the module}} + +#undef WANT_FOO +#define WANT_FOO 1 +@import config; // okay + +#define WANT_BAR 1 // expected-note{{macro was defined here}} +@import config; // expected-warning{{definition of configuration macro 'WANT_BAR' has no effect on the import of 'config'; pass '-DWANT_BAR=...' on the command line to configure the module}} + +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules -x objective-c -fmodules-cache-path=%t -DWANT_FOO=1 -emit-module -fmodule-name=config %S/Inputs/module.map +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -DWANT_FOO=1 %s -verify + -- cgit v1.2.3-70-g09d2 From 970e441671be130c9a12b7eda2a0b795008812a5 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 20 Mar 2013 03:59:18 +0000 Subject: Make sure that Module::ConfigMacrosExhaustive gets initialized and deserialized correctly. This fixes regressions introduced in r177466 that caused several module tests to fail sporadically. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@177481 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Basic/Module.cpp | 7 ++++--- lib/Serialization/ASTReader.cpp | 9 ++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'lib/Basic/Module.cpp') diff --git a/lib/Basic/Module.cpp b/lib/Basic/Module.cpp index 019d04732b..197c53fb14 100644 --- a/lib/Basic/Module.cpp +++ b/lib/Basic/Module.cpp @@ -28,7 +28,8 @@ Module::Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent, Umbrella(), ASTFile(0), IsAvailable(true), IsFromModuleFile(false), IsFramework(IsFramework), IsExplicit(IsExplicit), IsSystem(false), InferSubmodules(false), InferExplicitSubmodules(false), - InferExportWildcard(false), NameVisibility(Hidden) + InferExportWildcard(false), ConfigMacrosExhaustive(false), + NameVisibility(Hidden) { if (Parent) { if (!Parent->isAvailable()) @@ -46,7 +47,6 @@ Module::~Module() { I != IEnd; ++I) { delete *I; } - } /// \brief Determine whether a translation unit built using the current @@ -284,12 +284,13 @@ void Module::print(raw_ostream &OS, unsigned Indent) const { OS.indent(Indent + 2); OS << "config_macros "; if (ConfigMacrosExhaustive) - OS << "[exhausive]"; + OS << "[exhaustive]"; for (unsigned I = 0, N = ConfigMacros.size(); I != N; ++I) { if (I) OS << ", "; OS << ConfigMacros[I]; } + OS << "\n"; } for (unsigned I = 0, N = Headers.size(); I != N; ++I) { diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 00e5ce910b..126770b5b8 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -3475,7 +3475,7 @@ bool ASTReader::ReadSubmoduleBlock(ModuleFile &F) { return true; } - if (Record.size() < 7) { + if (Record.size() < 8) { Error("malformed module definition"); return true; } @@ -3489,7 +3489,8 @@ bool ASTReader::ReadSubmoduleBlock(ModuleFile &F) { bool InferSubmodules = Record[5]; bool InferExplicitSubmodules = Record[6]; bool InferExportWildcard = Record[7]; - + bool ConfigMacrosExhaustive = Record[8]; + Module *ParentModule = 0; if (Parent) ParentModule = getSubmodule(Parent); @@ -3527,13 +3528,15 @@ bool ASTReader::ReadSubmoduleBlock(ModuleFile &F) { CurrentModule->InferSubmodules = InferSubmodules; CurrentModule->InferExplicitSubmodules = InferExplicitSubmodules; CurrentModule->InferExportWildcard = InferExportWildcard; + CurrentModule->ConfigMacrosExhaustive = ConfigMacrosExhaustive; if (DeserializationListener) DeserializationListener->ModuleRead(GlobalID, CurrentModule); SubmodulesLoaded[GlobalIndex] = CurrentModule; - // Clear out link libraries; the module file has them. + // Clear out link libraries and config macros; the module file has them. CurrentModule->LinkLibraries.clear(); + CurrentModule->ConfigMacros.clear(); break; } -- cgit v1.2.3-70-g09d2 From 906d66acc5cf2679453e10a4f0a67feedd765b21 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 20 Mar 2013 21:10:35 +0000 Subject: Extend module maps with a 'conflict' declaration, and warn when a newly-imported module conflicts with an already-imported module. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@177577 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticGroups.td | 1 + include/clang/Basic/DiagnosticLexKinds.td | 4 + .../clang/Basic/DiagnosticSerializationKinds.td | 5 +- include/clang/Basic/Module.h | 25 +++++ include/clang/Frontend/ASTUnit.h | 3 +- include/clang/Frontend/CompilerInstance.h | 3 +- include/clang/Lex/ModuleLoader.h | 3 +- include/clang/Lex/ModuleMap.h | 27 ++++- include/clang/Serialization/ASTBitCodes.h | 4 +- include/clang/Serialization/ASTReader.h | 26 +++-- lib/Basic/Module.cpp | 18 ++++ lib/Frontend/CompilerInstance.cpp | 10 +- lib/Lex/ModuleMap.cpp | 120 +++++++++++++++++---- lib/Sema/Sema.cpp | 5 +- lib/Sema/SemaDecl.cpp | 3 +- lib/Serialization/ASTReader.cpp | 86 +++++++++++---- lib/Serialization/ASTWriter.cpp | 17 +++ test/Modules/Inputs/Conflicts/conflict_a.h | 1 + test/Modules/Inputs/Conflicts/conflict_b.h | 1 + test/Modules/Inputs/Conflicts/module.map | 10 ++ test/Modules/conflicts.m | 7 ++ unittests/Basic/SourceManagerTest.cpp | 3 +- unittests/Lex/LexerTest.cpp | 3 +- unittests/Lex/PPCallbacksTest.cpp | 3 +- unittests/Lex/PPConditionalDirectiveRecordTest.cpp | 3 +- 25 files changed, 328 insertions(+), 63 deletions(-) create mode 100644 test/Modules/Inputs/Conflicts/conflict_a.h create mode 100644 test/Modules/Inputs/Conflicts/conflict_b.h create mode 100644 test/Modules/Inputs/Conflicts/module.map create mode 100644 test/Modules/conflicts.m (limited to 'lib/Basic/Module.cpp') diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index 9af83b10a7..4ddf2077e7 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -159,6 +159,7 @@ def MismatchedParameterTypes : DiagGroup<"mismatched-parameter-types">; def MismatchedReturnTypes : DiagGroup<"mismatched-return-types">; def MismatchedTags : DiagGroup<"mismatched-tags">; def MissingFieldInitializers : DiagGroup<"missing-field-initializers">; +def ModuleConflict : DiagGroup<"module-conflict">; def NullArithmetic : DiagGroup<"null-arithmetic">; def NullCharacter : DiagGroup<"null-character">; def NullDereference : DiagGroup<"null-dereference">; diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td index 80c52492f4..339788b75d 100644 --- a/include/clang/Basic/DiagnosticLexKinds.td +++ b/include/clang/Basic/DiagnosticLexKinds.td @@ -536,6 +536,10 @@ def err_mmap_config_macro_submodule : Error< "configuration macros are only allowed on top-level modules">; def err_mmap_expected_config_macro : Error< "expected configuration macro name after ','">; +def err_mmap_expected_conflicts_comma : Error< + "expected ',' after conflicting module name">; +def err_mmap_expected_conflicts_message : Error< + "expected a message describing the conflict with '%0'">; def err_mmap_missing_module_unqualified : Error< "no module named '%0' visible from '%1'">; def err_mmap_missing_module_qualified : Error< diff --git a/include/clang/Basic/DiagnosticSerializationKinds.td b/include/clang/Basic/DiagnosticSerializationKinds.td index bc5bd4e2ad..7137404a69 100644 --- a/include/clang/Basic/DiagnosticSerializationKinds.td +++ b/include/clang/Basic/DiagnosticSerializationKinds.td @@ -44,7 +44,10 @@ def warn_pch_different_branch : Error< def err_pch_with_compiler_errors : Error< "PCH file contains compiler errors">; - +def warn_module_conflict : Warning< + "module '%0' conflicts with already-imported module '%1': %2">, + InGroup; + def err_pch_macro_def_undef : Error< "macro '%0' was %select{defined|undef'd}1 in the precompiled header but " "%select{undef'd|defined}1 on the command line">; diff --git a/include/clang/Basic/Module.h b/include/clang/Basic/Module.h index 2add58019f..d2a43f0219 100644 --- a/include/clang/Basic/Module.h +++ b/include/clang/Basic/Module.h @@ -201,6 +201,31 @@ public: /// (intentionally) change how this module is built. std::vector ConfigMacros; + /// \brief An unresolved conflict with another module. + struct UnresolvedConflict { + /// \brief The (unresolved) module id. + ModuleId Id; + + /// \brief The message provided to the user when there is a conflict. + std::string Message; + }; + + /// \brief The list of conflicts for which the module-id has not yet been + /// resolved. + std::vector UnresolvedConflicts; + + /// \brief A conflict between two modules. + struct Conflict { + /// \brief The module that this module conflicts with. + Module *Other; + + /// \brief The message provided to the user when there is a conflict. + std::string Message; + }; + + /// \brief The list of conflicts. + std::vector Conflicts; + /// \brief Construct a top-level module. explicit Module(StringRef Name, SourceLocation DefinitionLoc, bool IsFramework) diff --git a/include/clang/Frontend/ASTUnit.h b/include/clang/Frontend/ASTUnit.h index 108114dd67..02c57d7472 100644 --- a/include/clang/Frontend/ASTUnit.h +++ b/include/clang/Frontend/ASTUnit.h @@ -843,7 +843,8 @@ public: virtual void makeModuleVisible(Module *Mod, Module::NameVisibilityKind Visibility, - SourceLocation ImportLoc) { } + SourceLocation ImportLoc, + bool Complain) { } }; diff --git a/include/clang/Frontend/CompilerInstance.h b/include/clang/Frontend/CompilerInstance.h index 273fcc1082..0d674629fd 100644 --- a/include/clang/Frontend/CompilerInstance.h +++ b/include/clang/Frontend/CompilerInstance.h @@ -663,7 +663,8 @@ public: virtual void makeModuleVisible(Module *Mod, Module::NameVisibilityKind Visibility, - SourceLocation ImportLoc); + SourceLocation ImportLoc, + bool Complain); }; diff --git a/include/clang/Lex/ModuleLoader.h b/include/clang/Lex/ModuleLoader.h index 93e69a6eed..3acf9151bc 100644 --- a/include/clang/Lex/ModuleLoader.h +++ b/include/clang/Lex/ModuleLoader.h @@ -83,7 +83,8 @@ public: /// \brief Make the given module visible. virtual void makeModuleVisible(Module *Mod, Module::NameVisibilityKind Visibility, - SourceLocation ImportLoc) = 0; + SourceLocation ImportLoc, + bool Complain) = 0; }; } diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h index cffa5b7b66..33c92f59a4 100644 --- a/include/clang/Lex/ModuleMap.h +++ b/include/clang/Lex/ModuleMap.h @@ -134,7 +134,20 @@ class ModuleMap { Module::ExportDecl resolveExport(Module *Mod, const Module::UnresolvedExportDecl &Unresolved, bool Complain) const; - + + /// \brief Resolve the given module id to an actual module. + /// + /// \param Id The module-id to resolve. + /// + /// \param Mod The module in which we're resolving the module-id. + /// + /// \param Complain Whether this routine should complain about unresolvable + /// module-ids. + /// + /// \returns The resolved module, or null if the module-id could not be + /// resolved. + Module *resolveModuleId(const ModuleId &Id, Module *Mod, bool Complain) const; + public: /// \brief Construct a new module map. /// @@ -265,7 +278,17 @@ public: /// false otherwise. bool resolveExports(Module *Mod, bool Complain); - /// \brief Infers the (sub)module based on the given source location and + /// \brief Resolve all of the unresolved conflicts in the given module. + /// + /// \param Mod The module whose conflicts should be resolved. + /// + /// \param Complain Whether to emit diagnostics for failures. + /// + /// \returns true if any errors were encountered while resolving conflicts, + /// false otherwise. + bool resolveConflicts(Module *Mod, bool Complain); + + /// \brief Infers the (sub)module based on the given source location and /// source manager. /// /// \param Loc The location within the source that we are querying, along diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 2669d8e6d7..85f88ad007 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -611,7 +611,9 @@ namespace clang { /// \brief Specifies a library or framework to link against. SUBMODULE_LINK_LIBRARY = 10, /// \brief Specifies a configuration macro for this module. - SUBMODULE_CONFIG_MACRO = 11 + SUBMODULE_CONFIG_MACRO = 11, + /// \brief Specifies a conflict with another module. + SUBMODULE_CONFLICT = 12 }; /// \brief Record types used within a comments block. diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h index 61a0cbebd7..9b4a5c04e4 100644 --- a/include/clang/Serialization/ASTReader.h +++ b/include/clang/Serialization/ASTReader.h @@ -520,27 +520,30 @@ private: HiddenNamesMapType HiddenNamesMap; - /// \brief A module import or export that hasn't yet been resolved. - struct UnresolvedModuleImportExport { + /// \brief A module import, export, or conflict that hasn't yet been resolved. + struct UnresolvedModuleRef { /// \brief The file in which this module resides. ModuleFile *File; /// \brief The module that is importing or exporting. Module *Mod; - + + /// \brief The kind of module reference. + enum { Import, Export, Conflict } Kind; + /// \brief The local ID of the module that is being exported. unsigned ID; - - /// \brief Whether this is an import (vs. an export). - unsigned IsImport : 1; - + /// \brief Whether this is a wildcard export. unsigned IsWildcard : 1; + + /// \brief String data. + StringRef String; }; /// \brief The set of module imports and exports that still need to be /// resolved. - SmallVector UnresolvedModuleImportExports; + SmallVector UnresolvedModuleRefs; /// \brief A vector containing selectors that have already been loaded. /// @@ -1188,9 +1191,14 @@ public: /// /// \param NameVisibility The level of visibility to give the names in the /// module. Visibility can only be increased over time. + /// + /// \param ImportLoc The location at which the import occurs. + /// + /// \param Complain Whether to complain about conflicting module imports. void makeModuleVisible(Module *Mod, Module::NameVisibilityKind NameVisibility, - SourceLocation ImportLoc); + SourceLocation ImportLoc, + bool Complain); /// \brief Make the names within this set of hidden names visible. void makeNamesVisible(const HiddenNames &Names); diff --git a/lib/Basic/Module.cpp b/lib/Basic/Module.cpp index 197c53fb14..13518cde66 100644 --- a/lib/Basic/Module.cpp +++ b/lib/Basic/Module.cpp @@ -347,6 +347,24 @@ void Module::print(raw_ostream &OS, unsigned Indent) const { OS << "\""; } + for (unsigned I = 0, N = UnresolvedConflicts.size(); I != N; ++I) { + OS.indent(Indent + 2); + OS << "conflict "; + printModuleId(OS, UnresolvedConflicts[I].Id); + OS << ", \""; + OS.write_escaped(UnresolvedConflicts[I].Message); + OS << "\"\n"; + } + + for (unsigned I = 0, N = Conflicts.size(); I != N; ++I) { + OS.indent(Indent + 2); + OS << "conflict "; + OS << Conflicts[I].Other->getFullModuleName(); + OS << ", \""; + OS.write_escaped(Conflicts[I].Message); + OS << "\"\n"; + } + if (InferSubmodules) { OS.indent(Indent + 2); if (InferExplicitSubmodules) diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index a20e7d7ed0..633f3c5868 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -1007,7 +1007,7 @@ CompilerInstance::loadModule(SourceLocation ImportLoc, // Make the named module visible. if (LastModuleImportResult) ModuleManager->makeModuleVisible(LastModuleImportResult, Visibility, - ImportLoc); + ImportLoc, /*Complain=*/false); return LastModuleImportResult; } @@ -1265,7 +1265,8 @@ CompilerInstance::loadModule(SourceLocation ImportLoc, return ModuleLoadResult(); } - ModuleManager->makeModuleVisible(Module, Visibility, ImportLoc); + ModuleManager->makeModuleVisible(Module, Visibility, ImportLoc, + /*Complain=*/true); } // Check for any configuration macros that have changed. @@ -1294,7 +1295,8 @@ CompilerInstance::loadModule(SourceLocation ImportLoc, void CompilerInstance::makeModuleVisible(Module *Mod, Module::NameVisibilityKind Visibility, - SourceLocation ImportLoc){ - ModuleManager->makeModuleVisible(Mod, Visibility, ImportLoc); + SourceLocation ImportLoc, + bool Complain){ + ModuleManager->makeModuleVisible(Mod, Visibility, ImportLoc, Complain); } diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index 9cea5aace7..0c03201aa6 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -45,35 +45,42 @@ ModuleMap::resolveExport(Module *Mod, return Module::ExportDecl(0, true); } + // Resolve the module-id. + Module *Context = resolveModuleId(Unresolved.Id, Mod, Complain); + if (!Context) + return Module::ExportDecl(); + + return Module::ExportDecl(Context, Unresolved.Wildcard); +} + +Module *ModuleMap::resolveModuleId(const ModuleId &Id, Module *Mod, + bool Complain) const { // Find the starting module. - Module *Context = lookupModuleUnqualified(Unresolved.Id[0].first, Mod); + Module *Context = lookupModuleUnqualified(Id[0].first, Mod); if (!Context) { if (Complain) - Diags->Report(Unresolved.Id[0].second, - diag::err_mmap_missing_module_unqualified) - << Unresolved.Id[0].first << Mod->getFullModuleName(); - - return Module::ExportDecl(); + Diags->Report(Id[0].second, diag::err_mmap_missing_module_unqualified) + << Id[0].first << Mod->getFullModuleName(); + + return 0; } // Dig into the module path. - for (unsigned I = 1, N = Unresolved.Id.size(); I != N; ++I) { - Module *Sub = lookupModuleQualified(Unresolved.Id[I].first, - Context); + for (unsigned I = 1, N = Id.size(); I != N; ++I) { + Module *Sub = lookupModuleQualified(Id[I].first, Context); if (!Sub) { if (Complain) - Diags->Report(Unresolved.Id[I].second, - diag::err_mmap_missing_module_qualified) - << Unresolved.Id[I].first << Context->getFullModuleName() - << SourceRange(Unresolved.Id[0].second, Unresolved.Id[I-1].second); - - return Module::ExportDecl(); + Diags->Report(Id[I].second, diag::err_mmap_missing_module_qualified) + << Id[I].first << Context->getFullModuleName() + << SourceRange(Id[0].second, Id[I-1].second); + + return 0; } - + Context = Sub; } - - return Module::ExportDecl(Context, Unresolved.Wildcard); + + return Context; } ModuleMap::ModuleMap(FileManager &FileMgr, const DiagnosticConsumer &DC, @@ -603,6 +610,25 @@ bool ModuleMap::resolveExports(Module *Mod, bool Complain) { return HadError; } +bool ModuleMap::resolveConflicts(Module *Mod, bool Complain) { + bool HadError = false; + for (unsigned I = 0, N = Mod->UnresolvedConflicts.size(); I != N; ++I) { + Module *OtherMod = resolveModuleId(Mod->UnresolvedConflicts[I].Id, + Mod, Complain); + if (!OtherMod) { + HadError = true; + continue; + } + + Module::Conflict Conflict; + Conflict.Other = OtherMod; + Conflict.Message = Mod->UnresolvedConflicts[I].Message; + Mod->Conflicts.push_back(Conflict); + } + Mod->UnresolvedConflicts.clear(); + return HadError; +} + Module *ModuleMap::inferModuleFromLocation(FullSourceLoc Loc) { if (Loc.isInvalid()) return 0; @@ -644,6 +670,7 @@ namespace clang { enum TokenKind { Comma, ConfigMacros, + Conflict, EndOfFile, HeaderKeyword, Identifier, @@ -744,6 +771,7 @@ namespace clang { void parseExportDecl(); void parseLinkDecl(); void parseConfigMacros(); + void parseConflict(); void parseInferredModuleDecl(bool Framework, bool Explicit); bool parseOptionalAttributes(Attributes &Attrs); @@ -782,6 +810,7 @@ retry: Tok.StringLength = LToken.getLength(); Tok.Kind = llvm::StringSwitch(Tok.getString()) .Case("config_macros", MMToken::ConfigMacros) + .Case("conflict", MMToken::Conflict) .Case("exclude", MMToken::ExcludeKeyword) .Case("explicit", MMToken::ExplicitKeyword) .Case("export", MMToken::ExportKeyword) @@ -1107,6 +1136,10 @@ void ModuleMapParser::parseModuleDecl() { parseConfigMacros(); break; + case MMToken::Conflict: + parseConflict(); + break; + case MMToken::ExplicitKeyword: case MMToken::FrameworkKeyword: case MMToken::ModuleKeyword: @@ -1554,6 +1587,56 @@ void ModuleMapParser::parseConfigMacros() { } while (true); } +/// \brief Format a module-id into a string. +static std::string formatModuleId(const ModuleId &Id) { + std::string result; + { + llvm::raw_string_ostream OS(result); + + for (unsigned I = 0, N = Id.size(); I != N; ++I) { + if (I) + OS << "."; + OS << Id[I].first; + } + } + + return result; +} + +/// \brief Parse a conflict declaration. +/// +/// module-declaration: +/// 'conflict' module-id ',' string-literal +void ModuleMapParser::parseConflict() { + assert(Tok.is(MMToken::Conflict)); + SourceLocation ConflictLoc = consumeToken(); + Module::UnresolvedConflict Conflict; + + // Parse the module-id. + if (parseModuleId(Conflict.Id)) + return; + + // Parse the ','. + if (!Tok.is(MMToken::Comma)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_conflicts_comma) + << SourceRange(ConflictLoc); + return; + } + consumeToken(); + + // Parse the message. + if (!Tok.is(MMToken::StringLiteral)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_conflicts_message) + << formatModuleId(Conflict.Id); + return; + } + Conflict.Message = Tok.getString().str(); + consumeToken(); + + // Add this unresolved conflict. + ActiveModule->UnresolvedConflicts.push_back(Conflict); +} + /// \brief Parse an inferred module declaration (wildcard modules). /// /// module-declaration: @@ -1801,6 +1884,7 @@ bool ModuleMapParser::parseModuleMapFile() { case MMToken::Comma: case MMToken::ConfigMacros: + case MMToken::Conflict: case MMToken::ExcludeKeyword: case MMToken::ExportKeyword: case MMToken::HeaderKeyword: diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 4aeb511abd..8b17c123fd 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -634,11 +634,12 @@ void Sema::ActOnEndOfTranslationUnit() { Module *Mod = Stack.back(); Stack.pop_back(); - // Resolve the exported declarations. + // Resolve the exported declarations and conflicts. // FIXME: Actually complain, once we figure out how to teach the - // diagnostic client to deal with complains in the module map at this + // diagnostic client to deal with complaints in the module map at this // point. ModMap.resolveExports(Mod, /*Complain=*/false); + ModMap.resolveConflicts(Mod, /*Complain=*/false); // Queue the submodules, so their exports will also be resolved. for (Module::submodule_iterator Sub = Mod->submodule_begin(), diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 1cffce0808..d046f642b2 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -11826,7 +11826,8 @@ void Sema::createImplicitModuleImport(SourceLocation Loc, Module *Mod) { Consumer.HandleImplicitImportDecl(ImportD); // Make the module visible. - PP.getModuleLoader().makeModuleVisible(Mod, Module::AllVisible, Loc); + PP.getModuleLoader().makeModuleVisible(Mod, Module::AllVisible, Loc, + /*Complain=*/false); } void Sema::ActOnPragmaRedefineExtname(IdentifierInfo* Name, diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 126770b5b8..29538a13ef 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -2716,7 +2716,8 @@ void ASTReader::makeNamesVisible(const HiddenNames &Names) { void ASTReader::makeModuleVisible(Module *Mod, Module::NameVisibilityKind NameVisibility, - SourceLocation ImportLoc) { + SourceLocation ImportLoc, + bool Complain) { llvm::SmallPtrSet Visited; SmallVector Stack; Stack.push_back(Mod); @@ -2764,6 +2765,20 @@ void ASTReader::makeModuleVisible(Module *Mod, if (Visited.insert(Exported)) Stack.push_back(Exported); } + + // Detect any conflicts. + if (Complain) { + assert(ImportLoc.isValid() && "Missing import location"); + for (unsigned I = 0, N = Mod->Conflicts.size(); I != N; ++I) { + if (Mod->Conflicts[I].Other->NameVisibility >= NameVisibility) { + Diag(ImportLoc, diag::warn_module_conflict) + << Mod->getFullModuleName() + << Mod->Conflicts[I].Other->getFullModuleName() + << Mod->Conflicts[I].Message; + // FIXME: Need note where the other module was imported. + } + } + } } } @@ -2879,22 +2894,34 @@ ASTReader::ASTReadResult ASTReader::ReadAST(const std::string &FileName, Id->second->setOutOfDate(true); // Resolve any unresolved module exports. - for (unsigned I = 0, N = UnresolvedModuleImportExports.size(); I != N; ++I) { - UnresolvedModuleImportExport &Unresolved = UnresolvedModuleImportExports[I]; + for (unsigned I = 0, N = UnresolvedModuleRefs.size(); I != N; ++I) { + UnresolvedModuleRef &Unresolved = UnresolvedModuleRefs[I]; SubmoduleID GlobalID = getGlobalSubmoduleID(*Unresolved.File,Unresolved.ID); Module *ResolvedMod = getSubmodule(GlobalID); - - if (Unresolved.IsImport) { + + switch (Unresolved.Kind) { + case UnresolvedModuleRef::Conflict: + if (ResolvedMod) { + Module::Conflict Conflict; + Conflict.Other = ResolvedMod; + Conflict.Message = Unresolved.String.str(); + Unresolved.Mod->Conflicts.push_back(Conflict); + } + continue; + + case UnresolvedModuleRef::Import: if (ResolvedMod) Unresolved.Mod->Imports.push_back(ResolvedMod); continue; - } - if (ResolvedMod || Unresolved.IsWildcard) - Unresolved.Mod->Exports.push_back( - Module::ExportDecl(ResolvedMod, Unresolved.IsWildcard)); + case UnresolvedModuleRef::Export: + if (ResolvedMod || Unresolved.IsWildcard) + Unresolved.Mod->Exports.push_back( + Module::ExportDecl(ResolvedMod, Unresolved.IsWildcard)); + continue; + } } - UnresolvedModuleImportExports.clear(); + UnresolvedModuleRefs.clear(); InitializeContext(); @@ -3196,7 +3223,8 @@ void ASTReader::InitializeContext() { for (unsigned I = 0, N = ImportedModules.size(); I != N; ++I) { if (Module *Imported = getSubmodule(ImportedModules[I])) makeModuleVisible(Imported, Module::AllVisible, - /*ImportLoc=*/SourceLocation()); + /*ImportLoc=*/SourceLocation(), + /*Complain=*/false); } ImportedModules.clear(); } @@ -3534,9 +3562,11 @@ bool ASTReader::ReadSubmoduleBlock(ModuleFile &F) { SubmodulesLoaded[GlobalIndex] = CurrentModule; - // Clear out link libraries and config macros; the module file has them. + // Clear out data that will be replaced by what is the module file. CurrentModule->LinkLibraries.clear(); CurrentModule->ConfigMacros.clear(); + CurrentModule->UnresolvedConflicts.clear(); + CurrentModule->Conflicts.clear(); break; } @@ -3660,13 +3690,13 @@ bool ASTReader::ReadSubmoduleBlock(ModuleFile &F) { break; for (unsigned Idx = 0; Idx != Record.size(); ++Idx) { - UnresolvedModuleImportExport Unresolved; + UnresolvedModuleRef Unresolved; Unresolved.File = &F; Unresolved.Mod = CurrentModule; Unresolved.ID = Record[Idx]; - Unresolved.IsImport = true; + Unresolved.Kind = UnresolvedModuleRef::Import; Unresolved.IsWildcard = false; - UnresolvedModuleImportExports.push_back(Unresolved); + UnresolvedModuleRefs.push_back(Unresolved); } break; } @@ -3681,13 +3711,13 @@ bool ASTReader::ReadSubmoduleBlock(ModuleFile &F) { break; for (unsigned Idx = 0; Idx + 1 < Record.size(); Idx += 2) { - UnresolvedModuleImportExport Unresolved; + UnresolvedModuleRef Unresolved; Unresolved.File = &F; Unresolved.Mod = CurrentModule; Unresolved.ID = Record[Idx]; - Unresolved.IsImport = false; + Unresolved.Kind = UnresolvedModuleRef::Export; Unresolved.IsWildcard = Record[Idx + 1]; - UnresolvedModuleImportExports.push_back(Unresolved); + UnresolvedModuleRefs.push_back(Unresolved); } // Once we've loaded the set of exports, there's no reason to keep @@ -3733,6 +3763,26 @@ bool ASTReader::ReadSubmoduleBlock(ModuleFile &F) { CurrentModule->ConfigMacros.push_back(Blob.str()); break; + + case SUBMODULE_CONFLICT: { + if (First) { + Error("missing submodule metadata record at beginning of block"); + return true; + } + + if (!CurrentModule) + break; + + UnresolvedModuleRef Unresolved; + Unresolved.File = &F; + Unresolved.Mod = CurrentModule; + Unresolved.ID = Record[0]; + Unresolved.Kind = UnresolvedModuleRef::Conflict; + Unresolved.IsWildcard = false; + Unresolved.String = Blob; + UnresolvedModuleRefs.push_back(Unresolved); + break; + } } } } diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index de5e6e0275..f0b12009fe 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -2180,6 +2180,12 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Macro name unsigned ConfigMacroAbbrev = Stream.EmitAbbrev(Abbrev); + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(SUBMODULE_CONFLICT)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Other module + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Message + unsigned ConflictAbbrev = Stream.EmitAbbrev(Abbrev); + // Write the submodule metadata block. RecordData Record; Record.push_back(getNumberOfModules(WritingModule)); @@ -2295,6 +2301,17 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { Mod->LinkLibraries[I].Library); } + // Emit the conflicts. + for (unsigned I = 0, N = Mod->Conflicts.size(); I != N; ++I) { + Record.clear(); + Record.push_back(SUBMODULE_CONFLICT); + unsigned OtherID = getSubmoduleID(Mod->Conflicts[I].Other); + assert(OtherID && "Unknown submodule!"); + Record.push_back(OtherID); + Stream.EmitRecordWithBlob(ConflictAbbrev, Record, + Mod->Conflicts[I].Message); + } + // Emit the configuration macros. for (unsigned I = 0, N = Mod->ConfigMacros.size(); I != N; ++I) { Record.clear(); diff --git a/test/Modules/Inputs/Conflicts/conflict_a.h b/test/Modules/Inputs/Conflicts/conflict_a.h new file mode 100644 index 0000000000..c16b5f5ef2 --- /dev/null +++ b/test/Modules/Inputs/Conflicts/conflict_a.h @@ -0,0 +1 @@ +int conflict_a; diff --git a/test/Modules/Inputs/Conflicts/conflict_b.h b/test/Modules/Inputs/Conflicts/conflict_b.h new file mode 100644 index 0000000000..4baf16f88e --- /dev/null +++ b/test/Modules/Inputs/Conflicts/conflict_b.h @@ -0,0 +1 @@ +int conflict_b; diff --git a/test/Modules/Inputs/Conflicts/module.map b/test/Modules/Inputs/Conflicts/module.map new file mode 100644 index 0000000000..e6aafaccec --- /dev/null +++ b/test/Modules/Inputs/Conflicts/module.map @@ -0,0 +1,10 @@ +module Conflicts { + explicit module A { + header "conflict_a.h" + conflict B, "we just don't like B" + } + + module B { + header "conflict_b.h" + } +} diff --git a/test/Modules/conflicts.m b/test/Modules/conflicts.m new file mode 100644 index 0000000000..2388e6f1d1 --- /dev/null +++ b/test/Modules/conflicts.m @@ -0,0 +1,7 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -Wauto-import -fmodules-cache-path=%t -fmodules -I %S/Inputs/Conflicts %s -verify + +@import Conflicts; + +@import Conflicts.A; // expected-warning{{module 'Conflicts.A' conflicts with already-imported module 'Conflicts.B': we just don't like B}} + diff --git a/unittests/Basic/SourceManagerTest.cpp b/unittests/Basic/SourceManagerTest.cpp index 130ea0a5a8..3f09cbb0f9 100644 --- a/unittests/Basic/SourceManagerTest.cpp +++ b/unittests/Basic/SourceManagerTest.cpp @@ -61,7 +61,8 @@ class VoidModuleLoader : public ModuleLoader { virtual void makeModuleVisible(Module *Mod, Module::NameVisibilityKind Visibility, - SourceLocation ImportLoc) { } + SourceLocation ImportLoc, + bool Complain) { } }; TEST_F(SourceManagerTest, isBeforeInTranslationUnit) { diff --git a/unittests/Lex/LexerTest.cpp b/unittests/Lex/LexerTest.cpp index 505be75ab4..c9b1840e1c 100644 --- a/unittests/Lex/LexerTest.cpp +++ b/unittests/Lex/LexerTest.cpp @@ -62,7 +62,8 @@ class VoidModuleLoader : public ModuleLoader { virtual void makeModuleVisible(Module *Mod, Module::NameVisibilityKind Visibility, - SourceLocation ImportLoc) { } + SourceLocation ImportLoc, + bool Complain) { } }; TEST_F(LexerTest, LexAPI) { diff --git a/unittests/Lex/PPCallbacksTest.cpp b/unittests/Lex/PPCallbacksTest.cpp index 13104cbc03..36bd5f9395 100644 --- a/unittests/Lex/PPCallbacksTest.cpp +++ b/unittests/Lex/PPCallbacksTest.cpp @@ -39,7 +39,8 @@ class VoidModuleLoader : public ModuleLoader { virtual void makeModuleVisible(Module *Mod, Module::NameVisibilityKind Visibility, - SourceLocation ImportLoc) { } + SourceLocation ImportLoc, + bool Complain) { } }; // Stub to collect data from InclusionDirective callbacks. diff --git a/unittests/Lex/PPConditionalDirectiveRecordTest.cpp b/unittests/Lex/PPConditionalDirectiveRecordTest.cpp index 856a8ff26d..082eced2d8 100644 --- a/unittests/Lex/PPConditionalDirectiveRecordTest.cpp +++ b/unittests/Lex/PPConditionalDirectiveRecordTest.cpp @@ -62,7 +62,8 @@ class VoidModuleLoader : public ModuleLoader { virtual void makeModuleVisible(Module *Mod, Module::NameVisibilityKind Visibility, - SourceLocation ImportLoc) { } + SourceLocation ImportLoc, + bool Complain) { } }; TEST_F(PPConditionalDirectiveRecordTest, PPRecAPI) { -- cgit v1.2.3-70-g09d2