From: Richard Smith Date: Fri, 26 Aug 2016 00:14:38 +0000 (+0000) Subject: C++ Modules TS: add frontend support for building pcm files from module X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d6055341405831ba2a0483b72e8baf0c23de6749;p=clang C++ Modules TS: add frontend support for building pcm files from module interface files. At the moment, all declarations (and no macros) are exported, and 'export' declarations are not supported yet. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@279794 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td index 2aa8f10350..43b5dfc83d 100644 --- a/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/include/clang/Basic/DiagnosticFrontendKinds.td @@ -174,6 +174,8 @@ def warn_incompatible_analyzer_plugin_api : Warning< def note_incompatible_analyzer_plugin_api : Note< "current API version is '%0', but plugin was compiled with version '%1'">; +def err_module_interface_requires_modules_ts : Error< + "module interface compilation requires '-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; @@ -217,4 +219,4 @@ def err_invalid_vfs_overlay : Error< def warn_option_invalid_ocl_version : Warning< "OpenCL version %0 does not support the option '%1'">, InGroup; -} \ No newline at end of file +} diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 34280b902e..513820727a 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -1021,6 +1021,10 @@ def warn_pragma_unroll_cuda_value_in_parens : Warning< } // end of Parse Issue category. let CategoryName = "Modules Issue" in { +def err_expected_module_interface_decl : Error< + "expected module declaration at start of module interface">; +def err_unexpected_module_decl : Error< + "module declaration must be the first declaration in the translation unit">; def err_module_expected_ident : Error< "expected a module name after module%select{| import}0">; def err_unexpected_module_kind : Error< diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index bf6e6ecab9..ff98590057 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -8467,6 +8467,10 @@ def err_module_interface_implementation_mismatch : Error< "found while %select{not |not |}0building module interface">; def err_current_module_name_mismatch : Error< "module name '%0' specified on command line does not match name of module">; +def err_module_redefinition : Error< + "redefinition of module '%0'">; +def note_prev_module_definition : Note<"previously defined here">; +def note_prev_module_definition_from_ast_file : Note<"module loaded from '%0'">; def err_module_private_specialization : Error< "%select{template|partial|member}0 specialization cannot be " "declared __module_private__">; diff --git a/include/clang/Basic/LangOptions.def b/include/clang/Basic/LangOptions.def index 764e9bcb89..b738f83717 100644 --- a/include/clang/Basic/LangOptions.def +++ b/include/clang/Basic/LangOptions.def @@ -142,8 +142,9 @@ BENIGN_LANGOPT(EmitAllDecls , 1, 0, "emitting all declarations") LANGOPT(MathErrno , 1, 1, "errno in math functions") BENIGN_LANGOPT(HeinousExtensions , 1, 0, "extensions that we really don't like and may be ripped out at any time") LANGOPT(Modules , 1, 0, "modules extension to C") -LANGOPT(ModulesTS , 1, 0, "C++ Modules TS") -BENIGN_LANGOPT(CompilingModule, 1, 0, "compiling a module interface") +COMPATIBLE_LANGOPT(ModulesTS , 1, 0, "C++ Modules TS") +BENIGN_ENUM_LANGOPT(CompilingModule, CompilingModuleKind, 2, CMK_None, + "compiling a module interface") COMPATIBLE_LANGOPT(ModulesDeclUse , 1, 0, "require declaration of module uses") BENIGN_LANGOPT(ModulesSearchAll , 1, 1, "searching even non-imported modules to find unresolved references") COMPATIBLE_LANGOPT(ModulesStrictDeclUse, 1, 0, "requiring declaration of module uses and all headers to be in modules") diff --git a/include/clang/Basic/LangOptions.h b/include/clang/Basic/LangOptions.h index 6ec499f1c7..5be78fecc7 100644 --- a/include/clang/Basic/LangOptions.h +++ b/include/clang/Basic/LangOptions.h @@ -58,6 +58,12 @@ public: SOB_Trapping // -ftrapv }; + enum CompilingModuleKind { + CMK_None, ///< Not compiling a module interface at all. + CMK_ModuleMap, ///< Compiling a module from a module map. + CMK_ModuleInterface ///< Compiling a C++ modules TS module interface unit. + }; + enum PragmaMSPointersToMembersKind { PPTMK_BestCase, PPTMK_FullGeneralitySingleInheritance, @@ -134,7 +140,12 @@ public: Type get##Name() const { return static_cast(Name); } \ void set##Name(Type Value) { Name = static_cast(Value); } #include "clang/Basic/LangOptions.def" - + + /// Are we compiling a module interface (.cppm or module map)? + bool isCompilingModule() const { + return getCompilingModule() != CMK_None; + } + bool isSignedOverflowDefined() const { return getSignedOverflowBehavior() == SOB_Defined; } diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index f2cfc11c9c..9eaf0c0261 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -459,6 +459,8 @@ def print_decl_contexts : Flag<["-"], "print-decl-contexts">, HelpText<"Print DeclContexts and their Decls">; 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_pth : Flag<["-"], "emit-pth">, HelpText<"Generate pre-tokenized header file">; def emit_pch : Flag<["-"], "emit-pch">, diff --git a/include/clang/Frontend/FrontendActions.h b/include/clang/Frontend/FrontendActions.h index b56a04ae06..a073ca5bfd 100644 --- a/include/clang/Frontend/FrontendActions.h +++ b/include/clang/Frontend/FrontendActions.h @@ -91,11 +91,12 @@ public: }; class GenerateModuleAction : public ASTFrontendAction { - clang::Module *Module; - const FileEntry *ModuleMapForUniquing; - bool IsSystem; - + virtual std::unique_ptr + CreateOutputFile(CompilerInstance &CI, StringRef InFile) = 0; + protected: + bool BeginSourceFileAction(CompilerInstance &CI, StringRef Filename) override; + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override; @@ -104,22 +105,31 @@ protected: } bool hasASTFileSupport() const override { return false; } +}; + +class GenerateModuleFromModuleMapAction : public GenerateModuleAction { + clang::Module *Module = nullptr; + const FileEntry *ModuleMapForUniquing = nullptr; + bool IsSystem = false; + +private: + bool BeginSourceFileAction(CompilerInstance &CI, StringRef Filename) override; + + std::unique_ptr + CreateOutputFile(CompilerInstance &CI, StringRef InFile) override; public: - GenerateModuleAction(const FileEntry *ModuleMap = nullptr, - bool IsSystem = false) - : ASTFrontendAction(), ModuleMapForUniquing(ModuleMap), IsSystem(IsSystem) - { } + GenerateModuleFromModuleMapAction() {} + GenerateModuleFromModuleMapAction(const FileEntry *ModuleMap, bool IsSystem) + : ModuleMapForUniquing(ModuleMap), IsSystem(IsSystem) {} +}; +class GenerateModuleInterfaceAction : public GenerateModuleAction { +private: bool BeginSourceFileAction(CompilerInstance &CI, StringRef Filename) override; - /// \brief Compute the AST consumer arguments that will be used to - /// create the PCHGenerator instance returned by CreateASTConsumer. - /// - /// \returns true if an error occurred, false otherwise. std::unique_ptr - ComputeASTConsumerArguments(CompilerInstance &CI, StringRef InFile, - std::string &Sysroot, std::string &OutputFile); + CreateOutputFile(CompilerInstance &CI, StringRef InFile) override; }; class SyntaxOnlyAction : public ASTFrontendAction { diff --git a/include/clang/Frontend/FrontendOptions.h b/include/clang/Frontend/FrontendOptions.h index a75523f256..4df5e2bb9b 100644 --- a/include/clang/Frontend/FrontendOptions.h +++ b/include/clang/Frontend/FrontendOptions.h @@ -40,7 +40,9 @@ namespace frontend { EmitCodeGenOnly, ///< Generate machine code, but don't emit anything. EmitObj, ///< Emit a .o file. FixIt, ///< Parse and apply any fixits to the source. - GenerateModule, ///< Generate pre-compiled module. + GenerateModule, ///< Generate pre-compiled module from a module map. + GenerateModuleInterface,///< Generate pre-compiled module from a C++ module + ///< interface file. GeneratePCH, ///< Generate pre-compiled header. GeneratePTH, ///< Generate pre-tokenized header. InitOnly, ///< Only execute frontend initialization. diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h index 1e86f73698..9ab33abfef 100644 --- a/include/clang/Lex/ModuleMap.h +++ b/include/clang/Lex/ModuleMap.h @@ -402,6 +402,15 @@ public: bool IsFramework, bool IsExplicit); + /// \brief Create a new module for a C++ Modules TS module interface unit. + /// The module must not already exist, and will be configured for the current + /// compilation. + /// + /// Note that this also sets the current module to the newly-created module. + /// + /// \returns The newly-created module. + Module *createModuleForInterfaceUnit(SourceLocation Loc, StringRef Name); + /// \brief Infer the contents of a framework module map from the given /// framework directory. Module *inferFrameworkModule(const DirectoryEntry *FrameworkDir, diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp index f66ff140aa..3acd64587b 100644 --- a/lib/Frontend/ASTUnit.cpp +++ b/lib/Frontend/ASTUnit.cpp @@ -2825,7 +2825,7 @@ const FileEntry *ASTUnit::getPCHFile() { } bool ASTUnit::isModuleFile() { - return isMainFileAST() && ASTFileLangOpts.CompilingModule; + return isMainFileAST() && ASTFileLangOpts.isCompilingModule(); } void ASTUnit::PreambleData::countLines() const { diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index 89907f1171..627134e8dd 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -1026,7 +1026,7 @@ static bool compileModuleImpl(CompilerInstance &ImportingInstance, // Construct a module-generating action. Passing through the module map is // safe because the FileManager is shared between the compiler instances. - GenerateModuleAction CreateModuleAction( + GenerateModuleFromModuleMapAction CreateModuleAction( ModMap.getModuleMapFileForUniquing(Module), Module->IsSystem); ImportingInstance.getDiagnostics().Report(ImportLoc, diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 97c37defa1..3154291d26 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1116,6 +1116,8 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, Opts.ProgramAction = frontend::FixIt; break; case OPT_emit_module: Opts.ProgramAction = frontend::GenerateModule; break; + case OPT_emit_module_interface: + Opts.ProgramAction = frontend::GenerateModuleInterface; break; case OPT_emit_pch: Opts.ProgramAction = frontend::GeneratePCH; break; case OPT_emit_pth: @@ -2259,6 +2261,7 @@ static void ParsePreprocessorOutputArgs(PreprocessorOutputOptions &Opts, case frontend::EmitObj: case frontend::FixIt: case frontend::GenerateModule: + case frontend::GenerateModuleInterface: case frontend::GeneratePCH: case frontend::GeneratePTH: case frontend::ParseSyntaxOnly: diff --git a/lib/Frontend/FrontendActions.cpp b/lib/Frontend/FrontendActions.cpp index d73d46984c..eb91940cbb 100644 --- a/lib/Frontend/FrontendActions.cpp +++ b/lib/Frontend/FrontendActions.cpp @@ -130,13 +130,13 @@ GeneratePCHAction::ComputeASTConsumerArguments(CompilerInstance &CI, std::unique_ptr GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { - std::string Sysroot; - std::string OutputFile; - std::unique_ptr OS = - ComputeASTConsumerArguments(CI, InFile, Sysroot, OutputFile); + std::unique_ptr OS = CreateOutputFile(CI, InFile); if (!OS) return nullptr; + std::string OutputFile = CI.getFrontendOpts().OutputFile; + std::string Sysroot; + auto Buffer = std::make_shared(); std::vector> Consumers; @@ -151,6 +151,23 @@ GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI, return llvm::make_unique(std::move(Consumers)); } +bool GenerateModuleAction::BeginSourceFileAction(CompilerInstance &CI, + StringRef Filename) { + // Set up embedding for any specified files. Do this before we load any + // source files, including the primary module map for the compilation. + for (const auto &F : CI.getFrontendOpts().ModulesEmbedFiles) { + if (const auto *FE = CI.getFileManager().getFile(F, /*openFile*/true)) + CI.getSourceManager().setFileIsTransient(FE); + else + CI.getDiagnostics().Report(diag::err_modules_embed_file_not_found) << F; + } + if (CI.getFrontendOpts().ModulesEmbedAllFiles) + CI.getSourceManager().setAllFilesAreTransient(true); + + return true; +} + + static SmallVectorImpl & operator+=(SmallVectorImpl &Includes, StringRef RHS) { Includes.append(RHS.begin(), RHS.end()); @@ -266,9 +283,12 @@ collectModuleHeaderIncludes(const LangOptions &LangOpts, FileManager &FileMgr, return std::error_code(); } -bool GenerateModuleAction::BeginSourceFileAction(CompilerInstance &CI, - StringRef Filename) { - CI.getLangOpts().CompilingModule = true; +bool GenerateModuleFromModuleMapAction::BeginSourceFileAction( + CompilerInstance &CI, StringRef Filename) { + CI.getLangOpts().setCompilingModule(LangOptions::CMK_ModuleMap); + + if (!GenerateModuleAction::BeginSourceFileAction(CI, Filename)) + return false; // Find the module map file. const FileEntry *ModuleMap = @@ -279,17 +299,6 @@ bool GenerateModuleAction::BeginSourceFileAction(CompilerInstance &CI, return false; } - // Set up embedding for any specified files. Do this before we load any - // source files, including the primary module map for the compilation. - for (const auto &F : CI.getFrontendOpts().ModulesEmbedFiles) { - if (const auto *FE = CI.getFileManager().getFile(F, /*openFile*/true)) - CI.getSourceManager().setFileIsTransient(FE); - else - CI.getDiagnostics().Report(diag::err_modules_embed_file_not_found) << F; - } - if (CI.getFrontendOpts().ModulesEmbedAllFiles) - CI.getSourceManager().setAllFilesAreTransient(true); - // Parse the module map file. HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); if (HS.loadModuleMapFile(ModuleMap, IsSystem)) @@ -381,10 +390,8 @@ bool GenerateModuleAction::BeginSourceFileAction(CompilerInstance &CI, } std::unique_ptr -GenerateModuleAction::ComputeASTConsumerArguments(CompilerInstance &CI, - StringRef InFile, - std::string &Sysroot, - std::string &OutputFile) { +GenerateModuleFromModuleMapAction::CreateOutputFile(CompilerInstance &CI, + StringRef InFile) { // If no output file was provided, figure out where this module would go // in the module cache. if (CI.getFrontendOpts().OutputFile.empty()) { @@ -398,16 +405,28 @@ GenerateModuleAction::ComputeASTConsumerArguments(CompilerInstance &CI, // We use createOutputFile here because this is exposed via libclang, and we // must disable the RemoveFileOnSignal behavior. // We use a temporary to avoid race conditions. - std::unique_ptr OS = - CI.createOutputFile(CI.getFrontendOpts().OutputFile, /*Binary=*/true, - /*RemoveFileOnSignal=*/false, InFile, - /*Extension=*/"", /*useTemporary=*/true, - /*CreateMissingDirectories=*/true); - if (!OS) - return nullptr; + return CI.createOutputFile(CI.getFrontendOpts().OutputFile, /*Binary=*/true, + /*RemoveFileOnSignal=*/false, InFile, + /*Extension=*/"", /*useTemporary=*/true, + /*CreateMissingDirectories=*/true); +} - OutputFile = CI.getFrontendOpts().OutputFile; - return OS; +bool GenerateModuleInterfaceAction::BeginSourceFileAction(CompilerInstance &CI, + StringRef Filename) { + if (!CI.getLangOpts().ModulesTS) { + CI.getDiagnostics().Report(diag::err_module_interface_requires_modules_ts); + return false; + } + + CI.getLangOpts().setCompilingModule(LangOptions::CMK_ModuleInterface); + + return GenerateModuleAction::BeginSourceFileAction(CI, Filename); +} + +std::unique_ptr +GenerateModuleInterfaceAction::CreateOutputFile(CompilerInstance &CI, + StringRef InFile) { + return CI.createDefaultOutputFile(/*Binary=*/true, InFile, "pcm"); } SyntaxOnlyAction::~SyntaxOnlyAction() { diff --git a/lib/Frontend/FrontendOptions.cpp b/lib/Frontend/FrontendOptions.cpp index 9ede674e47..6a82084aff 100644 --- a/lib/Frontend/FrontendOptions.cpp +++ b/lib/Frontend/FrontendOptions.cpp @@ -25,6 +25,8 @@ InputKind FrontendOptions::getInputKindForExtension(StringRef Extension) { .Case("mii", IK_PreprocessedObjCXX) .Cases("C", "cc", "cp", IK_CXX) .Cases("cpp", "CPP", "c++", "cxx", "hpp", IK_CXX) + .Case("cppm", IK_CXX) + .Case("iim", IK_PreprocessedCXX) .Case("cl", IK_OpenCL) .Case("cu", IK_CUDA) .Cases("ll", "bc", IK_LLVM_IR) diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp index 13cb52aa1e..187a6e7624 100644 --- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -52,7 +52,10 @@ CreateFrontendBaseAction(CompilerInstance &CI) { case EmitCodeGenOnly: return llvm::make_unique(); case EmitObj: return llvm::make_unique(); case FixIt: return llvm::make_unique(); - case GenerateModule: return llvm::make_unique(); + case GenerateModule: + return llvm::make_unique(); + case GenerateModuleInterface: + 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 3e3215dee8..50eb6f82c2 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -558,6 +558,25 @@ ModuleMap::findOrCreateModule(StringRef Name, Module *Parent, bool IsFramework, return std::make_pair(Result, true); } +Module *ModuleMap::createModuleForInterfaceUnit(SourceLocation Loc, + StringRef Name) { + assert(LangOpts.CurrentModule == Name && "module name mismatch"); + assert(!Modules[Name] && "redefining existing module"); + + auto *Result = + new Module(Name, Loc, nullptr, /*IsFramework*/ false, + /*IsExplicit*/ false, NumCreatedModules++); + Modules[Name] = SourceModule = Result; + + // Mark the main source file as being within the newly-created module so that + // declarations and macros are properly visibility-restricted to it. + auto *MainFile = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()); + assert(MainFile && "no input file for module interface"); + Headers[MainFile].push_back(KnownHeader(Result, PrivateHeader)); + + return Result; +} + /// \brief For a framework module, infer the framework against which we /// should link. static void inferFrameworkLink(Module *Mod, const DirectoryEntry *FrameworkDir, @@ -805,7 +824,7 @@ void ModuleMap::addHeader(Module *Mod, Module::Header Header, Mod->Headers[headerRoleToKind(Role)].push_back(std::move(Header)); bool isCompilingModuleHeader = - LangOpts.CompilingModule && Mod->getTopLevelModule() == SourceModule; + LangOpts.isCompilingModule() && Mod->getTopLevelModule() == SourceModule; if (!Imported || isCompilingModuleHeader) { // When we import HeaderFileInfo, the external source is expected to // set the isModuleHeader flag itself. diff --git a/lib/Lex/PPLexerChange.cpp b/lib/Lex/PPLexerChange.cpp index e2eceafd98..33d690de8b 100644 --- a/lib/Lex/PPLexerChange.cpp +++ b/lib/Lex/PPLexerChange.cpp @@ -685,7 +685,7 @@ bool Preprocessor::needModuleMacros() const { return true; // Otherwise, we only need module macros if we're actually compiling a module // interface. - return getLangOpts().CompilingModule; + return getLangOpts().isCompilingModule(); } void Preprocessor::LeaveSubmodule() { diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index a201838e81..3566c3429f 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -1795,7 +1795,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { [this](Token &Tok, bool &HasLexedNextToken) -> int { IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this, diag::err_expected_id_building_module); - return getLangOpts().CompilingModule && II && + return getLangOpts().isCompilingModule() && II && (II->getName() == getLangOpts().CurrentModule); }); } else if (II == Ident__MODULE__) { diff --git a/lib/Lex/Preprocessor.cpp b/lib/Lex/Preprocessor.cpp index 096a6107a9..1f28b3b268 100644 --- a/lib/Lex/Preprocessor.cpp +++ b/lib/Lex/Preprocessor.cpp @@ -480,7 +480,7 @@ void Preprocessor::CreateString(StringRef Str, Token &Tok, } Module *Preprocessor::getCurrentModule() { - if (!getLangOpts().CompilingModule) + if (!getLangOpts().isCompilingModule()) return nullptr; return getHeaderSearchInfo().lookupModule(getLangOpts().CurrentModule); @@ -795,6 +795,23 @@ void Preprocessor::LexAfterModuleImport(Token &Result) { // If we have a non-empty module path, load the named module. if (!ModuleImportPath.empty()) { + // Under the Modules TS, the dot is just part of the module name, and not + // a real hierarachy separator. Flatten such module names now. + // + // FIXME: Is this the right level to be performing this transformation? + std::string FlatModuleName; + if (getLangOpts().ModulesTS) { + for (auto &Piece : ModuleImportPath) { + if (!FlatModuleName.empty()) + FlatModuleName += "."; + FlatModuleName += Piece.first->getName(); + } + SourceLocation FirstPathLoc = ModuleImportPath[0].second; + ModuleImportPath.clear(); + ModuleImportPath.push_back( + std::make_pair(getIdentifierInfo(FlatModuleName), FirstPathLoc)); + } + Module *Imported = nullptr; if (getLangOpts().Modules) { Imported = TheModuleLoader.loadModule(ModuleImportLoc, diff --git a/lib/Parse/ParseAST.cpp b/lib/Parse/ParseAST.cpp index bab3dbefb0..d018d4c08e 100644 --- a/lib/Parse/ParseAST.cpp +++ b/lib/Parse/ParseAST.cpp @@ -138,26 +138,18 @@ void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) { S.getPreprocessor().EnterMainSourceFile(); P.Initialize(); - // C11 6.9p1 says translation units must have at least one top-level - // declaration. C++ doesn't have this restriction. We also don't want to - // complain if we have a precompiled header, although technically if the PCH - // is empty we should still emit the (pedantic) diagnostic. Parser::DeclGroupPtrTy ADecl; ExternalASTSource *External = S.getASTContext().getExternalSource(); if (External) External->StartTranslationUnit(Consumer); - if (P.ParseFirstTopLevelDecl(ADecl)) { - if (!External && !S.getLangOpts().CPlusPlus) - P.Diag(diag::ext_empty_translation_unit); - } else { - do { - // If we got a null return and something *was* parsed, ignore it. This - // is due to a top-level semicolon, an action override, or a parse error - // skipping something. - if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get())) - return; - } while (!P.ParseTopLevelDecl(ADecl)); + for (bool AtEOF = P.ParseFirstTopLevelDecl(ADecl); !AtEOF; + AtEOF = P.ParseTopLevelDecl(ADecl)) { + // If we got a null return and something *was* parsed, ignore it. This + // is due to a top-level semicolon, an action override, or a parse error + // skipping something. + if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get())) + return; } // Process any TopLevelDecls generated by #pragma weak. diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index a391363ff3..e58e3f7c20 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -544,11 +544,21 @@ bool Parser::ParseFirstTopLevelDecl(DeclGroupPtrTy &Result) { if (Tok.is(tok::kw_module)) { Result = ParseModuleDecl(); return false; + } else if (getLangOpts().getCompilingModule() == + LangOptions::CMK_ModuleInterface) { + Diag(Tok, diag::err_expected_module_interface_decl); } - // FIXME: If we're parsing a module interface and we don't have a module - // declaration here, diagnose. - return ParseTopLevelDecl(Result); + // C11 6.9p1 says translation units must have at least one top-level + // declaration. C++ doesn't have this restriction. We also don't want to + // complain if we have a precompiled header, although technically if the PCH + // is empty we should still emit the (pedantic) diagnostic. + bool NoTopLevelDecls = ParseTopLevelDecl(Result); + if (NoTopLevelDecls && !Actions.getASTContext().getExternalSource() && + !getLangOpts().CPlusPlus) + Diag(diag::ext_empty_translation_unit); + + return NoTopLevelDecls; } /// ParseTopLevelDecl - Parse one top-level declaration, return whatever the @@ -820,6 +830,11 @@ Parser::ParseExternalDeclaration(ParsedAttributesWithRange &attrs, ParseMicrosoftIfExistsExternalDeclaration(); return nullptr; + case tok::kw_module: + Diag(Tok, diag::err_unexpected_module_decl); + SkipUntil(tok::semi); + return nullptr; + default: dont_know: // We can't tell whether this is a function-definition or declaration yet. diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 9122030e16..c756d1f6ab 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -15180,48 +15180,84 @@ void Sema::diagnoseMisplacedModuleImport(Module *M, SourceLocation ImportLoc) { Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation ModuleLoc, ModuleDeclKind MDK, ModuleIdPath Path) { - // We should see 'module implementation' if and only if we are not compiling - // a module interface. - if (getLangOpts().CompilingModule == - (MDK == ModuleDeclKind::Implementation)) { + // 'module implementation' requires that we are not compiling a module of any + // kind. 'module' and 'module partition' require that we are compiling a + // module inteface (not a module map). + auto CMK = getLangOpts().getCompilingModule(); + if (MDK == ModuleDeclKind::Implementation + ? CMK != LangOptions::CMK_None + : CMK != LangOptions::CMK_ModuleInterface) { Diag(ModuleLoc, diag::err_module_interface_implementation_mismatch) << (unsigned)MDK; return nullptr; } // FIXME: Create a ModuleDecl and return it. - // FIXME: Teach the lexer to handle this declaration too. + + // FIXME: Most of this work should be done by the preprocessor rather than + // here, in case we look ahead across something where the current + // module matters (eg a #include). + + // The dots in a module name in the Modules TS are a lie. Unlike Clang's + // hierarchical module map modules, the dots here are just another character + // that can appear in a module name. Flatten down to the actual module name. + std::string ModuleName; + for (auto &Piece : Path) { + if (!ModuleName.empty()) + ModuleName += "."; + ModuleName += Piece.first->getName(); + } + + // If a module name was explicitly specified on the command line, it must be + // correct. + if (!getLangOpts().CurrentModule.empty() && + getLangOpts().CurrentModule != ModuleName) { + Diag(Path.front().second, diag::err_current_module_name_mismatch) + << SourceRange(Path.front().second, Path.back().second) + << getLangOpts().CurrentModule; + return nullptr; + } + const_cast(getLangOpts()).CurrentModule = ModuleName; + + auto &Map = PP.getHeaderSearchInfo().getModuleMap(); switch (MDK) { - case ModuleDeclKind::Module: + case ModuleDeclKind::Module: { // FIXME: Check we're not in a submodule. - // FIXME: Set CurrentModule and create a corresponding Module object. + + // We can't have imported a definition of this module or parsed a module + // map defining it already. + if (auto *M = Map.findModule(ModuleName)) { + Diag(Path[0].second, diag::err_module_redefinition) << ModuleName; + if (M->DefinitionLoc.isValid()) + Diag(M->DefinitionLoc, diag::note_prev_module_definition); + else if (const auto *FE = M->getASTFile()) + Diag(M->DefinitionLoc, diag::note_prev_module_definition_from_ast_file) + << FE->getName(); + return nullptr; + } + + // Create a Module for the module that we're defining. + Module *Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName); + assert(Mod && "module creation should not fail"); + + // Enter the semantic scope of the module. + ActOnModuleBegin(ModuleLoc, Mod); return nullptr; + } case ModuleDeclKind::Partition: // FIXME: Check we are in a submodule of the named module. return nullptr; case ModuleDeclKind::Implementation: - DeclResult Import = ActOnModuleImport(ModuleLoc, ModuleLoc, Path); + std::pair ModuleNameLoc( + PP.getIdentifierInfo(ModuleName), Path[0].second); + + DeclResult Import = ActOnModuleImport(ModuleLoc, ModuleLoc, ModuleNameLoc); if (Import.isInvalid()) return nullptr; - ImportDecl *ID = cast(Import.get()); - - // The current module is whatever we just loaded. - // - // FIXME: We should probably do this from the lexer rather than waiting - // until now, in case we look ahead across something where the current - // module matters (eg a #include). - auto Name = ID->getImportedModule()->getTopLevelModuleName(); - if (!getLangOpts().CurrentModule.empty() && - getLangOpts().CurrentModule != Name) { - Diag(Path.front().second, diag::err_current_module_name_mismatch) - << SourceRange(Path.front().second, Path.back().second) - << getLangOpts().CurrentModule; - } - const_cast(getLangOpts()).CurrentModule = Name; - return ConvertDeclToDeclGroup(ID); + return ConvertDeclToDeclGroup(Import.get()); } llvm_unreachable("unexpected module decl kind"); @@ -15246,8 +15282,8 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, // Import-from-implementation is valid in the Modules TS. FIXME: Should we // warn on a redundant import of the current module? if (Mod->getTopLevelModuleName() == getLangOpts().CurrentModule && - (getLangOpts().CompilingModule || !getLangOpts().ModulesTS)) - Diag(ImportLoc, getLangOpts().CompilingModule + (getLangOpts().isCompilingModule() || !getLangOpts().ModulesTS)) + Diag(ImportLoc, getLangOpts().isCompilingModule() ? diag::err_module_self_import : diag::err_module_import_in_implementation) << Mod->getFullModuleName() << getLangOpts().CurrentModule; diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index 9750d53743..3237cd0052 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -3217,7 +3217,7 @@ void Sema::addMethodToGlobalList(ObjCMethodList *List, ObjCMethodList *ListWithSameDeclaration = nullptr; for (; List; Previous = List, List = List->getNext()) { // If we are building a module, keep all of the methods. - if (getLangOpts().CompilingModule) + if (getLangOpts().isCompilingModule()) continue; bool SameDeclaration = MatchTwoMethodDeclarations(Method, diff --git a/lib/Serialization/GeneratePCH.cpp b/lib/Serialization/GeneratePCH.cpp index b2d79a2087..e1765dafd9 100644 --- a/lib/Serialization/GeneratePCH.cpp +++ b/lib/Serialization/GeneratePCH.cpp @@ -46,10 +46,13 @@ void PCHGenerator::HandleTranslationUnit(ASTContext &Ctx) { return; Module *Module = nullptr; - if (PP.getLangOpts().CompilingModule) { + if (PP.getLangOpts().isCompilingModule()) { Module = PP.getHeaderSearchInfo().lookupModule( PP.getLangOpts().CurrentModule, /*AllowSearch*/ false); - assert(Module && "emitting module but current module doesn't exist"); + if (!Module) { + assert(hasErrors && "emitting module but current module doesn't exist"); + return; + } } // Emit the PCH file to the Buffer. diff --git a/test/Parser/cxx-modules-import.cpp b/test/Parser/cxx-modules-import.cpp index cd4174eabd..0600eddecb 100644 --- a/test/Parser/cxx-modules-import.cpp +++ b/test/Parser/cxx-modules-import.cpp @@ -1,22 +1,26 @@ // RUN: rm -rf %t // RUN: mkdir -p %t -// RUN: echo 'int a, b;' > %t/x.h -// RUN: echo 'module x { header "x.h" module y {} } module z {}' > %t/map -// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \ +// RUN: echo 'module x; int a, b;' > %t/x.cppm +// RUN: echo 'module x.y; int c;' > %t/x.y.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/x.y.cppm -o %t/x.y.pcm +// +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ // RUN: -DTEST=1 -DMODULE_KIND=implementation -DMODULE_NAME=z -// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \ +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ // RUN: -DTEST=2 -DMODULE_KIND=implementation -DMODULE_NAME=x -// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \ +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ // RUN: -DTEST=3 -DMODULE_KIND= -DMODULE_NAME=z -// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \ +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ // RUN: -DTEST=4 -DMODULE_KIND=partition -DMODULE_NAME=z -// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \ +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ // RUN: -DTEST=5 -DMODULE_KIND=elderberry -DMODULE_NAME=z -// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \ +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ // RUN: -DTEST=1 -DMODULE_KIND=implementation -DMODULE_NAME='z [[]]' -// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \ +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ // RUN: -DTEST=6 -DMODULE_KIND=implementation -DMODULE_NAME='z [[fancy]]' -// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \ +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ // RUN: -DTEST=7 -DMODULE_KIND=implementation -DMODULE_NAME='z [[maybe_unused]]' module MODULE_KIND MODULE_NAME; @@ -34,7 +38,8 @@ module MODULE_KIND MODULE_NAME; int use_1 = a; #if TEST != 2 -// expected-error@-2 {{undeclared}} +// expected-error@-2 {{declaration of 'a' must be imported from module 'x' before it is required}} +// expected-note@x.cppm:1 {{here}} #endif import x; diff --git a/test/Parser/cxx-modules-interface.cppm b/test/Parser/cxx-modules-interface.cppm new file mode 100644 index 0000000000..88747714e5 --- /dev/null +++ b/test/Parser/cxx-modules-interface.cppm @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface %s -o %t.pcm -verify -DTEST=0 +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface %s -o %t.pcm -verify -Dmodule=int -DTEST=1 +// RUN: not %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface %s -fmodule-file=%t.pcm -o %t.pcm -DTEST=2 2>&1 | FileCheck %s --check-prefix=CHECK-2 +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface %s -fmodule-file=%t.pcm -o %t.pcm -verify -Dfoo=bar -DTEST=3 + +#if TEST == 0 +// expected-no-diagnostics +#endif + +module foo; +#if TEST == 1 +// expected-error@-2 {{expected module declaration at start of module interface}} +#elif TEST == 2 +// CHECK-2: error: redefinition of module 'foo' +#endif + +int n; +#if TEST == 3 +// expected-error@-2 {{redefinition of 'n'}} +// expected-note@-3 {{previous}} +#endif diff --git a/test/lit.cfg b/test/lit.cfg index e7ce8fac7c..497058b58b 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -44,7 +44,7 @@ else: config.test_format = lit.formats.ShTest(execute_external) # suffixes: A list of file extensions to treat as test files. -config.suffixes = ['.c', '.cpp', '.m', '.mm', '.cu', '.ll', '.cl', '.s', '.S', '.modulemap', '.test', '.rs'] +config.suffixes = ['.c', '.cpp', '.cppm', '.m', '.mm', '.cu', '.ll', '.cl', '.s', '.S', '.modulemap', '.test', '.rs'] # excludes: A list of directories to exclude from the testsuite. The 'Inputs' # subdirectories contain auxiliary inputs for various tests in their parent