Index: docs/ClangCommandLineReference.rst =================================================================== --- docs/ClangCommandLineReference.rst +++ docs/ClangCommandLineReference.rst @@ -1347,7 +1347,7 @@ .. option:: -fmodule-file-deps, -fno-module-file-deps -.. option:: -fmodule-file= +.. option:: -fmodule-file=[=] Load this precompiled module file Index: docs/Modules.rst =================================================================== --- docs/Modules.rst +++ docs/Modules.rst @@ -213,8 +213,14 @@ ``-fno-implicit-modules`` All modules used by the build must be specified with ``-fmodule-file``. -``-fmodule-file=`` - Load the given precompiled module file. +``-fmodule-file=[=]`` + Specify the mapping of module names to precompiled module files. If the + name is omitted, then the module file is loaded whether actually required + or not. If the name is specified, then the mapping is treated as another + prebuilt module search mechanism (in addition to ``-fprebuilt-module-path``) + and the module is only loaded if required. Note that in this case the + specified file also overrides this module's paths that might be embedded + in other precompiled module files. ``-fprebuilt-module-path=`` Specify the path to the prebuilt modules. If specified, we will look for modules in this directory for a given top-level module name. We don't need a module map for loading prebuilt modules in this directory and the compiler will not try to rebuild these modules. This can be specified multiple times. @@ -945,4 +951,3 @@ .. [#] The preprocessing context in which the modules are parsed is actually dependent on the command-line options provided to the compiler, including the language dialect and any ``-D`` options. However, the compiled modules for different command-line options are kept distinct, and any preprocessor directives that occur within the translation unit are ignored. See the section on the `Configuration macros declaration`_ for more information. .. _PCHInternals: PCHInternals.html - Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -1101,8 +1101,8 @@ Group, Flags<[DriverOption,CC1Option]>, MetaVarName<"">, HelpText<"Load this module map file">; def fmodule_file : Joined<["-"], "fmodule-file=">, - Group, Flags<[DriverOption,CC1Option]>, - HelpText<"Load this precompiled module file">, MetaVarName<"">; + Group, Flags<[DriverOption,CC1Option]>, MetaVarName<"[=]">, + HelpText<"Specify the mapping of module name to precompiled module file loading it if name is omitted.">; def fmodules_ignore_macro : Joined<["-"], "fmodules-ignore-macro=">, Group, Flags<[CC1Option]>, HelpText<"Ignore the definition of the given macro when building and loading modules">; def fmodules_decluse : Flag <["-"], "fmodules-decluse">, Group, Index: include/clang/Lex/HeaderSearch.h =================================================================== --- include/clang/Lex/HeaderSearch.h +++ include/clang/Lex/HeaderSearch.h @@ -480,20 +480,32 @@ /// or an empty string if this module does not correspond to any module file. std::string getModuleFileName(Module *Module); - /// \brief Retrieve the name of the module file that should be used to - /// load a module with the given name. + /// \brief Retrieve the name of the prebuilt module file that should be used + /// to load a module with the given name. + /// + /// \param ModuleName The module whose module file name will be returned. + /// + /// \param FileMapOnly If true, then only look in the explicit module name + // to file name map and skip the directory search. + /// + /// \returns The name of the module file that corresponds to this module, + /// or an empty string if this module does not correspond to any module file. + std::string getPrebuiltModuleFileName(StringRef ModuleName, + bool FileMapOnly = false); + + + /// \brief Retrieve the name of the (to-be-)cached module file that should + /// be used to load a module with the given name. /// /// \param ModuleName The module whose module file name will be returned. /// /// \param ModuleMapPath A path that when combined with \c ModuleName /// uniquely identifies this module. See Module::ModuleMap. /// - /// \param UsePrebuiltPath Whether we should use the prebuilt module path. - /// /// \returns The name of the module file that corresponds to this module, /// or an empty string if this module does not correspond to any module file. - std::string getModuleFileName(StringRef ModuleName, StringRef ModuleMapPath, - bool UsePrebuiltPath); + std::string getCachedModuleFileName(StringRef ModuleName, + StringRef ModuleMapPath); /// \brief Lookup a module Search for a module with the given name. /// Index: include/clang/Lex/HeaderSearchOptions.h =================================================================== --- include/clang/Lex/HeaderSearchOptions.h +++ include/clang/Lex/HeaderSearchOptions.h @@ -17,6 +17,7 @@ #include "llvm/ADT/StringRef.h" #include #include +#include namespace clang { @@ -94,6 +95,9 @@ /// \brief The directory used for a user build. std::string ModuleUserBuildPath; + /// \brief The mapping of module names to prebuilt module files. + std::map PrebuiltModuleFiles; + /// \brief The directories used to load prebuilt module files. std::vector PrebuiltModulePaths; Index: include/clang/Serialization/ASTReader.h =================================================================== --- include/clang/Serialization/ASTReader.h +++ include/clang/Serialization/ASTReader.h @@ -2144,9 +2144,19 @@ // \brief Read a string static std::string ReadString(const RecordData &Record, unsigned &Idx); + // \brief Skip a string + static void SkipString(const RecordData &Record, unsigned &Idx) { + Idx += Record[Idx] + 1; + } + // \brief Read a path std::string ReadPath(ModuleFile &F, const RecordData &Record, unsigned &Idx); + // \brief Skip a path + static void SkipPath(const RecordData &Record, unsigned &Idx) { + SkipString(Record, Idx); + } + /// \brief Read a version tuple. static VersionTuple ReadVersionTuple(const RecordData &Record, unsigned &Idx); Index: include/clang/Serialization/ModuleManager.h =================================================================== --- include/clang/Serialization/ModuleManager.h +++ include/clang/Serialization/ModuleManager.h @@ -20,6 +20,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/iterator.h" +#include namespace clang { @@ -45,9 +46,12 @@ // to implement short-circuiting logic when running DFS over the dependencies. SmallVector Roots; - /// \brief All loaded modules, indexed by name. + /// \brief All loaded modules, indexed by file name. llvm::DenseMap Modules; + /// \brief All loaded prebuilt/explicit modules, indexed by module name. + std::map PrebuiltModules; + /// \brief FileManager that handles translating between filenames and /// FileEntry *. FileManager &FileMgr; @@ -163,12 +167,15 @@ /// \brief Returns the module associated with the given index ModuleFile &operator[](unsigned Index) const { return *Chain[Index]; } - /// \brief Returns the module associated with the given name + /// \brief Returns the module associated with the given file name. ModuleFile *lookup(StringRef Name) const; /// \brief Returns the module associated with the given module file. ModuleFile *lookup(const FileEntry *File) const; + /// \brief Returns the module associated with the given module name. + ModuleFile *lookupPrebuilt(StringRef Name) const; + /// \brief Returns the in-memory (virtual file) buffer with the given name std::unique_ptr lookupBuffer(StringRef Name); @@ -232,6 +239,10 @@ ModuleFile *&Module, std::string &ErrorStr); + /// \brief Register a new prebuilt module to be discoverable by module + /// name. + void registerPrebuilt(ModuleFile &MF); + /// \brief Remove the modules starting from First (to the end). void removeModules(ModuleIterator First, llvm::SmallPtrSetImpl &LoadedSuccessfully, Index: lib/Driver/ToolChains/Clang.cpp =================================================================== --- lib/Driver/ToolChains/Clang.cpp +++ lib/Driver/ToolChains/Clang.cpp @@ -3568,10 +3568,24 @@ } if (HaveAnyModules) { + // The -fmodule-file== form specifies the mapping of module + // names to prebuilt module files. + for (const Arg *A : Args.filtered(options::OPT_fmodule_file)) { + StringRef Val = A->getValue(); + if (Val.find('=') != StringRef::npos) { + CmdArgs.push_back(Args.MakeArgString("-fmodule-file=" + Val)); + A->claim(); + } + } + } + + if (HaveAnyModules) { // -fprebuilt-module-path specifies where to load the prebuilt module files. - for (const Arg *A : Args.filtered(options::OPT_fprebuilt_module_path)) + for (const Arg *A : Args.filtered(options::OPT_fprebuilt_module_path)) { CmdArgs.push_back(Args.MakeArgString( std::string("-fprebuilt-module-path=") + A->getValue())); + A->claim(); + } } // -fmodule-name specifies the module that is currently being built (or @@ -3594,11 +3608,16 @@ } } - // -fmodule-file can be used to specify files containing precompiled modules. + // The -fmodule-file= form can be used to load files containing + // precompiled modules. + for (const Arg *A : Args.filtered(options::OPT_fmodule_file)) { + StringRef Val = A->getValue(); + if (Val.find('=') == StringRef::npos) { if (HaveAnyModules) - Args.AddAllArgs(CmdArgs, options::OPT_fmodule_file); - else - Args.ClaimAllArgs(options::OPT_fmodule_file); + CmdArgs.push_back(Args.MakeArgString("-fmodule-file=" + Val)); + A->claim(); + } + } // When building modules and generating crashdumps, we need to dump a module // dependency VFS alongside the output. Index: lib/Frontend/CompilerInstance.cpp =================================================================== --- lib/Frontend/CompilerInstance.cpp +++ lib/Frontend/CompilerInstance.cpp @@ -1620,6 +1620,14 @@ } else if (ModuleName == getLangOpts().CurrentModule) { // This is the module we're building. Module = PP->getHeaderSearchInfo().lookupModule(ModuleName); + /// FIXME: perhaps we should (a) look for a module using the module name + // to file map (PrebuiltModuleFiles) and (b) diagnose if still not found? + //if (Module == nullptr) { + // getDiagnostics().Report(ModuleNameLoc, diag::err_module_not_found) + // << ModuleName; + // ModuleBuildFailed = true; + // return ModuleLoadResult(); + //} Known = KnownModules.insert(std::make_pair(Path[0].first, Module)).first; } else { // Search for a module with the given name. @@ -1641,9 +1649,10 @@ } // Try to load the module from the prebuilt module path. - if (Source == ModuleNotFound && !HSOpts.PrebuiltModulePaths.empty()) { - ModuleFileName = PP->getHeaderSearchInfo().getModuleFileName( - ModuleName, "", /*UsePrebuiltPath*/ true); + if (Source == ModuleNotFound && (!HSOpts.PrebuiltModuleFiles.empty() || + !HSOpts.PrebuiltModulePaths.empty())) { + ModuleFileName = + PP->getHeaderSearchInfo().getPrebuiltModuleFileName(ModuleName); if (!ModuleFileName.empty()) Source = PrebuiltModulePath; } Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -976,9 +976,12 @@ // They won't be discovered by the regular preprocessor, so // we let make / ninja to know about this implicit dependency. Opts.ExtraDeps = Args.getAllArgValues(OPT_fdepfile_entry); - auto ModuleFiles = Args.getAllArgValues(OPT_fmodule_file); - Opts.ExtraDeps.insert(Opts.ExtraDeps.end(), ModuleFiles.begin(), - ModuleFiles.end()); + // Only the -fmodule-file= form. + for (const Arg *A : Args.filtered(OPT_fmodule_file)) { + StringRef Val = A->getValue(); + if (Val.find('=') == StringRef::npos) + Opts.ExtraDeps.push_back(Val); + } } static bool parseShowColorsArgs(const ArgList &Args, bool DefaultColor) { @@ -1307,7 +1310,12 @@ Opts.UseGlobalModuleIndex = !Args.hasArg(OPT_fno_modules_global_index); Opts.GenerateGlobalModuleIndex = Opts.UseGlobalModuleIndex; Opts.ModuleMapFiles = Args.getAllArgValues(OPT_fmodule_map_file); - Opts.ModuleFiles = Args.getAllArgValues(OPT_fmodule_file); + // Only the -fmodule-file= form. + for (const Arg *A : Args.filtered(OPT_fmodule_file)) { + StringRef Val = A->getValue(); + if (Val.find('=') == StringRef::npos) + Opts.ModuleFiles.push_back(Val); + } Opts.ModulesEmbedFiles = Args.getAllArgValues(OPT_fmodules_embed_file_EQ); Opts.ModulesEmbedAllFiles = Args.hasArg(OPT_fmodules_embed_all_files); Opts.IncludeTimestamps = !Args.hasArg(OPT_fno_pch_timestamp); @@ -1511,6 +1519,12 @@ Opts.ModuleCachePath = P.str(); Opts.ModuleUserBuildPath = Args.getLastArgValue(OPT_fmodules_user_build_path); + // Only the -fmodule-file== form. + for (const Arg *A : Args.filtered(OPT_fmodule_file)) { + StringRef Val = A->getValue(); + if (Val.find('=') != StringRef::npos) + Opts.PrebuiltModuleFiles.insert(Val.split('=')); + } for (const Arg *A : Args.filtered(OPT_fprebuilt_module_path)) Opts.AddPrebuiltModulePath(A->getValue()); Opts.DisableModuleHash = Args.hasArg(OPT_fdisable_module_hash); Index: lib/Frontend/FrontendActions.cpp =================================================================== --- lib/Frontend/FrontendActions.cpp +++ lib/Frontend/FrontendActions.cpp @@ -185,8 +185,8 @@ HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); CI.getFrontendOpts().OutputFile = - HS.getModuleFileName(CI.getLangOpts().CurrentModule, ModuleMapFile, - /*UsePrebuiltPath=*/false); + HS.getCachedModuleFileName(CI.getLangOpts().CurrentModule, + ModuleMapFile); } // We use createOutputFile here because this is exposed via libclang, and we Index: lib/Lex/HeaderSearch.cpp =================================================================== --- lib/Lex/HeaderSearch.cpp +++ lib/Lex/HeaderSearch.cpp @@ -131,18 +131,21 @@ std::string HeaderSearch::getModuleFileName(Module *Module) { const FileEntry *ModuleMap = getModuleMap().getModuleMapFileForUniquing(Module); - return getModuleFileName(Module->Name, ModuleMap->getName(), - /*UsePrebuiltPath*/false); + return getCachedModuleFileName(Module->Name, ModuleMap->getName()); } -std::string HeaderSearch::getModuleFileName(StringRef ModuleName, - StringRef ModuleMapPath, - bool UsePrebuiltPath) { - if (UsePrebuiltPath) { - if (HSOpts->PrebuiltModulePaths.empty()) +std::string HeaderSearch::getPrebuiltModuleFileName(StringRef ModuleName, + bool FileMapOnly) { + // First check the module name to pcm file map. + auto i (HSOpts->PrebuiltModuleFiles.find(ModuleName)); + if (i != HSOpts->PrebuiltModuleFiles.end()) + return i->second; + + if (FileMapOnly || HSOpts->PrebuiltModulePaths.empty()) return std::string(); - // Go though each prebuilt module path and try to find the pcm file. + // Then go through each prebuilt module directory and try to find the pcm + // file. for (const std::string &Dir : HSOpts->PrebuiltModulePaths) { SmallString<256> Result(Dir); llvm::sys::fs::make_absolute(Result); @@ -154,6 +157,8 @@ return std::string(); } +std::string HeaderSearch::getCachedModuleFileName(StringRef ModuleName, + StringRef ModuleMapPath) { // If we don't have a module cache path or aren't supposed to use one, we // can't do anything. if (getModuleCachePath().empty()) Index: lib/Serialization/ASTReader.cpp =================================================================== --- lib/Serialization/ASTReader.cpp +++ lib/Serialization/ASTReader.cpp @@ -2487,7 +2487,23 @@ {{(uint32_t)Record[Idx++], (uint32_t)Record[Idx++], (uint32_t)Record[Idx++], (uint32_t)Record[Idx++], (uint32_t)Record[Idx++]}}}; - auto ImportedFile = ReadPath(F, Record, Idx); + + std::string ImportedName = ReadString(Record, Idx); + std::string ImportedFile; + + // For prebuilt and explicit modules first consult the file map for + // an override. Note that here we don't search prebuilt module + // directories, only the explicit name to file mappings. Also, we will + // still verify the size/signature making sure it is essentially the + // same file but perhaps in a different location. + if (ImportedKind == MK_PrebuiltModule || ImportedKind == MK_ExplicitModule) + ImportedFile = PP.getHeaderSearchInfo().getPrebuiltModuleFileName( + ImportedName, /*FileMapOnly*/ true); + + if (ImportedFile.empty()) + ImportedFile = ReadPath(F, Record, Idx); + else + SkipPath(Record, Idx); // If our client can't cope with us being out of date, we can't cope with // our dependency being missing. @@ -3421,12 +3437,18 @@ RemapBuilder TypeRemap(F.TypeRemap); while (Data < DataEnd) { - // FIXME: Looking up dependency modules by filename is horrible. + // FIXME: Looking up dependency modules by filename is horrible. Let's + // start fixing this with prebuilt and explicit modules and see how it + // goes... using namespace llvm::support; + ModuleKind Kind = static_cast( + endian::readNext(Data)); uint16_t Len = endian::readNext(Data); StringRef Name = StringRef((const char*)Data, Len); Data += Len; - ModuleFile *OM = ModuleMgr.lookup(Name); + ModuleFile *OM = (Kind == MK_PrebuiltModule || Kind == MK_ExplicitModule + ? ModuleMgr.lookupPrebuilt(Name) // By module name. + : ModuleMgr.lookup(Name)); // By file name. if (!OM) { std::string Msg = "SourceLocation remap refers to unknown module, cannot find "; @@ -4118,6 +4140,10 @@ Diag(diag::err_module_file_not_module) << FileName; return Result; } + // Now that we know the module name, register it with the manager for + // by-name lookup. + if (Type == MK_PrebuiltModule || Type == MK_ExplicitModule) + ModuleMgr.registerPrebuilt(F); break; case Failure: return Failure; @@ -4764,6 +4790,7 @@ while (Idx < N) { // Read information about the AST file. Idx += 5; // ImportLoc, Size, ModTime, Signature + SkipString(Record, Idx); // Module name; FIXME: pass to listener? std::string Filename = ReadString(Record, Idx); ResolveImportedPath(Filename, ModuleDir); Listener.visitImport(Filename); Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -1505,6 +1505,7 @@ for (auto I : M.Signature) Record.push_back(I); + AddString(M.ModuleName, Record); AddPath(M.FileName, Record); } Stream.EmitRecord(IMPORTS, Record); @@ -4778,7 +4779,8 @@ // each of those modules were mapped into our own offset/ID space, so that // the reader can build the appropriate mapping to its own offset/ID space. // The map consists solely of a blob with the following format: - // *(module-name-len:i16 module-name:len*i8 + // *(module-kind:i8 + // module-name-len:i16 module-name:len*i8 // source-location-offset:i32 // identifier-id:i32 // preprocessed-entity-id:i32 @@ -4789,6 +4791,10 @@ // c++-base-specifiers-id:i32 // type-id:i32) // + // module-kind is the ModuleKind enum value. If it is MK_PrebuiltModule or + // MK_ExplicitModule, then the module-name is the module name. Otherwise, + // it is the module file name. + // auto Abbrev = std::make_shared(); Abbrev->Add(BitCodeAbbrevOp(MODULE_OFFSET_MAP)); Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); @@ -4799,9 +4805,13 @@ for (ModuleFile &M : Chain->ModuleMgr) { using namespace llvm::support; endian::Writer LE(Out); - StringRef FileName = M.FileName; - LE.write(FileName.size()); - Out.write(FileName.data(), FileName.size()); + LE.write(static_cast(M.Kind)); + StringRef Name = + M.Kind == MK_PrebuiltModule || M.Kind == MK_ExplicitModule + ? M.ModuleName + : M.FileName; + LE.write(Name.size()); + Out.write(Name.data(), Name.size()); // Note: if a base ID was uint max, it would not be possible to load // another module after it or have more than one entity inside it. Index: lib/Serialization/GlobalModuleIndex.cpp =================================================================== --- lib/Serialization/GlobalModuleIndex.cpp +++ lib/Serialization/GlobalModuleIndex.cpp @@ -619,6 +619,10 @@ (uint32_t)Record[Idx++], (uint32_t)Record[Idx++], (uint32_t)Record[Idx++]}}}; + // Skip the module name (currently this is only used for prebuilt + // modules while here we are only dealing with cached). + Idx += Record[Idx] + 1; + // Retrieve the imported file name. unsigned Length = Record[Idx++]; SmallString<128> ImportedFile(Record.begin() + Idx, Index: lib/Serialization/ModuleManager.cpp =================================================================== --- lib/Serialization/ModuleManager.cpp +++ lib/Serialization/ModuleManager.cpp @@ -45,6 +45,14 @@ return Known->second; } +ModuleFile *ModuleManager::lookupPrebuilt(StringRef Name) const { + auto Known = PrebuiltModules.find(Name); + if (Known == PrebuiltModules.end()) + return nullptr; + + return Known->second; +} + std::unique_ptr ModuleManager::lookupBuffer(StringRef Name) { const FileEntry *Entry = FileMgr.getFile(Name, /*openFile=*/false, @@ -192,6 +200,11 @@ return NewlyLoaded; } +void ModuleManager::registerPrebuilt (ModuleFile &M) { + assert (!M.ModuleName.empty()); + PrebuiltModules[M.ModuleName] = &M; +} + void ModuleManager::removeModules( ModuleIterator First, llvm::SmallPtrSetImpl &LoadedSuccessfully, @@ -232,6 +245,8 @@ // Delete the modules and erase them from the various structures. for (ModuleIterator victim = First; victim != Last; ++victim) { Modules.erase(victim->File); + if (!victim->ModuleName.empty()) + PrebuiltModules.erase(victim->ModuleName); if (modMap) { StringRef ModuleName = victim->ModuleName; Index: test/CXX/modules-ts/basic/basic.search/module-import.cpp =================================================================== --- /dev/null +++ test/CXX/modules-ts/basic/basic.search/module-import.cpp @@ -0,0 +1,39 @@ +// Tests for imported module search. +// +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: echo 'export module x; int a, b;' > %t/x.cppm +// RUN: echo 'export module y; import x; int c;' > %t/y.cppm +// RUN: echo 'export module z; import y; int d;' > %t/z.cppm +// +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface %t/x.cppm -o %t/x.pcm +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface -fmodule-file=%t/x.pcm %t/y.cppm -o %t/y.pcm +// +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.pcm -verify %s \ +// RUN: -DMODULE_NAME=x +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/y.pcm -verify %s \ +// RUN: -DMODULE_NAME=y +// +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=x=%t/x.pcm -verify %s \ +// RUN: -DMODULE_NAME=x +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=y=%t/y.pcm -verify %s \ +// RUN: -DMODULE_NAME=y +// +// RUN: mv %t/x.pcm %t/a.pcm +// +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=x=%t/a.pcm -verify %s \ +// RUN: -DMODULE_NAME=x +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/y.pcm -fmodule-file=x=%t/a.pcm -verify %s \ +// RUN: -DMODULE_NAME=y +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=y=%t/y.pcm -fmodule-file=x=%t/a.pcm -verify %s \ +// RUN: -DMODULE_NAME=y +// +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface -fmodule-file=y=%t/y.pcm -fmodule-file=x=%t/a.pcm %t/z.cppm -o %t/z.pcm +// +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=z=%t/z.pcm -fmodule-file=y=%t/y.pcm -fmodule-file=x=%t/a.pcm -verify %s \ +// RUN: -DMODULE_NAME=z +// + +import MODULE_NAME; + +// expected-no-diagnostics