diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index 6f27c4a9bb1..f4a7f246ab5 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -385,6 +385,8 @@ def fno_dllexport_inlines : Flag<["-"], "fno-dllexport-inlines">; def sys_header_deps : Flag<["-"], "sys-header-deps">, HelpText<"Include system headers in dependency output">; +def skip_unused_modulemap_file_deps : Flag<["-"], "skip-unused-modulemap-deps">, + HelpText<"Include module map files only for imported modules in dependency output">; def module_file_deps : Flag<["-"], "module-file-deps">, HelpText<"Include module files in dependency output">; def header_include_file : Separate<["-"], "header-include-file">, diff --git a/include/clang/Frontend/DependencyOutputOptions.h b/include/clang/Frontend/DependencyOutputOptions.h index f419d264364..e6d1df148d7 100644 --- a/include/clang/Frontend/DependencyOutputOptions.h +++ b/include/clang/Frontend/DependencyOutputOptions.h @@ -32,6 +32,7 @@ class DependencyOutputOptions { /// problems. unsigned AddMissingHeaderDeps : 1; ///< Add missing headers to dependency list unsigned IncludeModuleFiles : 1; ///< Include module file dependencies. + unsigned SkipUnusedModuleMaps : 1; ///< Skip unused module map dependencies. /// Destination of cl.exe style /showIncludes info. ShowIncludesDestination ShowIncludesDest = ShowIncludesDestination::None; @@ -67,7 +68,7 @@ class DependencyOutputOptions { public: DependencyOutputOptions() : IncludeSystemHeaders(0), ShowHeaderIncludes(0), UsePhonyTargets(0), - AddMissingHeaderDeps(0), IncludeModuleFiles(0) {} + AddMissingHeaderDeps(0), IncludeModuleFiles(0), SkipUnusedModuleMaps(0) {} }; } // end namespace clang diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h index 0f8abb3bacf..a8cf5c80a4a 100644 --- a/include/clang/Lex/ModuleMap.h +++ b/include/clang/Lex/ModuleMap.h @@ -59,6 +59,14 @@ class ModuleMapCallbacks { virtual void moduleMapFileRead(SourceLocation FileStart, const FileEntry &File, bool IsSystem) {} + /// Called when a module map file matches a module lookup + /// + /// \param File The file itself. + /// \param M The module found that matches this module map. + /// \param IsSystem Whether this is a module map from a system include path. + virtual void moduleMapFoundForModule(const FileEntry &File, const Module *M, + bool IsSystem) {} + /// Called when a header is added during module map parsing. /// /// \param Filename The header file itself. diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index a5a55d642ac..aa8ccb5048a 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -536,13 +536,13 @@ class Sema { /// element type here is ExprWithCleanups::Object. SmallVector ExprCleanupObjects; - /// Store a list of either DeclRefExprs or MemberExprs - /// that contain a reference to a variable (constant) that may or may not - /// be odr-used in this Expr, and we won't know until all lvalue-to-rvalue - /// and discarded value conversions have been applied to all subexpressions - /// of the enclosing full expression. This is cleared at the end of each - /// full expression. - llvm::SmallPtrSet MaybeODRUseExprs; + /// Store a set of either DeclRefExprs or MemberExprs that contain a reference + /// to a variable (constant) that may or may not be odr-used in this Expr, and + /// we won't know until all lvalue-to-rvalue and discarded value conversions + /// have been applied to all subexpressions of the enclosing full expression. + /// This is cleared at the end of each full expression. + using MaybeODRUseExprSet = llvm::SmallPtrSet; + MaybeODRUseExprSet MaybeODRUseExprs; std::unique_ptr PreallocatedFunctionScope; @@ -982,7 +982,7 @@ class Sema { /// context (i.e. the number of TypoExprs created). unsigned NumTypos; - llvm::SmallPtrSet SavedMaybeODRUseExprs; + MaybeODRUseExprSet SavedMaybeODRUseExprs; /// The lambdas that are present within this context, if it /// is indeed an unevaluated context. diff --git a/lib/Analysis/ThreadSafetyCommon.cpp b/lib/Analysis/ThreadSafetyCommon.cpp index 14d1d9c7a8f..24d450b62b5 100644 --- a/lib/Analysis/ThreadSafetyCommon.cpp +++ b/lib/Analysis/ThreadSafetyCommon.cpp @@ -277,18 +277,23 @@ til::SExpr *SExprBuilder::translateDeclRefExpr(const DeclRefExpr *DRE, // Function parameters require substitution and/or renaming. if (const auto *PV = dyn_cast_or_null(VD)) { - const auto *FD = - cast(PV->getDeclContext())->getCanonicalDecl(); unsigned I = PV->getFunctionScopeIndex(); - - if (Ctx && Ctx->FunArgs && FD == Ctx->AttrDecl->getCanonicalDecl()) { - // Substitute call arguments for references to function parameters - assert(I < Ctx->NumArgs); - return translate(Ctx->FunArgs[I], Ctx->Prev); + const DeclContext *D = PV->getDeclContext(); + if (Ctx && Ctx->FunArgs) { + const Decl *Canonical = Ctx->AttrDecl->getCanonicalDecl(); + if (isa(D) + ? (cast(D)->getCanonicalDecl() == Canonical) + : (cast(D)->getCanonicalDecl() == Canonical)) { + // Substitute call arguments for references to function parameters + assert(I < Ctx->NumArgs); + return translate(Ctx->FunArgs[I], Ctx->Prev); + } } // Map the param back to the param of the original function declaration // for consistent comparisons. - VD = FD->getParamDecl(I); + VD = isa(D) + ? cast(D)->getCanonicalDecl()->getParamDecl(I) + : cast(D)->getCanonicalDecl()->getParamDecl(I); } // For non-local variables, treat it as a reference to a named object. diff --git a/lib/Driver/ToolChains/Clang.cpp b/lib/Driver/ToolChains/Clang.cpp index b152793a67f..b1e63241bcd 100644 --- a/lib/Driver/ToolChains/Clang.cpp +++ b/lib/Driver/ToolChains/Clang.cpp @@ -1043,6 +1043,7 @@ void Clang::AddPreprocessingOptions(Compilation &C, const JobAction &JA, } CmdArgs.push_back("-dependency-file"); CmdArgs.push_back(DepFile); + CmdArgs.push_back("-skip-unused-modulemap-deps"); // Add a default target if one wasn't specified. if (!Args.hasArg(options::OPT_MT) && !Args.hasArg(options::OPT_MQ)) { diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 534b8b3f545..790a53e439d 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1337,6 +1337,7 @@ static void ParseDependencyOutputArgs(DependencyOutputOptions &Opts, Opts.Targets = Args.getAllArgValues(OPT_MT); Opts.IncludeSystemHeaders = Args.hasArg(OPT_sys_header_deps); Opts.IncludeModuleFiles = Args.hasArg(OPT_module_file_deps); + Opts.SkipUnusedModuleMaps = Args.hasArg(OPT_skip_unused_modulemap_file_deps); Opts.UsePhonyTargets = Args.hasArg(OPT_MP); Opts.ShowHeaderIncludes = Args.hasArg(OPT_H); Opts.HeaderIncludeOutputFile = Args.getLastArgValue(OPT_header_include_file); diff --git a/lib/Frontend/DependencyFile.cpp b/lib/Frontend/DependencyFile.cpp index a03d4b79c8b..5b433c6788f 100644 --- a/lib/Frontend/DependencyFile.cpp +++ b/lib/Frontend/DependencyFile.cpp @@ -161,6 +161,7 @@ class DFGImpl : public PPCallbacks { bool AddMissingHeaderDeps; bool SeenMissingHeader; bool IncludeModuleFiles; + bool SkipUnusedModuleMaps; DependencyOutputFormat OutputFormat; unsigned InputFileIndex; @@ -177,6 +178,7 @@ class DFGImpl : public PPCallbacks { AddMissingHeaderDeps(Opts.AddMissingHeaderDeps), SeenMissingHeader(false), IncludeModuleFiles(Opts.IncludeModuleFiles), + SkipUnusedModuleMaps(Opts.SkipUnusedModuleMaps), OutputFormat(Opts.OutputFormat), InputFileIndex(0) { for (const auto &ExtraDep : Opts.ExtraDeps) { @@ -210,6 +212,7 @@ class DFGImpl : public PPCallbacks { bool AddFilename(StringRef Filename); bool includeSystemHeaders() const { return IncludeSystemHeaders; } bool includeModuleFiles() const { return IncludeModuleFiles; } + bool skipUnusedModuleMaps() const { return SkipUnusedModuleMaps; } }; class DFGMMCallback : public ModuleMapCallbacks { @@ -218,9 +221,17 @@ class DFGMMCallback : public ModuleMapCallbacks { DFGMMCallback(DFGImpl &Parent) : Parent(Parent) {} void moduleMapFileRead(SourceLocation Loc, const FileEntry &Entry, bool IsSystem) override { + if (Parent.skipUnusedModuleMaps()) + return; if (!IsSystem || Parent.includeSystemHeaders()) Parent.AddFilename(Entry.getName()); } + void moduleMapFoundForModule(const FileEntry &Entry, const Module *M, + bool IsSystem) override { + if (Parent.skipUnusedModuleMaps() && + (!IsSystem || Parent.includeSystemHeaders())) + Parent.AddFilename(Entry.getName()); + } }; class DFGASTReaderListener : public ASTReaderListener { diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index 6baf0548eb2..582dae1f58e 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -764,8 +764,17 @@ ModuleMap::isHeaderUnavailableInModule(const FileEntry *Header, Module *ModuleMap::findModule(StringRef Name) const { llvm::StringMap::const_iterator Known = Modules.find(Name); - if (Known != Modules.end()) - return Known->getValue(); + if (Known != Modules.end()) { + Module *M = Known->getValue(); + // Notify callbacks that we found a module map for the module. + if (!M->DefinitionLoc.isInvalid()) + for (const auto &Cb : Callbacks) + Cb->moduleMapFoundForModule( + *getContainingModuleMapFile(M), M, + SourceMgr.getFileCharacteristic(M->DefinitionLoc) == + SrcMgr::C_System_ModuleMap); + return M; + } return nullptr; } diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index c123c552f32..9d64507e546 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -15604,7 +15604,12 @@ ExprResult Sema::ActOnConstantExpression(ExprResult Res) { } void Sema::CleanupVarDeclMarking() { - for (Expr *E : MaybeODRUseExprs) { + // Iterate through a local copy in case MarkVarDeclODRUsed makes a recursive + // call. + MaybeODRUseExprSet LocalMaybeODRUseExprs; + std::swap(LocalMaybeODRUseExprs, MaybeODRUseExprs); + + for (Expr *E : LocalMaybeODRUseExprs) { VarDecl *Var; SourceLocation Loc; if (DeclRefExpr *DRE = dyn_cast(E)) { @@ -15621,10 +15626,10 @@ void Sema::CleanupVarDeclMarking() { /*MaxFunctionScopeIndex Pointer*/ nullptr); } - MaybeODRUseExprs.clear(); + assert(MaybeODRUseExprs.empty() && + "MarkVarDeclODRUsed failed to cleanup MaybeODRUseExprs?"); } - static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc, VarDecl *Var, Expr *E) { assert((!E || isa(E) || isa(E)) && diff --git a/test/Modules/Inputs/dependency-skip-unused/module.modulemap b/test/Modules/Inputs/dependency-skip-unused/module.modulemap new file mode 100644 index 00000000000..e50630d3a18 --- /dev/null +++ b/test/Modules/Inputs/dependency-skip-unused/module.modulemap @@ -0,0 +1 @@ +module A {} diff --git a/test/Modules/Inputs/dependency-skip-unused/x/module.modulemap b/test/Modules/Inputs/dependency-skip-unused/x/module.modulemap new file mode 100644 index 00000000000..a8ecb09390a --- /dev/null +++ b/test/Modules/Inputs/dependency-skip-unused/x/module.modulemap @@ -0,0 +1 @@ +module X {} diff --git a/test/Modules/Inputs/dependency-skip-unused/y/module.modulemap b/test/Modules/Inputs/dependency-skip-unused/y/module.modulemap new file mode 100644 index 00000000000..e6830bc2b4f --- /dev/null +++ b/test/Modules/Inputs/dependency-skip-unused/y/module.modulemap @@ -0,0 +1 @@ +module Y {} diff --git a/test/Modules/dependency-skip-unused-modulemaps.m b/test/Modules/dependency-skip-unused-modulemaps.m new file mode 100644 index 00000000000..fb9a049f9c8 --- /dev/null +++ b/test/Modules/dependency-skip-unused-modulemaps.m @@ -0,0 +1,10 @@ +// RUN: rm -rf %t.cache %t.d %t.cmd +// RUN: %clang_cc1 -fsyntax-only -fmodules -fimplicit-module-maps -fmodules-cache-path=%t.cache -dependency-file %t.d -MT dependencies -I%S/Inputs/dependency-skip-unused/x -I%S/Inputs/dependency-skip-unused/y -I%S/Inputs/dependency-skip-unused -skip-unused-modulemap-deps %s +// RUN: FileCheck %s < %t.d +// CHECK-NOT: dependency-skip-unused{{.}}x{{.}}module.modulemap +// CHECK-NOT: dependency-skip-unused{{.}}y{{.}}module.modulemap + +// RUN: %clang -fsyntax-only -fmodules -fimplicit-module-maps -fmodules-cache-path=%t.cache -MMD -MT dependencies -MF %t.d -I%S/Inputs/dependency-skip-unused/x -I%S/Inputs/dependency-skip-unused/y -I%S/Inputs/dependency-skip-unused %s -### 2>&1 | FileCheck -check-prefix=CC1_INV %s +// CC1_INV: -skip-unused-modulemap-deps + +@import A; diff --git a/test/SemaCXX/blocks.cpp b/test/SemaCXX/blocks.cpp index 0521802172f..aacf63cfab4 100644 --- a/test/SemaCXX/blocks.cpp +++ b/test/SemaCXX/blocks.cpp @@ -145,3 +145,11 @@ namespace test6c { A::foo(); }); } } + +namespace test7 { +struct S {}; +void f() { + constexpr S s; + auto some_block = ^{ (void)s; }; +} +} diff --git a/test/SemaObjCXX/no-crash-thread-safety-analysis.mm b/test/SemaObjCXX/no-crash-thread-safety-analysis.mm new file mode 100644 index 00000000000..6abd391dc2e --- /dev/null +++ b/test/SemaObjCXX/no-crash-thread-safety-analysis.mm @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fsyntax-only -Wthread-safety -Wno-objc-root-class %s + +// Thread safety analysis used to crash when used with ObjC methods. + +#include "thread-safety-analysis.h" + +@interface MyInterface +- (void)doIt:(class Lock *)myLock; +@end + +@implementation MyInterface +- (void)doIt:(class Lock *)myLock { + AutoLock lock(*myLock); +} +@end diff --git a/test/SemaObjCXX/thread-safety-analysis.h b/test/SemaObjCXX/thread-safety-analysis.h new file mode 100644 index 00000000000..f657b7e50a9 --- /dev/null +++ b/test/SemaObjCXX/thread-safety-analysis.h @@ -0,0 +1,17 @@ +class __attribute__((lockable)) Lock { +public: + void Acquire() __attribute__((exclusive_lock_function())) {} + void Release() __attribute__((unlock_function())) {} +}; + +class __attribute__((scoped_lockable)) AutoLock { +public: + AutoLock(Lock &lock) __attribute__((exclusive_lock_function(lock))) + : lock_(lock) { + lock.Acquire(); + } + ~AutoLock() __attribute__((unlock_function())) { lock_.Release(); } + +private: + Lock &lock_; +}; diff --git a/test/SemaObjCXX/warn-thread-safety-analysis.mm b/test/SemaObjCXX/warn-thread-safety-analysis.mm index 262ab7d108a..a50c6f2ee13 100644 --- a/test/SemaObjCXX/warn-thread-safety-analysis.mm +++ b/test/SemaObjCXX/warn-thread-safety-analysis.mm @@ -1,22 +1,6 @@ // RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety -Wthread-safety-beta -Wno-objc-root-class %s -class __attribute__((lockable)) Lock { -public: - void Acquire() __attribute__((exclusive_lock_function())) {} - void Release() __attribute__((unlock_function())) {} -}; - -class __attribute__((scoped_lockable)) AutoLock { -public: - AutoLock(Lock &lock) __attribute__((exclusive_lock_function(lock))) - : lock_(lock) { - lock.Acquire(); - } - ~AutoLock() __attribute__((unlock_function())) { lock_.Release(); } - -private: - Lock &lock_; -}; +#include "thread-safety-analysis.h" @interface MyInterface { @private