diff options
32 files changed, 402 insertions, 106 deletions
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index b6f8c735cc8..049221ad914 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -284,6 +284,11 @@ class ASTContext : public RefCountedBase<ASTContext> { /// merged into. llvm::DenseMap<Decl*, Decl*> MergedDecls; + /// \brief A mapping from a defining declaration to a list of modules (other + /// than the owning module of the declaration) that contain merged + /// definitions of that entity. + llvm::DenseMap<NamedDecl*, llvm::TinyPtrVector<Module*>> MergedDefModules; + public: /// \brief A type synonym for the TemplateOrInstantiation mapping. typedef llvm::PointerUnion<VarTemplateDecl *, MemberSpecializationInfo *> @@ -781,6 +786,23 @@ public: MergedDecls[D] = Primary; } + /// \brief Note that the definition \p ND has been merged into module \p M, + /// and should be visible whenever \p M is visible. + void mergeDefinitionIntoModule(NamedDecl *ND, Module *M, + bool NotifyListeners = true); + /// \brief Clean up the merged definition list. Call this if you might have + /// added duplicates into the list. + void deduplicateMergedDefinitonsFor(NamedDecl *ND); + + /// \brief Get the additional modules in which the definition \p Def has + /// been merged. + ArrayRef<Module*> getModulesWithMergedDefinition(NamedDecl *Def) { + auto MergedIt = MergedDefModules.find(Def); + if (MergedIt == MergedDefModules.end()) + return None; + return MergedIt->second; + } + TranslationUnitDecl *getTranslationUnitDecl() const { return TUDecl; } ExternCContextDecl *getExternCContextDecl() const; diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 1f7b03ab23c..b6f45c3faed 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -82,10 +82,7 @@ class TranslationUnitDecl : public Decl, public DeclContext { /// translation unit, if one has been created. NamespaceDecl *AnonymousNamespace; - explicit TranslationUnitDecl(ASTContext &ctx) - : Decl(TranslationUnit, nullptr, SourceLocation()), - DeclContext(TranslationUnit), - Ctx(ctx), AnonymousNamespace(nullptr) {} + explicit TranslationUnitDecl(ASTContext &ctx); public: ASTContext &getASTContext() const { return Ctx; } @@ -2590,7 +2587,10 @@ public: /// Retrieves the tag declaration for which this is the typedef name for /// linkage purposes, if any. - TagDecl *getAnonDeclWithTypedefName() const; + /// + /// \param AnyRedecl Look for the tag declaration in any redeclaration of + /// this typedef declaration. + TagDecl *getAnonDeclWithTypedefName(bool AnyRedecl = false) const; // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index b3ecbf76d72..5c382b0d578 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -317,7 +317,7 @@ protected: : NextInContextAndBits(), DeclCtx(DC), Loc(L), DeclKind(DK), InvalidDecl(0), HasAttrs(false), Implicit(false), Used(false), Referenced(false), - Access(AS_none), FromASTFile(0), Hidden(0), + Access(AS_none), FromASTFile(0), Hidden(DC && cast<Decl>(DC)->Hidden), IdentifierNamespace(getIdentifierNamespaceForKind(DK)), CacheValidAndLinkage(0) { @@ -639,13 +639,28 @@ private: Module *getOwningModuleSlow() const; public: - Module *getOwningModule() const { + /// \brief Get the imported owning module, if this decl is from an imported + /// (non-local) module. + Module *getImportedOwningModule() const { if (!isFromASTFile()) return nullptr; return getOwningModuleSlow(); } + /// \brief Get the local owning module, if known. Returns nullptr if owner is + /// not yet known or declaration is not from a module. + Module *getLocalOwningModule() const { + if (isFromASTFile() || !Hidden) + return nullptr; + return reinterpret_cast<Module *const *>(this)[-1]; + } + void setLocalOwningModule(Module *M) { + assert(!isFromASTFile() && Hidden && + "should not have a cached owning module"); + reinterpret_cast<Module **>(this)[-1] = M; + } + unsigned getIdentifierNamespace() const { return IdentifierNamespace; } diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h index 2789223f9a6..cbaa4ba27ce 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -39,6 +39,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/Stmt.h" diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h index f31a304609e..524986f29d9 100644 --- a/clang/include/clang/Basic/Module.h +++ b/clang/include/clang/Basic/Module.h @@ -16,6 +16,7 @@ #define LLVM_CLANG_BASIC_MODULE_H #include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/PointerUnion.h" @@ -517,7 +518,9 @@ public: ConflictCallback; /// \brief Make a specific module visible. void setVisible(Module *M, SourceLocation Loc, - VisibleCallback Vis, ConflictCallback Cb); + VisibleCallback Vis = [](Module *) {}, + ConflictCallback Cb = [](ArrayRef<Module *>, Module *, + StringRef) {}); private: /// Import locations for each visible module. Indexed by the module's diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 3bc1b4d7d36..41bc8554d52 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -222,15 +222,17 @@ class Sema { static bool mightHaveNonExternalLinkage(const DeclaratorDecl *FD); - static bool - shouldLinkPossiblyHiddenDecl(const NamedDecl *Old, const NamedDecl *New) { + bool isVisibleSlow(const NamedDecl *D); + + bool shouldLinkPossiblyHiddenDecl(const NamedDecl *Old, + const NamedDecl *New) { // We are about to link these. It is now safe to compute the linkage of // the new decl. If the new decl has external linkage, we will // link it with the hidden decl (which also has external linkage) and // it will keep having external linkage. If it has internal linkage, we // will not link it. Since it has no previous decls, it will remain // with internal linkage. - return !Old->isHidden() || New->isExternallyVisible(); + return isVisible(Old) || New->isExternallyVisible(); } public: @@ -1278,11 +1280,28 @@ public: private: bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T, TypeDiagnoser &Diagnoser); + + VisibleModuleSet VisibleModules; + llvm::SmallVector<VisibleModuleSet, 16> VisibleModulesStack; + + Module *CachedFakeTopLevelModule; + public: + /// \brief Get the module owning an entity. + Module *getOwningModule(Decl *Entity); + /// \brief Make a merged definition of an existing hidden definition \p ND /// visible at the specified location. void makeMergedDefinitionVisible(NamedDecl *ND, SourceLocation Loc); + bool isModuleVisible(Module *M) { return VisibleModules.isVisible(M); } + + /// Determine whether a declaration is visible to name lookup. + bool isVisible(const NamedDecl *D) { + return !D->isHidden() || isVisibleSlow(D); + } + bool hasVisibleMergedDefinition(NamedDecl *Def); + /// Determine if \p D has a visible definition. If not, suggest a declaration /// that should be made visible to expose the definition. bool hasVisibleDefinition(NamedDecl *D, NamedDecl **Suggested); @@ -1679,6 +1698,11 @@ public: /// #include or similar preprocessing directive. void ActOnModuleInclude(SourceLocation DirectiveLoc, Module *Mod); + /// \brief The parsed has entered a submodule. + void ActOnModuleBegin(SourceLocation DirectiveLoc, Module *Mod); + /// \brief The parser has left a submodule. + void ActOnModuleEnd(SourceLocation DirectiveLoc, Module *Mod); + /// \brief Create an implicit import of the given module at the given /// source location, for error recovery, if possible. /// @@ -8687,6 +8711,7 @@ protected: friend class Parser; friend class InitializationSequence; friend class ASTReader; + friend class ASTDeclReader; friend class ASTWriter; public: diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index 9f9b45e8a59..722f7a86faa 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -516,6 +516,10 @@ private: /// \brief Functions or methods that have bodies that will be attached. PendingBodiesMap PendingBodies; + /// \brief Definitions for which we have added merged definitions but not yet + /// performed deduplication. + llvm::SetVector<NamedDecl*> PendingMergedDefinitionsToDeduplicate; + /// \brief Read the records that describe the contents of declcontexts. bool ReadDeclContextStorage(ModuleFile &M, llvm::BitstreamCursor &Cursor, diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 0fc9a4e96b0..770a2cf4df5 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -866,6 +866,31 @@ void ASTContext::PrintStats() const { BumpAlloc.PrintStats(); } +void ASTContext::mergeDefinitionIntoModule(NamedDecl *ND, Module *M, + bool NotifyListeners) { + if (NotifyListeners) + if (auto *Listener = getASTMutationListener()) + Listener->RedefinedHiddenDefinition(ND, M); + + if (getLangOpts().ModulesLocalVisibility) + MergedDefModules[ND].push_back(M); + else + ND->setHidden(false); +} + +void ASTContext::deduplicateMergedDefinitonsFor(NamedDecl *ND) { + auto It = MergedDefModules.find(ND); + if (It == MergedDefModules.end()) + return; + + auto &Merged = It->second; + llvm::DenseSet<Module*> Found; + for (Module *&M : Merged) + if (!Found.insert(M).second) + M = nullptr; + Merged.erase(std::remove(Merged.begin(), Merged.end(), nullptr), Merged.end()); +} + ExternCContextDecl *ASTContext::getExternCContextDecl() const { if (!ExternCContext) ExternCContext = ExternCContextDecl::Create(*this, getTranslationUnitDecl()); diff --git a/clang/lib/AST/ASTDumper.cpp b/clang/lib/AST/ASTDumper.cpp index 711c3292760..60cbb060113 100644 --- a/clang/lib/AST/ASTDumper.cpp +++ b/clang/lib/AST/ASTDumper.cpp @@ -977,8 +977,10 @@ void ASTDumper::dumpDecl(const Decl *D) { dumpSourceRange(D->getSourceRange()); OS << ' '; dumpLocation(D->getLocation()); - if (Module *M = D->getOwningModule()) + if (Module *M = D->getImportedOwningModule()) OS << " in " << M->getFullModuleName(); + else if (Module *M = D->getLocalOwningModule()) + OS << " in (local) " << M->getFullModuleName(); if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) if (ND->isHidden()) OS << " hidden"; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 550810367d9..4a8eaaf99ad 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -44,6 +44,12 @@ bool Decl::isOutOfLine() const { return !getLexicalDeclContext()->Equals(getDeclContext()); } +TranslationUnitDecl::TranslationUnitDecl(ASTContext &ctx) + : Decl(TranslationUnit, nullptr, SourceLocation()), + DeclContext(TranslationUnit), Ctx(ctx), AnonymousNamespace(nullptr) { + Hidden = Ctx.getLangOpts().ModulesLocalVisibility; +} + //===----------------------------------------------------------------------===// // NamedDecl Implementation //===----------------------------------------------------------------------===// @@ -3934,10 +3940,17 @@ TypedefDecl *TypedefDecl::Create(ASTContext &C, DeclContext *DC, void TypedefNameDecl::anchor() { } -TagDecl *TypedefNameDecl::getAnonDeclWithTypedefName() const { - if (auto *TT = getTypeSourceInfo()->getType()->getAs<TagType>()) - if (TT->getDecl()->getTypedefNameForAnonDecl() == this) +TagDecl *TypedefNameDecl::getAnonDeclWithTypedefName(bool AnyRedecl) const { + if (auto *TT = getTypeSourceInfo()->getType()->getAs<TagType>()) { + auto *OwningTypedef = TT->getDecl()->getTypedefNameForAnonDecl(); + auto *ThisTypedef = this; + if (AnyRedecl && OwningTypedef) { + OwningTypedef = OwningTypedef->getCanonicalDecl(); + ThisTypedef = ThisTypedef->getCanonicalDecl(); + } + if (OwningTypedef == ThisTypedef) return TT->getDecl(); + } return nullptr; } diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 2f0fffea64c..79cadcfcb16 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -66,6 +66,12 @@ void *Decl::operator new(std::size_t Size, const ASTContext &Context, void *Decl::operator new(std::size_t Size, const ASTContext &Ctx, DeclContext *Parent, std::size_t Extra) { assert(!Parent || &Parent->getParentASTContext() == &Ctx); + // With local visibility enabled, we track the owning module even for local + // declarations. + if (Ctx.getLangOpts().ModulesLocalVisibility) { + void *Buffer = ::operator new(sizeof(Module *) + Size + Extra, Ctx); + return new (Buffer) Module*(nullptr) + 1; + } return ::operator new(Size + Extra, Ctx); } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index e0c4faad45a..7c091923b31 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -3359,7 +3359,7 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { auto *Import = cast<ImportDecl>(D); // Ignore import declarations that come from imported modules. - if (clang::Module *Owner = Import->getOwningModule()) { + if (clang::Module *Owner = Import->getImportedOwningModule()) { if (getLangOpts().CurrentModule.empty() || Owner->getTopLevelModule()->Name == getLangOpts().CurrentModule) break; diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index 2583ad199de..b64424a9aed 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -1637,6 +1637,11 @@ CompilerInstance::loadModule(SourceLocation ImportLoc, void CompilerInstance::makeModuleVisible(Module *Mod, Module::NameVisibilityKind Visibility, SourceLocation ImportLoc) { + if (!ModuleManager) + createModuleManager(); + if (!ModuleManager) + return; + ModuleManager->makeModuleVisible(Mod, Visibility, ImportLoc); } diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 1d6723f33a0..11750224d8c 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1597,6 +1597,12 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, << Opts.CurrentModule << Opts.ImplementationOfModule; } + // For now, we only support local submodule visibility in C++ (because we + // heavily depend on the ODR for merging redefinitions). + if (Opts.ModulesLocalVisibility && !Opts.CPlusPlus) + Diags.Report(diag::err_drv_argument_not_allowed_with) + << "-fmodules-local-submodule-visibility" << "C"; + if (Arg *A = Args.getLastArg(OPT_faddress_space_map_mangling_EQ)) { switch (llvm::StringSwitch<unsigned>(A->getValue()) .Case("target", LangOptions::ASMM_Target) diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp index 29594994ff6..287ee146c6b 100644 --- a/clang/lib/Lex/ModuleMap.cpp +++ b/clang/lib/Lex/ModuleMap.cpp @@ -865,50 +865,44 @@ void ModuleMap::dump() { } bool ModuleMap::resolveExports(Module *Mod, bool Complain) { - bool HadError = false; - for (unsigned I = 0, N = Mod->UnresolvedExports.size(); I != N; ++I) { - Module::ExportDecl Export = resolveExport(Mod, Mod->UnresolvedExports[I], - Complain); + auto Unresolved = std::move(Mod->UnresolvedExports); + Mod->UnresolvedExports.clear(); + for (auto &UE : Unresolved) { + Module::ExportDecl Export = resolveExport(Mod, UE, Complain); if (Export.getPointer() || Export.getInt()) Mod->Exports.push_back(Export); else - HadError = true; + Mod->UnresolvedExports.push_back(UE); } - Mod->UnresolvedExports.clear(); - return HadError; + return !Mod->UnresolvedExports.empty(); } bool ModuleMap::resolveUses(Module *Mod, bool Complain) { - bool HadError = false; - for (unsigned I = 0, N = Mod->UnresolvedDirectUses.size(); I != N; ++I) { - Module *DirectUse = - resolveModuleId(Mod->UnresolvedDirectUses[I], Mod, Complain); + auto Unresolved = std::move(Mod->UnresolvedDirectUses); + Mod->UnresolvedDirectUses.clear(); + for (auto &UDU : Unresolved) { + Module *DirectUse = resolveModuleId(UDU, Mod, Complain); if (DirectUse) Mod->DirectUses.push_back(DirectUse); else - HadError = true; + Mod->UnresolvedDirectUses.push_back(UDU); } - Mod->UnresolvedDirectUses.clear(); - return HadError; + return !Mod->UnresolvedDirectUses.empty(); } 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); - } + auto Unresolved = std::move(Mod->UnresolvedConflicts); Mod->UnresolvedConflicts.clear(); - return HadError; + for (auto &UC : Unresolved) { + if (Module *OtherMod = resolveModuleId(UC.Id, Mod, Complain)) { + Module::Conflict Conflict; + Conflict.Other = OtherMod; + Conflict.Message = UC.Message; + Mod->Conflicts.push_back(Conflict); + } else + Mod->UnresolvedConflicts.push_back(UC); + } + return !Mod->UnresolvedConflicts.empty(); } Module *ModuleMap::inferModuleFromLocation(FullSourceLoc Loc) { @@ -1759,7 +1753,13 @@ void ModuleMapParser::parseHeaderDecl(MMToken::TokenKind LeadingToken, // If Clang supplies this header but the underlying system does not, // just silently swap in our builtin version. Otherwise, we'll end // up adding both (later). - if (!File && BuiltinFile) { + // + // For local visibility, entirely replace the system file with our + // one and textually include the system one. We need to pass macros + // from our header to the system one if we #include_next it. + // + // FIXME: Can we do this in all cases? + if (BuiltinFile && (!File || Map.LangOpts.ModulesLocalVisibility)) { File = BuiltinFile; RelativePathName = BuiltinPathName; BuiltinFile = nullptr; diff --git a/clang/lib/Lex/PPLexerChange.cpp b/clang/lib/Lex/PPLexerChange.cpp index 4af68b0b80a..ddc3fa646b9 100644 --- a/clang/lib/Lex/PPLexerChange.cpp +++ b/clang/lib/Lex/PPLexerChange.cpp @@ -615,9 +615,21 @@ void Preprocessor::EnterSubmodule(Module *M, SourceLocation ImportLoc) { auto &Info = BuildingSubmoduleStack.back(); Info.Macros.swap(Macros); // Save our visible modules set. This is guaranteed to clear the set. - if (getLangOpts().ModulesLocalVisibility) + if (getLangOpts().ModulesLocalVisibility) { Info.VisibleModules = std::move(VisibleModules); + // Resolve as much of the module definition as we can now, before we enter + // one if its headers. + // FIXME: Can we enable Complain here? + ModuleMap &ModMap = getHeaderSearchInfo().getModuleMap(); + ModMap.resolveExports(M, /*Complain=*/false); + ModMap.resolveUses(M, /*Complain=*/false); + ModMap.resolveConflicts(M, /*Complain=*/false); + + // This module is visible to itself. + makeModuleVisible(M, ImportLoc); + } + // Determine the set of starting macros for this submodule. // FIXME: If we re-enter a submodule, should we restore its MacroDirectives? auto &StartingMacros = getLangOpts().ModulesLocalVisibility @@ -628,7 +640,7 @@ void Preprocessor::EnterSubmodule(Module *M, SourceLocation ImportLoc) { // FIXME: Do this lazily, when each macro name is first referenced. for (auto &Macro : StartingMacros) { MacroState MS(Macro.second.getLatest()); - MS.setOverriddenMacros(*this, MS.getOverriddenMacros()); + MS.setOverriddenMacros(*this, Macro.second.getOverriddenMacros()); Macros.insert(std::make_pair(Macro.first, std::move(MS))); } } @@ -712,6 +724,7 @@ void Preprocessor::LeaveSubmodule() { BuildingSubmoduleStack.pop_back(); // A nested #include makes the included submodule visible. - if (!BuildingSubmoduleStack.empty() || !getLangOpts().ModulesLocalVisibility) + if (!BuildingSubmoduleStack.empty() || + !getLangOpts().ModulesLocalVisibility) makeModuleVisible(LeavingMod, ImportLoc); } diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index 7121578c8fb..aa4e8b67644 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -238,7 +238,7 @@ void Preprocessor::dumpMacroInfo(const IdentifierInfo *II) { llvm::errs() << " active"; else if (!VisibleModules.isVisible(MM->getOwningModule())) llvm::errs() << " hidden"; - else + else if (MM->getMacroInfo()) llvm::errs() << " overridden"; if (!MM->overrides().empty()) { diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 697fda9215d..a45eaa0e333 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -541,8 +541,14 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result) { return false; case tok::annot_module_begin: + Actions.ActOnModuleBegin(Tok.getLocation(), reinterpret_cast<Module *>( + Tok.getAnnotationValue())); + ConsumeToken(); + return false; + case tok::annot_module_end: - // FIXME: Update visibility based on the submodule we're in. + Actions.ActOnModuleEnd(Tok.getLocation(), reinterpret_cast<Module *>( + Tok.getAnnotationValue())); ConsumeToken(); return false; diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 6825dfa41fa..e9619b369c1 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -99,6 +99,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, GlobalNewDeleteDeclared(false), TUKind(TUKind), NumSFINAEErrors(0), + CachedFakeTopLevelModule(nullptr), AccessCheckingSFINAE(false), InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0), ArgumentPackSubstitutionIndex(-1), CurrentInstantiationScope(nullptr), DisableTypoCorrection(false), diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 8be7286f66f..8c079f8647d 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -1784,11 +1784,11 @@ NamedDecl *Sema::LazilyCreateBuiltin(IdentifierInfo *II, unsigned ID, /// should not consider because they are not permitted to conflict, e.g., /// because they come from hidden sub-modules and do not refer to the same /// entity. -static void filterNonConflictingPreviousDecls(ASTContext &context, +static void filterNonConflictingPreviousDecls(Sema &S, NamedDecl *decl, LookupResult &previous){ // This is only interesting when modules are enabled. - if (!context.getLangOpts().Modules) + if (!S.getLangOpts().Modules) return; // Empty sets are uninteresting. @@ -1800,7 +1800,7 @@ static void filterNonConflictingPreviousDecls(ASTContext &context, NamedDecl *old = filter.next(); // Non-hidden declarations are never ignored. - if (!old->isHidden()) + if (S.isVisible(old)) continue; if (!old->isExternallyVisible()) @@ -1814,11 +1814,11 @@ static void filterNonConflictingPreviousDecls(ASTContext &context, /// entity if their types are the same. /// FIXME: This is notionally doing the same thing as ASTReaderDecl's /// isSameEntity. -static void filterNonConflictingPreviousTypedefDecls(ASTContext &Context, +static void filterNonConflictingPreviousTypedefDecls(Sema &S, TypedefNameDecl *Decl, LookupResult &Previous) { // This is only interesting when modules are enabled. - if (!Context.getLangOpts().Modules) + if (!S.getLangOpts().Modules) return; // Empty sets are uninteresting. @@ -1830,19 +1830,19 @@ static void filterNonConflictingPreviousTypedefDecls(ASTContext &Context, NamedDecl *Old = Filter.next(); // Non-hidden declarations are never ignored. - if (!Old->isHidden()) + if (S.isVisible(Old)) continue; // Declarations of the same entity are not ignored, even if they have // different linkages. if (auto *OldTD = dyn_cast<TypedefNameDecl>(Old)) { - if (Context.hasSameType(OldTD->getUnderlyingType(), - Decl->getUnderlyingType())) + if (S.Context.hasSameType(OldTD->getUnderlyingType(), + Decl->getUnderlyingType())) continue; // If both declarations give a tag declaration a typedef name for linkage // purposes, then they declare the same entity. - if (OldTD->getAnonDeclWithTypedefName() && + if (OldTD->getAnonDeclWithTypedefName(/*AnyRedecl*/true) && Decl->getAnonDeclWithTypedefName()) continue; } @@ -1957,7 +1957,7 @@ void Sema::MergeTypedefNameDecl(TypedefNameDecl *New, LookupResult &OldDecls) { return New->setInvalidDecl(); if (auto *OldTD = dyn_cast<TypedefNameDecl>(Old)) { - auto *OldTag = OldTD->getAnonDeclWithTypedefName(); + auto *OldTag = OldTD->getAnonDeclWithTypedefName(/*AnyRedecl*/true); auto *NewTag = New->getAnonDeclWithTypedefName(); NamedDecl *Hidden = nullptr; if (getLangOpts().CPlusPlus && OldTag && NewTag && @@ -5065,7 +5065,7 @@ Sema::ActOnTypedefNameDecl(Scope *S, DeclContext *DC, TypedefNameDecl *NewTD, // in an outer scope, it isn't the same thing. FilterLookupForScope(Previous, DC, S, /*ConsiderLinkage*/false, /*AllowInlineNamespace*/false); - filterNonConflictingPreviousTypedefDecls(Context, NewTD, Previous); + filterNonConflictingPreviousTypedefDecls(*this, NewTD, Previous); if (!Previous.empty()) { Redeclaration = true; MergeTypedefNameDecl(NewTD, Previous); @@ -6381,7 +6381,7 @@ bool Sema::CheckVariableDeclaration(VarDecl *NewVD, LookupResult &Previous) { Previous.setShadowed(); // Filter out any non-conflicting previous declarations. - filterNonConflictingPreviousDecls(Context, NewVD, Previous); + filterNonConflictingPreviousDecls(*this, NewVD, Previous); if (!Previous.empty()) { MergeVarDecl(NewVD, Previous); @@ -7930,7 +7930,7 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, !Previous.isShadowed(); // Filter out any non-conflicting previous declarations. - filterNonConflictingPreviousDecls(Context, NewFD, Previous); + filterNonConflictingPreviousDecls(*this, NewFD, Previous); bool Redeclaration = false; NamedDecl *OldDecl = nullptr; @@ -7985,7 +7985,7 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, // Check for a previous extern "C" declaration with this name. if (!Redeclaration && checkForConflictWithNonVisibleExternC(*this, NewFD, Previous)) { - filterNonConflictingPreviousDecls(Context, NewFD, Previous); + filterNonConflictingPreviousDecls(*this, NewFD, Previous); if (!Previous.empty()) { // This is an extern "C" declaration with the same name as a previous // declaration, and thus redeclares that entity... @@ -14076,6 +14076,8 @@ DeclResult Sema::ActOnModuleImport(SourceLocation AtLoc, if (!Mod) return true; + VisibleModules.setVisible(Mod, ImportLoc); + checkModuleImportContext(*this, Mod, ImportLoc, CurContext); // FIXME: we should support importing a submodule within a different submodule @@ -14113,6 +14115,25 @@ void Sema::ActOnModuleInclude(SourceLocation DirectiveLoc, Module *Mod) { // FIXME: Should we synthesize an ImportDecl here? getModuleLoader().makeModuleVisible(Mod, Module::AllVisible, DirectiveLoc); + VisibleModules.setVisible(Mod, DirectiveLoc); +} + +void Sema::ActOnModuleBegin(SourceLocation DirectiveLoc, Module *Mod) { + checkModuleImportContext(*this, Mod, DirectiveLoc, CurContext); + + if (getLangOpts().ModulesLocalVisibility) + VisibleModulesStack.push_back(std::move(VisibleModules)); + VisibleModules.setVisible(Mod, DirectiveLoc); +} + +void Sema::ActOnModuleEnd(SourceLocation DirectiveLoc, Module *Mod) { + checkModuleImportContext(*this, Mod, DirectiveLoc, CurContext); + + if (getLangOpts().ModulesLocalVisibility) { + VisibleModules = std::move(VisibleModulesStack.back()); + VisibleModulesStack.pop_back(); + VisibleModules.setVisible(Mod, DirectiveLoc); + } } void Sema::createImplicitModuleImportForErrorRecovery(SourceLocation Loc, @@ -14130,6 +14151,7 @@ void Sema::createImplicitModuleImportForErrorRecovery(SourceLocation Loc, // Make the module visible. getModuleLoader().makeModuleVisible(Mod, Module::AllVisible, Loc); + VisibleModules.setVisible(Mod, Loc); } void Sema::ActOnPragmaRedefineExtname(IdentifierInfo* Name, diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 5e7e34a8928..a8a143fa9a9 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -24,6 +24,7 @@ #include "clang/AST/ExprCXX.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/LangOptions.h" +#include "clang/Lex/HeaderSearch.h" #include "clang/Lex/ModuleLoader.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/DeclSpec.h" @@ -1171,15 +1172,59 @@ static Decl *getInstantiatedFrom(Decl *D, MemberSpecializationInfo *MSInfo) { return MSInfo->isExplicitSpecialization() ? D : MSInfo->getInstantiatedFrom(); } +Module *Sema::getOwningModule(Decl *Entity) { + // If it's imported, grab its owning module. + Module *M = Entity->getImportedOwningModule(); + if (M || !isa<NamedDecl>(Entity) || !cast<NamedDecl>(Entity)->isHidden()) + return M; + assert(!Entity->isFromASTFile() && + "hidden entity from AST file has no owning module"); + + // It's local and hidden; grab or compute its owning module. + M = Entity->getLocalOwningModule(); + if (M) + return M; + + if (auto *Containing = + PP.getModuleContainingLocation(Entity->getLocation())) { + M = Containing; + } else if (Entity->isInvalidDecl() || Entity->getLocation().isInvalid()) { + // Don't bother tracking visibility for invalid declarations with broken + // locations. + cast<NamedDecl>(Entity)->setHidden(false); + } else { + // We need to assign a module to an entity that exists outside of any + // module, so that we can hide it from modules that we textually enter. + // Invent a fake module for all such entities. + if (!CachedFakeTopLevelModule) { + CachedFakeTopLevelModule = + PP.getHeaderSearchInfo().getModuleMap().findOrCreateModule( + "<top-level>", nullptr, false, false).first; + + auto &SrcMgr = PP.getSourceManager(); + SourceLocation StartLoc = + SrcMgr.getLocForStartOfFile(SrcMgr.getMainFileID()); + auto &TopLevel = + VisibleModulesStack.empty() ? VisibleModules : VisibleModulesStack[0]; + TopLevel.setVisible(CachedFakeTopLevelModule, StartLoc); + } + + M = CachedFakeTopLevelModule; + } + + if (M) + Entity->setLocalOwningModule(M); + return M; +} + void Sema::makeMergedDefinitionVisible(NamedDecl *ND, SourceLocation Loc) { - if (auto *Listener = getASTMutationListener()) - Listener->RedefinedHiddenDefinition(ND, - PP.getModuleContainingLocation(Loc)); - ND->setHidden(false); + auto *M = PP.getModuleContainingLocation(Loc); + assert(M && "hidden definition not in any module"); + Context.mergeDefinitionIntoModule(ND, M); } /// \brief Find the module in which the given declaration was defined. -static Module *getDefiningModule(Decl *Entity) { +static Module *getDefiningModule(Sema &S, Decl *Entity) { if (FunctionDecl *FD = dyn_cast<FunctionDecl>(Entity)) { // If this function was instantiated from a template, the defining module is // the module containing the pattern. @@ -1201,15 +1246,16 @@ static Module *getDefiningModule(Decl *Entity) { // from a template. DeclContext *Context = Entity->getDeclContext(); if (Context->isFileContext()) - return Entity->getOwningModule(); - return getDefiningModule(cast<Decl>(Context)); + return S.getOwningModule(Entity); + return getDefiningModule(S, cast<Decl>(Context)); } llvm::DenseSet<Module*> &Sema::getLookupModules() { unsigned N = ActiveTemplateInstantiations.size(); for (unsigned I = ActiveTemplateInstantiationLookupModules.size(); I != N; ++I) { - Module *M = getDefiningModule(ActiveTemplateInstantiations[I].Entity); + Module *M = + getDefiningModule(*this, ActiveTemplateInstantiations[I].Entity); if (M && !LookupModulesCache.insert(M).second) M = nullptr; ActiveTemplateInstantiationLookupModules.push_back(M); @@ -1217,6 +1263,13 @@ llvm::DenseSet<Module*> &Sema::getLookupModules() { return LookupModulesCache; } +bool Sema::hasVisibleMergedDefinition(NamedDecl *Def) { + for (Module *Merged : Context.getModulesWithMergedDefinition(Def)) + if (isModuleVisible(Merged)) + return true; + return false; +} + /// \brief Determine whether a declaration is visible to name lookup. /// /// This routine determines whether the declaration D is visible in the current @@ -1227,8 +1280,24 @@ llvm::DenseSet<Module*> &Sema::getLookupModules() { /// your module can see, including those later on in your module). bool LookupResult::isVisibleSlow(Sema &SemaRef, NamedDecl *D) { assert(D->isHidden() && "should not call this: not in slow case"); - Module *DeclModule = D->getOwningModule(); - assert(DeclModule && "hidden decl not from a module"); + Module *DeclModule = SemaRef.getOwningModule(D); + if (!DeclModule) { + // getOwningModule() may have decided the declaration should not be hidden. + assert(!D->isHidden() && "hidden decl not from a module"); + return true; + } + + // If the owning module is visible, and the decl is not module private, + // then the decl is visible too. (Module private is ignored within the same + // top-level module.) + if (!D->isFromASTFile() || !D->isModulePrivate()) { + if (SemaRef.isModuleVisible(DeclModule)) + return true; + // Also check merged definitions. + if (SemaRef.getLangOpts().ModulesLocalVisibility && + SemaRef.hasVisibleMergedDefinition(D)) + return true; + } // If this declaration is not at namespace scope nor module-private, // then it is visible if its lexical parent has a visible definition. @@ -1236,7 +1305,9 @@ bool LookupResult::isVisibleSlow(Sema &SemaRef, NamedDecl *D) { if (!D->isModulePrivate() && DC && !DC->isFileContext() && !isa<LinkageSpecDecl>(DC)) { if (SemaRef.hasVisibleDefinition(cast<NamedDecl>(DC))) { - if (SemaRef.ActiveTemplateInstantiations.empty()) { + if (SemaRef.ActiveTemplateInstantiations.empty() && + // FIXME: Do something better in this case. + !SemaRef.getLangOpts().ModulesLocalVisibility) { // Cache the fact that this declaration is implicitly visible because // its parent has a visible definition. D->setHidden(false); @@ -1269,6 +1340,10 @@ bool LookupResult::isVisibleSlow(Sema &SemaRef, NamedDecl *D) { return false; } +bool Sema::isVisibleSlow(const NamedDecl *D) { + return LookupResult::isVisible(*this, const_cast<NamedDecl*>(D)); +} + /// \brief Retrieve the visible declaration corresponding to D, if any. /// /// This routine determines whether the declaration D is visible in the current @@ -4524,18 +4599,18 @@ void Sema::diagnoseTypo(const TypoCorrection &Correction, /// Find which declaration we should import to provide the definition of /// the given declaration. -static const NamedDecl *getDefinitionToImport(const NamedDecl *D) { - if (const VarDecl *VD = dyn_cast<VarDecl>(D)) +static NamedDecl *getDefinitionToImport(NamedDecl *D) { + if (VarDecl *VD = dyn_cast<VarDecl>(D)) return VD->getDefinition(); if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) - return FD->isDefined(FD) ? FD : nullptr; - if (const TagDecl *TD = dyn_cast<TagDecl>(D)) + return FD->isDefined(FD) ? const_cast<FunctionDecl*>(FD) : nullptr; + if (TagDecl *TD = dyn_cast<TagDecl>(D)) return TD->getDefinition(); - if (const ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(D)) + if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(D)) return ID->getDefinition(); - if (const ObjCProtocolDecl *PD = dyn_cast<ObjCProtocolDecl>(D)) + if (ObjCProtocolDecl *PD = dyn_cast<ObjCProtocolDecl>(D)) return PD->getDefinition(); - if (const TemplateDecl *TD = dyn_cast<TemplateDecl>(D)) + if (TemplateDecl *TD = dyn_cast<TemplateDecl>(D)) return getDefinitionToImport(TD->getTemplatedDecl()); return nullptr; } @@ -4568,10 +4643,10 @@ void Sema::diagnoseTypo(const TypoCorrection &Correction, // Suggest importing a module providing the definition of this entity, if // possible. - const NamedDecl *Def = getDefinitionToImport(Decl); + NamedDecl *Def = getDefinitionToImport(Decl); if (!Def) Def = Decl; - Module *Owner = Def->getOwningModule(); + Module *Owner = getOwningModule(Def); assert(Owner && "definition of hidden declaration is not in a module"); Diag(Correction.getCorrectionRange().getBegin(), diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 8729f481d6d..0c6eac1e95a 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -5148,7 +5148,11 @@ bool Sema::hasVisibleDefinition(NamedDecl *D, NamedDecl **Suggested) { // If this definition was instantiated from a template, map back to the // pattern from which it was instantiated. - if (auto *RD = dyn_cast<CXXRecordDecl>(D)) { + if (isa<TagDecl>(D) && cast<TagDecl>(D)->isBeingDefined()) { + // We're in the middle of defining it; this definition should be treated + // as visible. + return true; + } else if (auto *RD = dyn_cast<CXXRecordDecl>(D)) { if (auto *Pattern = RD->getTemplateInstantiationPattern()) RD = Pattern; D = RD->getDefinition(); @@ -5231,7 +5235,7 @@ bool Sema::RequireCompleteTypeImpl(SourceLocation Loc, QualType T, // repeating the diagnostic. // FIXME: Add a Fix-It that imports the corresponding module or includes // the header. - Module *Owner = SuggestedDef->getOwningModule(); + Module *Owner = getOwningModule(SuggestedDef); Diag(Loc, diag::err_module_private_definition) << T << Owner->getFullModuleName(); Diag(SuggestedDef->getLocation(), diag::note_previous_definition); diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index c4b4aec24a0..08fd923f47d 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -7997,7 +7997,7 @@ void ASTReader::getInputFiles(ModuleFile &F, std::string ASTReader::getOwningModuleNameForDiagnostic(const Decl *D) { // If we know the owning module, use it. - if (Module *M = D->getOwningModule()) + if (Module *M = D->getImportedOwningModule()) return M->getFullModuleName(); // Otherwise, use the name of the top-level module the decl is within. @@ -8168,6 +8168,11 @@ void ASTReader::finishPendingActions() { MD->setLazyBody(PB->second); } PendingBodies.clear(); + + // Do some cleanup. + for (auto *ND : PendingMergedDefinitionsToDeduplicate) + getContext().deduplicateMergedDefinitonsFor(ND); + PendingMergedDefinitionsToDeduplicate.clear(); } void ASTReader::diagnoseOdrViolations() { diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 4ff81d8d92b..ed684537a66 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -458,24 +458,28 @@ void ASTDeclReader::VisitDecl(Decl *D) { D->FromASTFile = true; D->setModulePrivate(Record[Idx++]); D->Hidden = D->isModulePrivate(); - + // Determine whether this declaration is part of a (sub)module. If so, it // may not yet be visible. if (unsigned SubmoduleID = readSubmoduleID(Record, Idx)) { // Store the owning submodule ID in the declaration. D->setOwningModuleID(SubmoduleID); - - // Module-private declarations are never visible, so there is no work to do. - if (!D->isModulePrivate()) { - if (Module *Owner = Reader.getSubmodule(SubmoduleID)) { - if (Owner->NameVisibility != Module::AllVisible) { - // The owning module is not visible. Mark this declaration as hidden. - D->Hidden = true; - - // Note that this declaration was hidden because its owning module is - // not yet visible. - Reader.HiddenNamesMap[Owner].push_back(D); - } + + if (D->Hidden) { + // Module-private declarations are never visible, so there is no work to do. + } else if (Reader.getContext().getLangOpts().ModulesLocalVisibility) { + // If local visibility is being tracked, this declaration will become + // hidden and visible as the owning module does. Inform Sema that this + // declaration might not be visible. + D->Hidden = true; + } else if (Module *Owner = Reader.getSubmodule(SubmoduleID)) { + if (Owner->NameVisibility != Module::AllVisible) { + // The owning module is not visible. Mark this declaration as hidden. + D->Hidden = true; + + // Note that this declaration was hidden because its owning module is + // not yet visible. + Reader.HiddenNamesMap[Owner].push_back(D); } } } @@ -1399,7 +1403,12 @@ void ASTDeclReader::MergeDefinitionData( // If MergeDD is visible or becomes visible, make the definition visible. if (!MergeDD.Definition->isHidden()) DD.Definition->Hidden = false; - else { + else if (Reader.getContext().getLangOpts().ModulesLocalVisibility) { + Reader.getContext().mergeDefinitionIntoModule( + DD.Definition, MergeDD.Definition->getImportedOwningModule(), + /*NotifyListeners*/ false); + Reader.PendingMergedDefinitionsToDeduplicate.insert(DD.Definition); + } else { auto SubmoduleID = MergeDD.Definition->getOwningModuleID(); assert(SubmoduleID && "hidden definition in no module"); Reader.HiddenNamesMap[Reader.getSubmodule(SubmoduleID)].push_back( @@ -3813,7 +3822,13 @@ void ASTDeclReader::UpdateDecl(Decl *D, ModuleFile &ModuleFile, case UPD_DECL_EXPORTED: unsigned SubmoduleID = readSubmoduleID(Record, Idx); Module *Owner = SubmoduleID ? Reader.getSubmodule(SubmoduleID) : nullptr; - if (Owner && Owner->NameVisibility != Module::AllVisible) { + if (Reader.getContext().getLangOpts().ModulesLocalVisibility) { + // FIXME: This doesn't send the right notifications if there are + // ASTMutationListeners other than an ASTWriter. + Reader.getContext().mergeDefinitionIntoModule(cast<NamedDecl>(D), Owner, + /*NotifyListeners*/false); + Reader.PendingMergedDefinitionsToDeduplicate.insert(cast<NamedDecl>(D)); + } else if (Owner && Owner->NameVisibility != Module::AllVisible) { // If Owner is made visible at some later point, make this declaration // visible too. Reader.HiddenNamesMap[Owner].push_back(D); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 1e7fc5cdb6a..2963b32857a 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -5746,6 +5746,8 @@ void ASTWriter::DeclarationMarkedOpenMPThreadPrivate(const Decl *D) { void ASTWriter::RedefinedHiddenDefinition(const NamedDecl *D, Module *M) { assert(!WritingAST && "Already writing the AST!"); assert(D->isHidden() && "expected a hidden declaration"); - assert(D->isFromASTFile() && "hidden decl not from AST file"); + if (!D->isFromASTFile()) + return; + DeclUpdates[D].push_back(DeclUpdate(UPD_DECL_EXPORTED, M)); } diff --git a/clang/test/Modules/Inputs/submodule-visibility/a.h b/clang/test/Modules/Inputs/submodule-visibility/a.h new file mode 100644 index 00000000000..d8805c92f24 --- /dev/null +++ b/clang/test/Modules/Inputs/submodule-visibility/a.h @@ -0,0 +1 @@ +int n; diff --git a/clang/test/Modules/Inputs/submodule-visibility/b.h b/clang/test/Modules/Inputs/submodule-visibility/b.h new file mode 100644 index 00000000000..fa419c0c5c4 --- /dev/null +++ b/clang/test/Modules/Inputs/submodule-visibility/b.h @@ -0,0 +1 @@ +int m = n; diff --git a/clang/test/Modules/Inputs/submodule-visibility/module.modulemap b/clang/test/Modules/Inputs/submodule-visibility/module.modulemap new file mode 100644 index 00000000000..447a1f42d45 --- /dev/null +++ b/clang/test/Modules/Inputs/submodule-visibility/module.modulemap @@ -0,0 +1 @@ +module x { module a { header "a.h" } module b { header "b.h" } } diff --git a/clang/test/Modules/macros.c b/clang/test/Modules/macros.c index 7b7b52aa017..538d4a8d6a7 100644 --- a/clang/test/Modules/macros.c +++ b/clang/test/Modules/macros.c @@ -1,7 +1,7 @@ // RUN: rm -rf %t // RUN: %clang_cc1 -fmodules -x objective-c -verify -fmodules-cache-path=%t -I %S/Inputs %s // RUN: %clang_cc1 -fmodules -x objective-c -verify -fmodules-cache-path=%t -I %S/Inputs %s -detailed-preprocessing-record -// RUN: %clang_cc1 -fmodules -DLOCAL_VISIBILITY -fmodules-local-submodule-visibility -x objective-c -verify -fmodules-cache-path=%t -I %S/Inputs %s +// RUN: %clang_cc1 -fmodules -DLOCAL_VISIBILITY -fmodules-local-submodule-visibility -x objective-c++ -verify -fmodules-cache-path=%t -I %S/Inputs %s // RUN: not %clang_cc1 -E -fmodules -x objective-c -fmodules-cache-path=%t -I %S/Inputs %s | FileCheck -check-prefix CHECK-PREPROCESSED %s // FIXME: When we have a syntax for modules in C, use that. // These notes come from headers in modules, and are bogus. diff --git a/clang/test/Modules/macros2.c b/clang/test/Modules/macros2.c index addb9b495cf..0bb801ee995 100644 --- a/clang/test/Modules/macros2.c +++ b/clang/test/Modules/macros2.c @@ -1,6 +1,6 @@ // RUN: rm -rf %t // RUN: %clang_cc1 -fmodules -x objective-c -verify -fmodules-cache-path=%t -I %S/Inputs %s -// RUN: %clang_cc1 -fmodules -fmodules-local-submodule-visibility -x objective-c -verify -fmodules-cache-path=%t -I %S/Inputs %s -DLOCAL_VISIBILITY +// RUN: %clang_cc1 -fmodules -fmodules-local-submodule-visibility -x objective-c++ -verify -fmodules-cache-path=%t -I %S/Inputs %s -DLOCAL_VISIBILITY // This test checks some of the same things as macros.c, but imports modules in // a different order. diff --git a/clang/test/Modules/submodule-visibility.cpp b/clang/test/Modules/submodule-visibility.cpp new file mode 100644 index 00000000000..c63d942cc9e --- /dev/null +++ b/clang/test/Modules/submodule-visibility.cpp @@ -0,0 +1,21 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -I%S/Inputs/submodule-visibility -verify %s -DALLOW_NAME_LEAKAGE +// RUN: %clang_cc1 -fmodules -fmodules-local-submodule-visibility -fmodules-cache-path=%t -I%S/Inputs/submodule-visibility -verify %s -DIMPORT +// RUN: %clang_cc1 -fmodules -fmodules-local-submodule-visibility -fmodules-cache-path=%t -fmodule-name=x -I%S/Inputs/submodule-visibility -verify %s + +#include "a.h" +#include "b.h" + +#if ALLOW_NAME_LEAKAGE +// expected-no-diagnostics +#elif IMPORT +// expected-error@-6 {{could not build module 'x'}} +#else +// The use of -fmodule-name=x causes us to textually include the above headers. +// The submodule visibility rules are still applied in this case. +// +// expected-error@b.h:1 {{declaration of 'n' must be imported from module 'x.a'}} +// expected-note@a.h:1 {{here}} +#endif + +int k = n + m; // OK, a and b are visible here. diff --git a/clang/test/Modules/submodules-merge-defs.cpp b/clang/test/Modules/submodules-merge-defs.cpp index 79213cae8fa..0e2f5d9e546 100644 --- a/clang/test/Modules/submodules-merge-defs.cpp +++ b/clang/test/Modules/submodules-merge-defs.cpp @@ -1,6 +1,8 @@ // RUN: rm -rf %t // RUN: %clang_cc1 -x c++ -std=c++11 -fmodules-cache-path=%t -fmodules -I %S/Inputs/submodules-merge-defs %s -verify -fno-modules-error-recovery -DTEXTUAL // RUN: %clang_cc1 -x c++ -std=c++11 -fmodules-cache-path=%t -fmodules -I %S/Inputs/submodules-merge-defs %s -verify -fno-modules-error-recovery +// RUN: %clang_cc1 -x c++ -std=c++11 -fmodules-cache-path=%t -fmodules -I %S/Inputs/submodules-merge-defs %s -verify -fno-modules-error-recovery -fmodules-local-submodule-visibility -DTEXTUAL +// RUN: %clang_cc1 -x c++ -std=c++11 -fmodules-cache-path=%t -fmodules -I %S/Inputs/submodules-merge-defs %s -verify -fno-modules-error-recovery -fmodules-local-submodule-visibility // Trigger import of definitions, but don't make them visible. #include "empty.h" |