Permalink
Browse files

Allow modules specified by -fmodule-map-file to shadow implicitly fou…

…nd ones

When modules come from module map files explicitly specified by
-fmodule-map-file= arguments, allow those to override/shadow modules
with the same name that are found implicitly by header search. If such a
module is looked up by name (e.g. @import), we will always find the one
from -fmodule-map-file.  If we try to use a shadowed module by
including one of its headers report an error.

This enables developers to force use of a specific copy of their module
to be used if there are multiple copies that would otherwise be visible,
for example if they develop modules that are installed in the default
search paths.

rdar://problem/23467372
  • Loading branch information...
benlangmuir committed Nov 10, 2015
1 parent 70949ee commit ead19a9fbe2c8884b9cf292579244e41a997f86a
@@ -88,6 +88,8 @@ def err_module_unavailable : Error<
"module '%0' %select{is incompatible with|requires}1 feature '%2'">;
def err_module_header_missing : Error<
"%select{|umbrella }0header '%1' not found">;
def err_module_shadowed : Error<
"import of shadowed module '%0'">;
def err_module_lock_failure : Error<
"could not acquire lock file for module '%0'">, DefaultFatal;
def err_module_lock_timeout : Error<
@@ -149,6 +149,9 @@ class Module {
/// will be false to indicate that this (sub)module is not available.
SmallVector<Requirement, 2> Requirements;
/// \brief A module with the same name that shadows this module.
Module *ShadowingModule = nullptr;
/// \brief Whether this module is missing a feature from \c Requirements.
unsigned IsMissingRequirement : 1;
@@ -323,13 +326,20 @@ class Module {
///
/// \param Target The target options used for the current translation unit.
///
/// \param Req If this module is unavailable, this parameter
/// will be set to one of the requirements that is not met for use of
/// this module.
/// \param Req If this module is unavailable because of a missing requirement,
/// this parameter will be set to one of the requirements that is not met for
/// use of this module.
///
/// \param MissingHeader If this module is unavailable because of a missing
/// header, this parameter will be set to one of the missing headers.
///
/// \param ShadowingModule If this module is unavailable because it is
/// shadowed, this parameter will be set to the shadowing module.
bool isAvailable(const LangOptions &LangOpts,
const TargetInfo &Target,
Requirement &Req,
UnresolvedHeaderDirective &MissingHeader) const;
UnresolvedHeaderDirective &MissingHeader,
Module *&ShadowingModule) const;
/// \brief Determine whether this module is a submodule.
bool isSubModule() const { return Parent != nullptr; }
@@ -79,7 +79,7 @@ class ModuleMap {
std::string SourceModuleName;
private:
/// \brief The top-level modules that are known.
/// \brief The unshadowed top-level modules that are known.
llvm::StringMap<Module *> Modules;
/// \brief The number of modules we have created in total.
@@ -155,6 +155,15 @@ class ModuleMap {
/// header.
llvm::DenseMap<const DirectoryEntry *, Module *> UmbrellaDirs;
/// \brief A generation counter that is used to test whether modules of the
/// same name may shadow or are illegal redefintions.
///
/// Modules from earlier scopes may shadow modules from later ones.
/// Modules from the same scope may not have the same name.
unsigned CurrentModuleScopeID = 0;
llvm::DenseMap<Module *, unsigned> ModuleScopeIDs;
/// \brief The set of attributes that can be attached to a module.
struct Attributes {
Attributes() : IsSystem(), IsExternC(), IsExhaustive() {}
@@ -388,6 +397,24 @@ class ModuleMap {
Module *inferFrameworkModule(const DirectoryEntry *FrameworkDir,
bool IsSystem, Module *Parent);
/// \brief Create a new top-level module that is shadowed by
/// \p ShadowingModule.
Module *createShadowedModule(StringRef Name, bool IsFramework,
Module *ShadowingModule);
/// \brief Creates a new declaration scope for module names, allowing
/// previously defined modules to shadow definitions from the new scope.
///
/// \note Module names from earlier scopes will shadow names from the new
/// scope, which is the opposite of how shadowing works for variables.
void finishModuleDeclarationScope() { CurrentModuleScopeID += 1; }
bool mayShadowNewModule(Module *ExistingModule) {
assert(!ExistingModule->Parent && "expected top-level module");
assert(ModuleScopeIDs.count(ExistingModule) && "unknown module");
return ModuleScopeIDs[ExistingModule] < CurrentModuleScopeID;
}
/// \brief Retrieve the module map file containing the definition of the given
/// module.
///
View
@@ -79,11 +79,16 @@ static bool hasFeature(StringRef Feature, const LangOptions &LangOpts,
bool Module::isAvailable(const LangOptions &LangOpts, const TargetInfo &Target,
Requirement &Req,
UnresolvedHeaderDirective &MissingHeader) const {
UnresolvedHeaderDirective &MissingHeader,
Module *&ShadowingModule) const {
if (IsAvailable)
return true;
for (const Module *Current = this; Current; Current = Current->Parent) {
if (Current->ShadowingModule) {
ShadowingModule = Current->ShadowingModule;
return false;
}
for (unsigned I = 0, N = Current->Requirements.size(); I != N; ++I) {
if (hasFeature(Current->Requirements[I].first, LangOpts, Target) !=
Current->Requirements[I].second) {
@@ -1605,8 +1605,13 @@ CompilerInstance::loadModule(SourceLocation ImportLoc,
// Check whether this module is available.
clang::Module::Requirement Requirement;
clang::Module::UnresolvedHeaderDirective MissingHeader;
clang::Module *ShadowingModule = nullptr;
if (!Module->isAvailable(getLangOpts(), getTarget(), Requirement,
MissingHeader)) {
MissingHeader, ShadowingModule)) {
assert(!ShadowingModule &&
"lookup of module by name should never find shadowed module");
if (MissingHeader.FileNameLoc.isValid()) {
getDiagnostics().Report(MissingHeader.FileNameLoc,
diag::err_module_header_missing)
@@ -394,6 +394,13 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
CI.getDiagnostics().Report(diag::err_module_map_not_found) << Filename;
}
// Add a module declaration scope so that modules from -fmodule-map-file
// arguments may shadow modules found implicitly in search paths.
CI.getPreprocessor()
.getHeaderSearchInfo()
.getModuleMap()
.finishModuleDeclarationScope();
// If we were asked to load any module files, do so now.
for (const auto &ModuleFile : CI.getFrontendOpts().ModuleFiles)
if (!CI.loadModuleFile(ModuleFile))
@@ -321,8 +321,13 @@ bool GenerateModuleAction::BeginSourceFileAction(CompilerInstance &CI,
// Check whether we can build this module at all.
clang::Module::Requirement Requirement;
clang::Module::UnresolvedHeaderDirective MissingHeader;
clang::Module *ShadowingModule = nullptr;
if (!Module->isAvailable(CI.getLangOpts(), CI.getTarget(), Requirement,
MissingHeader)) {
MissingHeader, ShadowingModule)) {
assert(!ShadowingModule &&
"lookup of module by name should never find shadowed module");
if (MissingHeader.FileNameLoc.isValid()) {
CI.getDiagnostics().Report(MissingHeader.FileNameLoc,
diag::err_module_header_missing)
View
@@ -562,6 +562,7 @@ ModuleMap::findOrCreateModule(StringRef Name, Module *Parent, bool IsFramework,
}
if (!Parent) {
Modules[Name] = Result;
ModuleScopeIDs[Result] = CurrentModuleScopeID;
if (!LangOpts.CurrentModule.empty() && !CompilingModule &&
Name == LangOpts.CurrentModule) {
CompilingModule = Result;
@@ -570,6 +571,20 @@ ModuleMap::findOrCreateModule(StringRef Name, Module *Parent, bool IsFramework,
return std::make_pair(Result, true);
}
Module *ModuleMap::createShadowedModule(StringRef Name, bool IsFramework,
Module *ShadowingModule) {
// Create a new module with this name.
Module *Result =
new Module(Name, SourceLocation(), /*Parent=*/nullptr, IsFramework,
/*IsExplicit=*/false, NumCreatedModules++);
Result->ShadowingModule = ShadowingModule;
Result->IsAvailable = false;
ModuleScopeIDs[Result] = CurrentModuleScopeID;
return Result;
}
/// \brief For a framework module, infer the framework against which we
/// should link.
static void inferFrameworkLink(Module *Mod, const DirectoryEntry *FrameworkDir,
@@ -682,6 +697,8 @@ Module *ModuleMap::inferFrameworkModule(const DirectoryEntry *FrameworkDir,
Module *Result = new Module(ModuleName, SourceLocation(), Parent,
/*IsFramework=*/true, /*IsExplicit=*/false,
NumCreatedModules++);
if (!Parent)
ModuleScopeIDs[Result] = CurrentModuleScopeID;
InferredModuleAllowedBy[Result] = ModuleMapFile;
Result->IsInferred = true;
if (LangOpts.CurrentModule == ModuleName) {
@@ -1412,6 +1429,7 @@ void ModuleMapParser::parseModuleDecl() {
SourceLocation LBraceLoc = consumeToken();
// Determine whether this (sub)module has already been defined.
Module *ShadowingModule = nullptr;
if (Module *Existing = Map.lookupModuleQualified(ModuleName, ActiveModule)) {
if (Existing->DefinitionLoc.isInvalid() && !ActiveModule) {
// Skip the module definition.
@@ -1425,23 +1443,35 @@ void ModuleMapParser::parseModuleDecl() {
}
return;
}
Diags.Report(ModuleNameLoc, diag::err_mmap_module_redefinition)
<< ModuleName;
Diags.Report(Existing->DefinitionLoc, diag::note_mmap_prev_definition);
// Skip the module definition.
skipUntil(MMToken::RBrace);
if (Tok.is(MMToken::RBrace))
consumeToken();
HadError = true;
return;
if (!Existing->Parent && Map.mayShadowNewModule(Existing)) {
ShadowingModule = Existing;
} else {
// This is not a shawdowed module decl, it is an illegal redefinition.
Diags.Report(ModuleNameLoc, diag::err_mmap_module_redefinition)
<< ModuleName;
Diags.Report(Existing->DefinitionLoc, diag::note_mmap_prev_definition);
// Skip the module definition.
skipUntil(MMToken::RBrace);
if (Tok.is(MMToken::RBrace))
consumeToken();
HadError = true;
return;
}
}
// Start defining this module.
ActiveModule = Map.findOrCreateModule(ModuleName, ActiveModule, Framework,
Explicit).first;
if (ShadowingModule) {
ActiveModule =
Map.createShadowedModule(ModuleName, Framework, ShadowingModule);
} else {
ActiveModule =
Map.findOrCreateModule(ModuleName, ActiveModule, Framework, Explicit)
.first;
}
ActiveModule->DefinitionLoc = ModuleNameLoc;
if (Attrs.IsSystem || IsSystem)
ActiveModule->IsSystem = true;
View
@@ -1683,13 +1683,17 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc,
if (!SuggestedModule.getModule()->isAvailable()) {
clang::Module::Requirement Requirement;
clang::Module::UnresolvedHeaderDirective MissingHeader;
clang::Module *ShadowingModule = nullptr;
Module *M = SuggestedModule.getModule();
// Identify the cause.
(void)M->isAvailable(getLangOpts(), getTargetInfo(), Requirement,
MissingHeader);
MissingHeader, ShadowingModule);
if (MissingHeader.FileNameLoc.isValid()) {
Diag(MissingHeader.FileNameLoc, diag::err_module_header_missing)
<< MissingHeader.IsUmbrella << MissingHeader.FileName;
} else if (ShadowingModule) {
Diag(M->DefinitionLoc, diag::err_module_shadowed) << M->Name;
Diag(ShadowingModule->DefinitionLoc, diag::note_previous_definition);
} else {
Diag(M->DefinitionLoc, diag::err_module_unavailable)
<< M->getFullModuleName() << Requirement.second << Requirement.first;
@@ -0,0 +1 @@
#define A1_A_h
@@ -0,0 +1,5 @@
module A {
header "A.h"
}
module A1 {}
@@ -0,0 +1 @@
#define A2_A_h
@@ -0,0 +1,5 @@
module A {
header "A.h"
}
module A2 {}
View
@@ -0,0 +1,21 @@
// RUN: rm -rf %t
// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs/shadow/A1 -I %S/Inputs/shadow/A2 %s -fsyntax-only 2>&1 | FileCheck %s -check-prefix=REDEFINITION
// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/shadow/A1/module.modulemap -fmodule-map-file=%S/Inputs/shadow/A2/module.modulemap %s -fsyntax-only 2>&1 | FileCheck %s -check-prefix=REDEFINITION
// REDEFINITION: error: redefinition of module 'A'
// REDEFINITION: note: previously defined
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/shadow/A1/module.modulemap -I %S/Inputs/shadow %s -verify
@import A1;
@import A2;
@import A;
#import "A2/A.h" // expected-note {{implicitly imported}}
// expected-error@A2/module.modulemap:1 {{import of shadowed module 'A'}}
// expected-note@A1/module.modulemap:1 {{previous definition}}
#if defined(A2_A_h)
#error got the wrong definition of module A
#elif !defined(A1_A_h)
#error missing definition from A1
#endif

0 comments on commit ead19a9

Please sign in to comment.