From fb9126578ec3b320272da281ce60aa7cd11e8a06 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 20 Mar 2013 21:10:35 +0000 Subject: [PATCH] Extend module maps with a 'conflict' declaration, and warn when a newly-imported module conflicts with an already-imported module. llvm-svn: 177577 --- clang/include/clang/Basic/DiagnosticGroups.td | 1 + clang/include/clang/Basic/DiagnosticLexKinds.td | 4 + .../clang/Basic/DiagnosticSerializationKinds.td | 5 +- clang/include/clang/Basic/Module.h | 25 +++++ clang/include/clang/Frontend/ASTUnit.h | 3 +- clang/include/clang/Frontend/CompilerInstance.h | 3 +- clang/include/clang/Lex/ModuleLoader.h | 3 +- clang/include/clang/Lex/ModuleMap.h | 27 ++++- clang/include/clang/Serialization/ASTBitCodes.h | 4 +- clang/include/clang/Serialization/ASTReader.h | 26 +++-- clang/lib/Basic/Module.cpp | 18 ++++ clang/lib/Frontend/CompilerInstance.cpp | 10 +- clang/lib/Lex/ModuleMap.cpp | 120 +++++++++++++++++---- clang/lib/Sema/Sema.cpp | 5 +- clang/lib/Sema/SemaDecl.cpp | 3 +- clang/lib/Serialization/ASTReader.cpp | 86 +++++++++++---- clang/lib/Serialization/ASTWriter.cpp | 17 +++ clang/test/Modules/Inputs/Conflicts/conflict_a.h | 1 + clang/test/Modules/Inputs/Conflicts/conflict_b.h | 1 + clang/test/Modules/Inputs/Conflicts/module.map | 10 ++ clang/test/Modules/conflicts.m | 7 ++ clang/unittests/Basic/SourceManagerTest.cpp | 3 +- clang/unittests/Lex/LexerTest.cpp | 3 +- clang/unittests/Lex/PPCallbacksTest.cpp | 3 +- .../Lex/PPConditionalDirectiveRecordTest.cpp | 3 +- 25 files changed, 328 insertions(+), 63 deletions(-) create mode 100644 clang/test/Modules/Inputs/Conflicts/conflict_a.h create mode 100644 clang/test/Modules/Inputs/Conflicts/conflict_b.h create mode 100644 clang/test/Modules/Inputs/Conflicts/module.map create mode 100644 clang/test/Modules/conflicts.m diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 9af83b1..4ddf207 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/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/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td index 80c5249..339788b 100644 --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/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/clang/include/clang/Basic/DiagnosticSerializationKinds.td b/clang/include/clang/Basic/DiagnosticSerializationKinds.td index bc5bd4e..7137404 100644 --- a/clang/include/clang/Basic/DiagnosticSerializationKinds.td +++ b/clang/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/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h index 2add580..d2a43f0 100644 --- a/clang/include/clang/Basic/Module.h +++ b/clang/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/clang/include/clang/Frontend/ASTUnit.h b/clang/include/clang/Frontend/ASTUnit.h index 108114d..02c57d7 100644 --- a/clang/include/clang/Frontend/ASTUnit.h +++ b/clang/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/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h index 273fcc1..0d67462 100644 --- a/clang/include/clang/Frontend/CompilerInstance.h +++ b/clang/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/clang/include/clang/Lex/ModuleLoader.h b/clang/include/clang/Lex/ModuleLoader.h index 93e69a6..3acf915 100644 --- a/clang/include/clang/Lex/ModuleLoader.h +++ b/clang/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/clang/include/clang/Lex/ModuleMap.h b/clang/include/clang/Lex/ModuleMap.h index cffa5b7..33c92f5 100644 --- a/clang/include/clang/Lex/ModuleMap.h +++ b/clang/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/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 2669d8e..85f88ad 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/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/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index 61a0cbe..9b4a5c0 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/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/clang/lib/Basic/Module.cpp b/clang/lib/Basic/Module.cpp index 197c53f..13518cd 100644 --- a/clang/lib/Basic/Module.cpp +++ b/clang/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/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index a20e7d7..633f3c5 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/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/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp index 9cea5aa..0c03201 100644 --- a/clang/lib/Lex/ModuleMap.cpp +++ b/clang/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/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 4aeb511..8b17c12 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/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/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 1cffce0..d046f64 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/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/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 126770b..29538a1 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/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/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index de5e6e0..f0b1200 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/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/clang/test/Modules/Inputs/Conflicts/conflict_a.h b/clang/test/Modules/Inputs/Conflicts/conflict_a.h new file mode 100644 index 0000000..c16b5f5 --- /dev/null +++ b/clang/test/Modules/Inputs/Conflicts/conflict_a.h @@ -0,0 +1 @@ +int conflict_a; diff --git a/clang/test/Modules/Inputs/Conflicts/conflict_b.h b/clang/test/Modules/Inputs/Conflicts/conflict_b.h new file mode 100644 index 0000000..4baf16f --- /dev/null +++ b/clang/test/Modules/Inputs/Conflicts/conflict_b.h @@ -0,0 +1 @@ +int conflict_b; diff --git a/clang/test/Modules/Inputs/Conflicts/module.map b/clang/test/Modules/Inputs/Conflicts/module.map new file mode 100644 index 0000000..e6aafac --- /dev/null +++ b/clang/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/clang/test/Modules/conflicts.m b/clang/test/Modules/conflicts.m new file mode 100644 index 0000000..2388e6f --- /dev/null +++ b/clang/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/clang/unittests/Basic/SourceManagerTest.cpp b/clang/unittests/Basic/SourceManagerTest.cpp index 130ea0a..3f09cbb 100644 --- a/clang/unittests/Basic/SourceManagerTest.cpp +++ b/clang/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/clang/unittests/Lex/LexerTest.cpp b/clang/unittests/Lex/LexerTest.cpp index 505be75a..c9b1840 100644 --- a/clang/unittests/Lex/LexerTest.cpp +++ b/clang/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/clang/unittests/Lex/PPCallbacksTest.cpp b/clang/unittests/Lex/PPCallbacksTest.cpp index 13104cb..36bd5f9 100644 --- a/clang/unittests/Lex/PPCallbacksTest.cpp +++ b/clang/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/clang/unittests/Lex/PPConditionalDirectiveRecordTest.cpp b/clang/unittests/Lex/PPConditionalDirectiveRecordTest.cpp index 856a8ff..082eced 100644 --- a/clang/unittests/Lex/PPConditionalDirectiveRecordTest.cpp +++ b/clang/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) { -- 2.7.4