From 84853833c24ba9c1b5196d4c556e353781b8d789 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Sat, 15 Sep 2018 01:21:15 +0000 Subject: [PATCH] [modules] Frontend support for building a header module from a list of headaer files. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@342304 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../clang/Basic/DiagnosticFrontendKinds.td | 6 ++ include/clang/Basic/DiagnosticIDs.h | 2 +- include/clang/Basic/DiagnosticSemaKinds.td | 2 + include/clang/Basic/LangOptions.h | 5 +- include/clang/Driver/CC1Options.td | 2 + include/clang/Frontend/FrontendAction.h | 20 +++++- include/clang/Frontend/FrontendActions.h | 13 ++++ include/clang/Frontend/FrontendOptions.h | 3 + include/clang/Lex/ModuleMap.h | 12 ++-- lib/Frontend/CompilerInstance.cpp | 22 ++---- lib/Frontend/CompilerInvocation.cpp | 3 + lib/Frontend/FrontendAction.cpp | 9 ++- lib/Frontend/FrontendActions.cpp | 70 +++++++++++++++++++ .../ExecuteCompilerInvocation.cpp | 2 + lib/Lex/ModuleMap.cpp | 39 ++++++++--- lib/Sema/Sema.cpp | 3 +- lib/Sema/SemaDecl.cpp | 20 +++++- test/Modules/Inputs/no-module-map/a.h | 4 ++ test/Modules/Inputs/no-module-map/b.h | 5 ++ test/Modules/no-module-map.cpp | 43 ++++++++++++ 20 files changed, 248 insertions(+), 37 deletions(-) create mode 100644 test/Modules/Inputs/no-module-map/a.h create mode 100644 test/Modules/Inputs/no-module-map/b.h create mode 100644 test/Modules/no-module-map.cpp diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td index 2bf85e0098..8636aae20d 100644 --- a/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/include/clang/Basic/DiagnosticFrontendKinds.td @@ -187,6 +187,8 @@ def err_module_build_requires_fmodules : Error< "module compilation requires '-fmodules'">; def err_module_interface_requires_modules_ts : Error< "module interface compilation requires '-fmodules-ts'">; +def err_header_module_requires_modules : Error< + "header module compilation requires '-fmodules' or '-fmodules-ts'">; def warn_module_config_mismatch : Warning< "module file %0 cannot be loaded due to a configuration mismatch with the current " "compilation">, InGroup>, DefaultError; @@ -224,6 +226,10 @@ def remark_module_build_done : Remark<"finished building module '%0'">, def err_modules_embed_file_not_found : Error<"file '%0' specified by '-fmodules-embed-file=' not found">, DefaultFatal; +def err_module_header_file_not_found : + Error<"module header file '%0' not found">, DefaultFatal; +def err_module_header_file_invalid : + Error<"unexpected module header file input '%0'">, DefaultFatal; def err_test_module_file_extension_version : Error< "test module file extension '%0' has different version (%1.%2) than expected " diff --git a/include/clang/Basic/DiagnosticIDs.h b/include/clang/Basic/DiagnosticIDs.h index 983dcb21cf..876629f373 100644 --- a/include/clang/Basic/DiagnosticIDs.h +++ b/include/clang/Basic/DiagnosticIDs.h @@ -30,7 +30,7 @@ namespace clang { enum { DIAG_SIZE_COMMON = 300, DIAG_SIZE_DRIVER = 200, - DIAG_SIZE_FRONTEND = 100, + DIAG_SIZE_FRONTEND = 150, DIAG_SIZE_SERIALIZATION = 120, DIAG_SIZE_LEX = 400, DIAG_SIZE_PARSE = 500, diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 0aa8b2a5d4..08dfbd3c74 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -9090,6 +9090,8 @@ def err_invalid_type_for_program_scope_var : Error< let CategoryName = "Modules Issue" in { def err_module_decl_in_module_map_module : Error< "'module' declaration found while building module from module map">; +def err_module_decl_in_header_module : Error< + "'module' declaration found while building header module">; def err_module_interface_implementation_mismatch : Error< "missing 'export' specifier in module declaration while " "building module interface">; diff --git a/include/clang/Basic/LangOptions.h b/include/clang/Basic/LangOptions.h index a7a5105fdf..30ee20395b 100644 --- a/include/clang/Basic/LangOptions.h +++ b/include/clang/Basic/LangOptions.h @@ -73,8 +73,11 @@ public: /// Compiling a module from a module map. CMK_ModuleMap, + /// Compiling a module from a list of header files. + CMK_HeaderModule, + /// Compiling a C++ modules TS module interface unit. - CMK_ModuleInterface + CMK_ModuleInterface, }; enum PragmaMSPointersToMembersKind { diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index 3ac092645a..511dded5c1 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -543,6 +543,8 @@ def emit_module : Flag<["-"], "emit-module">, HelpText<"Generate pre-compiled module file from a module map">; def emit_module_interface : Flag<["-"], "emit-module-interface">, HelpText<"Generate pre-compiled module file from a C++ module interface">; +def emit_header_module : Flag<["-"], "emit-header-module">, + HelpText<"Generate pre-compiled module file from a set of header files">; def emit_pth : Flag<["-"], "emit-pth">, HelpText<"Generate pre-tokenized header file">; def emit_pch : Flag<["-"], "emit-pch">, diff --git a/include/clang/Frontend/FrontendAction.h b/include/clang/Frontend/FrontendAction.h index 2a4077d343..22314386e0 100644 --- a/include/clang/Frontend/FrontendAction.h +++ b/include/clang/Frontend/FrontendAction.h @@ -48,6 +48,12 @@ protected: /// @name Implementation Action Interface /// @{ + /// Prepare to execute the action on the given CompilerInstance. + /// + /// This is called before executing the action on any inputs, and can modify + /// the configuration as needed (including adjusting the input list). + virtual bool PrepareToExecuteAction(CompilerInstance &CI) { return true; } + /// Create the AST consumer object for this action, if supported. /// /// This routine is called as part of BeginSourceFile(), which will @@ -130,11 +136,18 @@ public: return CurrentInput; } - const StringRef getCurrentFile() const { + StringRef getCurrentFile() const { assert(!CurrentInput.isEmpty() && "No current file!"); return CurrentInput.getFile(); } + StringRef getCurrentFileOrBufferName() const { + assert(!CurrentInput.isEmpty() && "No current file!"); + return CurrentInput.isFile() + ? CurrentInput.getFile() + : CurrentInput.getBuffer()->getBufferIdentifier(); + } + InputKind getCurrentFileKind() const { assert(!CurrentInput.isEmpty() && "No current file!"); return CurrentInput.getKind(); @@ -190,6 +203,11 @@ public: /// @name Public Action Interface /// @{ + /// Prepare the action to execute on the given compiler instance. + bool PrepareToExecute(CompilerInstance &CI) { + return PrepareToExecuteAction(CI); + } + /// Prepare the action for processing the input file \p Input. /// /// This is run after the options and frontend have been initialized, diff --git a/include/clang/Frontend/FrontendActions.h b/include/clang/Frontend/FrontendActions.h index 54539a5b3c..39860801cf 100644 --- a/include/clang/Frontend/FrontendActions.h +++ b/include/clang/Frontend/FrontendActions.h @@ -142,6 +142,19 @@ private: CreateOutputFile(CompilerInstance &CI, StringRef InFile) override; }; +class GenerateHeaderModuleAction : public GenerateModuleAction { + /// The synthesized module input buffer for the current compilation. + std::unique_ptr Buffer; + std::vector ModuleHeaders; + +private: + bool PrepareToExecuteAction(CompilerInstance &CI) override; + bool BeginSourceFileAction(CompilerInstance &CI) override; + + std::unique_ptr + CreateOutputFile(CompilerInstance &CI, StringRef InFile) override; +}; + class SyntaxOnlyAction : public ASTFrontendAction { protected: std::unique_ptr CreateASTConsumer(CompilerInstance &CI, diff --git a/include/clang/Frontend/FrontendOptions.h b/include/clang/Frontend/FrontendOptions.h index 668df83274..51f11b8b28 100644 --- a/include/clang/Frontend/FrontendOptions.h +++ b/include/clang/Frontend/FrontendOptions.h @@ -82,6 +82,9 @@ enum ActionKind { /// Generate pre-compiled module from a C++ module interface file. GenerateModuleInterface, + /// Generate pre-compiled module from a set of header files. + GenerateHeaderModule, + /// Generate pre-compiled header. GeneratePCH, diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h index 4a3984e746..4daa1dd817 100644 --- a/include/clang/Lex/ModuleMap.h +++ b/include/clang/Lex/ModuleMap.h @@ -92,9 +92,9 @@ class ModuleMap { /// named LangOpts::CurrentModule, if we've loaded it). Module *SourceModule = nullptr; - /// The global module for the current TU, if we still own it. (Ownership is - /// transferred if/when we create an enclosing module. - std::unique_ptr PendingGlobalModule; + /// Submodules of the current module that have not yet been attached to it. + /// (Ownership is transferred if/when we create an enclosing module.) + llvm::SmallVector, 8> PendingSubmodules; /// The top-level modules that are known. llvm::StringMap Modules; @@ -519,8 +519,7 @@ public: bool IsFramework, bool IsExplicit); - /// Create a 'global module' for a C++ Modules TS module interface - /// unit. + /// Create a 'global module' for a C++ Modules TS module interface unit. /// /// We model the global module as a submodule of the module interface unit. /// Unfortunately, we can't create the module interface unit's Module until @@ -537,6 +536,9 @@ public: Module *createModuleForInterfaceUnit(SourceLocation Loc, StringRef Name, Module *GlobalModule); + /// Create a header module from the specified list of headers. + Module *createHeaderModule(StringRef Name, ArrayRef Headers); + /// Infer the contents of a framework module map from the given /// framework directory. Module *inferFrameworkModule(const DirectoryEntry *FrameworkDir, diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index 38a09a0dac..05ba3c0265 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -911,6 +911,9 @@ bool CompilerInstance::ExecuteAction(FrontendAction &Act) { // taking it as an input instead of hard-coding llvm::errs. raw_ostream &OS = llvm::errs(); + if (!Act.PrepareToExecute(*this)) + return false; + // Create the target instance. setTarget(TargetInfo::CreateTargetInfo(getDiagnostics(), getInvocation().TargetOpts)); @@ -1615,22 +1618,7 @@ CompilerInstance::loadModule(SourceLocation ImportLoc, Module::NameVisibilityKind Visibility, bool IsInclusionDirective) { // Determine what file we're searching from. - // FIXME: Should we be deciding whether this is a submodule (here and - // below) based on -fmodules-ts or should we pass a flag and make the - // caller decide? - std::string ModuleName; - if (getLangOpts().ModulesTS) { - // FIXME: Same code as Sema::ActOnModuleDecl() so there is probably a - // better place/way to do this. - for (auto &Piece : Path) { - if (!ModuleName.empty()) - ModuleName += "."; - ModuleName += Piece.first->getName(); - } - } - else - ModuleName = Path[0].first->getName(); - + StringRef ModuleName = Path[0].first->getName(); SourceLocation ModuleNameLoc = Path[0].second; // If we've already handled this import, just return the cached result. @@ -1859,7 +1847,7 @@ CompilerInstance::loadModule(SourceLocation ImportLoc, // Verify that the rest of the module path actually corresponds to // a submodule. bool MapPrivateSubModToTopLevel = false; - if (!getLangOpts().ModulesTS && Path.size() > 1) { + if (Path.size() > 1) { for (unsigned I = 1, N = Path.size(); I != N; ++I) { StringRef Name = Path[I].first->getName(); clang::Module *Sub = Module->findSubmodule(Name); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index b11581ddf7..55745b1963 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1454,6 +1454,8 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, Opts.ProgramAction = frontend::GenerateModule; break; case OPT_emit_module_interface: Opts.ProgramAction = frontend::GenerateModuleInterface; break; + case OPT_emit_header_module: + Opts.ProgramAction = frontend::GenerateHeaderModule; break; case OPT_emit_pch: Opts.ProgramAction = frontend::GeneratePCH; break; case OPT_emit_pth: @@ -2830,6 +2832,7 @@ static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) { case frontend::FixIt: case frontend::GenerateModule: case frontend::GenerateModuleInterface: + case frontend::GenerateHeaderModule: case frontend::GeneratePCH: case frontend::GeneratePTH: case frontend::ParseSyntaxOnly: diff --git a/lib/Frontend/FrontendAction.cpp b/lib/Frontend/FrontendAction.cpp index ddb522ae05..10f1d1ef61 100644 --- a/lib/Frontend/FrontendAction.cpp +++ b/lib/Frontend/FrontendAction.cpp @@ -523,7 +523,6 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, setCurrentInput(Input); setCompilerInstance(&CI); - StringRef InputFile = Input.getFile(); bool HasBegunSourceFile = false; bool ReplayASTFile = Input.getKind().getFormat() == InputKind::Precompiled && usesPreprocessorOnly(); @@ -541,6 +540,9 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, &Diags->getDiagnosticOptions())); ASTDiags->setClient(Diags->getClient(), /*OwnsClient*/false); + // FIXME: What if the input is a memory buffer? + StringRef InputFile = Input.getFile(); + std::unique_ptr AST = ASTUnit::LoadFromASTFile( InputFile, CI.getPCHContainerReader(), ASTUnit::LoadPreprocessorOnly, ASTDiags, CI.getFileSystemOpts(), CI.getCodeGenOpts().DebugTypeExtRefs); @@ -604,6 +606,9 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, IntrusiveRefCntPtr Diags(&CI.getDiagnostics()); + // FIXME: What if the input is a memory buffer? + StringRef InputFile = Input.getFile(); + std::unique_ptr AST = ASTUnit::LoadFromASTFile( InputFile, CI.getPCHContainerReader(), ASTUnit::LoadEverything, Diags, CI.getFileSystemOpts(), CI.getCodeGenOpts().DebugTypeExtRefs); @@ -791,7 +796,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, // For preprocessed files, check if the first line specifies the original // source file name with a linemarker. - std::string PresumedInputFile = InputFile; + std::string PresumedInputFile = getCurrentFileOrBufferName(); if (Input.isPreprocessed()) ReadOriginalFileName(CI, PresumedInputFile); diff --git a/lib/Frontend/FrontendActions.cpp b/lib/Frontend/FrontendActions.cpp index 40008bf3cd..d0d83076f1 100644 --- a/lib/Frontend/FrontendActions.cpp +++ b/lib/Frontend/FrontendActions.cpp @@ -242,6 +242,76 @@ GenerateModuleInterfaceAction::CreateOutputFile(CompilerInstance &CI, return CI.createDefaultOutputFile(/*Binary=*/true, InFile, "pcm"); } +bool GenerateHeaderModuleAction::PrepareToExecuteAction( + CompilerInstance &CI) { + if (!CI.getLangOpts().Modules && !CI.getLangOpts().ModulesTS) { + CI.getDiagnostics().Report(diag::err_header_module_requires_modules); + return false; + } + + auto &Inputs = CI.getFrontendOpts().Inputs; + if (Inputs.empty()) + return GenerateModuleAction::BeginInvocation(CI); + + auto Kind = Inputs[0].getKind(); + + // Convert the header file inputs into a single module input buffer. + SmallString<256> HeaderContents; + ModuleHeaders.reserve(Inputs.size()); + for (const FrontendInputFile &FIF : Inputs) { + // FIXME: We should support re-compiling from an AST file. + if (FIF.getKind().getFormat() != InputKind::Source || !FIF.isFile()) { + CI.getDiagnostics().Report(diag::err_module_header_file_not_found) + << (FIF.isFile() ? FIF.getFile() + : FIF.getBuffer()->getBufferIdentifier()); + return true; + } + + HeaderContents += "#include \""; + HeaderContents += FIF.getFile(); + HeaderContents += "\"\n"; + ModuleHeaders.push_back(FIF.getFile()); + } + Buffer = llvm::MemoryBuffer::getMemBufferCopy( + HeaderContents, Module::getModuleInputBufferName()); + + // Set that buffer up as our "real" input. + Inputs.clear(); + Inputs.push_back(FrontendInputFile(Buffer.get(), Kind, /*IsSystem*/false)); + + return GenerateModuleAction::PrepareToExecuteAction(CI); +} + +bool GenerateHeaderModuleAction::BeginSourceFileAction( + CompilerInstance &CI) { + CI.getLangOpts().setCompilingModule(LangOptions::CMK_HeaderModule); + + // Synthesize a Module object for the given headers. + auto &HS = CI.getPreprocessor().getHeaderSearchInfo(); + SmallVector Headers; + for (StringRef Name : ModuleHeaders) { + const DirectoryLookup *CurDir = nullptr; + const FileEntry *FE = HS.LookupFile( + Name, SourceLocation(), /*Angled*/ false, nullptr, CurDir, + None, nullptr, nullptr, nullptr, nullptr, nullptr); + if (!FE) { + CI.getDiagnostics().Report(diag::err_module_header_file_not_found) + << Name; + continue; + } + Headers.push_back({Name, FE}); + } + HS.getModuleMap().createHeaderModule(CI.getLangOpts().CurrentModule, Headers); + + return GenerateModuleAction::BeginSourceFileAction(CI); +} + +std::unique_ptr +GenerateHeaderModuleAction::CreateOutputFile(CompilerInstance &CI, + StringRef InFile) { + return CI.createDefaultOutputFile(/*Binary=*/true, InFile, "pcm"); +} + SyntaxOnlyAction::~SyntaxOnlyAction() { } diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp index 747fdd2416..4deebcd477 100644 --- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -61,6 +61,8 @@ CreateFrontendBaseAction(CompilerInstance &CI) { return llvm::make_unique(); case GenerateModuleInterface: return llvm::make_unique(); + case GenerateHeaderModule: + return llvm::make_unique(); case GeneratePCH: return llvm::make_unique(); case GeneratePTH: return llvm::make_unique(); case InitOnly: return llvm::make_unique(); diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index a1e9138217..b6a6e26d6a 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -806,12 +806,11 @@ std::pair ModuleMap::findOrCreateModule(StringRef Name, } Module *ModuleMap::createGlobalModuleForInterfaceUnit(SourceLocation Loc) { - assert(!PendingGlobalModule && "created multiple global modules"); - PendingGlobalModule.reset( + PendingSubmodules.emplace_back( new Module("", Loc, nullptr, /*IsFramework*/ false, /*IsExplicit*/ true, NumCreatedModules++)); - PendingGlobalModule->Kind = Module::GlobalModuleFragment; - return PendingGlobalModule.get(); + PendingSubmodules.back()->Kind = Module::GlobalModuleFragment; + return PendingSubmodules.back().get(); } Module *ModuleMap::createModuleForInterfaceUnit(SourceLocation Loc, @@ -827,10 +826,11 @@ Module *ModuleMap::createModuleForInterfaceUnit(SourceLocation Loc, Modules[Name] = SourceModule = Result; // Reparent the current global module fragment as a submodule of this module. - assert(GlobalModule == PendingGlobalModule.get() && - "unexpected global module"); - GlobalModule->setParent(Result); - PendingGlobalModule.release(); // now owned by parent + for (auto &Submodule : PendingSubmodules) { + Submodule->setParent(Result); + Submodule.release(); // now owned by parent + } + PendingSubmodules.clear(); // Mark the main source file as being within the newly-created module so that // declarations and macros are properly visibility-restricted to it. @@ -841,6 +841,29 @@ Module *ModuleMap::createModuleForInterfaceUnit(SourceLocation Loc, return Result; } +Module *ModuleMap::createHeaderModule(StringRef Name, + ArrayRef Headers) { + assert(LangOpts.CurrentModule == Name && "module name mismatch"); + assert(!Modules[Name] && "redefining existing module"); + + auto *Result = + new Module(Name, SourceLocation(), nullptr, /*IsFramework*/ false, + /*IsExplicit*/ false, NumCreatedModules++); + Result->Kind = Module::ModuleInterfaceUnit; + Modules[Name] = SourceModule = Result; + + for (const Module::Header &H : Headers) { + auto *M = new Module(H.NameAsWritten, SourceLocation(), Result, + /*IsFramework*/ false, + /*IsExplicit*/ true, NumCreatedModules++); + // Header modules are implicitly 'export *'. + M->Exports.push_back(Module::ExportDecl(nullptr, true)); + addHeader(M, H, NormalHeader); + } + + return Result; +} + /// For a framework module, infer the framework against which we /// should link. static void inferFrameworkLink(Module *Mod, const DirectoryEntry *FrameworkDir, diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 82a04e1070..4b0e69c7f6 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -977,7 +977,8 @@ void Sema::ActOnEndOfTranslationUnit() { // module declaration by now. if (getLangOpts().getCompilingModule() == LangOptions::CMK_ModuleInterface && - ModuleScopes.back().Module->Kind != Module::ModuleInterfaceUnit) { + (ModuleScopes.empty() || + ModuleScopes.back().Module->Kind != Module::ModuleInterfaceUnit)) { // FIXME: Make a better guess as to where to put the module declaration. Diag(getSourceManager().getLocForStartOfFile( getSourceManager().getMainFileID()), diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index f1e9286b7e..eacf713b9d 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -16832,6 +16832,10 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc, case LangOptions::CMK_ModuleMap: Diag(ModuleLoc, diag::err_module_decl_in_module_map_module); return nullptr; + + case LangOptions::CMK_HeaderModule: + Diag(ModuleLoc, diag::err_module_decl_in_header_module); + return nullptr; } assert(ModuleScopes.size() == 1 && "expected to be at global module scope"); @@ -16900,7 +16904,8 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc, case ModuleDeclKind::Implementation: std::pair ModuleNameLoc( PP.getIdentifierInfo(ModuleName), Path[0].second); - Mod = getModuleLoader().loadModule(ModuleLoc, Path, Module::AllVisible, + Mod = getModuleLoader().loadModule(ModuleLoc, {ModuleNameLoc}, + Module::AllVisible, /*IsIncludeDirective=*/false); if (!Mod) { Diag(ModuleLoc, diag::err_module_not_defined) << ModuleName; @@ -16930,6 +16935,19 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc, DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, SourceLocation ImportLoc, ModuleIdPath Path) { + // Flatten the module path for a Modules TS module name. + std::pair ModuleNameLoc; + if (getLangOpts().ModulesTS) { + std::string ModuleName; + for (auto &Piece : Path) { + if (!ModuleName.empty()) + ModuleName += "."; + ModuleName += Piece.first->getName(); + } + ModuleNameLoc = {PP.getIdentifierInfo(ModuleName), Path[0].second}; + Path = ModuleIdPath(ModuleNameLoc); + } + Module *Mod = getModuleLoader().loadModule(ImportLoc, Path, Module::AllVisible, /*IsIncludeDirective=*/false); diff --git a/test/Modules/Inputs/no-module-map/a.h b/test/Modules/Inputs/no-module-map/a.h new file mode 100644 index 0000000000..546714f4ca --- /dev/null +++ b/test/Modules/Inputs/no-module-map/a.h @@ -0,0 +1,4 @@ +#ifndef A_H +#define A_H +void a(); +#endif diff --git a/test/Modules/Inputs/no-module-map/b.h b/test/Modules/Inputs/no-module-map/b.h new file mode 100644 index 0000000000..d3fbc8b3ad --- /dev/null +++ b/test/Modules/Inputs/no-module-map/b.h @@ -0,0 +1,5 @@ +#ifndef B_H +#define B_H +#include "a.h" +void b(); +#endif diff --git a/test/Modules/no-module-map.cpp b/test/Modules/no-module-map.cpp new file mode 100644 index 0000000000..08a864bc84 --- /dev/null +++ b/test/Modules/no-module-map.cpp @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -fmodules-ts -fmodule-name=ab -x c++-header %S/Inputs/no-module-map/a.h %S/Inputs/no-module-map/b.h -emit-header-module -o %t.pcm +// RUN: %clang_cc1 -fmodules-ts -fmodule-file=%t.pcm %s -I%S/Inputs/no-module-map -verify +// RUN: %clang_cc1 -fmodules-ts -fmodule-file=%t.pcm %s -I%S/Inputs/no-module-map -verify -DA +// RUN: %clang_cc1 -fmodules-ts -fmodule-file=%t.pcm %s -I%S/Inputs/no-module-map -verify -DB +// RUN: %clang_cc1 -fmodules-ts -fmodule-file=%t.pcm %s -I%S/Inputs/no-module-map -verify -DA -DB + +#ifdef B +// expected-no-diagnostics +#endif + +#ifdef A +#include "a.h" +#endif + +#ifdef B +#include "b.h" +#endif + +#if defined(A) || defined(B) +#ifndef A_H +#error A_H should be defined +#endif +#else +#ifdef A_H +#error A_H should not be defined +#endif +// expected-error@+3 {{must be imported from}} +// expected-note@* {{previous declaration}} +#endif +void use_a() { a(); } + +#if defined(B) +#ifndef B_H +#error B_H should be defined +#endif +#else +#ifdef B_H +#error B_H should not be defined +#endif +// expected-error@+3 {{must be imported from}} +// expected-note@* {{previous declaration}} +#endif +void use_b() { b(); } -- 2.40.0