/// 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 *>
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;
/// 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; }
/// 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()); }
: 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)
{
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;
}
#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"
#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"
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
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:
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);
/// #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.
///
friend class Parser;
friend class InitializationSequence;
friend class ASTReader;
+ friend class ASTDeclReader;
friend class ASTWriter;
public:
/// \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,
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());
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";
return !getLexicalDeclContext()->Equals(getDeclContext());
}
+TranslationUnitDecl::TranslationUnitDecl(ASTContext &ctx)
+ : Decl(TranslationUnit, nullptr, SourceLocation()),
+ DeclContext(TranslationUnit), Ctx(ctx), AnonymousNamespace(nullptr) {
+ Hidden = Ctx.getLangOpts().ModulesLocalVisibility;
+}
+
//===----------------------------------------------------------------------===//
// NamedDecl Implementation
//===----------------------------------------------------------------------===//
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;
}
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);
}
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;
void CompilerInstance::makeModuleVisible(Module *Mod,
Module::NameVisibilityKind Visibility,
SourceLocation ImportLoc) {
+ if (!ModuleManager)
+ createModuleManager();
+ if (!ModuleManager)
+ return;
+
ModuleManager->makeModuleVisible(Mod, Visibility, ImportLoc);
}
<< 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)
}
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) {
// 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;
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
// 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)));
}
}
BuildingSubmoduleStack.pop_back();
// A nested #include makes the included submodule visible.
- if (!BuildingSubmoduleStack.empty() || !getLangOpts().ModulesLocalVisibility)
+ if (!BuildingSubmoduleStack.empty() ||
+ !getLangOpts().ModulesLocalVisibility)
makeModuleVisible(LeavingMod, ImportLoc);
}
llvm::errs() << " active";
else if (!VisibleModules.isVisible(MM->getOwningModule()))
llvm::errs() << " hidden";
- else
+ else if (MM->getMacroInfo())
llvm::errs() << " overridden";
if (!MM->overrides().empty()) {
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;
GlobalNewDeleteDeclared(false),
TUKind(TUKind),
NumSFINAEErrors(0),
+ CachedFakeTopLevelModule(nullptr),
AccessCheckingSFINAE(false), InNonInstantiationSFINAEContext(false),
NonInstantiationEntries(0), ArgumentPackSubstitutionIndex(-1),
CurrentInstantiationScope(nullptr), DisableTypoCorrection(false),
/// 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.
NamedDecl *old = filter.next();
// Non-hidden declarations are never ignored.
- if (!old->isHidden())
+ if (S.isVisible(old))
continue;
if (!old->isExternallyVisible())
/// 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.
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;
}
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 &&
// 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);
Previous.setShadowed();
// Filter out any non-conflicting previous declarations.
- filterNonConflictingPreviousDecls(Context, NewVD, Previous);
+ filterNonConflictingPreviousDecls(*this, NewVD, Previous);
if (!Previous.empty()) {
MergeVarDecl(NewVD, Previous);
!Previous.isShadowed();
// Filter out any non-conflicting previous declarations.
- filterNonConflictingPreviousDecls(Context, NewFD, Previous);
+ filterNonConflictingPreviousDecls(*this, NewFD, Previous);
bool Redeclaration = false;
NamedDecl *OldDecl = nullptr;
// 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...
if (!Mod)
return true;
+ VisibleModules.setVisible(Mod, ImportLoc);
+
checkModuleImportContext(*this, Mod, ImportLoc, CurContext);
// FIXME: we should support importing a submodule within a different submodule
// 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,
// Make the module visible.
getModuleLoader().makeModuleVisible(Mod, Module::AllVisible, Loc);
+ VisibleModules.setVisible(Mod, Loc);
}
void Sema::ActOnPragmaRedefineExtname(IdentifierInfo* Name,
#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"
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.
// 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);
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
/// 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.
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);
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
/// 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;
}
// 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(),
// 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();
// 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);
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.
MD->setLazyBody(PB->second);
}
PendingBodies.clear();
+
+ // Do some cleanup.
+ for (auto *ND : PendingMergedDefinitionsToDeduplicate)
+ getContext().deduplicateMergedDefinitonsFor(ND);
+ PendingMergedDefinitionsToDeduplicate.clear();
}
void ASTReader::diagnoseOdrViolations() {
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);
}
}
}
// 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(
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);
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));
}
--- /dev/null
+int m = n;
--- /dev/null
+module x { module a { header "a.h" } module b { header "b.h" } }
// 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.
// 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.
--- /dev/null
+// 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.
// 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"