diff --git a/clang-tools-extra/clang-tidy/CMakeLists.txt b/clang-tools-extra/clang-tidy/CMakeLists.txt index 774915f3cc451..6dadb27177119 100644 --- a/clang-tools-extra/clang-tidy/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/CMakeLists.txt @@ -37,12 +37,15 @@ if(CLANG_ENABLE_STATIC_ANALYZER) ) endif() +# Checks. +# If you add a check, also add it to ClangTidyForceLinker.h in this directory. add_subdirectory(android) add_subdirectory(abseil) add_subdirectory(boost) add_subdirectory(bugprone) add_subdirectory(cert) add_subdirectory(cppcoreguidelines) +add_subdirectory(darwin) add_subdirectory(fuchsia) add_subdirectory(google) add_subdirectory(hicpp) @@ -56,9 +59,38 @@ endif() add_subdirectory(objc) add_subdirectory(openmp) add_subdirectory(performance) -add_subdirectory(plugin) add_subdirectory(portability) add_subdirectory(readability) +add_subdirectory(zircon) +set(ALL_CLANG_TIDY_CHECKS + clangTidyAndroidModule + clangTidyAbseilModule + clangTidyBoostModule + clangTidyBugproneModule + clangTidyCERTModule + clangTidyCppCoreGuidelinesModule + clangTidyDarwinModule + clangTidyFuchsiaModule + clangTidyGoogleModule + clangTidyHICPPModule + clangTidyLinuxKernelModule + clangTidyLLVMModule + clangTidyMiscModule + clangTidyModernizeModule + clangTidyObjCModule + clangTidyOpenMPModule + clangTidyPerformanceModule + clangTidyPortabilityModule + clangTidyReadabilityModule + clangTidyZirconModule + ) +if(CLANG_ENABLE_STATIC_ANALYZER) + list(APPEND ALL_CLANG_TIDY_CHECKS clangTidyMPIModule) +endif() +set(ALL_CLANG_TIDY_CHECKS ${ALL_CLANG_TIDY_CHECKS} PARENT_SCOPE) + +# Other subtargets. These may reference ALL_CLANG_TIDY_CHECKS +# and must be below its definition. +add_subdirectory(plugin) add_subdirectory(tool) add_subdirectory(utils) -add_subdirectory(zircon) diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp index 4c4745f810247..21984aea0e856 100644 --- a/clang-tools-extra/clang-tidy/ClangTidy.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp @@ -384,8 +384,8 @@ ClangTidyASTConsumerFactory::CreateASTConsumer( if (WorkingDir) Context.setCurrentBuildDirectory(WorkingDir.get()); - std::vector> Checks; - CheckFactories->createChecks(&Context, Checks); + std::vector> Checks = + CheckFactories->createChecks(&Context); ast_matchers::MatchFinder::MatchFinderOptions FinderOptions; @@ -459,8 +459,8 @@ std::vector ClangTidyASTConsumerFactory::getCheckNames() { ClangTidyOptions::OptionMap ClangTidyASTConsumerFactory::getCheckOptions() { ClangTidyOptions::OptionMap Options; - std::vector> Checks; - CheckFactories->createChecks(&Context, Checks); + std::vector> Checks = + CheckFactories->createChecks(&Context); for (const auto &Check : Checks) Check->storeOptions(Options); return Options; diff --git a/clang-tools-extra/clang-tidy/ClangTidyForceLinker.h b/clang-tools-extra/clang-tidy/ClangTidyForceLinker.h index ec3b5278f74e4..3ab700118587b 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyForceLinker.h +++ b/clang-tools-extra/clang-tidy/ClangTidyForceLinker.h @@ -50,6 +50,11 @@ extern volatile int CppCoreGuidelinesModuleAnchorSource; static int LLVM_ATTRIBUTE_UNUSED CppCoreGuidelinesModuleAnchorDestination = CppCoreGuidelinesModuleAnchorSource; +// This anchor is used to force the linker to link the DarwinModule. +extern volatile int DarwinModuleAnchorSource; +static int LLVM_ATTRIBUTE_UNUSED DarwinModuleAnchorDestination = + DarwinModuleAnchorSource; + // This anchor is used to force the linker to link the FuchsiaModule. extern volatile int FuchsiaModuleAnchorSource; static int LLVM_ATTRIBUTE_UNUSED FuchsiaModuleAnchorDestination = @@ -75,7 +80,8 @@ extern volatile int ModernizeModuleAnchorSource; static int LLVM_ATTRIBUTE_UNUSED ModernizeModuleAnchorDestination = ModernizeModuleAnchorSource; -#if CLANG_ENABLE_STATIC_ANALYZER +#if CLANG_ENABLE_STATIC_ANALYZER && \ + !defined(CLANG_TIDY_DISABLE_STATIC_ANALYZER_CHECKS) // This anchor is used to force the linker to link the MPIModule. extern volatile int MPIModuleAnchorSource; static int LLVM_ATTRIBUTE_UNUSED MPIModuleAnchorDestination = diff --git a/clang-tools-extra/clang-tidy/ClangTidyModule.cpp b/clang-tools-extra/clang-tidy/ClangTidyModule.cpp index 7d6de87a6ae88..ff83e7e940541 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidyModule.cpp @@ -20,13 +20,14 @@ void ClangTidyCheckFactories::registerCheckFactory(StringRef Name, Factories[Name] = std::move(Factory); } -void ClangTidyCheckFactories::createChecks( - ClangTidyContext *Context, - std::vector> &Checks) { +std::vector> +ClangTidyCheckFactories::createChecks(ClangTidyContext *Context) { + std::vector> Checks; for (const auto &Factory : Factories) { if (Context->isCheckEnabled(Factory.first)) Checks.emplace_back(Factory.second(Factory.first, Context)); } + return Checks; } ClangTidyOptions ClangTidyModule::getModuleOptions() { diff --git a/clang-tools-extra/clang-tidy/ClangTidyModule.h b/clang-tools-extra/clang-tidy/ClangTidyModule.h index 894093d95b909..5c484f1a68ddb 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyModule.h +++ b/clang-tools-extra/clang-tidy/ClangTidyModule.h @@ -13,6 +13,7 @@ #include "llvm/ADT/StringRef.h" #include #include +#include #include #include @@ -25,9 +26,8 @@ namespace tidy { /// this object. class ClangTidyCheckFactories { public: - typedef std::function - CheckFactory; + using CheckFactory = std::function( + StringRef Name, ClangTidyContext *Context)>; /// Registers check \p Factory with name \p Name. /// @@ -58,16 +58,13 @@ class ClangTidyCheckFactories { template void registerCheck(StringRef CheckName) { registerCheckFactory(CheckName, [](StringRef Name, ClangTidyContext *Context) { - return new CheckType(Name, Context); + return std::make_unique(Name, Context); }); } - /// Create instances of all checks matching \p CheckRegexString and - /// store them in \p Checks. - /// - /// The caller takes ownership of the return \c ClangTidyChecks. - void createChecks(ClangTidyContext *Context, - std::vector> &Checks); + /// Create instances of checks that are enabled. + std::vector> + createChecks(ClangTidyContext *Context); typedef std::map FactoryMap; FactoryMap::const_iterator begin() const { return Factories.begin(); } diff --git a/clang-tools-extra/clang-tidy/bugprone/ArgumentCommentCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ArgumentCommentCheck.cpp index c690e797e0e9e..4da91d1162ace 100644 --- a/clang-tools-extra/clang-tidy/bugprone/ArgumentCommentCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/ArgumentCommentCheck.cpp @@ -324,7 +324,7 @@ void ArgumentCommentCheck::checkCallArgs(ASTContext *Ctx, << FixItHint::CreateInsertion(Args[I]->getBeginLoc(), ArgComment); } } -} // namespace bugprone +} void ArgumentCommentCheck::check(const MatchFinder::MatchResult &Result) { const auto *E = Result.Nodes.getNodeAs("expr"); @@ -337,7 +337,7 @@ void ArgumentCommentCheck::check(const MatchFinder::MatchResult &Result) { llvm::makeArrayRef(Call->getArgs(), Call->getNumArgs())); } else { const auto *Construct = cast(E); - if (Construct->getNumArgs() == 1 && + if (Construct->getNumArgs() > 0 && Construct->getArg(0)->getSourceRange() == Construct->getSourceRange()) { // Ignore implicit construction. return; diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp index 895bd03f32b48..14058a152c1b9 100644 --- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -23,6 +23,7 @@ #include "ForwardingReferenceOverloadCheck.h" #include "InaccurateEraseCheck.h" #include "IncorrectRoundingsCheck.h" +#include "InfiniteLoopCheck.h" #include "IntegerDivisionCheck.h" #include "LambdaFunctionNameCheck.h" #include "MacroParenthesesCheck.h" @@ -88,6 +89,8 @@ class BugproneModule : public ClangTidyModule { "bugprone-inaccurate-erase"); CheckFactories.registerCheck( "bugprone-incorrect-roundings"); + CheckFactories.registerCheck( + "bugprone-infinite-loop"); CheckFactories.registerCheck( "bugprone-integer-division"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt index 705ee97514e7e..b1d8bbe5c82dd 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt @@ -15,6 +15,7 @@ add_clang_library(clangTidyBugproneModule ForwardingReferenceOverloadCheck.cpp InaccurateEraseCheck.cpp IncorrectRoundingsCheck.cpp + InfiniteLoopCheck.cpp IntegerDivisionCheck.cpp LambdaFunctionNameCheck.cpp MacroParenthesesCheck.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp new file mode 100644 index 0000000000000..81ae45a008961 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp @@ -0,0 +1,188 @@ +//===--- InfiniteLoopCheck.cpp - clang-tidy -------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "InfiniteLoopCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace bugprone { + +static internal::Matcher +loopEndingStmt(internal::Matcher Internal) { + return stmt(anyOf(breakStmt(Internal), returnStmt(Internal), + gotoStmt(Internal), cxxThrowExpr(Internal), + callExpr(Internal, callee(functionDecl(isNoReturn()))))); +} + +/// Return whether `S` is a reference to the declaration of `Var`. +static bool isAccessForVar(const Stmt *S, const VarDecl *Var) { + if (const auto *DRE = dyn_cast(S)) + return DRE->getDecl() == Var; + + return false; +} + +/// Return whether `Var` has a pointer or reference in `S`. +static bool isPtrOrReferenceForVar(const Stmt *S, const VarDecl *Var) { + if (const auto *DS = dyn_cast(S)) { + for (const Decl *D : DS->getDeclGroup()) { + if (const auto *LeftVar = dyn_cast(D)) { + if (LeftVar->hasInit() && LeftVar->getType()->isReferenceType()) { + return isAccessForVar(LeftVar->getInit(), Var); + } + } + } + } else if (const auto *UnOp = dyn_cast(S)) { + if (UnOp->getOpcode() == UO_AddrOf) + return isAccessForVar(UnOp->getSubExpr(), Var); + } + + return false; +} + +/// Return whether `Var` has a pointer or reference in `S`. +static bool hasPtrOrReferenceInStmt(const Stmt *S, const VarDecl *Var) { + if (isPtrOrReferenceForVar(S, Var)) + return true; + + for (const Stmt *Child : S->children()) { + if (!Child) + continue; + + if (hasPtrOrReferenceInStmt(Child, Var)) + return true; + } + + return false; +} + +/// Return whether `Var` has a pointer or reference in `Func`. +static bool hasPtrOrReferenceInFunc(const FunctionDecl *Func, + const VarDecl *Var) { + return hasPtrOrReferenceInStmt(Func->getBody(), Var); +} + +/// Return whether `Var` was changed in `LoopStmt`. +static bool isChanged(const Stmt *LoopStmt, const VarDecl *Var, + ASTContext *Context) { + if (const auto *ForLoop = dyn_cast(LoopStmt)) + return (ForLoop->getInc() && + ExprMutationAnalyzer(*ForLoop->getInc(), *Context) + .isMutated(Var)) || + (ForLoop->getBody() && + ExprMutationAnalyzer(*ForLoop->getBody(), *Context) + .isMutated(Var)) || + (ForLoop->getCond() && + ExprMutationAnalyzer(*ForLoop->getCond(), *Context).isMutated(Var)); + + return ExprMutationAnalyzer(*LoopStmt, *Context).isMutated(Var); +} + +/// Return whether `Cond` is a variable that is possibly changed in `LoopStmt`. +static bool isVarThatIsPossiblyChanged(const FunctionDecl *Func, + const Stmt *LoopStmt, const Stmt *Cond, + ASTContext *Context) { + if (const auto *DRE = dyn_cast(Cond)) { + if (const auto *Var = dyn_cast(DRE->getDecl())) { + if (!Var->isLocalVarDeclOrParm()) + return true; + + if (Var->getType().isVolatileQualified()) + return true; + + if (!Var->getType().getTypePtr()->isIntegerType()) + return true; + + return hasPtrOrReferenceInFunc(Func, Var) || + isChanged(LoopStmt, Var, Context); + // FIXME: Track references. + } + } else if (isa(Cond) || isa(Cond)) { + // FIXME: Handle MemberExpr. + return true; + } + + return false; +} + +/// Return whether at least one variable of `Cond` changed in `LoopStmt`. +static bool isAtLeastOneCondVarChanged(const FunctionDecl *Func, + const Stmt *LoopStmt, const Stmt *Cond, + ASTContext *Context) { + if (isVarThatIsPossiblyChanged(Func, LoopStmt, Cond, Context)) + return true; + + for (const Stmt *Child : Cond->children()) { + if (!Child) + continue; + + if (isAtLeastOneCondVarChanged(Func, LoopStmt, Child, Context)) + return true; + } + return false; +} + +/// Return the variable names in `Cond`. +static std::string getCondVarNames(const Stmt *Cond) { + if (const auto *DRE = dyn_cast(Cond)) { + if (const auto *Var = dyn_cast(DRE->getDecl())) + return Var->getName(); + } + + std::string Result; + for (const Stmt *Child : Cond->children()) { + if (!Child) + continue; + + std::string NewNames = getCondVarNames(Child); + if (!Result.empty() && !NewNames.empty()) + Result += ", "; + Result += NewNames; + } + return Result; +} + +void InfiniteLoopCheck::registerMatchers(MatchFinder *Finder) { + const auto LoopCondition = allOf( + hasCondition( + expr(forFunction(functionDecl().bind("func"))).bind("condition")), + unless(hasBody(hasDescendant( + loopEndingStmt(forFunction(equalsBoundNode("func"))))))); + + Finder->addMatcher(stmt(anyOf(whileStmt(LoopCondition), doStmt(LoopCondition), + forStmt(LoopCondition))) + .bind("loop-stmt"), + this); +} + +void InfiniteLoopCheck::check(const MatchFinder::MatchResult &Result) { + const auto *Cond = Result.Nodes.getNodeAs("condition"); + const auto *LoopStmt = Result.Nodes.getNodeAs("loop-stmt"); + const auto *Func = Result.Nodes.getNodeAs("func"); + + if (isAtLeastOneCondVarChanged(Func, LoopStmt, Cond, Result.Context)) + return; + + std::string CondVarNames = getCondVarNames(Cond); + if (CondVarNames.empty()) + return; + + diag(LoopStmt->getBeginLoc(), + "this loop is infinite; none of its condition variables (%0)" + " are updated in the loop body") + << CondVarNames; +} + +} // namespace bugprone +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.h b/clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.h new file mode 100644 index 0000000000000..45620bf3286a7 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.h @@ -0,0 +1,35 @@ +//===--- InfiniteLoopCheck.h - clang-tidy -----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_INFINITELOOPCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_INFINITELOOPCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace bugprone { + +/// Finds obvious infinite loops (loops where the condition variable is +/// not changed at all). +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-infinite-loop.html +class InfiniteLoopCheck : public ClangTidyCheck { +public: + InfiniteLoopCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace bugprone +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_INFINITELOOPCHECK_H diff --git a/clang-tools-extra/clang-tidy/bugprone/PosixReturnCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/PosixReturnCheck.cpp index 4e984dede0e23..4a470e5c9def6 100644 --- a/clang-tools-extra/clang-tidy/bugprone/PosixReturnCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/PosixReturnCheck.cpp @@ -28,26 +28,31 @@ static StringRef getFunctionSpelling(const MatchFinder::MatchResult &Result, } void PosixReturnCheck::registerMatchers(MatchFinder *Finder) { - Finder->addMatcher(binaryOperator(hasOperatorName("<"), - hasLHS(callExpr(callee(functionDecl( - matchesName("^::posix_"), - unless(hasName("::posix_openpt")))))), - hasRHS(integerLiteral(equals(0)))) - .bind("ltzop"), - this); - Finder->addMatcher(binaryOperator(hasOperatorName(">="), - hasLHS(callExpr(callee(functionDecl( - matchesName("^::posix_"), - unless(hasName("::posix_openpt")))))), - hasRHS(integerLiteral(equals(0)))) - .bind("atop"), - this); + Finder->addMatcher( + binaryOperator( + hasOperatorName("<"), + hasLHS(callExpr(callee(functionDecl( + anyOf(matchesName("^::posix_"), matchesName("^::pthread_")), + unless(hasName("::posix_openpt")))))), + hasRHS(integerLiteral(equals(0)))) + .bind("ltzop"), + this); + Finder->addMatcher( + binaryOperator( + hasOperatorName(">="), + hasLHS(callExpr(callee(functionDecl( + anyOf(matchesName("^::posix_"), matchesName("^::pthread_")), + unless(hasName("::posix_openpt")))))), + hasRHS(integerLiteral(equals(0)))) + .bind("atop"), + this); Finder->addMatcher( binaryOperator( anyOf(hasOperatorName("=="), hasOperatorName("!="), hasOperatorName("<="), hasOperatorName("<")), hasLHS(callExpr(callee(functionDecl( - matchesName("^::posix_"), unless(hasName("::posix_openpt")))))), + anyOf(matchesName("^::posix_"), matchesName("^::pthread_")), + unless(hasName("::posix_openpt")))))), hasRHS(unaryOperator(hasOperatorName("-"), hasUnaryOperand(integerLiteral())))) .bind("binop"), diff --git a/clang-tools-extra/clang-tidy/bugprone/PosixReturnCheck.h b/clang-tools-extra/clang-tidy/bugprone/PosixReturnCheck.h index 979a17fed3506..fe5c94c6ac7ca 100644 --- a/clang-tools-extra/clang-tidy/bugprone/PosixReturnCheck.h +++ b/clang-tools-extra/clang-tidy/bugprone/PosixReturnCheck.h @@ -1,4 +1,4 @@ -//===--- PosixReturnCheck.h - clang-tidy-------------------------*- C++ -*-===// +//===--- PosixReturnCheck.h - clang-tidy ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt index be85285932509..13c15bc9d227b 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt @@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangTidyCppCoreGuidelinesModule AvoidGotoCheck.cpp CppCoreGuidelinesTidyModule.cpp + InitVariablesCheck.cpp InterfacesGlobalInitCheck.cpp MacroUsageCheck.cpp NarrowingConversionsCheck.cpp diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp index 7c7fd1b72cb78..8886eb8337958 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp @@ -15,6 +15,7 @@ #include "../modernize/UseOverrideCheck.h" #include "../readability/MagicNumbersCheck.h" #include "AvoidGotoCheck.h" +#include "InitVariablesCheck.h" #include "InterfacesGlobalInitCheck.h" #include "MacroUsageCheck.h" #include "NarrowingConversionsCheck.h" @@ -49,6 +50,8 @@ class CppCoreGuidelinesModule : public ClangTidyModule { "cppcoreguidelines-avoid-magic-numbers"); CheckFactories.registerCheck( "cppcoreguidelines-explicit-virtual-functions"); + CheckFactories.registerCheck( + "cppcoreguidelines-init-variables"); CheckFactories.registerCheck( "cppcoreguidelines-interfaces-global-init"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/InitVariablesCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/InitVariablesCheck.cpp new file mode 100644 index 0000000000000..8a628317a302f --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/InitVariablesCheck.cpp @@ -0,0 +1,105 @@ +//===--- InitVariablesCheck.cpp - clang-tidy ------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "InitVariablesCheck.h" + +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace cppcoreguidelines { + +namespace { +AST_MATCHER(VarDecl, isLocalVarDecl) { return Node.isLocalVarDecl(); } +} // namespace + +InitVariablesCheck::InitVariablesCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + IncludeStyle(utils::IncludeSorter::parseIncludeStyle( + Options.getLocalOrGlobal("IncludeStyle", "llvm"))), + MathHeader(Options.get("MathHeader", "math.h")) {} + +void InitVariablesCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(varDecl(unless(hasInitializer(anything())), + unless(isInstantiated()), isLocalVarDecl(), + unless(isStaticLocal()), isDefinition()) + .bind("vardecl"), + this); +} + +void InitVariablesCheck::registerPPCallbacks(const SourceManager &SM, + Preprocessor *PP, + Preprocessor *ModuleExpanderPP) { + IncludeInserter = + std::make_unique(SM, getLangOpts(), IncludeStyle); + PP->addPPCallbacks(IncludeInserter->CreatePPCallbacks()); +} + +void InitVariablesCheck::check(const MatchFinder::MatchResult &Result) { + const auto *MatchedDecl = Result.Nodes.getNodeAs("vardecl"); + const ASTContext &Context = *Result.Context; + const SourceManager &Source = Context.getSourceManager(); + + // We want to warn about cases where the type name + // comes from a macro like this: + // + // TYPENAME_FROM_MACRO var; + // + // but not if the entire declaration comes from + // one: + // + // DEFINE_SOME_VARIABLE(); + // + // or if the definition comes from a macro like SWAP + // that uses an internal temporary variable. + // + // Thus check that the variable name does + // not come from a macro expansion. + if (MatchedDecl->getEndLoc().isMacroID()) + return; + + QualType TypePtr = MatchedDecl->getType(); + const char *InitializationString = nullptr; + bool AddMathInclude = false; + + if (TypePtr->isIntegerType()) + InitializationString = " = 0"; + else if (TypePtr->isFloatingType()) { + InitializationString = " = NAN"; + AddMathInclude = true; + } else if (TypePtr->isAnyPointerType()) { + if (getLangOpts().CPlusPlus11) + InitializationString = " = nullptr"; + else + InitializationString = " = NULL"; + } + + if (InitializationString) { + auto Diagnostic = + diag(MatchedDecl->getLocation(), "variable %0 is not initialized") + << MatchedDecl + << FixItHint::CreateInsertion( + MatchedDecl->getLocation().getLocWithOffset( + MatchedDecl->getName().size()), + InitializationString); + if (AddMathInclude) { + auto IncludeHint = IncludeInserter->CreateIncludeInsertion( + Source.getFileID(MatchedDecl->getBeginLoc()), MathHeader, false); + if (IncludeHint) + Diagnostic << *IncludeHint; + } + } +} + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/InitVariablesCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/InitVariablesCheck.h new file mode 100644 index 0000000000000..0cacf9e533cf8 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/InitVariablesCheck.h @@ -0,0 +1,42 @@ +//===--- InitVariablesCheck.h - clang-tidy ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_INITVARIABLESCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_INITVARIABLESCHECK_H + +#include "../ClangTidyCheck.h" +#include "../utils/IncludeInserter.h" +#include "../utils/OptionsUtils.h" + +namespace clang { +namespace tidy { +namespace cppcoreguidelines { + +/// Find uninitialized local variables. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-init-variables.html +class InitVariablesCheck : public ClangTidyCheck { +public: + InitVariablesCheck(StringRef Name, ClangTidyContext *Context); + void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, + Preprocessor *ModuleExpanderPP) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + std::unique_ptr IncludeInserter; + const utils::IncludeSorter::IncludeStyle IncludeStyle; + const std::string MathHeader; +}; + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_INITVARIABLESCHECK_H diff --git a/clang-tools-extra/clang-tidy/objc/AvoidSpinlockCheck.cpp b/clang-tools-extra/clang-tidy/darwin/AvoidSpinlockCheck.cpp similarity index 96% rename from clang-tools-extra/clang-tidy/objc/AvoidSpinlockCheck.cpp rename to clang-tools-extra/clang-tidy/darwin/AvoidSpinlockCheck.cpp index ac3d2b288efaa..2a52725c19c8d 100644 --- a/clang-tools-extra/clang-tidy/objc/AvoidSpinlockCheck.cpp +++ b/clang-tools-extra/clang-tidy/darwin/AvoidSpinlockCheck.cpp @@ -14,7 +14,7 @@ using namespace clang::ast_matchers; namespace clang { namespace tidy { -namespace objc { +namespace darwin { void AvoidSpinlockCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( @@ -31,6 +31,6 @@ void AvoidSpinlockCheck::check(const MatchFinder::MatchResult &Result) { "deprecated OSSpinLock"); } -} // namespace objc +} // namespace darwin } // namespace tidy } // namespace clang diff --git a/clang-tools-extra/clang-tidy/objc/AvoidSpinlockCheck.h b/clang-tools-extra/clang-tidy/darwin/AvoidSpinlockCheck.h similarity index 73% rename from clang-tools-extra/clang-tidy/objc/AvoidSpinlockCheck.h rename to clang-tools-extra/clang-tidy/darwin/AvoidSpinlockCheck.h index dd409625f3a13..6ea10c550d901 100644 --- a/clang-tools-extra/clang-tidy/objc/AvoidSpinlockCheck.h +++ b/clang-tools-extra/clang-tidy/darwin/AvoidSpinlockCheck.h @@ -6,20 +6,20 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_OBJC_AVOID_SPINLOCK_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_OBJC_AVOID_SPINLOCK_H +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_DARWIN_AVOIDSPINLOCKCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_DARWIN_AVOIDSPINLOCKCHECK_H #include "../ClangTidyCheck.h" namespace clang { namespace tidy { -namespace objc { +namespace darwin { /// Finds usages of OSSpinlock, which is deprecated due to potential livelock /// problems. /// /// For the user-facing documentation see: -/// http://clang.llvm.org/extra/clang-tidy/checks/objc-avoid-spinlock.html +/// http://clang.llvm.org/extra/clang-tidy/checks/darwin-avoid-spinlock.html class AvoidSpinlockCheck : public ClangTidyCheck { public: AvoidSpinlockCheck(StringRef Name, ClangTidyContext *Context) @@ -28,8 +28,8 @@ class AvoidSpinlockCheck : public ClangTidyCheck { void check(const ast_matchers::MatchFinder::MatchResult &Result) override; }; -} // namespace objc +} // namespace darwin } // namespace tidy } // namespace clang -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_OBJC_AVOID_SPINLOCK_H +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_DARWIN_AVOIDSPINLOCKCHECK_H diff --git a/clang-tools-extra/clang-tidy/darwin/CMakeLists.txt b/clang-tools-extra/clang-tidy/darwin/CMakeLists.txt new file mode 100644 index 0000000000000..c650efb2a5f27 --- /dev/null +++ b/clang-tools-extra/clang-tidy/darwin/CMakeLists.txt @@ -0,0 +1,16 @@ +set(LLVM_LINK_COMPONENTS support) + +add_clang_library(clangTidyDarwinModule + AvoidSpinlockCheck.cpp + DarwinTidyModule.cpp + DispatchOnceNonstaticCheck.cpp + + LINK_LIBS + clangAnalysis + clangAST + clangASTMatchers + clangBasic + clangLex + clangTidy + clangTidyUtils + ) diff --git a/clang-tools-extra/clang-tidy/darwin/DarwinTidyModule.cpp b/clang-tools-extra/clang-tidy/darwin/DarwinTidyModule.cpp new file mode 100644 index 0000000000000..d11ef1fa01c98 --- /dev/null +++ b/clang-tools-extra/clang-tidy/darwin/DarwinTidyModule.cpp @@ -0,0 +1,40 @@ +//===--- MiscTidyModule.cpp - clang-tidy ----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "../ClangTidy.h" +#include "../ClangTidyModule.h" +#include "../ClangTidyModuleRegistry.h" +#include "AvoidSpinlockCheck.h" +#include "DispatchOnceNonstaticCheck.h" + +namespace clang { +namespace tidy { +namespace darwin { + +class DarwinModule : public ClangTidyModule { +public: + void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck( + "darwin-avoid-spinlock"); + CheckFactories.registerCheck( + "darwin-dispatch-once-nonstatic"); + } +}; + +} // namespace darwin + +// Register the DarwinTidyModule using this statically initialized variable. +static ClangTidyModuleRegistry::Add + X("darwin-module", "Adds Darwin-specific lint checks."); + +// This anchor is used to force the linker to link in the generated object file +// and thus register the DarwinModule. +volatile int DarwinModuleAnchorSource = 0; + +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/darwin/DispatchOnceNonstaticCheck.cpp b/clang-tools-extra/clang-tidy/darwin/DispatchOnceNonstaticCheck.cpp new file mode 100644 index 0000000000000..dac7f83309714 --- /dev/null +++ b/clang-tools-extra/clang-tidy/darwin/DispatchOnceNonstaticCheck.cpp @@ -0,0 +1,62 @@ +//===--- DispatchOnceNonstaticCheck.cpp - clang-tidy ----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "DispatchOnceNonstaticCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Basic/Diagnostic.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace darwin { + +void DispatchOnceNonstaticCheck::registerMatchers(MatchFinder *Finder) { + // Find variables without static or global storage. VarDecls do not include + // struct/class members, which are FieldDecls. + Finder->addMatcher( + varDecl(hasLocalStorage(), hasType(asString("dispatch_once_t"))) + .bind("non-static-var"), + this); + + // Members of structs or classes might be okay, if the use is at static or + // global scope. These will be ignored for now. But ObjC ivars can be + // flagged immediately, since they cannot be static. + Finder->addMatcher( + objcIvarDecl(hasType(asString("dispatch_once_t"))).bind("ivar"), this); +} + +void DispatchOnceNonstaticCheck::check(const MatchFinder::MatchResult &Result) { + if (const auto *VD = Result.Nodes.getNodeAs("non-static-var")) { + if (const auto *PD = dyn_cast(VD)) { + // Catch function/method parameters, as any dispatch_once_t should be + // passed by pointer instead. + diag(PD->getTypeSpecStartLoc(), + "dispatch_once_t variables must have static or global storage " + "duration; function parameters should be pointer references"); + } else { + diag(VD->getTypeSpecStartLoc(), "dispatch_once_t variables must have " + "static or global storage duration") + << FixItHint::CreateInsertion(VD->getTypeSpecStartLoc(), "static "); + } + } + + if (const auto *D = Result.Nodes.getNodeAs("ivar")) { + diag(D->getTypeSpecStartLoc(), + "dispatch_once_t variables must have static or global storage " + "duration and cannot be Objective-C instance variables"); + } +} + +} // namespace darwin +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/darwin/DispatchOnceNonstaticCheck.h b/clang-tools-extra/clang-tidy/darwin/DispatchOnceNonstaticCheck.h new file mode 100644 index 0000000000000..be0719a778ea0 --- /dev/null +++ b/clang-tools-extra/clang-tidy/darwin/DispatchOnceNonstaticCheck.h @@ -0,0 +1,35 @@ +//===--- DispatchOnceNonstaticCheck.h - clang-tidy --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_DARWIN_DISPATCHONCENONSTATICCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_DARWIN_DISPATCHONCENONSTATICCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace darwin { + +/// Finds variables of type dispatch_once_t that do not have static or global +/// storage duration, as required by the libdispatch documentation. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/darwin-dispatch-once-nonstatic.html +class DispatchOnceNonstaticCheck : public ClangTidyCheck { +public: + DispatchOnceNonstaticCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace darwin +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_DARWIN_DISPATCHONCENONSTATICCHECK_H diff --git a/clang-tools-extra/clang-tidy/google/TodoCommentCheck.cpp b/clang-tools-extra/clang-tidy/google/TodoCommentCheck.cpp index 787c30548d4af..ec099f0e400c9 100644 --- a/clang-tools-extra/clang-tidy/google/TodoCommentCheck.cpp +++ b/clang-tools-extra/clang-tidy/google/TodoCommentCheck.cpp @@ -55,6 +55,8 @@ TodoCommentCheck::TodoCommentCheck(StringRef Name, ClangTidyContext *Context) Handler(std::make_unique( *this, Context->getOptions().User)) {} +TodoCommentCheck::~TodoCommentCheck() = default; + void TodoCommentCheck::registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { diff --git a/clang-tools-extra/clang-tidy/google/TodoCommentCheck.h b/clang-tools-extra/clang-tidy/google/TodoCommentCheck.h index d1343b74fdbf1..8c32dddc28373 100644 --- a/clang-tools-extra/clang-tidy/google/TodoCommentCheck.h +++ b/clang-tools-extra/clang-tidy/google/TodoCommentCheck.h @@ -22,6 +22,8 @@ namespace readability { class TodoCommentCheck : public ClangTidyCheck { public: TodoCommentCheck(StringRef Name, ClangTidyContext *Context); + ~TodoCommentCheck(); + void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override; diff --git a/clang-tools-extra/clang-tidy/llvm/HeaderGuardCheck.cpp b/clang-tools-extra/clang-tidy/llvm/HeaderGuardCheck.cpp index c098268196a0a..8b32d8ddcc93b 100644 --- a/clang-tools-extra/clang-tidy/llvm/HeaderGuardCheck.cpp +++ b/clang-tools-extra/clang-tidy/llvm/HeaderGuardCheck.cpp @@ -33,6 +33,13 @@ std::string LLVMHeaderGuardCheck::getHeaderGuard(StringRef Filename, if (PosToolsClang != StringRef::npos) Guard = Guard.substr(PosToolsClang + std::strlen("tools/")); + // Unlike LLVM svn, LLVM git monorepo is named llvm-project, so we replace + // "/llvm-project/" with the cannonical "/llvm/". + const static StringRef LLVMProject = "/llvm-project/"; + size_t PosLLVMProject = Guard.rfind(LLVMProject); + if (PosLLVMProject != StringRef::npos) + Guard = Guard.replace(PosLLVMProject, LLVMProject.size(), "/llvm/"); + // The remainder is LLVM_FULL_PATH_TO_HEADER_H size_t PosLLVM = Guard.rfind("llvm/"); if (PosLLVM != StringRef::npos) diff --git a/clang-tools-extra/clang-tidy/objc/CMakeLists.txt b/clang-tools-extra/clang-tidy/objc/CMakeLists.txt index 4eeb14844a58d..68dda6530f7f7 100644 --- a/clang-tools-extra/clang-tidy/objc/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/objc/CMakeLists.txt @@ -2,8 +2,8 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangTidyObjCModule AvoidNSErrorInitCheck.cpp - AvoidSpinlockCheck.cpp ForbiddenSubclassingCheck.cpp + MissingHashCheck.cpp ObjCTidyModule.cpp PropertyDeclarationCheck.cpp SuperSelfCheck.cpp diff --git a/clang-tools-extra/clang-tidy/objc/MissingHashCheck.cpp b/clang-tools-extra/clang-tidy/objc/MissingHashCheck.cpp new file mode 100644 index 0000000000000..0da5571b2f26c --- /dev/null +++ b/clang-tools-extra/clang-tidy/objc/MissingHashCheck.cpp @@ -0,0 +1,62 @@ +//===--- MissingHashCheck.cpp - clang-tidy --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "MissingHashCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace objc { + +namespace { + +AST_MATCHER_P(ObjCImplementationDecl, hasInterface, + ast_matchers::internal::Matcher, Base) { + const ObjCInterfaceDecl *InterfaceDecl = Node.getClassInterface(); + return Base.matches(*InterfaceDecl, Finder, Builder); +} + +AST_MATCHER_P(ObjCContainerDecl, hasInstanceMethod, + ast_matchers::internal::Matcher, Base) { + // Check each instance method against the provided matcher. + for (const auto *I : Node.instance_methods()) { + if (Base.matches(*I, Finder, Builder)) + return true; + } + return false; +} + +} // namespace + +void MissingHashCheck::registerMatchers(MatchFinder *Finder) { + // This check should only be applied to Objective-C sources. + if (!getLangOpts().ObjC) + return; + + Finder->addMatcher( + objcMethodDecl( + hasName("isEqual:"), isInstanceMethod(), + hasDeclContext(objcImplementationDecl( + hasInterface(isDirectlyDerivedFrom("NSObject")), + unless(hasInstanceMethod(hasName("hash")))) + .bind("impl"))), + this); +} + +void MissingHashCheck::check(const MatchFinder::MatchResult &Result) { + const auto *ID = Result.Nodes.getNodeAs("impl"); + diag(ID->getLocation(), "%0 implements -isEqual: without implementing -hash") + << ID; +} + +} // namespace objc +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/objc/MissingHashCheck.h b/clang-tools-extra/clang-tidy/objc/MissingHashCheck.h new file mode 100644 index 0000000000000..4ac74d5865218 --- /dev/null +++ b/clang-tools-extra/clang-tidy/objc/MissingHashCheck.h @@ -0,0 +1,35 @@ +//===--- MissingHashCheck.h - clang-tidy ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_OBJC_MISSINGHASHCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_OBJC_MISSINGHASHCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace objc { + +/// Finds Objective-C implementations that implement -isEqual: without also +/// appropriately implementing -hash. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/objc-missing-hash.html +class MissingHashCheck : public ClangTidyCheck { +public: + MissingHashCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace objc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_OBJC_MISSINGHASHCHECK_H diff --git a/clang-tools-extra/clang-tidy/objc/ObjCTidyModule.cpp b/clang-tools-extra/clang-tidy/objc/ObjCTidyModule.cpp index 636e2c02af649..69913125451ce 100644 --- a/clang-tools-extra/clang-tidy/objc/ObjCTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/objc/ObjCTidyModule.cpp @@ -10,8 +10,8 @@ #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" #include "AvoidNSErrorInitCheck.h" -#include "AvoidSpinlockCheck.h" #include "ForbiddenSubclassingCheck.h" +#include "MissingHashCheck.h" #include "PropertyDeclarationCheck.h" #include "SuperSelfCheck.h" @@ -26,10 +26,10 @@ class ObjCModule : public ClangTidyModule { void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { CheckFactories.registerCheck( "objc-avoid-nserror-init"); - CheckFactories.registerCheck( - "objc-avoid-spinlock"); CheckFactories.registerCheck( "objc-forbidden-subclassing"); + CheckFactories.registerCheck( + "objc-missing-hash"); CheckFactories.registerCheck( "objc-property-declaration"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/clang-tidy/performance/InefficientVectorOperationCheck.cpp b/clang-tools-extra/clang-tidy/performance/InefficientVectorOperationCheck.cpp index 622649ac4594c..e93ed7e3a33b7 100644 --- a/clang-tools-extra/clang-tidy/performance/InefficientVectorOperationCheck.cpp +++ b/clang-tools-extra/clang-tidy/performance/InefficientVectorOperationCheck.cpp @@ -29,26 +29,37 @@ namespace { // for (int i = 0; i < 10 + 1; ++i) { // v.push_back(i); // } +// +// SomeProto p; +// for (int i = 0; i < 10 + 1; ++i) { +// p.add_xxx(i); +// } // } // \endcode // // The matcher names are bound to following parts of the AST: // - LoopCounterName: The entire for loop (as ForStmt). // - LoopParentName: The body of function f (as CompoundStmt). -// - VectorVarDeclName: 'v' in (as VarDecl). +// - VectorVarDeclName: 'v' (as VarDecl). // - VectorVarDeclStmatName: The entire 'std::vector v;' statement (as // DeclStmt). // - PushBackOrEmplaceBackCallName: 'v.push_back(i)' (as cxxMemberCallExpr). // - LoopInitVarName: 'i' (as VarDecl). // - LoopEndExpr: '10+1' (as Expr). +// If EnableProto, the proto related names are bound to the following parts: +// - ProtoVarDeclName: 'p' (as VarDecl). +// - ProtoVarDeclStmtName: The entire 'SomeProto p;' statement (as DeclStmt). +// - ProtoAddFieldCallName: 'p.add_xxx(i)' (as cxxMemberCallExpr). static const char LoopCounterName[] = "for_loop_counter"; static const char LoopParentName[] = "loop_parent"; static const char VectorVarDeclName[] = "vector_var_decl"; static const char VectorVarDeclStmtName[] = "vector_var_decl_stmt"; static const char PushBackOrEmplaceBackCallName[] = "append_call"; +static const char ProtoVarDeclName[] = "proto_var_decl"; +static const char ProtoVarDeclStmtName[] = "proto_var_decl_stmt"; +static const char ProtoAddFieldCallName[] = "proto_add_field"; static const char LoopInitVarName[] = "loop_init_var"; static const char LoopEndExprName[] = "loop_end_expr"; - static const char RangeLoopName[] = "for_range_loop"; ast_matchers::internal::Matcher supportedContainerTypesMatcher() { @@ -63,34 +74,35 @@ InefficientVectorOperationCheck::InefficientVectorOperationCheck( StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), VectorLikeClasses(utils::options::parseStringList( - Options.get("VectorLikeClasses", "::std::vector"))) {} + Options.get("VectorLikeClasses", "::std::vector"))), + EnableProto(Options.getLocalOrGlobal("EnableProto", false)) {} void InefficientVectorOperationCheck::storeOptions( ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "VectorLikeClasses", utils::options::serializeStringList(VectorLikeClasses)); + Options.store(Opts, "EnableProto", EnableProto); } -void InefficientVectorOperationCheck::registerMatchers(MatchFinder *Finder) { - const auto VectorDecl = cxxRecordDecl(hasAnyName(SmallVector( - VectorLikeClasses.begin(), VectorLikeClasses.end()))); - const auto VectorDefaultConstructorCall = cxxConstructExpr( - hasType(VectorDecl), +void InefficientVectorOperationCheck::AddMatcher( + const DeclarationMatcher &TargetRecordDecl, StringRef VarDeclName, + StringRef VarDeclStmtName, const DeclarationMatcher &AppendMethodDecl, + StringRef AppendCallName, MatchFinder *Finder) { + const auto DefaultConstructorCall = cxxConstructExpr( + hasType(TargetRecordDecl), hasDeclaration(cxxConstructorDecl(isDefaultConstructor()))); - const auto VectorVarDecl = - varDecl(hasInitializer(VectorDefaultConstructorCall)) - .bind(VectorVarDeclName); - const auto VectorAppendCallExpr = - cxxMemberCallExpr( - callee(cxxMethodDecl(hasAnyName("push_back", "emplace_back"))), - on(hasType(VectorDecl)), - onImplicitObjectArgument(declRefExpr(to(VectorVarDecl)))) - .bind(PushBackOrEmplaceBackCallName); - const auto VectorAppendCall = expr(ignoringImplicit(VectorAppendCallExpr)); - const auto VectorVarDefStmt = - declStmt(hasSingleDecl(equalsBoundNode(VectorVarDeclName))) - .bind(VectorVarDeclStmtName); + const auto TargetVarDecl = + varDecl(hasInitializer(DefaultConstructorCall)).bind(VarDeclName); + const auto TargetVarDefStmt = + declStmt(hasSingleDecl(equalsBoundNode(VarDeclName))) + .bind(VarDeclStmtName); + const auto AppendCallExpr = + cxxMemberCallExpr( + callee(AppendMethodDecl), on(hasType(TargetRecordDecl)), + onImplicitObjectArgument(declRefExpr(to(TargetVarDecl)))) + .bind(AppendCallName); + const auto AppendCall = expr(ignoringImplicit(AppendCallExpr)); const auto LoopVarInit = declStmt(hasSingleDecl(varDecl(hasInitializer(integerLiteral(equals(0)))) .bind(LoopInitVarName))); @@ -99,14 +111,16 @@ void InefficientVectorOperationCheck::registerMatchers(MatchFinder *Finder) { // Matchers for the loop whose body has only 1 push_back/emplace_back calling // statement. - const auto HasInterestingLoopBody = - hasBody(anyOf(compoundStmt(statementCountIs(1), has(VectorAppendCall)), - VectorAppendCall)); + const auto HasInterestingLoopBody = hasBody( + anyOf(compoundStmt(statementCountIs(1), has(AppendCall)), AppendCall)); const auto InInterestingCompoundStmt = - hasParent(compoundStmt(has(VectorVarDefStmt)).bind(LoopParentName)); + hasParent(compoundStmt(has(TargetVarDefStmt)).bind(LoopParentName)); // Match counter-based for loops: - // for (int i = 0; i < n; ++i) { v.push_back(...); } + // for (int i = 0; i < n; ++i) { + // v.push_back(...); + // // Or: proto.add_xxx(...); + // } // // FIXME: Support more types of counter-based loops like decrement loops. Finder->addMatcher( @@ -123,7 +137,10 @@ void InefficientVectorOperationCheck::registerMatchers(MatchFinder *Finder) { this); // Match for-range loops: - // for (const auto& E : data) { v.push_back(...); } + // for (const auto& E : data) { + // v.push_back(...); + // // Or: proto.add_xxx(...); + // } // // FIXME: Support more complex range-expressions. Finder->addMatcher( @@ -134,6 +151,28 @@ void InefficientVectorOperationCheck::registerMatchers(MatchFinder *Finder) { this); } +void InefficientVectorOperationCheck::registerMatchers(MatchFinder *Finder) { + const auto VectorDecl = cxxRecordDecl(hasAnyName(SmallVector( + VectorLikeClasses.begin(), VectorLikeClasses.end()))); + const auto AppendMethodDecl = + cxxMethodDecl(hasAnyName("push_back", "emplace_back")); + AddMatcher(VectorDecl, VectorVarDeclName, VectorVarDeclStmtName, + AppendMethodDecl, PushBackOrEmplaceBackCallName, Finder); + + if (EnableProto) { + const auto ProtoDecl = + cxxRecordDecl(isDerivedFrom("::proto2::MessageLite")); + + // A method's name starts with "add_" might not mean it's an add field + // call; it could be the getter for a proto field of which the name starts + // with "add_". So we exlude const methods. + const auto AddFieldMethodDecl = + cxxMethodDecl(matchesName("::add_"), unless(isConst())); + AddMatcher(ProtoDecl, ProtoVarDeclName, ProtoVarDeclStmtName, + AddFieldMethodDecl, ProtoAddFieldCallName, Finder); + } +} + void InefficientVectorOperationCheck::check( const MatchFinder::MatchResult &Result) { auto* Context = Result.Context; @@ -148,64 +187,84 @@ void InefficientVectorOperationCheck::check( Result.Nodes.getNodeAs(RangeLoopName); const auto *VectorAppendCall = Result.Nodes.getNodeAs(PushBackOrEmplaceBackCallName); + const auto *ProtoVarDecl = Result.Nodes.getNodeAs(ProtoVarDeclName); + const auto *ProtoAddFieldCall = + Result.Nodes.getNodeAs(ProtoAddFieldCallName); const auto *LoopEndExpr = Result.Nodes.getNodeAs(LoopEndExprName); const auto *LoopParent = Result.Nodes.getNodeAs(LoopParentName); + const CXXMemberCallExpr *AppendCall = + VectorAppendCall ? VectorAppendCall : ProtoAddFieldCall; + assert(AppendCall && "no append call expression"); + const Stmt *LoopStmt = ForLoop; if (!LoopStmt) LoopStmt = RangeLoop; - llvm::SmallPtrSet AllVectorVarRefs = - utils::decl_ref_expr::allDeclRefExprs(*VectorVarDecl, *LoopParent, + const auto *TargetVarDecl = VectorVarDecl; + if (!TargetVarDecl) + TargetVarDecl = ProtoVarDecl; + + llvm::SmallPtrSet AllVarRefs = + utils::decl_ref_expr::allDeclRefExprs(*TargetVarDecl, *LoopParent, *Context); - for (const auto *Ref : AllVectorVarRefs) { - // Skip cases where there are usages (defined as DeclRefExpr that refers to - // "v") of vector variable `v` before the for loop. We consider these usages - // are operations causing memory preallocation (e.g. "v.resize(n)", - // "v.reserve(n)"). + for (const auto *Ref : AllVarRefs) { + // Skip cases where there are usages (defined as DeclRefExpr that refers + // to "v") of vector variable / proto variable `v` before the for loop. We + // consider these usages are operations causing memory preallocation (e.g. + // "v.resize(n)", "v.reserve(n)"). // - // FIXME: make it more intelligent to identify the pre-allocating operations - // before the for loop. + // FIXME: make it more intelligent to identify the pre-allocating + // operations before the for loop. if (SM.isBeforeInTranslationUnit(Ref->getLocation(), LoopStmt->getBeginLoc())) { return; } } - llvm::StringRef VectorVarName = Lexer::getSourceText( + std::string PartialReserveStmt; + if (VectorAppendCall != nullptr) { + PartialReserveStmt = ".reserve"; + } else { + llvm::StringRef FieldName = ProtoAddFieldCall->getMethodDecl()->getName(); + FieldName.consume_front("add_"); + std::string MutableFieldName = ("mutable_" + FieldName).str(); + PartialReserveStmt = "." + MutableFieldName + + "()->Reserve"; // e.g., ".mutable_xxx()->Reserve" + } + + llvm::StringRef VarName = Lexer::getSourceText( CharSourceRange::getTokenRange( - VectorAppendCall->getImplicitObjectArgument()->getSourceRange()), + AppendCall->getImplicitObjectArgument()->getSourceRange()), SM, Context->getLangOpts()); - std::string ReserveStmt; + std::string ReserveSize; // Handle for-range loop cases. if (RangeLoop) { // Get the range-expression in a for-range statement represented as // `for (range-declarator: range-expression)`. - StringRef RangeInitExpName = Lexer::getSourceText( - CharSourceRange::getTokenRange( - RangeLoop->getRangeInit()->getSourceRange()), - SM, Context->getLangOpts()); - - ReserveStmt = - (VectorVarName + ".reserve(" + RangeInitExpName + ".size()" + ");\n") - .str(); + StringRef RangeInitExpName = + Lexer::getSourceText(CharSourceRange::getTokenRange( + RangeLoop->getRangeInit()->getSourceRange()), + SM, Context->getLangOpts()); + ReserveSize = (RangeInitExpName + ".size()").str(); } else if (ForLoop) { // Handle counter-based loop cases. StringRef LoopEndSource = Lexer::getSourceText( CharSourceRange::getTokenRange(LoopEndExpr->getSourceRange()), SM, Context->getLangOpts()); - ReserveStmt = (VectorVarName + ".reserve(" + LoopEndSource + ");\n").str(); + ReserveSize = LoopEndSource; } - auto Diag = - diag(VectorAppendCall->getBeginLoc(), - "%0 is called inside a loop; " - "consider pre-allocating the vector capacity before the loop") - << VectorAppendCall->getMethodDecl()->getDeclName(); - - if (!ReserveStmt.empty()) + auto Diag = diag(AppendCall->getBeginLoc(), + "%0 is called inside a loop; consider pre-allocating the " + "container capacity before the loop") + << AppendCall->getMethodDecl()->getDeclName(); + if (!ReserveSize.empty()) { + std::string ReserveStmt = + (VarName + PartialReserveStmt + "(" + ReserveSize + ");\n").str(); Diag << FixItHint::CreateInsertion(LoopStmt->getBeginLoc(), ReserveStmt); + } } } // namespace performance diff --git a/clang-tools-extra/clang-tidy/performance/InefficientVectorOperationCheck.h b/clang-tools-extra/clang-tidy/performance/InefficientVectorOperationCheck.h index bfd84d100c279..e224dcc90685f 100644 --- a/clang-tools-extra/clang-tidy/performance/InefficientVectorOperationCheck.h +++ b/clang-tools-extra/clang-tidy/performance/InefficientVectorOperationCheck.h @@ -18,6 +18,9 @@ namespace performance { /// Finds possible inefficient `std::vector` operations (e.g. `push_back`) in /// for loops that may cause unnecessary memory reallocations. /// +/// When EnableProto, also finds calls that add element to protobuf repeated +/// field without calling Reserve() first. +/// /// For the user-facing documentation see: /// http://clang.llvm.org/extra/clang-tidy/checks/performance-inefficient-vector-operation.html class InefficientVectorOperationCheck : public ClangTidyCheck { @@ -28,7 +31,14 @@ class InefficientVectorOperationCheck : public ClangTidyCheck { void storeOptions(ClangTidyOptions::OptionMap &Opts) override; private: + void AddMatcher(const ast_matchers::DeclarationMatcher &TargetRecordDecl, + StringRef VarDeclName, StringRef VarDeclStmtName, + const ast_matchers::DeclarationMatcher &AppendMethodDecl, + StringRef AppendCallName, ast_matchers::MatchFinder *Finder); const std::vector VectorLikeClasses; + + // If true, also check inefficient operations for proto repeated fields. + bool EnableProto; }; } // namespace performance diff --git a/clang-tools-extra/clang-tidy/plugin/CMakeLists.txt b/clang-tools-extra/clang-tidy/plugin/CMakeLists.txt index c92f44df097cb..4adc3f269770d 100644 --- a/clang-tools-extra/clang-tidy/plugin/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/plugin/CMakeLists.txt @@ -8,30 +8,6 @@ add_clang_library(clangTidyPlugin clangFrontend clangSema clangTidy - clangTidyAbseilModule - clangTidyAndroidModule - clangTidyBoostModule - clangTidyBugproneModule - clangTidyCERTModule - clangTidyCppCoreGuidelinesModule - clangTidyFuchsiaModule - clangTidyGoogleModule - clangTidyHICPPModule - clangTidyLinuxKernelModule - clangTidyLLVMModule - clangTidyMiscModule - clangTidyModernizeModule - clangTidyObjCModule - clangTidyOpenMPModule - clangTidyPerformanceModule - clangTidyPortabilityModule - clangTidyReadabilityModule - clangTidyZirconModule clangTooling + ${ALL_CLANG_TIDY_CHECKS} ) - -if(CLANG_ENABLE_STATIC_ANALYZER) - target_link_libraries(clangTidyPlugin PRIVATE - clangTidyMPIModule - ) -endif() diff --git a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp index dc24f663ae353..3228940413257 100644 --- a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp @@ -193,6 +193,8 @@ IdentifierNamingCheck::IdentifierNamingCheck(StringRef Name, IgnoreFailedSplit = Options.get("IgnoreFailedSplit", 0); } +IdentifierNamingCheck::~IdentifierNamingCheck() = default; + void IdentifierNamingCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { auto const toString = [](CaseType Type) { switch (Type) { diff --git a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.h b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.h index 5d5f056049484..250dc361ff74a 100644 --- a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.h +++ b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.h @@ -34,6 +34,7 @@ namespace readability { class IdentifierNamingCheck : public ClangTidyCheck { public: IdentifierNamingCheck(StringRef Name, ClangTidyContext *Context); + ~IdentifierNamingCheck(); void storeOptions(ClangTidyOptions::OptionMap &Opts) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override; diff --git a/clang-tools-extra/clang-tidy/tool/CMakeLists.txt b/clang-tools-extra/clang-tidy/tool/CMakeLists.txt index cbd87d15eef87..fc2b4ebd3b410 100644 --- a/clang-tools-extra/clang-tidy/tool/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/tool/CMakeLists.txt @@ -17,34 +17,11 @@ target_link_libraries(clang-tidy clangASTMatchers clangBasic clangTidy - clangTidyAndroidModule - clangTidyAbseilModule - clangTidyBoostModule - clangTidyBugproneModule - clangTidyCERTModule - clangTidyCppCoreGuidelinesModule - clangTidyFuchsiaModule - clangTidyGoogleModule - clangTidyHICPPModule - clangTidyLinuxKernelModule - clangTidyLLVMModule - clangTidyMiscModule - clangTidyModernizeModule - clangTidyObjCModule - clangTidyOpenMPModule - clangTidyPerformanceModule - clangTidyPortabilityModule - clangTidyReadabilityModule - clangTidyZirconModule clangTooling clangToolingCore + ${ALL_CLANG_TIDY_CHECKS} ) -if(CLANG_ENABLE_STATIC_ANALYZER) - target_link_libraries(clang-tidy PRIVATE - clangTidyMPIModule - ) -endif() install(PROGRAMS clang-tidy-diff.py DESTINATION share/clang diff --git a/clang-tools-extra/clang-tidy/utils/LexerUtils.h b/clang-tools-extra/clang-tidy/utils/LexerUtils.h index 06a03b6e52628..8330266e6341b 100644 --- a/clang-tools-extra/clang-tidy/utils/LexerUtils.h +++ b/clang-tools-extra/clang-tidy/utils/LexerUtils.h @@ -10,6 +10,7 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_LEXER_UTILS_H #include "clang/AST/ASTContext.h" +#include "clang/Basic/TokenKinds.h" #include "clang/Lex/Lexer.h" namespace clang { @@ -70,6 +71,11 @@ SourceLocation findNextAnyTokenKind(SourceLocation Start, if (PotentialMatch.isOneOf(TK, TKs...)) return PotentialMatch.getLocation(); + // If we reach the end of the file, and eof is not the target token, we stop + // the loop, otherwise we will get infinite loop (findNextToken will return + // eof on eof). + if (PotentialMatch.is(tok::eof)) + return SourceLocation(); Start = PotentialMatch.getLastLoc(); } } diff --git a/clang-tools-extra/clangd/CMakeLists.txt b/clang-tools-extra/clangd/CMakeLists.txt index 8d622c2c572a6..d428c8ad26945 100644 --- a/clang-tools-extra/clangd/CMakeLists.txt +++ b/clang-tools-extra/clangd/CMakeLists.txt @@ -56,6 +56,7 @@ add_clang_library(clangDaemon FuzzyMatch.cpp GlobalCompilationDatabase.cpp Headers.cpp + HeaderSourceSwitch.cpp IncludeFixer.cpp JSONTransport.cpp Logger.cpp @@ -66,6 +67,7 @@ add_clang_library(clangDaemon RIFF.cpp Selection.cpp SemanticHighlighting.cpp + SemanticSelection.cpp SourceCode.cpp QueryDriverDatabase.cpp Threading.cpp @@ -115,25 +117,6 @@ add_clang_library(clangDaemon clangSema clangSerialization clangTidy - clangTidyAndroidModule - clangTidyAbseilModule - clangTidyBoostModule - clangTidyBugproneModule - clangTidyCERTModule - clangTidyCppCoreGuidelinesModule - clangTidyFuchsiaModule - clangTidyGoogleModule - clangTidyHICPPModule - clangTidyLinuxKernelModule - clangTidyLLVMModule - clangTidyMiscModule - clangTidyModernizeModule - clangTidyObjCModule - clangTidyOpenMPModule - clangTidyPerformanceModule - clangTidyPortabilityModule - clangTidyReadabilityModule - clangTidyZirconModule clangTooling clangToolingCore clangToolingInclusions @@ -141,6 +124,7 @@ add_clang_library(clangDaemon clangToolingSyntax ${LLVM_PTHREAD_LIB} ${CLANGD_ATOMIC_LIB} + ${ALL_CLANG_TIDY_CHECKS} ) add_subdirectory(refactor/tweaks) diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp index 0930c80da06f6..692bd24072a47 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -22,14 +22,18 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator_range.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" #include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/JSON.h" #include "llvm/Support/Path.h" #include "llvm/Support/SHA1.h" #include "llvm/Support/ScopedPrinter.h" #include +#include #include +#include namespace clang { namespace clangd { @@ -127,6 +131,21 @@ llvm::Error validateEdits(const DraftStore &DraftMgr, const Tweak::Effect &E) { llvm::to_string(InvalidFileCount - 1) + " others)"); } +// Converts a list of Ranges to a LinkedList of SelectionRange. +SelectionRange render(const std::vector &Ranges) { + if (Ranges.empty()) + return {}; + SelectionRange Result; + Result.range = Ranges[0]; + auto *Next = &Result.parent; + for (const auto &R : llvm::make_range(Ranges.begin() + 1, Ranges.end())) { + *Next = std::make_unique(); + Next->get()->range = R; + Next = &Next->get()->parent; + } + return Result; +} + } // namespace // MessageHandler dispatches incoming LSP messages. @@ -536,6 +555,7 @@ void ClangdLSPServer::onInitialize(const InitializeParams &Params, {"documentHighlightProvider", true}, {"hoverProvider", true}, {"renameProvider", std::move(RenameProvider)}, + {"selectionRangeProvider", true}, {"documentSymbolProvider", true}, {"workspaceSymbolProvider", true}, {"referencesProvider", true}, @@ -680,7 +700,7 @@ void ClangdLSPServer::onCommand(const ExecuteCommandParams &Params, WorkspaceEdit WE; WE.changes.emplace(); for (const auto &It : R->ApplyEdits) { - (*WE.changes)[URI::create(It.first()).toString()] = + (*WE.changes)[URI::createFile(It.first()).toString()] = It.second.asTextEdits(); } // ApplyEdit will take care of calling Reply(). @@ -1018,10 +1038,16 @@ void ClangdLSPServer::onGoToDeclaration( void ClangdLSPServer::onSwitchSourceHeader( const TextDocumentIdentifier &Params, Callback> Reply) { - if (auto Result = Server->switchSourceHeader(Params.uri.file())) - Reply(URIForFile::canonicalize(*Result, Params.uri.file())); - else - Reply(llvm::None); + Server->switchSourceHeader( + Params.uri.file(), + [Reply = std::move(Reply), + Params](llvm::Expected> Path) mutable { + if (!Path) + return Reply(Path.takeError()); + if (*Path) + Reply(URIForFile::canonicalize(**Path, Params.uri.file())); + return Reply(llvm::None); + }); } void ClangdLSPServer::onDocumentHighlight( @@ -1125,6 +1151,30 @@ void ClangdLSPServer::onSymbolInfo(const TextDocumentPositionParams &Params, std::move(Reply)); } +void ClangdLSPServer::onSelectionRange( + const SelectionRangeParams &Params, + Callback> Reply) { + if (Params.positions.size() != 1) { + elog("{0} positions provided to SelectionRange. Supports exactly one " + "position.", + Params.positions.size()); + return Reply(llvm::make_error( + "SelectionRange supports exactly one position", + ErrorCode::InvalidRequest)); + } + Server->semanticRanges( + Params.textDocument.uri.file(), Params.positions[0], + [Reply = std::move(Reply)]( + llvm::Expected> Ranges) mutable { + if (!Ranges) { + return Reply(Ranges.takeError()); + } + std::vector Result; + Result.emplace_back(render(std::move(*Ranges))); + return Reply(std::move(Result)); + }); +} + ClangdLSPServer::ClangdLSPServer( class Transport &Transp, const FileSystemProvider &FSProvider, const clangd::CodeCompleteOptions &CCOpts, @@ -1167,6 +1217,7 @@ ClangdLSPServer::ClangdLSPServer( MsgHandler->bind("textDocument/symbolInfo", &ClangdLSPServer::onSymbolInfo); MsgHandler->bind("textDocument/typeHierarchy", &ClangdLSPServer::onTypeHierarchy); MsgHandler->bind("typeHierarchy/resolve", &ClangdLSPServer::onResolveTypeHierarchy); + MsgHandler->bind("textDocument/selectionRange", &ClangdLSPServer::onSelectionRange); // clang-format on } diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h index 4e7e042bb9ada..3f9f1cc9c90dc 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.h +++ b/clang-tools-extra/clangd/ClangdLSPServer.h @@ -107,6 +107,8 @@ class ClangdLSPServer : private DiagnosticsConsumer { void onChangeConfiguration(const DidChangeConfigurationParams &); void onSymbolInfo(const TextDocumentPositionParams &, Callback>); + void onSelectionRange(const SelectionRangeParams &, + Callback>); std::vector getFixes(StringRef File, const clangd::Diagnostic &D); diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp index 5b850e6ab4772..b59bb4136259c 100644 --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -11,12 +11,14 @@ #include "FindSymbols.h" #include "Format.h" #include "FormattedString.h" +#include "HeaderSourceSwitch.h" #include "Headers.h" #include "Logger.h" #include "ParsedAST.h" #include "Preamble.h" #include "Protocol.h" #include "SemanticHighlighting.h" +#include "SemanticSelection.h" #include "SourceCode.h" #include "TUScheduler.h" #include "Trace.h" @@ -125,8 +127,8 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB, // critical paths. WorkScheduler( CDB, Opts.AsyncThreadsCount, Opts.StorePreamblesInMemory, - std::make_unique( - DynamicIdx.get(), DiagConsumer, Opts.SemanticHighlighting), + std::make_unique(DynamicIdx.get(), DiagConsumer, + Opts.SemanticHighlighting), Opts.UpdateDebounce, Opts.RetentionPolicy) { // Adds an index to the stack, at higher priority than existing indexes. auto AddIndex = [&](SymbolIndex *Idx) { @@ -447,61 +449,24 @@ void ClangdServer::locateSymbolAt(PathRef File, Position Pos, WorkScheduler.runWithAST("Definitions", File, std::move(Action)); } -llvm::Optional ClangdServer::switchSourceHeader(PathRef Path) { - - llvm::StringRef SourceExtensions[] = {".cpp", ".c", ".cc", ".cxx", - ".c++", ".m", ".mm"}; - llvm::StringRef HeaderExtensions[] = {".h", ".hh", ".hpp", ".hxx", ".inc"}; - - llvm::StringRef PathExt = llvm::sys::path::extension(Path); - - // Lookup in a list of known extensions. - auto SourceIter = - llvm::find_if(SourceExtensions, [&PathExt](PathRef SourceExt) { - return SourceExt.equals_lower(PathExt); - }); - bool IsSource = SourceIter != std::end(SourceExtensions); - - auto HeaderIter = - llvm::find_if(HeaderExtensions, [&PathExt](PathRef HeaderExt) { - return HeaderExt.equals_lower(PathExt); - }); - - bool IsHeader = HeaderIter != std::end(HeaderExtensions); - - // We can only switch between the known extensions. - if (!IsSource && !IsHeader) - return None; - - // Array to lookup extensions for the switch. An opposite of where original - // extension was found. - llvm::ArrayRef NewExts; - if (IsSource) - NewExts = HeaderExtensions; - else - NewExts = SourceExtensions; - - // Storage for the new path. - llvm::SmallString<128> NewPath = llvm::StringRef(Path); - - // Instance of vfs::FileSystem, used for file existence checks. - auto FS = FSProvider.getFileSystem(); - - // Loop through switched extension candidates. - for (llvm::StringRef NewExt : NewExts) { - llvm::sys::path::replace_extension(NewPath, NewExt); - if (FS->exists(NewPath)) - return NewPath.str().str(); // First str() to convert from SmallString to - // StringRef, second to convert from StringRef - // to std::string - - // Also check NewExt in upper-case, just in case. - llvm::sys::path::replace_extension(NewPath, NewExt.upper()); - if (FS->exists(NewPath)) - return NewPath.str().str(); - } - - return None; +void ClangdServer::switchSourceHeader( + PathRef Path, Callback> CB) { + // We want to return the result as fast as possible, stragety is: + // 1) use the file-only heuristic, it requires some IO but it is much + // faster than building AST, but it only works when .h/.cc files are in + // the same directory. + // 2) if 1) fails, we use the AST&Index approach, it is slower but supports + // different code layout. + if (auto CorrespondingFile = + getCorrespondingHeaderOrSource(Path, FSProvider.getFileSystem())) + return CB(std::move(CorrespondingFile)); + auto Action = [Path, CB = std::move(CB), + this](llvm::Expected InpAST) mutable { + if (!InpAST) + return CB(InpAST.takeError()); + CB(getCorrespondingHeaderOrSource(Path, InpAST->AST, Index)); + }; + WorkScheduler.runWithAST("SwitchHeaderSource", Path, std::move(Action)); } llvm::Expected @@ -620,6 +585,17 @@ void ClangdServer::symbolInfo(PathRef File, Position Pos, WorkScheduler.runWithAST("SymbolInfo", File, std::move(Action)); } +void ClangdServer::semanticRanges(PathRef File, Position Pos, + Callback> CB) { + auto Action = + [Pos, CB = std::move(CB)](llvm::Expected InpAST) mutable { + if (!InpAST) + return CB(InpAST.takeError()); + CB(clangd::getSemanticRanges(InpAST->AST, Pos)); + }; + WorkScheduler.runWithAST("SemanticRanges", File, std::move(Action)); +} + std::vector> ClangdServer::getUsedBytesPerFile() const { return WorkScheduler.getUsedBytesPerFile(); diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h index 83a74ff54c51b..c04dc50849f8f 100644 --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -51,8 +51,6 @@ class DiagnosticsConsumer { virtual void onFileUpdated(PathRef File, const TUStatus &Status){}; /// Called by ClangdServer when some \p Highlightings for \p File are ready. - /// \p NumLines are the number of lines in the file where the highlightings - /// where generated from. virtual void onHighlightingsReady(PathRef File, std::vector Highlightings) {} @@ -194,9 +192,10 @@ class ClangdServer { void locateSymbolAt(PathRef File, Position Pos, Callback> CB); - /// Helper function that returns a path to the corresponding source file when - /// given a header file and vice versa. - llvm::Optional switchSourceHeader(PathRef Path); + /// Switch to a corresponding source file when given a header file, and vice + /// versa. + void switchSourceHeader(PathRef Path, + Callback> CB); /// Get document highlights for a given position. void findDocumentHighlights(PathRef File, Position Pos, @@ -279,6 +278,10 @@ class ClangdServer { void symbolInfo(PathRef File, Position Pos, Callback> CB); + /// Get semantic ranges around a specified position in a file. + void semanticRanges(PathRef File, Position Pos, + Callback> CB); + /// Returns estimated memory usage for each of the currently open files. /// The order of results is unspecified. /// Overall memory usage of clangd may be significantly more than reported diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp index b96636aaf63be..bc826ba0adcd6 100644 --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -318,11 +318,8 @@ struct CodeCompletionBuilder { // Turn absolute path into a literal string that can be #included. auto Inserted = [&](llvm::StringRef Header) -> llvm::Expected> { - auto DeclaringURI = - URI::parse(C.IndexResult->CanonicalDeclaration.FileURI); - if (!DeclaringURI) - return DeclaringURI.takeError(); - auto ResolvedDeclaring = URI::resolve(*DeclaringURI, FileName); + auto ResolvedDeclaring = + URI::resolve(C.IndexResult->CanonicalDeclaration.FileURI, FileName); if (!ResolvedDeclaring) return ResolvedDeclaring.takeError(); auto ResolvedInserted = toHeaderFile(Header, FileName); @@ -1033,8 +1030,8 @@ void loadMainFilePreambleMacros(const Preprocessor &PP, PP.getIdentifierTable().getExternalIdentifierLookup(); if (!PreambleIdentifiers || !PreambleMacros) return; - for (const auto &MacroName : Preamble.MainFileMacros) - if (auto *II = PreambleIdentifiers->get(MacroName)) + for (const auto &MacroName : Preamble.Macros.Names) + if (auto *II = PreambleIdentifiers->get(MacroName.getKey())) if (II->isOutOfDate()) PreambleMacros->updateOutOfDateIdentifier(*II); } diff --git a/clang-tools-extra/clangd/CollectMacros.h b/clang-tools-extra/clangd/CollectMacros.h new file mode 100644 index 0000000000000..21227e1ed570f --- /dev/null +++ b/clang-tools-extra/clangd/CollectMacros.h @@ -0,0 +1,74 @@ +//===--- CollectMacros.h -----------------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_COLLECTEDMACROS_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_COLLECTEDMACROS_H + +#include "Protocol.h" +#include "SourceCode.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Lex/PPCallbacks.h" +#include + +namespace clang { +namespace clangd { + +struct MainFileMacros { + llvm::StringSet<> Names; + // Instead of storing SourceLocation, we have to store the token range because + // SourceManager from preamble is not available when we build the AST. + std::vector Ranges; +}; + +/// Collects macro definitions and expansions in the main file. It is used to: +/// - collect macros in the preamble section of the main file (in Preamble.cpp) +/// - collect macros after the preamble of the main file (in ParsedAST.cpp) +class CollectMainFileMacros : public PPCallbacks { +public: + explicit CollectMainFileMacros(const SourceManager &SM, + const LangOptions &LangOpts, + MainFileMacros &Out) + : SM(SM), LangOpts(LangOpts), Out(Out) {} + + void FileChanged(SourceLocation Loc, FileChangeReason, + SrcMgr::CharacteristicKind, FileID) override { + InMainFile = isInsideMainFile(Loc, SM); + } + + void MacroDefined(const Token &MacroName, const MacroDirective *MD) override { + add(MacroName, MD->getMacroInfo()); + } + + void MacroExpands(const Token &MacroName, const MacroDefinition &MD, + SourceRange Range, const MacroArgs *Args) override { + add(MacroName, MD.getMacroInfo()); + } + +private: + void add(const Token &MacroNameTok, const MacroInfo *MI) { + if (!InMainFile) + return; + auto Loc = MacroNameTok.getLocation(); + if (Loc.isMacroID()) + return; + + if (auto Range = getTokenRange(SM, LangOpts, MacroNameTok.getLocation())) { + Out.Names.insert(MacroNameTok.getIdentifierInfo()->getName()); + Out.Ranges.push_back(*Range); + } + } + const SourceManager &SM; + const LangOptions &LangOpts; + bool InMainFile = true; + MainFileMacros &Out; +}; + +} // namespace clangd +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_COLLECTEDMACROS_H diff --git a/clang-tools-extra/clangd/FileDistance.cpp b/clang-tools-extra/clangd/FileDistance.cpp index a6a65ab7ead25..a9ce3e514bcc1 100644 --- a/clang-tools-extra/clangd/FileDistance.cpp +++ b/clang-tools-extra/clangd/FileDistance.cpp @@ -208,7 +208,7 @@ createScopeFileDistance(llvm::ArrayRef QueryScopes) { Param.MaxUpTraversals = std::max(Path.second - 1, 0); Sources[Path.first] = std::move(Param); } - return FileDistance(Sources, Opts); + return FileDistance(std::move(Sources), Opts); } ScopeDistance::ScopeDistance(llvm::ArrayRef QueryScopes) diff --git a/clang-tools-extra/clangd/FindSymbols.cpp b/clang-tools-extra/clangd/FindSymbols.cpp index 287a8a4df0b43..619f1a5cfdbcd 100644 --- a/clang-tools-extra/clangd/FindSymbols.cpp +++ b/clang-tools-extra/clangd/FindSymbols.cpp @@ -43,18 +43,11 @@ llvm::Expected symbolToLocation(const Symbol &Sym, llvm::StringRef HintPath) { // Prefer the definition over e.g. a function declaration in a header auto &CD = Sym.Definition ? Sym.Definition : Sym.CanonicalDeclaration; - auto Uri = URI::parse(CD.FileURI); - if (!Uri) { - return llvm::make_error( - formatv("Could not parse URI '{0}' for symbol '{1}'.", CD.FileURI, - Sym.Name), - llvm::inconvertibleErrorCode()); - } - auto Path = URI::resolve(*Uri, HintPath); + auto Path = URI::resolve(CD.FileURI, HintPath); if (!Path) { return llvm::make_error( - formatv("Could not resolve path for URI '{0}' for symbol '{1}'.", - Uri->toString(), Sym.Name), + formatv("Could not resolve path for symbol '{0}': {1}", + Sym.Name, llvm::toString(Path.takeError())), llvm::inconvertibleErrorCode()); } Location L; diff --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp index 2631185b47c89..ffe808d579637 100644 --- a/clang-tools-extra/clangd/FindTarget.cpp +++ b/clang-tools-extra/clangd/FindTarget.cpp @@ -10,22 +10,35 @@ #include "AST.h" #include "Logger.h" #include "clang/AST/ASTTypeTraits.h" +#include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclVisitor.h" #include "clang/AST/DeclarationName.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/NestedNameSpecifier.h" +#include "clang/AST/PrettyPrinter.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/StmtVisitor.h" +#include "clang/AST/TemplateBase.h" #include "clang/AST/Type.h" +#include "clang/AST/TypeLoc.h" #include "clang/AST/TypeLocVisitor.h" +#include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" +#include namespace clang { namespace clangd { namespace { +using ast_type_traits::DynTypedNode; LLVM_ATTRIBUTE_UNUSED std::string nodeToString(const ast_type_traits::DynTypedNode &N) { @@ -177,6 +190,10 @@ struct TargetFinder { D = USD; Outer.add(D, Flags); } + void VisitOverloadExpr(const OverloadExpr *OE) { + for (auto *D : OE->decls()) + Outer.add(D, Flags); + } void VisitCXXConstructExpr(const CXXConstructExpr *CCE) { Outer.add(CCE->getConstructor(), Flags); } @@ -348,12 +365,326 @@ allTargetDecls(const ast_type_traits::DynTypedNode &N) { llvm::SmallVector targetDecl(const ast_type_traits::DynTypedNode &N, DeclRelationSet Mask) { llvm::SmallVector Result; - for (const auto &Entry : allTargetDecls(N)) + for (const auto &Entry : allTargetDecls(N)) { if (!(Entry.second & ~Mask)) Result.push_back(Entry.first); + } return Result; } +namespace { +/// Find declarations explicitly referenced in the source code defined by \p N. +/// For templates, will prefer to return a template instantiation whenever +/// possible. However, can also return a template pattern if the specialization +/// cannot be picked, e.g. in dependent code or when there is no corresponding +/// Decl for a template instantitation, e.g. for templated using decls: +/// template using Ptr = T*; +/// Ptr x; +/// ^~~ there is no Decl for 'Ptr', so we return the template pattern. +llvm::SmallVector +explicitReferenceTargets(DynTypedNode N, DeclRelationSet Mask = {}) { + assert(!(Mask & (DeclRelation::TemplatePattern | + DeclRelation::TemplateInstantiation)) && + "explicitRefenceTargets handles templates on its own"); + auto Decls = allTargetDecls(N); + + // We prefer to return template instantiation, but fallback to template + // pattern if instantiation is not available. + Mask |= DeclRelation::TemplatePattern | DeclRelation::TemplateInstantiation; + + llvm::SmallVector TemplatePatterns; + llvm::SmallVector Targets; + bool SeenTemplateInstantiations = false; + for (auto &D : Decls) { + if (D.second & ~Mask) + continue; + if (D.second & DeclRelation::TemplatePattern) { + TemplatePatterns.push_back(llvm::cast(D.first)); + continue; + } + if (D.second & DeclRelation::TemplateInstantiation) + SeenTemplateInstantiations = true; + Targets.push_back(llvm::cast(D.first)); + } + if (!SeenTemplateInstantiations) + Targets.insert(Targets.end(), TemplatePatterns.begin(), + TemplatePatterns.end()); + return Targets; +} + +Optional refInDecl(const Decl *D) { + struct Visitor : ConstDeclVisitor { + llvm::Optional Ref; + + void VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) { + Ref = ReferenceLoc{D->getQualifierLoc(), + D->getIdentLocation(), + {D->getNominatedNamespaceAsWritten()}}; + } + + void VisitUsingDecl(const UsingDecl *D) { + Ref = ReferenceLoc{D->getQualifierLoc(), D->getLocation(), + explicitReferenceTargets(DynTypedNode::create(*D), + DeclRelation::Underlying)}; + } + + void VisitNamespaceAliasDecl(const NamespaceAliasDecl *D) { + Ref = ReferenceLoc{D->getQualifierLoc(), + D->getTargetNameLoc(), + {D->getAliasedNamespace()}}; + } + }; + + Visitor V; + V.Visit(D); + return V.Ref; +} + +Optional refInExpr(const Expr *E) { + struct Visitor : ConstStmtVisitor { + // FIXME: handle more complicated cases, e.g. ObjC, designated initializers. + llvm::Optional Ref; + + void VisitDeclRefExpr(const DeclRefExpr *E) { + Ref = ReferenceLoc{ + E->getQualifierLoc(), E->getNameInfo().getLoc(), {E->getFoundDecl()}}; + } + + void VisitMemberExpr(const MemberExpr *E) { + Ref = ReferenceLoc{E->getQualifierLoc(), + E->getMemberNameInfo().getLoc(), + {E->getFoundDecl()}}; + } + + void VisitOverloadExpr(const OverloadExpr *E) { + Ref = ReferenceLoc{E->getQualifierLoc(), E->getNameInfo().getLoc(), + llvm::SmallVector( + E->decls().begin(), E->decls().end())}; + } + }; + + Visitor V; + V.Visit(E); + return V.Ref; +} + +Optional refInTypeLoc(TypeLoc L) { + struct Visitor : TypeLocVisitor { + llvm::Optional Ref; + + void VisitElaboratedTypeLoc(ElaboratedTypeLoc L) { + // We only know about qualifier, rest if filled by inner locations. + Visit(L.getNamedTypeLoc().getUnqualifiedLoc()); + // Fill in the qualifier. + if (!Ref) + return; + assert(!Ref->Qualifier.hasQualifier() && "qualifier already set"); + Ref->Qualifier = L.getQualifierLoc(); + } + + void VisitTagTypeLoc(TagTypeLoc L) { + Ref = + ReferenceLoc{NestedNameSpecifierLoc(), L.getNameLoc(), {L.getDecl()}}; + } + + void VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc L) { + Ref = + ReferenceLoc{NestedNameSpecifierLoc(), L.getNameLoc(), {L.getDecl()}}; + } + + void VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc L) { + // We must ensure template type aliases are included in results if they + // were written in the source code, e.g. in + // template using valias = vector; + // ^valias x; + // 'explicitReferenceTargets' will return: + // 1. valias with mask 'Alias'. + // 2. 'vector' with mask 'Underlying'. + // we want to return only #1 in this case. + Ref = ReferenceLoc{ + NestedNameSpecifierLoc(), L.getTemplateNameLoc(), + explicitReferenceTargets(DynTypedNode::create(L.getType()), + DeclRelation::Alias)}; + } + void VisitDeducedTemplateSpecializationTypeLoc( + DeducedTemplateSpecializationTypeLoc L) { + Ref = ReferenceLoc{ + NestedNameSpecifierLoc(), L.getNameLoc(), + explicitReferenceTargets(DynTypedNode::create(L.getType()), + DeclRelation::Alias)}; + } + + void VisitDependentTemplateSpecializationTypeLoc( + DependentTemplateSpecializationTypeLoc L) { + Ref = ReferenceLoc{ + L.getQualifierLoc(), L.getTemplateNameLoc(), + explicitReferenceTargets(DynTypedNode::create(L.getType()))}; + } + + void VisitDependentNameTypeLoc(DependentNameTypeLoc L) { + Ref = ReferenceLoc{ + L.getQualifierLoc(), L.getNameLoc(), + explicitReferenceTargets(DynTypedNode::create(L.getType()))}; + } + + void VisitTypedefTypeLoc(TypedefTypeLoc L) { + Ref = ReferenceLoc{ + NestedNameSpecifierLoc(), L.getNameLoc(), {L.getTypedefNameDecl()}}; + } + }; + + Visitor V; + V.Visit(L.getUnqualifiedLoc()); + return V.Ref; +} + +class ExplicitReferenceColletor + : public RecursiveASTVisitor { +public: + ExplicitReferenceColletor(llvm::function_ref Out) + : Out(Out) { + assert(Out); + } + + bool VisitTypeLoc(TypeLoc TTL) { + if (TypeLocsToSkip.count(TTL.getBeginLoc().getRawEncoding())) + return true; + visitNode(DynTypedNode::create(TTL)); + return true; + } + + bool TraverseElaboratedTypeLoc(ElaboratedTypeLoc L) { + // ElaboratedTypeLoc will reports information for its inner type loc. + // Otherwise we loose information about inner types loc's qualifier. + TypeLoc Inner = L.getNamedTypeLoc().getUnqualifiedLoc(); + TypeLocsToSkip.insert(Inner.getBeginLoc().getRawEncoding()); + return RecursiveASTVisitor::TraverseElaboratedTypeLoc(L); + } + + bool VisitExpr(Expr *E) { + visitNode(DynTypedNode::create(*E)); + return true; + } + + // We re-define Traverse*, since there's no corresponding Visit*. + // TemplateArgumentLoc is the only way to get locations for references to + // template template parameters. + bool TraverseTemplateArgumentLoc(TemplateArgumentLoc A) { + switch (A.getArgument().getKind()) { + case TemplateArgument::Template: + case TemplateArgument::TemplateExpansion: + reportReference(ReferenceLoc{A.getTemplateQualifierLoc(), + A.getTemplateNameLoc(), + {A.getArgument() + .getAsTemplateOrTemplatePattern() + .getAsTemplateDecl()}}, + DynTypedNode::create(A.getArgument())); + break; + case TemplateArgument::Declaration: + break; // FIXME: can this actually happen in TemplateArgumentLoc? + case TemplateArgument::Integral: + case TemplateArgument::Null: + case TemplateArgument::NullPtr: + break; // no references. + case TemplateArgument::Pack: + case TemplateArgument::Type: + case TemplateArgument::Expression: + break; // Handled by VisitType and VisitExpression. + }; + return RecursiveASTVisitor::TraverseTemplateArgumentLoc(A); + } + + bool VisitDecl(Decl *D) { + visitNode(DynTypedNode::create(*D)); + return true; + } + + // We have to use Traverse* because there is no corresponding Visit*. + bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc L) { + if (!L.getNestedNameSpecifier()) + return true; + visitNode(DynTypedNode::create(L)); + // Inner type is missing information about its qualifier, skip it. + if (auto TL = L.getTypeLoc()) + TypeLocsToSkip.insert(TL.getBeginLoc().getRawEncoding()); + return RecursiveASTVisitor::TraverseNestedNameSpecifierLoc(L); + } + +private: + /// Obtain information about a reference directly defined in \p N. Does not + /// recurse into child nodes, e.g. do not expect references for constructor + /// initializers + /// + /// Any of the fields in the returned structure can be empty, but not all of + /// them, e.g. + /// - for implicitly generated nodes (e.g. MemberExpr from range-based-for), + /// source location information may be missing, + /// - for dependent code, targets may be empty. + /// + /// (!) For the purposes of this function declarations are not considered to + /// be references. However, declarations can have references inside them, + /// e.g. 'namespace foo = std' references namespace 'std' and this + /// function will return the corresponding reference. + llvm::Optional explicitReference(DynTypedNode N) { + if (auto *D = N.get()) + return refInDecl(D); + if (auto *E = N.get()) + return refInExpr(E); + if (auto *NNSL = N.get()) + return ReferenceLoc{NNSL->getPrefix(), NNSL->getLocalBeginLoc(), + explicitReferenceTargets(DynTypedNode::create( + *NNSL->getNestedNameSpecifier()))}; + if (const TypeLoc *TL = N.get()) + return refInTypeLoc(*TL); + if (const CXXCtorInitializer *CCI = N.get()) { + if (CCI->isBaseInitializer()) + return refInTypeLoc(CCI->getBaseClassLoc()); + assert(CCI->isAnyMemberInitializer()); + return ReferenceLoc{NestedNameSpecifierLoc(), + CCI->getMemberLocation(), + {CCI->getAnyMember()}}; + } + // We do not have location information for other nodes (QualType, etc) + return llvm::None; + } + + void visitNode(DynTypedNode N) { + auto Ref = explicitReference(N); + if (!Ref) + return; + reportReference(*Ref, N); + } + + void reportReference(const ReferenceLoc &Ref, DynTypedNode N) { + // Our promise is to return only references from the source code. If we lack + // location information, skip these nodes. + // Normally this should not happen in practice, unless there are bugs in the + // traversals or users started the traversal at an implicit node. + if (Ref.NameLoc.isInvalid()) { + dlog("invalid location at node {0}", nodeToString(N)); + return; + } + Out(Ref); + } + + llvm::function_ref Out; + /// TypeLocs starting at these locations must be skipped, see + /// TraverseElaboratedTypeSpecifierLoc for details. + llvm::DenseSet TypeLocsToSkip; +}; +} // namespace + +void findExplicitReferences(const Stmt *S, + llvm::function_ref Out) { + assert(S); + ExplicitReferenceColletor(Out).TraverseStmt(const_cast(S)); +} +void findExplicitReferences(const Decl *D, + llvm::function_ref Out) { + assert(D); + ExplicitReferenceColletor(Out).TraverseDecl(const_cast(D)); +} + llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, DeclRelation R) { switch (R) { #define REL_CASE(X) \ @@ -378,5 +709,26 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, DeclRelationSet RS) { return OS; } +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ReferenceLoc R) { + // note we cannot print R.NameLoc without a source manager. + OS << "targets = {"; + bool First = true; + for (const NamedDecl *T : R.Targets) { + if (!First) + OS << ", "; + else + First = false; + OS << printQualifiedName(*T) << printTemplateSpecializationArgs(*T); + } + OS << "}"; + if (R.Qualifier) { + OS << ", qualifier = '"; + R.Qualifier.getNestedNameSpecifier()->print(OS, + PrintingPolicy(LangOptions())); + OS << "'"; + } + return OS; +} + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/FindTarget.h b/clang-tools-extra/clangd/FindTarget.h index 1524603fa306f..80a8e787d6250 100644 --- a/clang-tools-extra/clangd/FindTarget.h +++ b/clang-tools-extra/clangd/FindTarget.h @@ -19,9 +19,18 @@ // //===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_FINDTARGET_H +#define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_FINDTARGET_H + #include "clang/AST/ASTTypeTraits.h" +#include "clang/AST/NestedNameSpecifier.h" +#include "clang/AST/Stmt.h" #include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/raw_ostream.h" #include @@ -69,6 +78,34 @@ class DeclRelationSet; llvm::SmallVector targetDecl(const ast_type_traits::DynTypedNode &, DeclRelationSet Mask); +/// Information about a reference written in the source code, independent of the +/// actual AST node that this reference lives in. +/// Useful for tools that are source-aware, e.g. refactorings. +struct ReferenceLoc { + /// Contains qualifier written in the code, if any, e.g. 'ns::' for 'ns::foo'. + NestedNameSpecifierLoc Qualifier; + /// Start location of the last name part, i.e. 'foo' in 'ns::foo'. + SourceLocation NameLoc; + // FIXME: add info about template arguments. + /// A list of targets referenced by this name. Normally this has a single + /// element, but multiple is also possible, e.g. in case of using declarations + /// or unresolved overloaded functions. + /// For dependent and unresolved references, Targets can also be empty. + llvm::SmallVector Targets; +}; +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ReferenceLoc R); + +/// Recursively traverse \p S and report all references explicitly written in +/// the code. The main use-case is refactorings that need to process all +/// references in some subrange of the file and apply simple edits, e.g. add +/// qualifiers. +/// FIXME: currently this does not report references to overloaded operators. +/// FIXME: extend to report location information about declaration names too. +void findExplicitReferences(const Stmt *S, + llvm::function_ref Out); +void findExplicitReferences(const Decl *D, + llvm::function_ref Out); + /// Similar to targetDecl(), however instead of applying a filter, all possible /// decls are returned along with their DeclRelationSets. /// This is suitable for indexing, where everything is recorded and filtering @@ -142,3 +179,5 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &, DeclRelationSet); } // namespace clangd } // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_FINDTARGET_H diff --git a/clang-tools-extra/clangd/HeaderSourceSwitch.cpp b/clang-tools-extra/clangd/HeaderSourceSwitch.cpp new file mode 100644 index 0000000000000..535c8d6d8e1dc --- /dev/null +++ b/clang-tools-extra/clangd/HeaderSourceSwitch.cpp @@ -0,0 +1,153 @@ +//===--- HeaderSourceSwitch.cpp - --------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "HeaderSourceSwitch.h" +#include "AST.h" +#include "Logger.h" +#include "index/SymbolCollector.h" +#include "clang/AST/Decl.h" + +namespace clang { +namespace clangd { + +llvm::Optional getCorrespondingHeaderOrSource( + const Path &OriginalFile, + llvm::IntrusiveRefCntPtr VFS) { + llvm::StringRef SourceExtensions[] = {".cpp", ".c", ".cc", ".cxx", + ".c++", ".m", ".mm"}; + llvm::StringRef HeaderExtensions[] = {".h", ".hh", ".hpp", ".hxx", ".inc"}; + + llvm::StringRef PathExt = llvm::sys::path::extension(OriginalFile); + + // Lookup in a list of known extensions. + auto SourceIter = + llvm::find_if(SourceExtensions, [&PathExt](PathRef SourceExt) { + return SourceExt.equals_lower(PathExt); + }); + bool IsSource = SourceIter != std::end(SourceExtensions); + + auto HeaderIter = + llvm::find_if(HeaderExtensions, [&PathExt](PathRef HeaderExt) { + return HeaderExt.equals_lower(PathExt); + }); + bool IsHeader = HeaderIter != std::end(HeaderExtensions); + + // We can only switch between the known extensions. + if (!IsSource && !IsHeader) + return None; + + // Array to lookup extensions for the switch. An opposite of where original + // extension was found. + llvm::ArrayRef NewExts; + if (IsSource) + NewExts = HeaderExtensions; + else + NewExts = SourceExtensions; + + // Storage for the new path. + llvm::SmallString<128> NewPath = llvm::StringRef(OriginalFile); + + // Loop through switched extension candidates. + for (llvm::StringRef NewExt : NewExts) { + llvm::sys::path::replace_extension(NewPath, NewExt); + if (VFS->exists(NewPath)) + return NewPath.str().str(); // First str() to convert from SmallString to + // StringRef, second to convert from StringRef + // to std::string + + // Also check NewExt in upper-case, just in case. + llvm::sys::path::replace_extension(NewPath, NewExt.upper()); + if (VFS->exists(NewPath)) + return NewPath.str().str(); + } + return None; +} + +llvm::Optional getCorrespondingHeaderOrSource(const Path &OriginalFile, + ParsedAST &AST, + const SymbolIndex *Index) { + if (!Index) { + // FIXME: use the AST to do the inference. + return None; + } + LookupRequest Request; + // Find all symbols present in the original file. + for (const auto *D : getIndexableLocalDecls(AST)) { + if (auto ID = getSymbolID(D)) + Request.IDs.insert(*ID); + } + llvm::StringMap Candidates; // Target path => score. + auto AwardTarget = [&](const char *TargetURI) { + if (auto TargetPath = URI::resolve(TargetURI, OriginalFile)) { + if (*TargetPath != OriginalFile) // exclude the original file. + ++Candidates[*TargetPath]; + }; + }; + // If we switch from a header, we are looking for the implementation + // file, so we use the definition loc; otherwise we look for the header file, + // we use the decl loc; + // + // For each symbol in the original file, we get its target location (decl or + // def) from the index, then award that target file. + bool IsHeader = AST.getASTContext().getLangOpts().IsHeaderFile; + Index->lookup(Request, [&](const Symbol &Sym) { + if (IsHeader) + AwardTarget(Sym.Definition.FileURI); + else + AwardTarget(Sym.CanonicalDeclaration.FileURI); + }); + // FIXME: our index doesn't have any interesting information (this could be + // that the background-index is not finished), we should use the decl/def + // locations from the AST to do the inference (from .cc to .h). + if (Candidates.empty()) + return None; + + // Pickup the winner, who contains most of symbols. + // FIXME: should we use other signals (file proximity) to help score? + auto Best = Candidates.begin(); + for (auto It = Candidates.begin(); It != Candidates.end(); ++It) { + if (It->second > Best->second) + Best = It; + else if (It->second == Best->second && It->first() < Best->first()) + // Select the first one in the lexical order if we have multiple + // candidates. + Best = It; + } + return Path(Best->first()); +} + +std::vector getIndexableLocalDecls(ParsedAST &AST) { + std::vector Results; + std::function TraverseDecl = [&](Decl *D) { + auto *ND = llvm::dyn_cast(D); + if (!ND || ND->isImplicit()) + return; + if (!SymbolCollector::shouldCollectSymbol(*ND, D->getASTContext(), {}, + /*IsMainFileSymbol=*/false)) + return; + if (!llvm::isa(ND)) { + // Visit the children, but we skip function decls as we are not interested + // in the function body. + if (auto *Scope = llvm::dyn_cast(ND)) { + for (auto *D : Scope->decls()) + TraverseDecl(D); + } + } + if (llvm::isa(D)) + return; // namespace is indexable, but we're not interested. + Results.push_back(D); + }; + // Traverses the ParsedAST directly to collect all decls present in the main + // file. + for (auto *TopLevel : AST.getLocalTopLevelDecls()) + TraverseDecl(TopLevel); + return Results; +} + +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/HeaderSourceSwitch.h b/clang-tools-extra/clangd/HeaderSourceSwitch.h new file mode 100644 index 0000000000000..a971b385d74ce --- /dev/null +++ b/clang-tools-extra/clangd/HeaderSourceSwitch.h @@ -0,0 +1,37 @@ +//===--- HeaderSourceSwitch.h - ----------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_HEADERSOURCESWITCH_H +#define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_HEADERSOURCESWITCH_H + +#include "ParsedAST.h" +#include "llvm/ADT/Optional.h" + +namespace clang { +namespace clangd { + +/// Given a header file, returns the best matching source file, and vice visa. +/// It only uses the filename heuristics to do the inference. +llvm::Optional getCorrespondingHeaderOrSource( + const Path &OriginalFile, + llvm::IntrusiveRefCntPtr VFS); + +/// Given a header file, returns the best matching source file, and vice visa. +/// The heuristics incorporate with the AST and the index (if provided). +llvm::Optional getCorrespondingHeaderOrSource(const Path &OriginalFile, + ParsedAST &AST, + const SymbolIndex *Index); + +/// Returns all indexable decls that are present in the main file of the AST. +/// Exposed for unittests. +std::vector getIndexableLocalDecls(ParsedAST &AST); + +} // namespace clangd +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_HEADERSOURCESWITCH_H diff --git a/clang-tools-extra/clangd/IncludeFixer.cpp b/clang-tools-extra/clangd/IncludeFixer.cpp index 081dad83583b4..93dee2eabfc6d 100644 --- a/clang-tools-extra/clangd/IncludeFixer.cpp +++ b/clang-tools-extra/clangd/IncludeFixer.cpp @@ -144,10 +144,8 @@ std::vector IncludeFixer::fixIncompleteType(const Type &T) const { std::vector IncludeFixer::fixesForSymbols(const SymbolSlab &Syms) const { auto Inserted = [&](const Symbol &Sym, llvm::StringRef Header) -> llvm::Expected> { - auto DeclaringURI = URI::parse(Sym.CanonicalDeclaration.FileURI); - if (!DeclaringURI) - return DeclaringURI.takeError(); - auto ResolvedDeclaring = URI::resolve(*DeclaringURI, File); + auto ResolvedDeclaring = + URI::resolve(Sym.CanonicalDeclaration.FileURI, File); if (!ResolvedDeclaring) return ResolvedDeclaring.takeError(); auto ResolvedInserted = toHeaderFile(Header, File); diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp index 3619144eae92d..7c397504a00bc 100644 --- a/clang-tools-extra/clangd/ParsedAST.cpp +++ b/clang-tools-extra/clangd/ParsedAST.cpp @@ -49,6 +49,11 @@ #include #include +// Force the linker to link in Clang-tidy modules. +// clangd doesn't support the static analyzer. +#define CLANG_TIDY_DISABLE_STATIC_ANALYZER_CHECKS +#include "../clang-tidy/ClangTidyForceLinker.h" + namespace clang { namespace clangd { namespace { @@ -98,33 +103,6 @@ class ClangdFrontendAction : public SyntaxOnlyAction { std::vector TopLevelDecls; }; -// This collects macro expansions/definitions in the main file. -// (Contrast with CollectMainFileMacros in Preamble.cpp, which collects macro -// *definitions* in the preamble region of the main file). -class CollectMainFileMacros : public PPCallbacks { - const SourceManager &SM; - std::vector &MainFileMacroLocs; - - void addLoc(SourceLocation Loc) { - if (!Loc.isMacroID() && isInsideMainFile(Loc, SM)) - MainFileMacroLocs.push_back(Loc); - } - -public: - CollectMainFileMacros(const SourceManager &SM, - std::vector &MainFileMacroLocs) - : SM(SM), MainFileMacroLocs(MainFileMacroLocs) {} - - void MacroDefined(const Token &MacroNameTok, - const MacroDirective *MD) override { - addLoc(MacroNameTok.getLocation()); - } - void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, - SourceRange Range, const MacroArgs *Args) override { - addLoc(MacroNameTok.getLocation()); - } -}; - // When using a preamble, only preprocessor events outside its bounds are seen. // This is almost what we want: replaying transitive preprocessing wastes time. // However this confuses clang-tidy checks: they don't see any #includes! @@ -288,7 +266,7 @@ ParsedAST::build(std::unique_ptr CI, CTContext->setDiagnosticsEngine(&Clang->getDiagnostics()); CTContext->setASTContext(&Clang->getASTContext()); CTContext->setCurrentFile(Filename); - CTFactories.createChecks(CTContext.getPointer(), CTChecks); + CTChecks = CTFactories.createChecks(CTContext.getPointer()); ASTDiags.setLevelAdjuster([&CTContext](DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info) { if (CTContext) { @@ -362,11 +340,14 @@ ParsedAST::build(std::unique_ptr CI, // (We can't *just* use the replayed includes, they don't have Resolved path). Clang->getPreprocessor().addPPCallbacks( collectIncludeStructureCallback(Clang->getSourceManager(), &Includes)); - // Collect the macro expansions in the main file. - std::vector MainFileMacroExpLocs; + // Copy over the macros in the preamble region of the main file, and combine + // with non-preamble macros below. + MainFileMacros Macros; + if (Preamble) + Macros = Preamble->Macros; Clang->getPreprocessor().addPPCallbacks( std::make_unique(Clang->getSourceManager(), - MainFileMacroExpLocs)); + Clang->getLangOpts(), Macros)); // Copy over the includes from the preamble, then combine with the // non-preamble includes below. @@ -420,9 +401,9 @@ ParsedAST::build(std::unique_ptr CI, Diags.insert(Diags.end(), D.begin(), D.end()); } return ParsedAST(std::move(Preamble), std::move(Clang), std::move(Action), - std::move(Tokens), std::move(MainFileMacroExpLocs), - std::move(ParsedDecls), std::move(Diags), - std::move(Includes), std::move(CanonIncludes)); + std::move(Tokens), std::move(Macros), std::move(ParsedDecls), + std::move(Diags), std::move(Includes), + std::move(CanonIncludes)); } ParsedAST::ParsedAST(ParsedAST &&Other) = default; @@ -460,9 +441,7 @@ llvm::ArrayRef ParsedAST::getLocalTopLevelDecls() { return LocalTopLevelDecls; } -llvm::ArrayRef ParsedAST::getMacros() const { - return MacroIdentifierLocs; -} +const MainFileMacros &ParsedAST::getMacros() const { return Macros; } const std::vector &ParsedAST::getDiagnostics() const { return Diags; } @@ -509,15 +488,13 @@ const CanonicalIncludes &ParsedAST::getCanonicalIncludes() const { ParsedAST::ParsedAST(std::shared_ptr Preamble, std::unique_ptr Clang, std::unique_ptr Action, - syntax::TokenBuffer Tokens, - std::vector MacroIdentifierLocs, + syntax::TokenBuffer Tokens, MainFileMacros Macros, std::vector LocalTopLevelDecls, std::vector Diags, IncludeStructure Includes, CanonicalIncludes CanonIncludes) : Preamble(std::move(Preamble)), Clang(std::move(Clang)), Action(std::move(Action)), Tokens(std::move(Tokens)), - MacroIdentifierLocs(std::move(MacroIdentifierLocs)), - Diags(std::move(Diags)), + Macros(std::move(Macros)), Diags(std::move(Diags)), LocalTopLevelDecls(std::move(LocalTopLevelDecls)), Includes(std::move(Includes)), CanonIncludes(std::move(CanonIncludes)) { assert(this->Clang); @@ -549,32 +526,4 @@ buildAST(PathRef FileName, std::unique_ptr Invocation, } } // namespace clangd -namespace tidy { -// Force the linker to link in Clang-tidy modules. -#define LINK_TIDY_MODULE(X) \ - extern volatile int X##ModuleAnchorSource; \ - static int LLVM_ATTRIBUTE_UNUSED X##ModuleAnchorDestination = \ - X##ModuleAnchorSource -LINK_TIDY_MODULE(Abseil); -LINK_TIDY_MODULE(Android); -LINK_TIDY_MODULE(Boost); -LINK_TIDY_MODULE(Bugprone); -LINK_TIDY_MODULE(CERT); -LINK_TIDY_MODULE(CppCoreGuidelines); -LINK_TIDY_MODULE(Fuchsia); -LINK_TIDY_MODULE(Google); -LINK_TIDY_MODULE(HICPP); -LINK_TIDY_MODULE(LinuxKernel); -LINK_TIDY_MODULE(LLVM); -LINK_TIDY_MODULE(Misc); -LINK_TIDY_MODULE(Modernize); -// LINK_TIDY_MODULE(MPI); // clangd doesn't support static analyzer. -LINK_TIDY_MODULE(ObjC); -LINK_TIDY_MODULE(OpenMP); -LINK_TIDY_MODULE(Performance); -LINK_TIDY_MODULE(Portability); -LINK_TIDY_MODULE(Readability); -LINK_TIDY_MODULE(Zircon); -#undef LINK_TIDY_MODULE -} // namespace tidy } // namespace clang diff --git a/clang-tools-extra/clangd/ParsedAST.h b/clang-tools-extra/clangd/ParsedAST.h index f37e14d805168..0b4a6ab73df83 100644 --- a/clang-tools-extra/clangd/ParsedAST.h +++ b/clang-tools-extra/clangd/ParsedAST.h @@ -21,6 +21,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_PARSEDAST_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_PARSEDAST_H +#include "CollectMacros.h" #include "Compiler.h" #include "Diagnostics.h" #include "Headers.h" @@ -89,10 +90,9 @@ class ParsedAST { const IncludeStructure &getIncludeStructure() const; const CanonicalIncludes &getCanonicalIncludes() const; - /// Gets all macro locations (definition, expansions) present in the main - /// file. - /// NOTE: macros inside the preamble are not included. - llvm::ArrayRef getMacros() const; + /// Gets all macro references (definition, expansions) present in the main + /// file, including those in the preamble region. + const MainFileMacros &getMacros() const; /// Tokens recorded while parsing the main file. /// (!) does not have tokens from the preamble. const syntax::TokenBuffer &getTokens() const { return Tokens; } @@ -101,9 +101,9 @@ class ParsedAST { ParsedAST(std::shared_ptr Preamble, std::unique_ptr Clang, std::unique_ptr Action, syntax::TokenBuffer Tokens, - std::vector MainFileMacroExpLocs, - std::vector LocalTopLevelDecls, std::vector Diags, - IncludeStructure Includes, CanonicalIncludes CanonIncludes); + MainFileMacros Macros, std::vector LocalTopLevelDecls, + std::vector Diags, IncludeStructure Includes, + CanonicalIncludes CanonIncludes); // In-memory preambles must outlive the AST, it is important that this member // goes before Clang and Action. @@ -121,10 +121,8 @@ class ParsedAST { /// - Does not have spelled or expanded tokens for files from preamble. syntax::TokenBuffer Tokens; - /// The start locations of all macro definitions/expansions spelled **after** - /// preamble. - /// Does not include locations from inside other macro expansions. - std::vector MacroIdentifierLocs; + /// All macro definitions and expansions in the main file. + MainFileMacros Macros; // Data, stored after parsing. std::vector Diags; // Top-level decls inside the current file. Not that this does not include diff --git a/clang-tools-extra/clangd/Preamble.cpp b/clang-tools-extra/clangd/Preamble.cpp index d68c1ec97fa43..41e3c1368eb72 100644 --- a/clang-tools-extra/clangd/Preamble.cpp +++ b/clang-tools-extra/clangd/Preamble.cpp @@ -24,49 +24,14 @@ bool compileCommandsAreEqual(const tooling::CompileCommand &LHS, llvm::makeArrayRef(LHS.CommandLine).equals(RHS.CommandLine); } -// This collects macro definitions in the *preamble region* of the main file. -// (Contrast with CollectMainFileMacroExpansions in ParsedAST.cpp, which -// collects macro *expansions* in the rest of the main file. -class CollectMainFileMacros : public PPCallbacks { -public: - explicit CollectMainFileMacros(const SourceManager &SM, - std::vector *Out) - : SM(SM), Out(Out) {} - - void FileChanged(SourceLocation Loc, FileChangeReason, - SrcMgr::CharacteristicKind, FileID Prev) { - InMainFile = SM.isWrittenInMainFile(Loc); - } - - void MacroDefined(const Token &MacroName, const MacroDirective *MD) { - if (InMainFile) - MainFileMacros.insert(MacroName.getIdentifierInfo()->getName()); - } - - void EndOfMainFile() { - for (const auto &Entry : MainFileMacros) - Out->push_back(Entry.getKey()); - llvm::sort(*Out); - } - -private: - const SourceManager &SM; - bool InMainFile = true; - llvm::StringSet<> MainFileMacros; - std::vector *Out; -}; - class CppFilePreambleCallbacks : public PreambleCallbacks { public: CppFilePreambleCallbacks(PathRef File, PreambleParsedCallback ParsedCallback) - : File(File), ParsedCallback(ParsedCallback) { - } + : File(File), ParsedCallback(ParsedCallback) {} IncludeStructure takeIncludes() { return std::move(Includes); } - std::vector takeMainFileMacros() { - return std::move(MainFileMacros); - } + MainFileMacros takeMacros() { return std::move(Macros); } CanonicalIncludes takeCanonicalIncludes() { return std::move(CanonIncludes); } @@ -79,14 +44,17 @@ class CppFilePreambleCallbacks : public PreambleCallbacks { void BeforeExecute(CompilerInstance &CI) override { CanonIncludes.addSystemHeadersMapping(CI.getLangOpts()); + LangOpts = &CI.getLangOpts(); SourceMgr = &CI.getSourceManager(); } std::unique_ptr createPPCallbacks() override { - assert(SourceMgr && "SourceMgr must be set at this point"); + assert(SourceMgr && LangOpts && + "SourceMgr and LangOpts must be set at this point"); + return std::make_unique( collectIncludeStructureCallback(*SourceMgr, &Includes), - std::make_unique(*SourceMgr, &MainFileMacros)); + std::make_unique(*SourceMgr, *LangOpts, Macros)); } CommentHandler *getCommentHandler() override { @@ -99,20 +67,21 @@ class CppFilePreambleCallbacks : public PreambleCallbacks { PreambleParsedCallback ParsedCallback; IncludeStructure Includes; CanonicalIncludes CanonIncludes; - std::vector MainFileMacros; + MainFileMacros Macros; std::unique_ptr IWYUHandler = nullptr; - SourceManager *SourceMgr = nullptr; + const clang::LangOptions *LangOpts = nullptr; + const SourceManager *SourceMgr = nullptr; }; } // namespace PreambleData::PreambleData(PrecompiledPreamble Preamble, std::vector Diags, IncludeStructure Includes, - std::vector MainFileMacros, + MainFileMacros Macros, std::unique_ptr StatCache, CanonicalIncludes CanonIncludes) : Preamble(std::move(Preamble)), Diags(std::move(Diags)), - Includes(std::move(Includes)), MainFileMacros(std::move(MainFileMacros)), + Includes(std::move(Includes)), Macros(std::move(Macros)), StatCache(std::move(StatCache)), CanonIncludes(std::move(CanonIncludes)) { } @@ -181,7 +150,7 @@ buildPreamble(PathRef FileName, CompilerInvocation &CI, return std::make_shared( std::move(*BuiltPreamble), std::move(Diags), SerializedDeclsCollector.takeIncludes(), - SerializedDeclsCollector.takeMainFileMacros(), std::move(StatCache), + SerializedDeclsCollector.takeMacros(), std::move(StatCache), SerializedDeclsCollector.takeCanonicalIncludes()); } else { elog("Could not build a preamble for file {0}", FileName); diff --git a/clang-tools-extra/clangd/Preamble.h b/clang-tools-extra/clangd/Preamble.h index c1632ff02b8c3..55dcce76d2eec 100644 --- a/clang-tools-extra/clangd/Preamble.h +++ b/clang-tools-extra/clangd/Preamble.h @@ -22,6 +22,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_PREAMBLE_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_PREAMBLE_H +#include "CollectMacros.h" #include "Compiler.h" #include "Diagnostics.h" #include "FS.h" @@ -43,8 +44,7 @@ namespace clangd { /// be obtained during parsing must be eagerly captured and stored here. struct PreambleData { PreambleData(PrecompiledPreamble Preamble, std::vector Diags, - IncludeStructure Includes, - std::vector MainFileMacros, + IncludeStructure Includes, MainFileMacros Macros, std::unique_ptr StatCache, CanonicalIncludes CanonIncludes); @@ -57,7 +57,7 @@ struct PreambleData { // Macros defined in the preamble section of the main file. // Users care about headers vs main-file, not preamble vs non-preamble. // These should be treated as main-file entities e.g. for code completion. - std::vector MainFileMacros; + MainFileMacros Macros; // Cache of FS operations performed when building the preamble. // When reusing a preamble, this cache can be consumed to save IO. std::unique_ptr StatCache; diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp index fb1481da5b5e9..9b0fbc5b2f3f7 100644 --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -1073,5 +1073,18 @@ llvm::json::Value toJSON(const SemanticHighlightingParams &Highlighting) { }; } +bool fromJSON(const llvm::json::Value &Params, SelectionRangeParams &P) { + llvm::json::ObjectMapper O(Params); + return O && O.map("textDocument", P.textDocument) && + O.map("positions", P.positions); +} + +llvm::json::Value toJSON(const SelectionRange &Out) { + if (Out.parent) { + return llvm::json::Object{{"range", Out.range}, + {"parent", toJSON(*Out.parent)}}; + } + return llvm::json::Object{{"range", Out.range}}; +} } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h index d244febfce456..6540365dccd81 100644 --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -30,6 +30,7 @@ #include "llvm/Support/JSON.h" #include "llvm/Support/raw_ostream.h" #include +#include #include #include @@ -1222,6 +1223,28 @@ struct SemanticHighlightingParams { }; llvm::json::Value toJSON(const SemanticHighlightingParams &Highlighting); +struct SelectionRangeParams { + /// The text document. + TextDocumentIdentifier textDocument; + + /// The positions inside the text document. + std::vector positions; +}; +bool fromJSON(const llvm::json::Value &, SelectionRangeParams &); + +struct SelectionRange { + /** + * The range of this selection range. + */ + Range range; + /** + * The parent selection range containing this range. Therefore `parent.range` + * must contain `this.range`. + */ + std::unique_ptr parent; +}; +llvm::json::Value toJSON(const SelectionRange &); + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/Selection.cpp b/clang-tools-extra/clangd/Selection.cpp index 1877b1cf7b673..8451c988ab667 100644 --- a/clang-tools-extra/clangd/Selection.cpp +++ b/clang-tools-extra/clangd/Selection.cpp @@ -61,13 +61,13 @@ class SelectedTokens { // Associates any tokens overlapping [Begin, End) with an AST node. // Tokens that were already claimed by another AST node are not claimed again. - // Returns whether the node is selected in the sense of SelectionTree. - SelectionTree::Selection claim(unsigned Begin, unsigned End) { + // Updates Result if the node is selected in the sense of SelectionTree. + void claim(unsigned Begin, unsigned End, SelectionTree::Selection &Result) { assert(Begin <= End); // Fast-path for missing the selection entirely. if (Begin >= SelEnd || End <= SelBegin) - return SelectionTree::Unselected; + return; // We will consider the range (at least partially) selected if it hit any // selected and previously unclaimed token. @@ -98,9 +98,13 @@ class SelectedTokens { } } - if (!ClaimedAnyToken) - return SelectionTree::Unselected; - return PartialSelection ? SelectionTree::Partial : SelectionTree::Complete; + // If some tokens were previously claimed (Result != Unselected), we may + // upgrade from Partial->Complete, even if no new tokens were claimed. + // Important for [[int a]]. + if (ClaimedAnyToken || Result) { + Result = std::max(Result, PartialSelection ? SelectionTree::Partial + : SelectionTree::Complete); + } } private: @@ -162,7 +166,9 @@ class SelectionVisitor : public RecursiveASTVisitor { assert(V.Stack.size() == 1 && "Unpaired push/pop?"); assert(V.Stack.top() == &V.Nodes.front()); // We selected TUDecl if tokens were unclaimed (or the file is empty). - if (V.Nodes.size() == 1 || V.Claimed.claim(Begin, End)) { + SelectionTree::Selection UnclaimedTokens = SelectionTree::Unselected; + V.Claimed.claim(Begin, End, UnclaimedTokens); + if (UnclaimedTokens || V.Nodes.size() == 1) { StringRef FileContent = AST.getSourceManager().getBufferData(File); // Don't require the trailing newlines to be selected. bool SelectedAll = Begin == 0 && End >= FileContent.rtrim().size(); @@ -333,10 +339,11 @@ class SelectionVisitor : public RecursiveASTVisitor { Nodes.emplace_back(); Nodes.back().ASTNode = std::move(Node); Nodes.back().Parent = Stack.top(); - // Early hit detection never selects the whole node. Stack.push(&Nodes.back()); - Nodes.back().Selected = - claimRange(Early) ? SelectionTree::Partial : SelectionTree::Unselected; + claimRange(Early, Nodes.back().Selected); + // Early hit detection never selects the whole node. + if (Nodes.back().Selected) + Nodes.back().Selected = SelectionTree::Partial; } // Pops a node off the ancestor stack, and finalizes it. Pairs with push(). @@ -344,8 +351,7 @@ class SelectionVisitor : public RecursiveASTVisitor { void pop() { Node &N = *Stack.top(); dlog("{1}pop: {0}", printNodeToString(N.ASTNode, PrintPolicy), indent(-1)); - if (auto Sel = claimRange(N.ASTNode.getSourceRange())) - N.Selected = Sel; + claimRange(N.ASTNode.getSourceRange(), N.Selected); if (N.Selected || !N.Children.empty()) { // Attach to the tree. N.Parent->Children.push_back(&N); @@ -375,9 +381,10 @@ class SelectionVisitor : public RecursiveASTVisitor { // Perform hit-testing of a complete Node against the selection. // This runs for every node in the AST, and must be fast in common cases. // This is usually called from pop(), so we can take children into account. - SelectionTree::Selection claimRange(SourceRange S) { + // The existing state of Result is relevant (early/late claims can interact). + void claimRange(SourceRange S, SelectionTree::Selection &Result) { if (!S.isValid()) - return SelectionTree::Unselected; + return; // toHalfOpenFileRange() allows selection of constructs in macro args. e.g: // #define LOOP_FOREVER(Body) for(;;) { Body } // void IncrementLots(int &x) { @@ -391,17 +398,16 @@ class SelectionVisitor : public RecursiveASTVisitor { auto E = SM.getDecomposedLoc(Range->getEnd()); // Otherwise, nodes in macro expansions can't be selected. if (B.first != SelFile || E.first != SelFile) - return SelectionTree::Unselected; + return; // Attempt to claim the remaining range. If there's nothing to claim, only // children were selected. - SelectionTree::Selection Result = Claimed.claim(B.second, E.second); + Claimed.claim(B.second, E.second, Result); if (Result) dlog("{1}hit selection: {0}", SourceRange(SM.getComposedLoc(B.first, B.second), SM.getComposedLoc(E.first, E.second)) .printToString(SM), indent()); - return Result; } std::string indent(int Offset = 0) { diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp index c38bf87cb3352..651490132c29f 100644 --- a/clang-tools-extra/clangd/SemanticHighlighting.cpp +++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp @@ -14,6 +14,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclarationName.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" @@ -24,6 +25,77 @@ namespace clang { namespace clangd { namespace { +/// Some names are not written in the source code and cannot be highlighted, +/// e.g. anonymous classes. This function detects those cases. +bool canHighlightName(DeclarationName Name) { + if (Name.getNameKind() == DeclarationName::CXXConstructorName || + Name.getNameKind() == DeclarationName::CXXUsingDirective) + return true; + auto *II = Name.getAsIdentifierInfo(); + return II && !II->getName().empty(); +} + +llvm::Optional kindForType(const Type *TP); +llvm::Optional kindForDecl(const NamedDecl *D) { + if (auto *TD = dyn_cast(D)) { + // We try to highlight typedefs as their underlying type. + if (auto K = kindForType(TD->getUnderlyingType().getTypePtrOrNull())) + return K; + // And fallback to a generic kind if this fails. + return HighlightingKind::Typedef; + } + // We highlight class decls, constructor decls and destructor decls as + // `Class` type. The destructor decls are handled in `VisitTypeLoc` (we + // will visit a TypeLoc where the underlying Type is a CXXRecordDecl). + if (auto *RD = llvm::dyn_cast(D)) { + // We don't want to highlight lambdas like classes. + if (RD->isLambda()) + return llvm::None; + return HighlightingKind::Class; + } + if (isa(D) || isa(D) || + isa(D)) + return HighlightingKind::Class; + if (auto *MD = dyn_cast(D)) + return MD->isStatic() ? HighlightingKind::StaticMethod + : HighlightingKind::Method; + if (isa(D)) + return HighlightingKind::Field; + if (isa(D)) + return HighlightingKind::Enum; + if (isa(D)) + return HighlightingKind::EnumConstant; + if (isa(D)) + return HighlightingKind::Parameter; + if (auto *VD = dyn_cast(D)) + return VD->isStaticDataMember() + ? HighlightingKind::StaticField + : VD->isLocalVarDecl() ? HighlightingKind::LocalVariable + : HighlightingKind::Variable; + if (isa(D)) + return HighlightingKind::Variable; + if (isa(D)) + return HighlightingKind::Function; + if (isa(D) || isa(D) || + isa(D)) + return HighlightingKind::Namespace; + if (isa(D) || isa(D) || + isa(D)) + return HighlightingKind::TemplateParameter; + return llvm::None; +} +llvm::Optional kindForType(const Type *TP) { + if (!TP) + return llvm::None; + if (TP->isBuiltinType()) // Builtins are special, they do not have decls. + return HighlightingKind::Primitive; + if (auto *TD = dyn_cast(TP)) + return kindForDecl(TD->getDecl()); + if (auto *TD = TP->getAsTagDecl()) + return kindForDecl(TD); + return llvm::None; +} + // Collects all semantic tokens in an ASTContext. class HighlightingTokenCollector : public RecursiveASTVisitor { @@ -38,8 +110,8 @@ class HighlightingTokenCollector TraverseAST(AST.getASTContext()); // Add highlightings for macro expansions as they are not traversed by the // visitor. - for (SourceLocation Loc : AST.getMacros()) - addToken(Loc, HighlightingKind::Macro); + for (const auto &M : AST.getMacros().Ranges) + Tokens.push_back({HighlightingKind::Macro, M}); // Initializer lists can give duplicates of tokens, therefore all tokens // must be deduplicated. llvm::sort(Tokens); @@ -70,66 +142,30 @@ class HighlightingTokenCollector bool VisitNamespaceAliasDecl(NamespaceAliasDecl *NAD) { // The target namespace of an alias can not be found in any other way. - addToken(NAD->getTargetNameLoc(), HighlightingKind::Namespace); + addToken(NAD->getTargetNameLoc(), NAD->getAliasedNamespace()); return true; } bool VisitMemberExpr(MemberExpr *ME) { - const auto *MD = ME->getMemberDecl(); - if (isa(MD)) - // When calling the destructor manually like: AAA::~A(); The ~ is a - // MemberExpr. Other methods should still be highlighted though. - return true; - if (isa(MD)) - // The MemberLoc is invalid for C++ conversion operators. We do not - // attempt to add tokens with invalid locations. - return true; - addToken(ME->getMemberLoc(), MD); + if (canHighlightName(ME->getMemberNameInfo().getName())) + addToken(ME->getMemberLoc(), ME->getMemberDecl()); return true; } bool VisitNamedDecl(NamedDecl *ND) { - // UsingDirectiveDecl's namespaces do not show up anywhere else in the - // Visit/Traverse mehods. But they should also be highlighted as a - // namespace. - if (const auto *UD = dyn_cast(ND)) { - addToken(UD->getIdentLocation(), HighlightingKind::Namespace); - return true; - } - - // Constructors' TypeLoc has a TypePtr that is a FunctionProtoType. It has - // no tag decl and therefore constructors must be gotten as NamedDecls - // instead. - if (ND->getDeclName().getNameKind() == - DeclarationName::CXXConstructorName) { + if (canHighlightName(ND->getDeclName())) addToken(ND->getLocation(), ND); - return true; - } - - if (ND->getDeclName().getNameKind() != DeclarationName::Identifier) - return true; - - addToken(ND->getLocation(), ND); return true; } bool VisitDeclRefExpr(DeclRefExpr *Ref) { - if (Ref->getNameInfo().getName().getNameKind() != - DeclarationName::Identifier) - // Only want to highlight identifiers. - return true; - - addToken(Ref->getLocation(), Ref->getDecl()); - return true; - } - - bool VisitTypedefNameDecl(TypedefNameDecl *TD) { - addTokenForTypedef(TD->getLocation(), TD); + if (canHighlightName(Ref->getNameInfo().getName())) + addToken(Ref->getLocation(), Ref->getDecl()); return true; } bool VisitTypedefTypeLoc(TypedefTypeLoc TL) { - addTokenForTypedef(TL.getBeginLoc(), TL.getTypedefNameDecl()); + addToken(TL.getBeginLoc(), TL.getTypedefNameDecl()); return true; } @@ -140,22 +176,29 @@ class HighlightingTokenCollector return true; } - bool VisitTypeLoc(TypeLoc &TL) { - // This check is for not getting two entries when there are anonymous - // structs. It also makes us not highlight certain namespace qualifiers - // twice. For elaborated types the actual type is highlighted as an inner - // TypeLoc. - if (TL.getTypeLocClass() != TypeLoc::TypeLocClass::Elaborated) - addType(TL.getBeginLoc(), TL.getTypePtr()); + bool WalkUpFromTagTypeLoc(TagTypeLoc L) { + if (L.isDefinition()) + return true; // Definition will be highligthed by VisitNamedDecl. + return RecursiveASTVisitor::WalkUpFromTagTypeLoc(L); + } + + bool WalkUpFromElaboratedTypeLoc(ElaboratedTypeLoc L) { + // Avoid highlighting 'struct' or 'enum' keywords. + return true; + } + + bool VisitTypeLoc(TypeLoc TL) { + if (auto K = kindForType(TL.getTypePtr())) + addToken(TL.getBeginLoc(), *K); return true; } bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNSLoc) { - if (NestedNameSpecifier *NNS = NNSLoc.getNestedNameSpecifier()) + if (auto *NNS = NNSLoc.getNestedNameSpecifier()) { if (NNS->getKind() == NestedNameSpecifier::Namespace || NNS->getKind() == NestedNameSpecifier::NamespaceAlias) addToken(NNSLoc.getLocalBeginLoc(), HighlightingKind::Namespace); - + } return RecursiveASTVisitor< HighlightingTokenCollector>::TraverseNestedNameSpecifierLoc(NNSLoc); } @@ -168,127 +211,21 @@ class HighlightingTokenCollector } bool VisitDeclaratorDecl(DeclaratorDecl *D) { - if ((!D->getTypeSourceInfo())) + // Highlight 'auto' with its underlying type. + auto *AT = D->getType()->getContainedAutoType(); + if (!AT) return true; - - if (auto *AT = D->getType()->getContainedAutoType()) { - const auto Deduced = AT->getDeducedType(); - if (!Deduced.isNull()) - addType(D->getTypeSpecStartLoc(), Deduced.getTypePtr()); - } + auto K = kindForType(AT->getDeducedType().getTypePtrOrNull()); + if (!K) + return true; + addToken(D->getTypeSpecStartLoc(), *K); return true; } private: - void addTokenForTypedef(SourceLocation Loc, const TypedefNameDecl *TD) { - auto *TSI = TD->getTypeSourceInfo(); - if (!TSI) - return; - // Try to highlight as underlying type. - if (addType(Loc, TSI->getType().getTypePtrOrNull())) - return; - // Fallback to the typedef highlighting kind. - addToken(Loc, HighlightingKind::Typedef); - } - - bool addType(SourceLocation Loc, const Type *TP) { - if (!TP) - return false; - if (TP->isBuiltinType()) { - // Builtins must be special cased as they do not have a TagDecl. - addToken(Loc, HighlightingKind::Primitive); - return true; - } - if (auto *TD = dyn_cast(TP)) { - // TemplateTypeParmType also do not have a TagDecl. - addToken(Loc, TD->getDecl()); - return true; - } - if (auto *TD = TP->getAsTagDecl()) { - addToken(Loc, TD); - return true; - } - return false; - } - - void addToken(SourceLocation Loc, const NamedDecl *D) { - if (D->getDeclName().isIdentifier() && D->getName().empty()) - // Don't add symbols that don't have any length. - return; - // We highlight class decls, constructor decls and destructor decls as - // `Class` type. The destructor decls are handled in `VisitTypeLoc` (we will - // visit a TypeLoc where the underlying Type is a CXXRecordDecl). - if (isa(D)) { - addToken(Loc, HighlightingKind::Class); - return; - } - if (isa(D)) { - addToken(Loc, HighlightingKind::Class); - return; - } - if (isa(D)) { - addToken(Loc, HighlightingKind::Class); - return; - } - if (auto *MD = dyn_cast(D)) { - addToken(Loc, MD->isStatic() ? HighlightingKind::StaticMethod - : HighlightingKind::Method); - return; - } - if (isa(D)) { - addToken(Loc, HighlightingKind::Field); - return; - } - if (isa(D)) { - addToken(Loc, HighlightingKind::Enum); - return; - } - if (isa(D)) { - addToken(Loc, HighlightingKind::EnumConstant); - return; - } - if (isa(D)) { - addToken(Loc, HighlightingKind::Parameter); - return; - } - if (const VarDecl *VD = dyn_cast(D)) { - addToken(Loc, VD->isStaticDataMember() - ? HighlightingKind::StaticField - : VD->isLocalVarDecl() ? HighlightingKind::LocalVariable - : HighlightingKind::Variable); - return; - } - if (isa(D)) { - addToken(Loc, HighlightingKind::Variable); - return; - } - if (isa(D)) { - addToken(Loc, HighlightingKind::Function); - return; - } - if (isa(D)) { - addToken(Loc, HighlightingKind::Namespace); - return; - } - if (isa(D)) { - addToken(Loc, HighlightingKind::Namespace); - return; - } - if (isa(D)) { - addToken(Loc, HighlightingKind::TemplateParameter); - return; - } - if (isa(D)) { - addToken(Loc, HighlightingKind::TemplateParameter); - return; - } - if (isa(D)) { - addToken(Loc, HighlightingKind::TemplateParameter); - return; - } - } - void addToken(SourceLocation Loc, HighlightingKind Kind) { + if (Loc.isInvalid()) + return; const auto &SM = AST.getSourceManager(); if (Loc.isMacroID()) { // Only intereseted in highlighting arguments in macros (DEF_X(arg)). @@ -298,8 +235,8 @@ class HighlightingTokenCollector } // Non top level decls that are included from a header are not filtered by - // topLevelDecls. (example: method declarations being included from another - // file for a class from another file) + // topLevelDecls. (example: method declarations being included from + // another file for a class from another file). // There are also cases with macros where the spelling loc will not be in // the main file and the highlighting would be incorrect. if (!isInsideMainFile(Loc, SM)) @@ -314,6 +251,11 @@ class HighlightingTokenCollector Tokens.push_back({Kind, R.getValue()}); } + + void addToken(SourceLocation Loc, const NamedDecl *D) { + if (auto K = kindForDecl(D)) + addToken(Loc, *K); + } }; // Encode binary data into base64. diff --git a/clang-tools-extra/clangd/SemanticSelection.cpp b/clang-tools-extra/clangd/SemanticSelection.cpp new file mode 100644 index 0000000000000..91a5582ac29a4 --- /dev/null +++ b/clang-tools-extra/clangd/SemanticSelection.cpp @@ -0,0 +1,64 @@ +//===--- SemanticSelection.cpp -----------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "SemanticSelection.h" +#include "ParsedAST.h" +#include "Protocol.h" +#include "Selection.h" +#include "SourceCode.h" +#include "clang/AST/DeclBase.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/Support/Error.h" + +namespace clang { +namespace clangd { +namespace { +// Adds Range \p R to the Result if it is distinct from the last added Range. +// Assumes that only consecutive ranges can coincide. +void addIfDistinct(const Range &R, std::vector &Result) { + if (Result.empty() || Result.back() != R) { + Result.push_back(R); + } +} +} // namespace + +llvm::Expected> getSemanticRanges(ParsedAST &AST, + Position Pos) { + std::vector Result; + const auto &SM = AST.getSourceManager(); + const auto &LangOpts = AST.getASTContext().getLangOpts(); + + auto FID = SM.getMainFileID(); + auto Offset = positionToOffset(SM.getBufferData(FID), Pos); + if (!Offset) { + return Offset.takeError(); + } + + // Get node under the cursor. + SelectionTree ST(AST.getASTContext(), AST.getTokens(), *Offset); + for (const auto *Node = ST.commonAncestor(); Node != nullptr; + Node = Node->Parent) { + if (const Decl *D = Node->ASTNode.get()) { + if (llvm::isa(D)) { + break; + } + } + + auto SR = toHalfOpenFileRange(SM, LangOpts, Node->ASTNode.getSourceRange()); + if (!SR.hasValue() || SM.getFileID(SR->getBegin()) != SM.getMainFileID()) { + continue; + } + Range R; + R.start = sourceLocToPosition(SM, SR->getBegin()); + R.end = sourceLocToPosition(SM, SR->getEnd()); + addIfDistinct(R, Result); + } + return Result; +} + +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/SemanticSelection.h b/clang-tools-extra/clangd/SemanticSelection.h new file mode 100644 index 0000000000000..b0d301e2efb89 --- /dev/null +++ b/clang-tools-extra/clangd/SemanticSelection.h @@ -0,0 +1,32 @@ +//===--- SemanticSelection.h -------------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Features for giving interesting semantic ranges around the cursor. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SEMANTICSELECTION_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SEMANTICSELECTION_H +#include "ParsedAST.h" +#include "Protocol.h" +#include "llvm/Support/Error.h" +#include +namespace clang { +namespace clangd { + +/// Returns the list of all interesting ranges around the Position \p Pos. +/// The interesting ranges corresponds to the AST nodes in the SelectionTree +/// containing \p Pos. +/// Any range in the result strictly contains all the previous ranges in the +/// result. front() is the inner most range. back() is the outermost range. +llvm::Expected> getSemanticRanges(ParsedAST &AST, + Position Pos); +} // namespace clangd +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_SEMANTICSELECTION_H diff --git a/clang-tools-extra/clangd/SourceCode.cpp b/clang-tools-extra/clangd/SourceCode.cpp index 95f250c9d6a23..05ca7aaaa1e86 100644 --- a/clang-tools-extra/clangd/SourceCode.cpp +++ b/clang-tools-extra/clangd/SourceCode.cpp @@ -237,6 +237,45 @@ llvm::Optional getTokenRange(const SourceManager &SM, return halfOpenToRange(SM, CharSourceRange::getCharRange(TokLoc, End)); } +namespace { + +enum TokenFlavor { Identifier, Operator, Whitespace, Other }; + +bool isOverloadedOperator(const Token &Tok) { + switch (Tok.getKind()) { +#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemOnly) \ + case tok::Token: +#define OVERLOADED_OPERATOR_MULTI(Name, Spelling, Unary, Binary, MemOnly) +#include "clang/Basic/OperatorKinds.def" + return true; + + default: + break; + } + return false; +} + +TokenFlavor getTokenFlavor(SourceLocation Loc, const SourceManager &SM, + const LangOptions &LangOpts) { + Token Tok; + Tok.setKind(tok::NUM_TOKENS); + if (Lexer::getRawToken(Loc, Tok, SM, LangOpts, + /*IgnoreWhiteSpace*/ false)) + return Other; + + // getRawToken will return false without setting Tok when the token is + // whitespace, so if the flag is not set, we are sure this is a whitespace. + if (Tok.is(tok::TokenKind::NUM_TOKENS)) + return Whitespace; + if (Tok.is(tok::TokenKind::raw_identifier)) + return Identifier; + if (isOverloadedOperator(Tok)) + return Operator; + return Other; +} + +} // namespace + SourceLocation getBeginningOfIdentifier(const Position &Pos, const SourceManager &SM, const LangOptions &LangOpts) { @@ -247,27 +286,57 @@ SourceLocation getBeginningOfIdentifier(const Position &Pos, return SourceLocation(); } - // GetBeginningOfToken(pos) is almost what we want, but does the wrong thing - // if the cursor is at the end of the identifier. - // Instead, we lex at GetBeginningOfToken(pos - 1). The cases are: - // 1) at the beginning of an identifier, we'll be looking at something - // that isn't an identifier. - // 2) at the middle or end of an identifier, we get the identifier. - // 3) anywhere outside an identifier, we'll get some non-identifier thing. - // We can't actually distinguish cases 1 and 3, but returning the original - // location is correct for both! + // GetBeginningOfToken(InputLoc) is almost what we want, but does the wrong + // thing if the cursor is at the end of the token (identifier or operator). + // The cases are: + // 1) at the beginning of the token + // 2) at the middle of the token + // 3) at the end of the token + // 4) anywhere outside the identifier or operator + // To distinguish all cases, we lex both at the + // GetBeginningOfToken(InputLoc-1) and GetBeginningOfToken(InputLoc), for + // cases 1 and 4, we just return the original location. SourceLocation InputLoc = SM.getComposedLoc(FID, *Offset); - if (*Offset == 0) // Case 1 or 3. + if (*Offset == 0) // Case 1 or 4. return InputLoc; SourceLocation Before = SM.getComposedLoc(FID, *Offset - 1); + SourceLocation BeforeTokBeginning = + Lexer::GetBeginningOfToken(Before, SM, LangOpts); + TokenFlavor BeforeKind = getTokenFlavor(BeforeTokBeginning, SM, LangOpts); + + SourceLocation CurrentTokBeginning = + Lexer::GetBeginningOfToken(InputLoc, SM, LangOpts); + TokenFlavor CurrentKind = getTokenFlavor(CurrentTokBeginning, SM, LangOpts); + + // At the middle of the token. + if (BeforeTokBeginning == CurrentTokBeginning) { + // For interesting token, we return the beginning of the token. + if (CurrentKind == Identifier || CurrentKind == Operator) + return CurrentTokBeginning; + // otherwise, we return the original loc. + return InputLoc; + } - Before = Lexer::GetBeginningOfToken(Before, SM, LangOpts); - Token Tok; - if (Before.isValid() && - !Lexer::getRawToken(Before, Tok, SM, LangOpts, false) && - Tok.is(tok::raw_identifier)) - return Before; // Case 2. - return InputLoc; // Case 1 or 3. + // Whitespace is not interesting. + if (BeforeKind == Whitespace) + return CurrentTokBeginning; + if (CurrentKind == Whitespace) + return BeforeTokBeginning; + + // The cursor is at the token boundary, e.g. "Before^Current", we prefer + // identifiers to other tokens. + if (CurrentKind == Identifier) + return CurrentTokBeginning; + if (BeforeKind == Identifier) + return BeforeTokBeginning; + // Then prefer overloaded operators to other tokens. + if (CurrentKind == Operator) + return CurrentTokBeginning; + if (BeforeKind == Operator) + return BeforeTokBeginning; + + // Non-interesting case, we just return the original location. + return InputLoc; } bool isValidFileRange(const SourceManager &Mgr, SourceRange R) { @@ -659,6 +728,9 @@ static void lex(llvm::StringRef Code, const format::FormatStyle &Style, while (!Lex.LexFromRawLexer(Tok)) A(Tok); + // LexFromRawLexer returns true after it lexes last token, so we still have + // one more token to report. + A(Tok); } llvm::StringMap collectIdentifiers(llvm::StringRef Content, diff --git a/clang-tools-extra/clangd/SourceCode.h b/clang-tools-extra/clangd/SourceCode.h index 53a140fe4abc7..3017746c17ee4 100644 --- a/clang-tools-extra/clangd/SourceCode.h +++ b/clang-tools-extra/clangd/SourceCode.h @@ -79,7 +79,7 @@ llvm::Expected sourceLocationInMainFile(const SourceManager &SM, Position P); /// Get the beginning SourceLocation at a specified \p Pos in the main file. -/// May be invalid if Pos is, or if there's no identifier. +/// May be invalid if Pos is, or if there's no identifier or operators. /// The returned position is in the main file, callers may prefer to /// obtain the macro expansion location. SourceLocation getBeginningOfIdentifier(const Position &Pos, diff --git a/clang-tools-extra/clangd/URI.cpp b/clang-tools-extra/clangd/URI.cpp index 9f5c92b684ff9..f8a9f59ca1378 100644 --- a/clang-tools-extra/clangd/URI.cpp +++ b/clang-tools-extra/clangd/URI.cpp @@ -183,6 +183,17 @@ llvm::Expected URI::parse(llvm::StringRef OrigUri) { return U; } +llvm::Expected URI::resolve(llvm::StringRef FileURI, + llvm::StringRef HintPath) { + auto Uri = URI::parse(FileURI); + if (!Uri) + return Uri.takeError(); + auto Path = URI::resolve(*Uri, HintPath); + if (!Path) + return Path.takeError(); + return *Path; +} + llvm::Expected URI::create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme) { if (!llvm::sys::path::is_absolute(AbsolutePath)) diff --git a/clang-tools-extra/clangd/URI.h b/clang-tools-extra/clangd/URI.h index 110b39af663cb..5f120ef9e386f 100644 --- a/clang-tools-extra/clangd/URI.h +++ b/clang-tools-extra/clangd/URI.h @@ -63,6 +63,10 @@ class URI { static llvm::Expected resolve(const URI &U, llvm::StringRef HintPath = ""); + /// Same as above, in addition it parses the \p FileURI using URI::parse. + static llvm::Expected resolve(llvm::StringRef FileURI, + llvm::StringRef HintPath = ""); + /// Resolves \p AbsPath into a canonical path of its URI, by converting /// \p AbsPath to URI and resolving the URI to get th canonical path. /// This ensures that paths with the same URI are resolved into consistent diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index 2c8d161c19310..f51891b8aec59 100644 --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -9,6 +9,7 @@ #include "AST.h" #include "CodeCompletionStrings.h" #include "FindSymbols.h" +#include "FindTarget.h" #include "FormattedString.h" #include "Logger.h" #include "ParsedAST.h" @@ -1299,5 +1300,18 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, return OS; } +llvm::DenseSet getNonLocalDeclRefs(ParsedAST &AST, + const FunctionDecl *FD) { + if (!FD->hasBody()) + return {}; + llvm::DenseSet DeclRefs; + findExplicitReferences(FD, [&](ReferenceLoc Ref) { + for (const Decl *D : Ref.Targets) { + if (!index::isFunctionLocalSymbol(D) && !D->isTemplateParameter()) + DeclRefs.insert(D); + } + }); + return DeclRefs; +} } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/XRefs.h b/clang-tools-extra/clangd/XRefs.h index d6b3930f513b9..dc2237cb0fd12 100644 --- a/clang-tools-extra/clangd/XRefs.h +++ b/clang-tools-extra/clangd/XRefs.h @@ -17,8 +17,8 @@ #include "Path.h" #include "Protocol.h" #include "index/Index.h" -#include "clang/AST/Type.h" #include "index/SymbolLocation.h" +#include "clang/AST/Type.h" #include "clang/Format/Format.h" #include "clang/Index/IndexSymbol.h" #include "llvm/ADT/Optional.h" @@ -158,6 +158,9 @@ llvm::Optional getDeducedType(ParsedAST &AST, /// SourceLocationBeg must point to the first character of the token bool hasDeducedType(ParsedAST &AST, SourceLocation SourceLocationBeg); +/// Returns all decls that are referenced in the \p FD except local symbols. +llvm::DenseSet getNonLocalDeclRefs(ParsedAST &AST, + const FunctionDecl *FD); } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/clients/clangd-vscode/DEVELOPING.md b/clang-tools-extra/clangd/clients/clangd-vscode/DEVELOPING.md index 92aad528db942..e888aba3ea205 100644 --- a/clang-tools-extra/clangd/clients/clangd-vscode/DEVELOPING.md +++ b/clang-tools-extra/clangd/clients/clangd-vscode/DEVELOPING.md @@ -48,6 +48,6 @@ please contact clangd-dev@lists.llvm.org. # For the first time, you need to login in the account. vsce will ask you for the Personal Access Token, and remember it for future commands. $ vsce login llvm-vs-code-extensions - $ vsce publish + # Publish the extension to the VSCode marketplace. + $ npm run publish ``` - diff --git a/clang-tools-extra/clangd/clients/clangd-vscode/package.json b/clang-tools-extra/clangd/clients/clangd-vscode/package.json index c2ef3f853e258..03de7b3e58b05 100644 --- a/clang-tools-extra/clangd/clients/clangd-vscode/package.json +++ b/clang-tools-extra/clangd/clients/clangd-vscode/package.json @@ -2,7 +2,7 @@ "name": "vscode-clangd", "displayName": "vscode-clangd", "description": "Clang Language Server", - "version": "0.0.16", + "version": "0.0.18", "publisher": "llvm-vs-code-extensions", "homepage": "https://clang.llvm.org/extra/clangd.html", "engines": { @@ -33,13 +33,15 @@ "compile": "tsc -watch -p ./", "postinstall": "node ./node_modules/vscode/bin/install", "format": "clang-format --style=LLVM -i --glob=\"{src,test}/*.ts\"", - "test": "node ./node_modules/vscode/bin/test" + "test": "node ./node_modules/vscode/bin/test", + "package": "vsce package --baseImagesUrl https://raw.githubusercontent.com/llvm/llvm-project/master/clang-tools-extra/clangd/clients/clangd-vscode/", + "publish": "vsce publish --baseImagesUrl https://raw.githubusercontent.com/llvm/llvm-project/master/clang-tools-extra/clangd/clients/clangd-vscode/" }, "dependencies": { "jsonc-parser": "^2.1.0", - "vscode-languageclient": "^5.3.0-next.6", - "vscode-languageserver": "^5.3.0-next.6", - "vscode-languageserver-types": "^3.14.0" + "vscode-languageclient": "^6.0.0-next.1", + "vscode-languageserver": "^6.0.0-next.1", + "vscode-languageserver-types": "^3.15.0-next.5" }, "devDependencies": { "@types/mocha": "^2.2.32", @@ -92,7 +94,7 @@ }, "clangd.semanticHighlighting": { "type": "boolean", - "default": "false", + "default": "true", "description": "Enable semantic highlighting in clangd" } } diff --git a/clang-tools-extra/clangd/index/Background.cpp b/clang-tools-extra/clangd/index/Background.cpp index 9ea21fdf6c590..ff6a9e5226843 100644 --- a/clang-tools-extra/clangd/index/Background.cpp +++ b/clang-tools-extra/clangd/index/Background.cpp @@ -69,13 +69,7 @@ class URIToFileCache { llvm::StringRef resolve(llvm::StringRef FileURI) { auto I = URIToPathCache.try_emplace(FileURI); if (I.second) { - auto U = URI::parse(FileURI); - if (!U) { - elog("Failed to parse URI {0}: {1}", FileURI, U.takeError()); - assert(false && "Failed to parse URI"); - return ""; - } - auto Path = URI::resolve(*U, HintPath); + auto Path = URI::resolve(FileURI, HintPath); if (!Path) { elog("Failed to resolve URI {0}: {1}", FileURI, Path.takeError()); assert(false && "Failed to resolve URI"); diff --git a/clang-tools-extra/clangd/index/BackgroundIndexLoader.cpp b/clang-tools-extra/clangd/index/BackgroundIndexLoader.cpp index 585d36716d296..ff690f9025733 100644 --- a/clang-tools-extra/clangd/index/BackgroundIndexLoader.cpp +++ b/clang-tools-extra/clangd/index/BackgroundIndexLoader.cpp @@ -24,16 +24,6 @@ namespace clang { namespace clangd { namespace { -llvm::Optional uriToAbsolutePath(llvm::StringRef URI, PathRef HintPath) { - auto U = URI::parse(URI); - if (!U) - return llvm::None; - auto AbsolutePath = URI::resolve(*U, HintPath); - if (!AbsolutePath) - return llvm::None; - return *AbsolutePath; -} - /// A helper class to cache BackgroundIndexStorage operations and keep the /// inverse dependency mapping. class BackgroundIndexLoader { @@ -79,9 +69,11 @@ BackgroundIndexLoader::loadShard(PathRef StartSourceFile, PathRef DependentTU) { LS.Shard = std::move(Shard); for (const auto &It : *LS.Shard->Sources) { - auto AbsPath = uriToAbsolutePath(It.getKey(), StartSourceFile); - if (!AbsPath) + auto AbsPath = URI::resolve(It.getKey(), StartSourceFile); + if (!AbsPath) { + elog("Failed to resolve URI: {0}", AbsPath.takeError()); continue; + } // A shard contains only edges for non main-file sources. if (*AbsPath != StartSourceFile) { Edges.push_back(*AbsPath); diff --git a/clang-tools-extra/clangd/index/BackgroundIndexStorage.cpp b/clang-tools-extra/clangd/index/BackgroundIndexStorage.cpp index 86c73ff7420a3..48510e11e8455 100644 --- a/clang-tools-extra/clangd/index/BackgroundIndexStorage.cpp +++ b/clang-tools-extra/clangd/index/BackgroundIndexStorage.cpp @@ -18,6 +18,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileUtilities.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include @@ -35,34 +36,6 @@ std::string getShardPathFromFilePath(llvm::StringRef ShardRoot, return ShardRootSS.str(); } -llvm::Error -writeAtomically(llvm::StringRef OutPath, - llvm::function_ref Writer) { - // Write to a temporary file first. - llvm::SmallString<128> TempPath; - int FD; - auto EC = - llvm::sys::fs::createUniqueFile(OutPath + ".tmp.%%%%%%%%", FD, TempPath); - if (EC) - return llvm::errorCodeToError(EC); - // Make sure temp file is destroyed on failure. - auto RemoveOnFail = - llvm::make_scope_exit([TempPath] { llvm::sys::fs::remove(TempPath); }); - llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true); - Writer(OS); - OS.close(); - if (OS.has_error()) - return llvm::errorCodeToError(OS.error()); - // Then move to real location. - EC = llvm::sys::fs::rename(TempPath, OutPath); - if (EC) - return llvm::errorCodeToError(EC); - // If everything went well, we already moved the file to another name. So - // don't delete the file, as the name might be taken by another file. - RemoveOnFail.release(); - return llvm::ErrorSuccess(); -} - // Uses disk as a storage for index shards. Creates a directory called // ".clangd/index/" under the path provided during construction. class DiskBackedIndexStorage : public BackgroundIndexStorage { @@ -100,9 +73,12 @@ class DiskBackedIndexStorage : public BackgroundIndexStorage { llvm::Error storeShard(llvm::StringRef ShardIdentifier, IndexFileOut Shard) const override { - return writeAtomically( - getShardPathFromFilePath(DiskShardRoot, ShardIdentifier), - [&Shard](llvm::raw_ostream &OS) { OS << Shard; }); + auto ShardPath = getShardPathFromFilePath(DiskShardRoot, ShardIdentifier); + return llvm::writeFileAtomically(ShardPath + ".tmp.%%%%%%%%", ShardPath, + [&Shard](llvm::raw_ostream &OS) { + OS << Shard; + return llvm::Error::success(); + }); } }; diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp b/clang-tools-extra/clangd/refactor/Rename.cpp index 770064fc61321..f38815a1f14ac 100644 --- a/clang-tools-extra/clangd/refactor/Rename.cpp +++ b/clang-tools-extra/clangd/refactor/Rename.cpp @@ -74,14 +74,22 @@ llvm::Optional renamableWithinFile(const Decl &RenameDecl, const SymbolIndex *Index) { if (llvm::isa(&RenameDecl)) return ReasonToReject::UnsupportedSymbol; + if (const auto *FD = llvm::dyn_cast(&RenameDecl)) { + if (FD->isOverloadedOperator()) + return ReasonToReject::UnsupportedSymbol; + } auto &ASTCtx = RenameDecl.getASTContext(); const auto &SM = ASTCtx.getSourceManager(); bool MainFileIsHeader = ASTCtx.getLangOpts().IsHeaderFile; bool DeclaredInMainFile = isInsideMainFile(RenameDecl.getBeginLoc(), SM); + if (!DeclaredInMainFile) + // We are sure the symbol is used externally, bail out early. + return UsedOutsideFile; + // If the symbol is declared in the main file (which is not a header), we // rename it. - if (DeclaredInMainFile && !MainFileIsHeader) + if (!MainFileIsHeader) return None; // Below are cases where the symbol is declared in the header. diff --git a/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp b/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp index 1532c951a26bb..1551f41a13184 100644 --- a/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp @@ -104,6 +104,11 @@ bool isRootStmt(const Node *N) { } // Returns the (unselected) parent of all RootStmts given the commonAncestor. +// Returns null if: +// 1. any node is partially selected +// 2. If all completely selected nodes don't have the same common parent +// 3. Any child of Parent isn't a RootStmt. +// Returns null if any child is not a RootStmt. // We only support extraction of RootStmts since it allows us to extract without // having to change the selection range. Also, this means that any scope that // begins in selection range, ends in selection range and any scope that begins @@ -111,28 +116,30 @@ bool isRootStmt(const Node *N) { const Node *getParentOfRootStmts(const Node *CommonAnc) { if (!CommonAnc) return nullptr; + const Node *Parent = nullptr; switch (CommonAnc->Selected) { case SelectionTree::Selection::Unselected: + // Typicaly a block, with the { and } unselected, could also be ForStmt etc // Ensure all Children are RootStmts. - return llvm::all_of(CommonAnc->Children, isRootStmt) ? CommonAnc : nullptr; + Parent = CommonAnc; + break; case SelectionTree::Selection::Partial: - // Treat Partially selected VarDecl as completely selected since - // SelectionTree doesn't always select VarDecls correctly. - // FIXME: Remove this after D66872 is upstream) - if (!CommonAnc->ASTNode.get()) - return nullptr; - LLVM_FALLTHROUGH; + // Only a fully-selected single statement can be selected. + return nullptr; case SelectionTree::Selection::Complete: // If the Common Ancestor is completely selected, then it's a root statement // and its parent will be unselected. - const Node *Parent = CommonAnc->Parent; + Parent = CommonAnc->Parent; // If parent is a DeclStmt, even though it's unselected, we consider it a // root statement and return its parent. This is done because the VarDecls // claim the entire selection range of the Declaration and DeclStmt is // always unselected. - return Parent->ASTNode.get() ? Parent->Parent : Parent; + if (Parent->ASTNode.get()) + Parent = Parent->Parent; + break; } - llvm_unreachable("Unhandled SelectionTree::Selection enum"); + // Ensure all Children are RootStmts. + return llvm::all_of(Parent->Children, isRootStmt) ? Parent : nullptr; } // The ExtractionZone class forms a view of the code wrt Zone. @@ -218,10 +225,24 @@ computeEnclosingFuncRange(const FunctionDecl *EnclosingFunction, return toHalfOpenFileRange(SM, LangOpts, EnclosingFunction->getSourceRange()); } +// returns true if Child can be a single RootStmt being extracted from +// EnclosingFunc. +bool validSingleChild(const Node *Child, const FunctionDecl *EnclosingFunc) { + // Don't extract expressions. + // FIXME: We should extract expressions that are "statements" i.e. not + // subexpressions + if (Child->ASTNode.get()) + return false; + // Extracting the body of EnclosingFunc would remove it's definition. + assert(EnclosingFunc->hasBody() && + "We should always be extracting from a function body."); + if (Child->ASTNode.get() == EnclosingFunc->getBody()) + return false; + return true; +} + // FIXME: Check we're not extracting from the initializer/condition of a control // flow structure. -// FIXME: Check that we don't extract the compound statement of the -// enclosingFunction. llvm::Optional findExtractionZone(const Node *CommonAnc, const SourceManager &SM, const LangOptions &LangOpts) { @@ -229,15 +250,14 @@ llvm::Optional findExtractionZone(const Node *CommonAnc, ExtZone.Parent = getParentOfRootStmts(CommonAnc); if (!ExtZone.Parent || ExtZone.Parent->Children.empty()) return llvm::None; - // Don't extract expressions. - // FIXME: We should extract expressions that are "statements" i.e. not - // subexpressions - if (ExtZone.Parent->Children.size() == 1 && - ExtZone.getLastRootStmt()->ASTNode.get()) - return llvm::None; ExtZone.EnclosingFunction = findEnclosingFunction(ExtZone.Parent); if (!ExtZone.EnclosingFunction) return llvm::None; + // When there is a single RootStmt, we must check if it's valid for + // extraction. + if (ExtZone.Parent->Children.size() == 1 && + !validSingleChild(ExtZone.getLastRootStmt(), ExtZone.EnclosingFunction)) + return llvm::None; if (auto FuncRange = computeEnclosingFuncRange(ExtZone.EnclosingFunction, SM, LangOpts)) ExtZone.EnclosingFuncRange = *FuncRange; diff --git a/clang-tools-extra/clangd/test/initialize-params.test b/clang-tools-extra/clangd/test/initialize-params.test index 10a354b27ceba..8a853b7c6223c 100644 --- a/clang-tools-extra/clangd/test/initialize-params.test +++ b/clang-tools-extra/clangd/test/initialize-params.test @@ -33,6 +33,7 @@ # CHECK-NEXT: "hoverProvider": true, # CHECK-NEXT: "referencesProvider": true, # CHECK-NEXT: "renameProvider": true, +# CHECK-NEXT: "selectionRangeProvider": true, # CHECK-NEXT: "signatureHelpProvider": { # CHECK-NEXT: "triggerCharacters": [ # CHECK-NEXT: "(", diff --git a/clang-tools-extra/clangd/test/selection-range.test b/clang-tools-extra/clangd/test/selection-range.test new file mode 100644 index 0000000000000..e7ff14a678f72 --- /dev/null +++ b/clang-tools-extra/clangd/test/selection-range.test @@ -0,0 +1,39 @@ +# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"void func() {\n}"}}} +--- +{"jsonrpc":"2.0","id":1,"method":"textDocument/selectionRange","params":{"textDocument":{"uri":"test:///main.cpp"},"positions":[{"line":1,"character":0}]}} +# CHECK: "id": 1 +# CHECK-NEXT: "jsonrpc": "2.0", +# CHECK-NEXT: "result": [ +# CHECK-NEXT: { +# CHECK-NEXT: "parent": { +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 1, +# CHECK-NEXT: "line": 1 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 1, +# CHECK-NEXT: "line": 1 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 12, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT:} +--- +{"jsonrpc":"2.0","id":3,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} diff --git a/clang-tools-extra/clangd/unittests/CMakeLists.txt b/clang-tools-extra/clangd/unittests/CMakeLists.txt index 913401c72fcd8..d25745f94c7b9 100644 --- a/clang-tools-extra/clangd/unittests/CMakeLists.txt +++ b/clang-tools-extra/clangd/unittests/CMakeLists.txt @@ -46,6 +46,7 @@ add_unittest(ClangdUnitTests ClangdTests FuzzyMatchTests.cpp GlobalCompilationDatabaseTests.cpp HeadersTests.cpp + HeaderSourceSwitchTests.cpp IndexActionTests.cpp IndexTests.cpp JSONTransportTests.cpp @@ -56,6 +57,7 @@ add_unittest(ClangdUnitTests ClangdTests RIFFTests.cpp SelectionTests.cpp SemanticHighlightingTests.cpp + SemanticSelectionTests.cpp SerializationTests.cpp SourceCodeTests.cpp SymbolCollectorTests.cpp diff --git a/clang-tools-extra/clangd/unittests/ClangdTests.cpp b/clang-tools-extra/clangd/unittests/ClangdTests.cpp index 25c82e4895fc6..3cbd33ab57ebf 100644 --- a/clang-tools-extra/clangd/unittests/ClangdTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdTests.cpp @@ -765,82 +765,6 @@ int d; } } -TEST_F(ClangdVFSTest, CheckSourceHeaderSwitch) { - MockFSProvider FS; - ErrorCheckingDiagConsumer DiagConsumer; - MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); - - auto SourceContents = R"cpp( - #include "foo.h" - int b = a; - )cpp"; - - auto FooCpp = testPath("foo.cpp"); - auto FooH = testPath("foo.h"); - auto Invalid = testPath("main.cpp"); - - FS.Files[FooCpp] = SourceContents; - FS.Files[FooH] = "int a;"; - FS.Files[Invalid] = "int main() { \n return 0; \n }"; - - Optional PathResult = Server.switchSourceHeader(FooCpp); - EXPECT_TRUE(PathResult.hasValue()); - ASSERT_EQ(PathResult.getValue(), FooH); - - PathResult = Server.switchSourceHeader(FooH); - EXPECT_TRUE(PathResult.hasValue()); - ASSERT_EQ(PathResult.getValue(), FooCpp); - - SourceContents = R"c( - #include "foo.HH" - int b = a; - )c"; - - // Test with header file in capital letters and different extension, source - // file with different extension - auto FooC = testPath("bar.c"); - auto FooHH = testPath("bar.HH"); - - FS.Files[FooC] = SourceContents; - FS.Files[FooHH] = "int a;"; - - PathResult = Server.switchSourceHeader(FooC); - EXPECT_TRUE(PathResult.hasValue()); - ASSERT_EQ(PathResult.getValue(), FooHH); - - // Test with both capital letters - auto Foo2C = testPath("foo2.C"); - auto Foo2HH = testPath("foo2.HH"); - FS.Files[Foo2C] = SourceContents; - FS.Files[Foo2HH] = "int a;"; - - PathResult = Server.switchSourceHeader(Foo2C); - EXPECT_TRUE(PathResult.hasValue()); - ASSERT_EQ(PathResult.getValue(), Foo2HH); - - // Test with source file as capital letter and .hxx header file - auto Foo3C = testPath("foo3.C"); - auto Foo3HXX = testPath("foo3.hxx"); - - SourceContents = R"c( - #include "foo3.hxx" - int b = a; - )c"; - - FS.Files[Foo3C] = SourceContents; - FS.Files[Foo3HXX] = "int a;"; - - PathResult = Server.switchSourceHeader(Foo3C); - EXPECT_TRUE(PathResult.hasValue()); - ASSERT_EQ(PathResult.getValue(), Foo3HXX); - - // Test if asking for a corresponding file that doesn't exist returns an empty - // string. - PathResult = Server.switchSourceHeader(Invalid); - EXPECT_FALSE(PathResult.hasValue()); -} - TEST_F(ClangdThreadingTest, NoConcurrentDiagnostics) { class NoConcurrentAccessDiagConsumer : public DiagnosticsConsumer { public: diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp index 88d7daa7e3b43..bf100630b30b5 100644 --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -9,6 +9,11 @@ #include "Selection.h" #include "TestTU.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Testing/Support/Annotations.h" #include "gmock/gmock.h" @@ -388,6 +393,32 @@ TEST_F(TargetDeclTest, Lambda) { EXPECT_DECLS("DeclRefExpr", "auto int x = 1"); } +TEST_F(TargetDeclTest, OverloadExpr) { + Code = R"cpp( + void func(int*); + void func(char*); + + template + void foo(T t) { + [[func]](t); + }; + )cpp"; + EXPECT_DECLS("UnresolvedLookupExpr", "void func(int *)", "void func(char *)"); + + Code = R"cpp( + struct X { + void func(int*); + void func(char*); + }; + + template + void foo(X x, T t) { + x.[[func]](t); + }; + )cpp"; + EXPECT_DECLS("UnresolvedMemberExpr", "void func(int *)", "void func(char *)"); +} + TEST_F(TargetDeclTest, ObjC) { Flags = {"-xobjective-c"}; Code = R"cpp( @@ -464,6 +495,307 @@ TEST_F(TargetDeclTest, ObjC) { EXPECT_DECLS("ObjCObjectTypeLoc"); } +class FindExplicitReferencesTest : public ::testing::Test { +protected: + struct AllRefs { + std::string AnnotatedCode; + std::string DumpedReferences; + }; + + /// Parses \p Code, finds function '::foo' and annotates its body with results + /// of findExplicitReferecnces. + /// See actual tests for examples of annotation format. + AllRefs annotateReferencesInFoo(llvm::StringRef Code) { + TestTU TU; + TU.Code = Code; + + auto AST = TU.build(); + + auto *TestDecl = &findDecl(AST, "foo"); + if (auto *T = llvm::dyn_cast(TestDecl)) + TestDecl = T->getTemplatedDecl(); + auto &Func = llvm::cast(*TestDecl); + + std::vector Refs; + findExplicitReferences(Func.getBody(), [&Refs](ReferenceLoc R) { + Refs.push_back(std::move(R)); + }); + + auto &SM = AST.getSourceManager(); + llvm::sort(Refs, [&](const ReferenceLoc &L, const ReferenceLoc &R) { + return SM.isBeforeInTranslationUnit(L.NameLoc, R.NameLoc); + }); + + std::string AnnotatedCode; + unsigned NextCodeChar = 0; + for (unsigned I = 0; I < Refs.size(); ++I) { + auto &R = Refs[I]; + + SourceLocation Pos = R.NameLoc; + assert(Pos.isValid()); + if (Pos.isMacroID()) // FIXME: figure out how to show macro locations. + Pos = SM.getExpansionLoc(Pos); + assert(Pos.isFileID()); + + FileID File; + unsigned Offset; + std::tie(File, Offset) = SM.getDecomposedLoc(Pos); + if (File == SM.getMainFileID()) { + // Print the reference in a source code. + assert(NextCodeChar <= Offset); + AnnotatedCode += Code.substr(NextCodeChar, Offset - NextCodeChar); + AnnotatedCode += "$" + std::to_string(I) + "^"; + + NextCodeChar = Offset; + } + } + AnnotatedCode += Code.substr(NextCodeChar); + + std::string DumpedReferences; + for (unsigned I = 0; I < Refs.size(); ++I) + DumpedReferences += llvm::formatv("{0}: {1}\n", I, Refs[I]); + + return AllRefs{std::move(AnnotatedCode), std::move(DumpedReferences)}; + } +}; + +TEST_F(FindExplicitReferencesTest, All) { + std::pair Cases[] = + { + // Simple expressions. + {R"cpp( + int global; + int func(); + void foo(int param) { + $0^global = $1^param + $2^func(); + } + )cpp", + "0: targets = {global}\n" + "1: targets = {param}\n" + "2: targets = {func}\n"}, + {R"cpp( + struct X { int a; }; + void foo(X x) { + $0^x.$1^a = 10; + } + )cpp", + "0: targets = {x}\n" + "1: targets = {X::a}\n"}, + // Namespaces and aliases. + {R"cpp( + namespace ns {} + namespace alias = ns; + void foo() { + using namespace $0^ns; + using namespace $1^alias; + } + )cpp", + "0: targets = {ns}\n" + "1: targets = {alias}\n"}, + // Using declarations. + {R"cpp( + namespace ns { int global; } + void foo() { + using $0^ns::$1^global; + } + )cpp", + "0: targets = {ns}\n" + "1: targets = {ns::global}, qualifier = 'ns::'\n"}, + // Simple types. + {R"cpp( + struct Struct { int a; }; + using Typedef = int; + void foo() { + $0^Struct x; + $1^Typedef y; + static_cast<$2^Struct*>(0); + } + )cpp", + "0: targets = {Struct}\n" + "1: targets = {Typedef}\n" + "2: targets = {Struct}\n"}, + // Name qualifiers. + {R"cpp( + namespace a { namespace b { struct S { typedef int type; }; } } + void foo() { + $0^a::$1^b::$2^S x; + using namespace $3^a::$4^b; + $5^S::$6^type y; + } + )cpp", + "0: targets = {a}\n" + "1: targets = {a::b}, qualifier = 'a::'\n" + "2: targets = {a::b::S}, qualifier = 'a::b::'\n" + "3: targets = {a}\n" + "4: targets = {a::b}, qualifier = 'a::'\n" + "5: targets = {a::b::S}\n" + "6: targets = {a::b::S::type}, qualifier = 'struct S::'\n"}, + // Simple templates. + {R"cpp( + template struct vector { using value_type = T; }; + template <> struct vector { using value_type = bool; }; + void foo() { + $0^vector vi; + $1^vector vb; + } + )cpp", + "0: targets = {vector}\n" + "1: targets = {vector}\n"}, + // Template type aliases. + {R"cpp( + template struct vector { using value_type = T; }; + template <> struct vector { using value_type = bool; }; + template using valias = vector; + void foo() { + $0^valias vi; + $1^valias vb; + } + )cpp", + "0: targets = {valias}\n" + "1: targets = {valias}\n"}, + // MemberExpr should know their using declaration. + {R"cpp( + struct X { void func(int); } + struct Y : X { + using X::func; + }; + void foo(Y y) { + $0^y.$1^func(1); + } + )cpp", + "0: targets = {y}\n" + "1: targets = {Y::func}\n"}, + // DeclRefExpr should know their using declaration. + {R"cpp( + namespace ns { void bar(int); } + using ns::bar; + + void foo() { + $0^bar(10); + } + )cpp", + "0: targets = {bar}\n"}, + // References from a macro. + {R"cpp( + #define FOO a + #define BAR b + + void foo(int a, int b) { + $0^FOO+$1^BAR; + } + )cpp", + "0: targets = {a}\n" + "1: targets = {b}\n"}, + // No references from implicit nodes. + {R"cpp( + struct vector { + int *begin(); + int *end(); + }; + + void foo() { + for (int x : $0^vector()) { + $1^x = 10; + } + } + )cpp", + "0: targets = {vector}\n" + "1: targets = {x}\n"}, + // Handle UnresolvedLookupExpr. + {R"cpp( + namespace ns1 { void func(char*); } + namespace ns2 { void func(int*); } + using namespace ns1; + using namespace ns2; + + template + void foo(T t) { + $0^func($1^t); + } + )cpp", + "0: targets = {ns1::func, ns2::func}\n" + "1: targets = {t}\n"}, + // Handle UnresolvedMemberExpr. + {R"cpp( + struct X { + void func(char*); + void func(int*); + }; + + template + void foo(X x, T t) { + $0^x.$1^func($2^t); + } + )cpp", + "0: targets = {x}\n" + "1: targets = {X::func, X::func}\n" + "2: targets = {t}\n"}, + // Type template parameters. + {R"cpp( + template + void foo() { + static_cast<$0^T>(0); + $1^T(); + $2^T t; + } + )cpp", + "0: targets = {T}\n" + "1: targets = {T}\n" + "2: targets = {T}\n"}, + // Non-type template parameters. + {R"cpp( + template + void foo() { + int x = $0^I; + } + )cpp", + "0: targets = {I}\n"}, + // Template template parameters. + {R"cpp( + template struct vector {}; + + template class TT, template class ...TP> + void foo() { + $0^TT x; + $1^foo<$2^TT>(); + $3^foo<$4^vector>() + $5^foo<$6^TP...>(); + } + )cpp", + "0: targets = {TT}\n" + "1: targets = {foo}\n" + "2: targets = {TT}\n" + "3: targets = {foo}\n" + "4: targets = {vector}\n" + "5: targets = {foo}\n" + "6: targets = {TP}\n"}, + // Non-type template parameters with declarations. + {R"cpp( + int func(); + template struct wrapper {}; + + template + void foo() { + $0^wrapper<$1^func> w; + $2^FuncParam(); + } + )cpp", + "0: targets = {wrapper<&func>}\n" + "1: targets = {func}\n" + "2: targets = {FuncParam}\n"}, + }; + + for (const auto &C : Cases) { + llvm::StringRef ExpectedCode = C.first; + llvm::StringRef ExpectedRefs = C.second; + + auto Actual = + annotateReferencesInFoo(llvm::Annotations(ExpectedCode).code()); + EXPECT_EQ(ExpectedCode, Actual.AnnotatedCode); + EXPECT_EQ(ExpectedRefs, Actual.DumpedReferences) << ExpectedCode; + } +} + } // namespace } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/HeaderSourceSwitchTests.cpp b/clang-tools-extra/clangd/unittests/HeaderSourceSwitchTests.cpp new file mode 100644 index 0000000000000..3b5fe86b96b6c --- /dev/null +++ b/clang-tools-extra/clangd/unittests/HeaderSourceSwitchTests.cpp @@ -0,0 +1,272 @@ +//===--- HeaderSourceSwitchTests.cpp - ---------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "HeaderSourceSwitch.h" + +#include "SyncAPI.h" +#include "TestFS.h" +#include "TestTU.h" +#include "index/MemIndex.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace clang { +namespace clangd { +namespace { + +TEST(HeaderSourceSwitchTest, FileHeuristic) { + MockFSProvider FS; + auto FooCpp = testPath("foo.cpp"); + auto FooH = testPath("foo.h"); + auto Invalid = testPath("main.cpp"); + + FS.Files[FooCpp]; + FS.Files[FooH]; + FS.Files[Invalid]; + Optional PathResult = + getCorrespondingHeaderOrSource(FooCpp, FS.getFileSystem()); + EXPECT_TRUE(PathResult.hasValue()); + ASSERT_EQ(PathResult.getValue(), FooH); + + PathResult = getCorrespondingHeaderOrSource(FooH, FS.getFileSystem()); + EXPECT_TRUE(PathResult.hasValue()); + ASSERT_EQ(PathResult.getValue(), FooCpp); + + // Test with header file in capital letters and different extension, source + // file with different extension + auto FooC = testPath("bar.c"); + auto FooHH = testPath("bar.HH"); + + FS.Files[FooC]; + FS.Files[FooHH]; + PathResult = getCorrespondingHeaderOrSource(FooC, FS.getFileSystem()); + EXPECT_TRUE(PathResult.hasValue()); + ASSERT_EQ(PathResult.getValue(), FooHH); + + // Test with both capital letters + auto Foo2C = testPath("foo2.C"); + auto Foo2HH = testPath("foo2.HH"); + FS.Files[Foo2C]; + FS.Files[Foo2HH]; + PathResult = getCorrespondingHeaderOrSource(Foo2C, FS.getFileSystem()); + EXPECT_TRUE(PathResult.hasValue()); + ASSERT_EQ(PathResult.getValue(), Foo2HH); + + // Test with source file as capital letter and .hxx header file + auto Foo3C = testPath("foo3.C"); + auto Foo3HXX = testPath("foo3.hxx"); + + FS.Files[Foo3C]; + FS.Files[Foo3HXX]; + PathResult = getCorrespondingHeaderOrSource(Foo3C, FS.getFileSystem()); + EXPECT_TRUE(PathResult.hasValue()); + ASSERT_EQ(PathResult.getValue(), Foo3HXX); + + // Test if asking for a corresponding file that doesn't exist returns an empty + // string. + PathResult = getCorrespondingHeaderOrSource(Invalid, FS.getFileSystem()); + EXPECT_FALSE(PathResult.hasValue()); +} + +MATCHER_P(DeclNamed, Name, "") { + if (const NamedDecl *ND = dyn_cast(arg)) + if (ND->getQualifiedNameAsString() == Name) + return true; + return false; +} + +TEST(HeaderSourceSwitchTest, GetLocalDecls) { + TestTU TU; + TU.HeaderCode = R"cpp( + void HeaderOnly(); + )cpp"; + TU.Code = R"cpp( + void MainF1(); + class Foo {}; + namespace ns { + class Foo { + void method(); + int field; + }; + } // namespace ns + + // Non-indexable symbols + namespace { + void Ignore1() {} + } + + )cpp"; + + auto AST = TU.build(); + EXPECT_THAT(getIndexableLocalDecls(AST), + testing::UnorderedElementsAre( + DeclNamed("MainF1"), DeclNamed("Foo"), DeclNamed("ns::Foo"), + DeclNamed("ns::Foo::method"), DeclNamed("ns::Foo::field"))); +} + +TEST(HeaderSourceSwitchTest, FromHeaderToSource) { + // build a proper index, which contains symbols: + // A_Sym1, declared in TestTU.h, defined in a.cpp + // B_Sym[1-2], declared in TestTU.h, defined in b.cpp + SymbolSlab::Builder AllSymbols; + TestTU Testing; + Testing.HeaderFilename = "TestTU.h"; + Testing.HeaderCode = "void A_Sym1();"; + Testing.Filename = "a.cpp"; + Testing.Code = "void A_Sym1() {};"; + for (auto &Sym : Testing.headerSymbols()) + AllSymbols.insert(Sym); + + Testing.HeaderCode = R"cpp( + void B_Sym1(); + void B_Sym2(); + )cpp"; + Testing.Filename = "b.cpp"; + Testing.Code = R"cpp( + void B_Sym1() {} + void B_Sym2() {} + )cpp"; + for (auto &Sym : Testing.headerSymbols()) + AllSymbols.insert(Sym); + auto Index = MemIndex::build(std::move(AllSymbols).build(), {}, {}); + + // Test for swtich from .h header to .cc source + struct { + llvm::StringRef HeaderCode; + llvm::Optional ExpectedSource; + } TestCases[] = { + {"// empty, no header found", llvm::None}, + {R"cpp( + // no definition found in the index. + void NonDefinition(); + )cpp", + llvm::None}, + {R"cpp( + void A_Sym1(); + )cpp", + testPath("a.cpp")}, + {R"cpp( + // b.cpp wins. + void A_Sym1(); + void B_Sym1(); + void B_Sym2(); + )cpp", + testPath("b.cpp")}, + {R"cpp( + // a.cpp and b.cpp have same scope, but a.cpp because "a.cpp" < "b.cpp". + void A_Sym1(); + void B_Sym1(); + )cpp", + testPath("a.cpp")}, + }; + for (const auto &Case : TestCases) { + TestTU TU = TestTU::withCode(Case.HeaderCode); + TU.Filename = "TestTU.h"; + TU.ExtraArgs.push_back("-xc++-header"); // inform clang this is a header. + auto HeaderAST = TU.build(); + EXPECT_EQ(Case.ExpectedSource, + getCorrespondingHeaderOrSource(testPath(TU.Filename), HeaderAST, + Index.get())); + } +} + +TEST(HeaderSourceSwitchTest, FromSourceToHeader) { + // build a proper index, which contains symbols: + // A_Sym1, declared in a.h, defined in TestTU.cpp + // B_Sym[1-2], declared in b.h, defined in TestTU.cpp + TestTU TUForIndex = TestTU::withCode(R"cpp( + #include "a.h" + #include "b.h" + + void A_Sym1() {} + + void B_Sym1() {} + void B_Sym2() {} + )cpp"); + TUForIndex.AdditionalFiles["a.h"] = R"cpp( + void A_Sym1(); + )cpp"; + TUForIndex.AdditionalFiles["b.h"] = R"cpp( + void B_Sym1(); + void B_Sym2(); + )cpp"; + TUForIndex.Filename = "TestTU.cpp"; + auto Index = TUForIndex.index(); + + // Test for switching from .cc source file to .h header. + struct { + llvm::StringRef SourceCode; + llvm::Optional ExpectedResult; + } TestCases[] = { + {"// empty, no header found", llvm::None}, + {R"cpp( + // symbol not in index, no header found + void Local() {} + )cpp", + llvm::None}, + + {R"cpp( + // a.h wins. + void A_Sym1() {} + )cpp", + testPath("a.h")}, + + {R"cpp( + // b.h wins. + void A_Sym1() {} + void B_Sym1() {} + void B_Sym2() {} + )cpp", + testPath("b.h")}, + + {R"cpp( + // a.h and b.h have same scope, but a.h wins because "a.h" < "b.h". + void A_Sym1() {} + void B_Sym1() {} + )cpp", + testPath("a.h")}, + }; + for (const auto &Case : TestCases) { + TestTU TU = TestTU::withCode(Case.SourceCode); + TU.Filename = "Test.cpp"; + auto AST = TU.build(); + EXPECT_EQ(Case.ExpectedResult, + getCorrespondingHeaderOrSource(testPath(TU.Filename), AST, + Index.get())); + } +} + +TEST(HeaderSourceSwitchTest, ClangdServerIntegration) { + class IgnoreDiagnostics : public DiagnosticsConsumer { + void onDiagnosticsReady(PathRef File, + std::vector Diagnostics) override {} + } DiagConsumer; + MockCompilationDatabase CDB; + CDB.ExtraClangFlags = {"-I" + + testPath("src/include")}; // add search directory. + MockFSProvider FS; + // File heuristic fails here, we rely on the index to find the .h file. + std::string CppPath = testPath("src/lib/test.cpp"); + std::string HeaderPath = testPath("src/include/test.h"); + FS.Files[HeaderPath] = "void foo();"; + const std::string FileContent = R"cpp( + #include "test.h" + void foo() {}; + )cpp"; + FS.Files[CppPath] = FileContent; + auto Options = ClangdServer::optsForTest(); + Options.BuildDynamicSymbolIndex = true; + ClangdServer Server(CDB, FS, DiagConsumer, Options); + runAddDocument(Server, CppPath, FileContent); + EXPECT_EQ(HeaderPath, + *llvm::cantFail(runSwitchHeaderSource(Server, CppPath))); +} + +} // namespace +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp b/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp index 65020bf2a95f7..53e2f4b68e158 100644 --- a/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp +++ b/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp @@ -229,8 +229,8 @@ TEST(ParsedASTTest, CanBuildInvocationWithUnknownArgs) { TEST(ParsedASTTest, CollectsMainFileMacroExpansions) { Annotations TestCase(R"cpp( - #define MACRO_ARGS(X, Y) X Y - // - premable ends, macros inside preamble are not considered in main file. + #define ^MACRO_ARGS(X, Y) X Y + // - preamble ends ^ID(int A); // Macro arguments included. ^MACRO_ARGS(^MACRO_ARGS(^MACRO_EXP(int), A), ^ID(= 2)); @@ -270,12 +270,11 @@ TEST(ParsedASTTest, CollectsMainFileMacroExpansions) { int D = DEF; )cpp"; ParsedAST AST = TU.build(); - const std::vector &MacroExpansionLocations = AST.getMacros(); std::vector MacroExpansionPositions; - for (const auto &L : MacroExpansionLocations) - MacroExpansionPositions.push_back( - sourceLocToPosition(AST.getSourceManager(), L)); - EXPECT_EQ(MacroExpansionPositions, TestCase.points()); + for (const auto &R : AST.getMacros().Ranges) + MacroExpansionPositions.push_back(R.start); + EXPECT_THAT(MacroExpansionPositions, + testing::UnorderedElementsAreArray(TestCase.points())); } } // namespace diff --git a/clang-tools-extra/clangd/unittests/RenameTests.cpp b/clang-tools-extra/clangd/unittests/RenameTests.cpp index 99e07e240e203..e3af711fbe36c 100644 --- a/clang-tools-extra/clangd/unittests/RenameTests.cpp +++ b/clang-tools-extra/clangd/unittests/RenameTests.cpp @@ -95,7 +95,19 @@ TEST(RenameTest, Renameable) { const char *Code; const char* ErrorMessage; // null if no error bool IsHeaderFile; + const SymbolIndex *Index; }; + TestTU OtherFile = TestTU::withCode("Outside s; auto ss = &foo;"); + const char *CommonHeader = R"cpp( + class Outside {}; + void foo(); + )cpp"; + OtherFile.HeaderCode = CommonHeader; + OtherFile.Filename = "other.cc"; + // The index has a "Outside" reference and a "foo" reference. + auto OtherFileIndex = OtherFile.index(); + const SymbolIndex *Index = OtherFileIndex.get(); + const bool HeaderFile = true; Case Cases[] = { {R"cpp(// allow -- function-local @@ -103,52 +115,55 @@ TEST(RenameTest, Renameable) { [[Local]] = 2; } )cpp", - nullptr, HeaderFile}, + nullptr, HeaderFile, Index}, {R"cpp(// allow -- symbol is indexable and has no refs in index. void [[On^lyInThisFile]](); )cpp", - nullptr, HeaderFile}, + nullptr, HeaderFile, Index}, {R"cpp(// disallow -- symbol is indexable and has other refs in index. void f() { Out^side s; } )cpp", - "used outside main file", HeaderFile}, + "used outside main file", HeaderFile, Index}, {R"cpp(// disallow -- symbol is not indexable. namespace { class Unin^dexable {}; } )cpp", - "not eligible for indexing", HeaderFile}, + "not eligible for indexing", HeaderFile, Index}, {R"cpp(// disallow -- namespace symbol isn't supported namespace fo^o {} )cpp", - "not a supported kind", HeaderFile}, + "not a supported kind", HeaderFile, Index}, { R"cpp( #define MACRO 1 int s = MAC^RO; )cpp", - "not a supported kind", HeaderFile}, + "not a supported kind", HeaderFile, Index}, + + { + + R"cpp( + struct X { X operator++(int) {} }; + void f(X x) {x+^+;})cpp", + "not a supported kind", HeaderFile, Index}, {R"cpp(// foo is declared outside the file. void fo^o() {} - )cpp", "used outside main file", !HeaderFile/*cc file*/}, + )cpp", "used outside main file", !HeaderFile /*cc file*/, Index}, + + {R"cpp( + // We should detect the symbol is used outside the file from the AST. + void fo^o() {})cpp", + "used outside main file", !HeaderFile, nullptr /*no index*/}, }; - const char *CommonHeader = R"cpp( - class Outside {}; - void foo(); - )cpp"; - TestTU OtherFile = TestTU::withCode("Outside s; auto ss = &foo;"); - OtherFile.HeaderCode = CommonHeader; - OtherFile.Filename = "other.cc"; - // The index has a "Outside" reference and a "foo" reference. - auto OtherFileIndex = OtherFile.index(); for (const auto& Case : Cases) { Annotations T(Case.Code); @@ -163,7 +178,7 @@ TEST(RenameTest, Renameable) { auto AST = TU.build(); auto Results = renameWithinFile(AST, testPath(TU.Filename), T.point(), - "dummyNewName", OtherFileIndex.get()); + "dummyNewName", Case.Index); bool WantRename = true; if (T.ranges().empty()) WantRename = false; diff --git a/clang-tools-extra/clangd/unittests/SelectionTests.cpp b/clang-tools-extra/clangd/unittests/SelectionTests.cpp index 964f0e98e2867..e2cbff37b3db0 100644 --- a/clang-tools-extra/clangd/unittests/SelectionTests.cpp +++ b/clang-tools-extra/clangd/unittests/SelectionTests.cpp @@ -354,6 +354,8 @@ TEST(SelectionTest, Selected) { #define ECHO(X) X ECHO(EC^HO([[$C[[int]]) EC^HO(a]])); ]])cpp", + R"cpp( $C[[^$C[[int]] a^]]; )cpp", + R"cpp( $C[[^$C[[int]] a = $C[[5]]^]]; )cpp", }; for (const char *C : Cases) { Annotations Test(C); diff --git a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp index 50955b3e69e56..06c8d3ae1b6ca 100644 --- a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp +++ b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp @@ -425,8 +425,8 @@ TEST(SemanticHighlighting, GetsCorrectTokens) { // Tokens that share a source range but have conflicting Kinds are not // highlighted. R"cpp( - #define DEF_MULTIPLE(X) namespace X { class X { int X; }; } - #define DEF_CLASS(T) class T {}; + #define $Macro[[DEF_MULTIPLE]](X) namespace X { class X { int X; }; } + #define $Macro[[DEF_CLASS]](T) class T {}; // Preamble ends. $Macro[[DEF_MULTIPLE]](XYZ); $Macro[[DEF_MULTIPLE]](XYZW); @@ -465,8 +465,8 @@ TEST(SemanticHighlighting, GetsCorrectTokens) { } )cpp", R"cpp( - #define fail(expr) expr - #define assert(COND) if (!(COND)) { fail("assertion failed" #COND); } + #define $Macro[[fail]](expr) expr + #define $Macro[[assert]](COND) if (!(COND)) { fail("assertion failed" #COND); } // Preamble ends. $Primitive[[int]] $Variable[[x]]; $Primitive[[int]] $Variable[[y]]; diff --git a/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp b/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp new file mode 100644 index 0000000000000..b9ca0273a8233 --- /dev/null +++ b/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp @@ -0,0 +1,181 @@ +//===-- SemanticSelectionTests.cpp ----------------*- C++ -*--------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Annotations.h" +#include "ClangdServer.h" +#include "Matchers.h" +#include "Protocol.h" +#include "SemanticSelection.h" +#include "SourceCode.h" +#include "SyncAPI.h" +#include "TestFS.h" +#include "TestTU.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/Support/Error.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include +namespace clang { +namespace clangd { +namespace { +using ::testing::ElementsAreArray; + +class IgnoreDiagnostics : public DiagnosticsConsumer { + void onDiagnosticsReady(PathRef File, + std::vector Diagnostics) override {} +}; + +TEST(SemanticSelection, All) { + const char *Tests[] = { + R"cpp( // Single statement in a function body. + [[void func() [[{ + [[[[int v = [[1^00]]]];]] + }]]]] + )cpp", + R"cpp( // Expression + [[void func() [[{ + int a = 1; + // int v = (10 + 2) * (a + a); + [[[[int v = [[[[([[[[10^]] + 2]])]] * (a + a)]]]];]] + }]]]] + )cpp", + R"cpp( // Function call. + int add(int x, int y) { return x + y; } + [[void callee() [[{ + // int res = add(11, 22); + [[[[int res = [[add([[1^1]], 22)]]]];]] + }]]]] + )cpp", + R"cpp( // Tricky macros. + #define MUL ) * ( + [[void func() [[{ + // int var = (4 + 15 MUL 6 + 10); + [[[[int var = [[[[([[4 + [[1^5]]]] MUL]] 6 + 10)]]]];]] + }]]]] + )cpp", + R"cpp( // Cursor inside a macro. + #define HASH(x) ((x) % 10) + [[void func() [[{ + [[[[int a = [[HASH([[[[2^3]] + 34]])]]]];]] + }]]]] + )cpp", + R"cpp( // Cursor on a macro. + #define HASH(x) ((x) % 10) + [[void func() [[{ + [[[[int a = [[HA^SH(23)]]]];]] + }]]]] + )cpp", + R"cpp( // Multiple declaration. + [[void func() [[{ + [[[[int var1, var^2]], var3;]] + }]]]] + )cpp", + R"cpp( // Before comment. + [[void func() [[{ + int var1 = 1; + [[[[int var2 = [[[[var1]]^ /*some comment*/ + 41]]]];]] + }]]]] + )cpp", + // Empty file. + "^", + // FIXME: We should get the whole DeclStmt as a range. + R"cpp( // Single statement in TU. + [[int v = [[1^00]]]]; + )cpp", + // FIXME: No node found associated to the position. + R"cpp( // Cursor at end of VarDecl. + void func() { + int v = 100 + 100^; + } + )cpp", + // FIXME: No node found associated to the position. + R"cpp( // Cursor in between spaces. + void func() { + int v = 100 + ^ 100; + } + )cpp", + // Structs. + R"cpp( + struct AAA { struct BBB { static int ccc(); };}; + [[void func() [[{ + // int x = AAA::BBB::ccc(); + [[[[int x = [[[[AAA::BBB::c^cc]]()]]]];]] + }]]]] + )cpp", + R"cpp( + struct AAA { struct BBB { static int ccc(); };}; + [[void func() [[{ + // int x = AAA::BBB::ccc(); + [[[[int x = [[[[[[[[[[AA^A]]::]]BBB::]]ccc]]()]]]];]] + }]]]] + )cpp", + R"cpp( // Inside struct. + struct A { static int a(); }; + [[struct B { + [[static int b() [[{ + [[return [[[[1^1]] + 2]]]]; + }]]]] + }]]; + )cpp", + // Namespaces. + R"cpp( + [[namespace nsa { + [[namespace nsb { + static int ccc(); + [[void func() [[{ + // int x = nsa::nsb::ccc(); + [[[[int x = [[[[nsa::nsb::cc^c]]()]]]];]] + }]]]] + }]] + }]] + )cpp", + + }; + + for (const char *Test : Tests) { + auto T = Annotations(Test); + auto AST = TestTU::withCode(T.code()).build(); + EXPECT_THAT(llvm::cantFail(getSemanticRanges(AST, T.point())), + ElementsAreArray(T.ranges())) + << Test; + } +} + +TEST(SemanticSelection, RunViaClangDServer) { + MockFSProvider FS; + IgnoreDiagnostics DiagConsumer; + MockCompilationDatabase CDB; + ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + + auto FooH = testPath("foo.h"); + FS.Files[FooH] = R"cpp( + int foo(int x); + #define HASH(x) ((x) % 10) + )cpp"; + + auto FooCpp = testPath("Foo.cpp"); + const char *SourceContents = R"cpp( + #include "foo.h" + [[void bar(int& inp) [[{ + // inp = HASH(foo(inp)); + [[inp = [[HASH([[foo([[in^p]])]])]]]]; + }]]]] + )cpp"; + Annotations SourceAnnotations(SourceContents); + FS.Files[FooCpp] = SourceAnnotations.code(); + Server.addDocument(FooCpp, SourceAnnotations.code()); + + auto Ranges = runSemanticRanges(Server, FooCpp, SourceAnnotations.point()); + ASSERT_TRUE(bool(Ranges)) + << "getSemanticRange returned an error: " << Ranges.takeError(); + EXPECT_THAT(*Ranges, ElementsAreArray(SourceAnnotations.ranges())); +} +} // namespace +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/unittests/SourceCodeTests.cpp b/clang-tools-extra/clangd/unittests/SourceCodeTests.cpp index cd9ccf0481a70..e49f64cef139e 100644 --- a/clang-tools-extra/clangd/unittests/SourceCodeTests.cpp +++ b/clang-tools-extra/clangd/unittests/SourceCodeTests.cpp @@ -319,14 +319,29 @@ struct Bar { int func(); }; Bar* bar; )cpp"; // First ^ is the expected beginning, last is the search position. - for (std::string Text : std::vector{ + for (const std::string &Text : std::vector{ "int ^f^oo();", // inside identifier "int ^foo();", // beginning of identifier "int ^foo^();", // end of identifier "int foo(^);", // non-identifier "^int foo();", // beginning of file (can't back up) "int ^f0^0();", // after a digit (lexing at N-1 is wrong) - "int ^λλ^λ();", // UTF-8 handled properly when backing up + "/^/ comments", // non-interesting token + "void f(int abc) { abc ^ ++; }", // whitespace + "void f(int abc) { ^abc^++; }", // range of identifier + "void f(int abc) { ++^abc^; }", // range of identifier + "void f(int abc) { ++^abc; }", // range of identifier + "void f(int abc) { ^+^+abc; }", // range of operator + "void f(int abc) { ^abc^ ++; }", // range of identifier + "void f(int abc) { abc ^++^; }", // range of operator + "void f(int abc) { ^++^ abc; }", // range of operator + "void f(int abc) { ++ ^abc^; }", // range of identifier + "void f(int abc) { ^++^/**/abc; }", // range of operator + "void f(int abc) { ++/**/^abc; }", // range of identifier + "void f(int abc) { ^abc^/**/++; }", // range of identifier + "void f(int abc) { abc/**/^++; }", // range of operator + "void f() {^ }", // outside of identifier and operator + "int ^λλ^λ();", // UTF-8 handled properly when backing up // identifier in macro arg "MACRO(bar->^func())", // beginning of identifier @@ -441,6 +456,16 @@ TEST(SourceCodeTests, VisibleNamespaces) { "c::d", }, }, + { + "", + {""}, + }, + { + R"cpp( + // Parse until EOF + namespace bar{})cpp", + {""}, + }, }; for (const auto& Case : Cases) { EXPECT_EQ(Case.second, diff --git a/clang-tools-extra/clangd/unittests/SyncAPI.cpp b/clang-tools-extra/clangd/unittests/SyncAPI.cpp index af9ef9ffa1e85..812fa7a0f2ecb 100644 --- a/clang-tools-extra/clangd/unittests/SyncAPI.cpp +++ b/clang-tools-extra/clangd/unittests/SyncAPI.cpp @@ -145,5 +145,19 @@ RefSlab getRefs(const SymbolIndex &Index, SymbolID ID) { return std::move(Slab).build(); } +llvm::Expected> +runSemanticRanges(ClangdServer &Server, PathRef File, Position Pos) { + llvm::Optional>> Result; + Server.semanticRanges(File, Pos, capture(Result)); + return std::move(*Result); +} + +llvm::Expected> +runSwitchHeaderSource(ClangdServer &Server, PathRef File) { + llvm::Optional>> Result; + Server.switchSourceHeader(File, capture(Result)); + return std::move(*Result); +} + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/SyncAPI.h b/clang-tools-extra/clangd/unittests/SyncAPI.h index c141652415313..5ffed1fbb120c 100644 --- a/clang-tools-extra/clangd/unittests/SyncAPI.h +++ b/clang-tools-extra/clangd/unittests/SyncAPI.h @@ -53,6 +53,12 @@ SymbolSlab runFuzzyFind(const SymbolIndex &Index, StringRef Query); SymbolSlab runFuzzyFind(const SymbolIndex &Index, const FuzzyFindRequest &Req); RefSlab getRefs(const SymbolIndex &Index, SymbolID ID); +llvm::Expected> +runSemanticRanges(ClangdServer &Server, PathRef File, Position Pos); + +llvm::Expected> +runSwitchHeaderSource(ClangdServer &Server, PathRef File); + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp b/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp index 5dbfca5f2462c..db05c70aa7332 100644 --- a/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp +++ b/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp @@ -769,12 +769,14 @@ TEST_F(TUSchedulerTests, CommandLineWarnings) { // We should not see warnings from command-line parsing. CDB.ExtraClangFlags = {"-Wsome-unknown-warning"}; + // (!) 'Ready' must live longer than TUScheduler. + Notification Ready; + TUScheduler S(CDB, /*AsyncThreadsCount=*/getDefaultAsyncThreadsCount(), /*StorePreambleInMemory=*/true, /*ASTCallbacks=*/captureDiags(), /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(), ASTRetentionPolicy()); - Notification Ready; std::vector Diagnostics; updateWithDiags(S, testPath("foo.cpp"), "void test() {}", WantDiagnostics::Yes, [&](std::vector D) { diff --git a/clang-tools-extra/clangd/unittests/TweakTests.cpp b/clang-tools-extra/clangd/unittests/TweakTests.cpp index c3384af082228..97cd4a2cafa66 100644 --- a/clang-tools-extra/clangd/unittests/TweakTests.cpp +++ b/clang-tools-extra/clangd/unittests/TweakTests.cpp @@ -540,13 +540,12 @@ TEST_F(ExtractFunctionTest, FunctionTest) { EXPECT_EQ(apply("int x = 0; [[x++;]]"), "unavailable"); // We don't support extraction from lambdas. EXPECT_EQ(apply("auto lam = [](){ [[int x;]] }; "), "unavailable"); + // Partial statements aren't extracted. + EXPECT_THAT(apply("int [[x = 0]];"), "unavailable"); // Ensure that end of Zone and Beginning of PostZone being adjacent doesn't // lead to break being included in the extraction zone. EXPECT_THAT(apply("for(;;) { [[int x;]]break; }"), HasSubstr("extracted")); - // FIXME: This should be unavailable since partially selected but - // selectionTree doesn't always work correctly for VarDecls. - EXPECT_THAT(apply("int [[x = 0]];"), HasSubstr("extracted")); // FIXME: ExtractFunction should be unavailable inside loop construct // initalizer/condition. EXPECT_THAT(apply(" for([[int i = 0;]];);"), HasSubstr("extracted")); @@ -554,7 +553,6 @@ TEST_F(ExtractFunctionTest, FunctionTest) { EXPECT_THAT(apply(" [[int a = 5;]] a++; "), StartsWith("fail")); // Don't extract return EXPECT_THAT(apply(" if(true) [[return;]] "), StartsWith("fail")); - } TEST_F(ExtractFunctionTest, FileTest) { @@ -631,6 +629,16 @@ void f(const int c) { F ([[int x = 0;]]) )cpp"; EXPECT_EQ(apply(MacroFailInput), "unavailable"); + + // Shouldn't crash. + EXPECT_EQ(apply("void f([[int a]]);"), "unavailable"); + // Don't extract if we select the entire function body (CompoundStmt). + std::string CompoundFailInput = R"cpp( + void f() [[{ + int a; + }]] + )cpp"; + EXPECT_EQ(apply(CompoundFailInput), "unavailable"); } TEST_F(ExtractFunctionTest, ControlFlow) { diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp index e4a4a6f82587a..60cba82492d72 100644 --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -10,6 +10,7 @@ #include "Matchers.h" #include "ParsedAST.h" #include "Protocol.h" +#include "SourceCode.h" #include "SyncAPI.h" #include "TestFS.h" #include "TestIndex.h" @@ -18,13 +19,19 @@ #include "index/FileIndex.h" #include "index/MemIndex.h" #include "index/SymbolCollector.h" +#include "clang/AST/Decl.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Index/IndexingAction.h" #include "llvm/ADT/None.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Error.h" #include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include +#include namespace clang { namespace clangd { @@ -434,6 +441,15 @@ TEST(LocateSymbol, All) { auto x = m^akeX(); } )cpp", + + R"cpp( + struct X { + X& [[operator]]++() {} + }; + void foo(X& x) { + +^+x; + } + )cpp", }; for (const char *Test : Tests) { Annotations T(Test); @@ -2187,6 +2203,84 @@ TEST(GetDeducedType, KwAutoExpansion) { } } +TEST(GetNonLocalDeclRefs, All) { + struct Case { + llvm::StringRef AnnotatedCode; + std::vector ExpectedDecls; + } Cases[] = { + { + // VarDecl and ParamVarDecl + R"cpp( + void bar(); + void ^foo(int baz) { + int x = 10; + bar(); + })cpp", + {"bar"}, + }, + { + // Method from class + R"cpp( + class Foo { public: void foo(); }; + class Bar { + void foo(); + void bar(); + }; + void Bar::^foo() { + Foo f; + bar(); + f.foo(); + })cpp", + {"Bar", "Bar::bar", "Foo", "Foo::foo"}, + }, + { + // Local types + R"cpp( + void ^foo() { + class Foo { public: void foo() {} }; + class Bar { public: void bar() {} }; + Foo f; + Bar b; + b.bar(); + f.foo(); + })cpp", + {}, + }, + { + // Template params + R"cpp( + template class Q> + void ^foo() { + T x; + Q y; + })cpp", + {}, + }, + }; + for (const Case &C : Cases) { + Annotations File(C.AnnotatedCode); + auto AST = TestTU::withCode(File.code()).build(); + ASSERT_TRUE(AST.getDiagnostics().empty()) + << AST.getDiagnostics().begin()->Message; + SourceLocation SL = llvm::cantFail( + sourceLocationInMainFile(AST.getSourceManager(), File.point())); + + const FunctionDecl *FD = + llvm::dyn_cast(&findDecl(AST, [SL](const NamedDecl &ND) { + return ND.getLocation() == SL && llvm::isa(ND); + })); + ASSERT_NE(FD, nullptr); + + auto NonLocalDeclRefs = getNonLocalDeclRefs(AST, FD); + std::vector Names; + for (const Decl *D : NonLocalDeclRefs) { + if (const auto *ND = llvm::dyn_cast(D)) + Names.push_back(ND->getQualifiedNameAsString()); + } + EXPECT_THAT(Names, UnorderedElementsAreArray(C.ExpectedDecls)); + } +} + } // namespace } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 766bbee49717f..e5be18202c9a8 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -73,11 +73,20 @@ Improvements to clang-tidy Finds instances where variables with static storage are initialized dynamically in header files. -- New :doc:`linuxkernel-must-use-errs - ` check. +- New :doc:`bugprone-infinite-loop + ` check. - Checks Linux kernel code to see if it uses the results from the functions in - ``linux/err.h``. + Finds obvious infinite loops (loops where the condition variable is not + changed at all). + +- New :doc:`cppcoreguidelines-init-variables + ` check. + +- New :doc:`darwin-dispatch-once-nonstatic + ` check. + + Finds declarations of ``dispatch_once_t`` variables without static or global + storage. - New :doc:`google-upgrade-googletest-case ` check. @@ -85,12 +94,32 @@ Improvements to clang-tidy Finds uses of deprecated Googletest APIs with names containing ``case`` and replaces them with equivalent APIs with ``suite``. +- New :doc:`linuxkernel-must-use-errs + ` check. + + Checks Linux kernel code to see if it uses the results from the functions in + ``linux/err.h``. + - New :doc:`llvm-prefer-register-over-unsigned ` check. Finds historical use of ``unsigned`` to hold vregs and physregs and rewrites them to use ``Register`` +- New :doc:`objc-missing-hash + ` check. + + Finds Objective-C implementations that implement ``-isEqual:`` without also + appropriately implementing ``-hash``. + +- Improved :doc:`bugprone-posix-return + ` check. + + Now also checks if any calls to ``pthread_*`` functions expect negative return + values. + +- The 'objc-avoid-spinlock' check was renamed to :doc:`darwin-avoid-spinlock + ` Improvements to include-fixer ----------------------------- diff --git a/clang-tools-extra/docs/clang-doc.rst b/clang-tools-extra/docs/clang-doc.rst index 6f61970a58577..0c47405fb5a97 100644 --- a/clang-tools-extra/docs/clang-doc.rst +++ b/clang-tools-extra/docs/clang-doc.rst @@ -12,7 +12,7 @@ source code and comments. The tool is in a very early development stage, so you might encounter bugs and crashes. Submitting reports with information about how to reproduce the issue -to `the LLVM bugtracker `_ will definitely help the +to `the LLVM bugtracker `_ will definitely help the project. If you have any ideas or suggestions, please to put a feature request there. diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone-infinite-loop.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone-infinite-loop.rst new file mode 100644 index 0000000000000..89502c1882a92 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone-infinite-loop.rst @@ -0,0 +1,32 @@ +.. title:: clang-tidy - bugprone-infinite-loop + +bugprone-infinite-loop +====================== + +Finds obvious infinite loops (loops where the condition variable is not changed +at all). + +Finding infinite loops is well-known to be impossible (halting problem). +However, it is possible to detect some obvious infinite loops, for example, if +the loop condition is not changed. This check detects such loops. A loop is +considered infinite if it does not have any loop exit statement (``break``, +``continue``, ``goto``, ``return``, ``throw`` or a call to a function called as +``[[noreturn]]``) and all of the following conditions hold for every variable in +the condition: + +- It is a local variable. +- It has no reference or pointer aliases. +- It is not a structure or class member. + +Furthermore, the condition must not contain a function call to consider the loop +infinite since functions may return different values for different calls. + +For example, the following loop is considered infinite `i` is not changed in +the body: + +.. code-block:: c++ + + int i = 0, j = 0; + while (i < 10) { + ++j; + } diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone-posix-return.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone-posix-return.rst index 2248efab551f4..a5c4ccb0c4d7c 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/bugprone-posix-return.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone-posix-return.rst @@ -3,9 +3,9 @@ bugprone-posix-return ===================== -Checks if any calls to POSIX functions (except ``posix_openpt``) expect negative -return values. These functions return either ``0`` on success or an ``errno`` on failure, -which is positive only. +Checks if any calls to ``pthread_*`` or ``posix_*`` functions +(except ``posix_openpt``) expect negative return values. These functions return +either ``0`` on success or an ``errno`` on failure, which is positive only. Example buggy usage looks like: diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-init-variables.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-init-variables.rst new file mode 100644 index 0000000000000..e1b0fe3230007 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-init-variables.rst @@ -0,0 +1,51 @@ +.. title:: clang-tidy - cppcoreguidelines-init-variables + +cppcoreguidelines-init-variables +================================ + +Checks whether there are local variables that are declared without an +initial value. These may lead to unexpected behaviour if there is a +code path that reads the variable before assigning to it. + +Only integers, booleans, floats, doubles and pointers are checked. The +fix option initializes all detected values with the value of zero. An +exception is float and double types, which are initialized to NaN. + +As an example a function that looks like this: + +.. code-block:: c++ + + void function() { + int x; + char *txt; + double d; + + // Rest of the function. + } + +Would be rewritten to look like this: + +.. code-block:: c++ + + #include + + void function() { + int x = 0; + char *txt = nullptr; + double d = NAN; + + // Rest of the function. + } + +Options +------- + +.. option:: IncludeStyle + + A string specifying which include-style is used, `llvm` or + `google`. Default is `llvm`. + +.. option:: MathHeader + + A string specifying the header to include to get the definition of + `NAN`. Default is `math.h`. diff --git a/clang-tools-extra/docs/clang-tidy/checks/objc-avoid-spinlock.rst b/clang-tools-extra/docs/clang-tidy/checks/darwin-avoid-spinlock.rst similarity index 79% rename from clang-tools-extra/docs/clang-tidy/checks/objc-avoid-spinlock.rst rename to clang-tools-extra/docs/clang-tidy/checks/darwin-avoid-spinlock.rst index d1b11fcbab68e..fd1f5d294a472 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/objc-avoid-spinlock.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/darwin-avoid-spinlock.rst @@ -1,7 +1,7 @@ -.. title:: clang-tidy - objc-avoid-spinlock +.. title:: clang-tidy - darwin-avoid-spinlock -objc-avoid-spinlock -=================== +darwin-avoid-spinlock +===================== Finds usages of ``OSSpinlock``, which is deprecated due to potential livelock problems. diff --git a/clang-tools-extra/docs/clang-tidy/checks/darwin-dispatch-once-nonstatic.rst b/clang-tools-extra/docs/clang-tidy/checks/darwin-dispatch-once-nonstatic.rst new file mode 100644 index 0000000000000..4506bfff2cf27 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/darwin-dispatch-once-nonstatic.rst @@ -0,0 +1,22 @@ +.. title:: clang-tidy - darwin-dispatch-once-nonstatic + +darwin-dispatch-once-nonstatic +============================== + +Finds declarations of ``dispatch_once_t`` variables without static or global +storage. The behavior of using ``dispatch_once_t`` predicates with automatic or +dynamic storage is undefined by libdispatch, and should be avoided. + +It is a common pattern to have functions initialize internal static or global +data once when the function runs, but programmers have been known to miss the +static on the ``dispatch_once_t`` predicate, leading to an uninitialized flag +value at the mercy of the stack. + +Programmers have also been known to make ``dispatch_once_t`` variables be +members of structs or classes, with the intent to lazily perform some expensive +struct or class member initialization only once; however, this violates the +libdispatch requirements. + +See the discussion section of +`Apple's dispatch_once documentation `_ +for more information. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index a906a5b291178..f0f35bd9e621d 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -51,6 +51,7 @@ Clang-Tidy Checks bugprone-forwarding-reference-overload bugprone-inaccurate-erase bugprone-incorrect-roundings + bugprone-infinite-loop bugprone-integer-division bugprone-lambda-function-name bugprone-macro-parentheses @@ -193,6 +194,7 @@ Clang-Tidy Checks cppcoreguidelines-avoid-magic-numbers (redirects to readability-magic-numbers) cppcoreguidelines-c-copy-assignment-signature (redirects to misc-unconventional-assign-operator) cppcoreguidelines-explicit-virtual-functions (redirects to modernize-use-override) + cppcoreguidelines-init-variables cppcoreguidelines-interfaces-global-init cppcoreguidelines-macro-usage cppcoreguidelines-narrowing-conversions @@ -211,6 +213,8 @@ Clang-Tidy Checks cppcoreguidelines-pro-type-vararg cppcoreguidelines-slicing cppcoreguidelines-special-member-functions + darwin-avoid-spinlock + darwin-dispatch-once-nonstatic fuchsia-default-arguments-calls fuchsia-default-arguments-declarations fuchsia-header-anon-namespaces (redirects to google-build-namespaces) @@ -323,8 +327,8 @@ Clang-Tidy Checks mpi-buffer-deref mpi-type-mismatch objc-avoid-nserror-init - objc-avoid-spinlock objc-forbidden-subclassing + objc-missing-hash objc-property-declaration objc-super-self openmp-exception-escape diff --git a/clang-tools-extra/docs/clang-tidy/checks/objc-missing-hash.rst b/clang-tools-extra/docs/clang-tidy/checks/objc-missing-hash.rst new file mode 100644 index 0000000000000..ea8f775897c21 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/objc-missing-hash.rst @@ -0,0 +1,16 @@ +.. title:: clang-tidy - objc-missing-hash + +objc-missing-hash +================= + +Finds Objective-C implementations that implement ``-isEqual:`` without also +appropriately implementing ``-hash``. + +Apple documentation highlights that objects that are equal must have the same +hash value: +https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418795-isequal?language=objc + +Note that the check only verifies the presence of ``-hash`` in scenarios where +its omission could result in unexpected behavior. The verification of the +implementation of ``-hash`` is the responsibility of the developer, e.g., +through the addition of unit tests to verify the implementation. diff --git a/clang-tools-extra/docs/clang-tidy/checks/performance-inefficient-vector-operation.rst b/clang-tools-extra/docs/clang-tidy/checks/performance-inefficient-vector-operation.rst index 8cf9318fd7bf7..f327ac27e8f0a 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/performance-inefficient-vector-operation.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/performance-inefficient-vector-operation.rst @@ -6,6 +6,10 @@ performance-inefficient-vector-operation Finds possible inefficient ``std::vector`` operations (e.g. ``push_back``, ``emplace_back``) that may cause unnecessary memory reallocations. +It can also find calls that add element to protobuf repeated field in a loop +without calling Reserve() before the loop. Calling Reserve() first can avoid +unnecessary memory reallocations. + Currently, the check only detects following kinds of loops with a single statement body: @@ -21,6 +25,13 @@ statement body: // statement before the for statement. } + SomeProto p; + for (int i = 0; i < n; ++i) { + p.add_xxx(n); + // This will trigger the warning since the add_xxx may cause multiple memory + // relloacations. This can be avoid by inserting a + // 'p.mutable_xxx().Reserve(n)' statement before the for statement. + } * For-range loops like ``for (range-declaration : range_expression)``, the type of ``range_expression`` can be ``std::vector``, ``std::array``, @@ -47,3 +58,9 @@ Options Semicolon-separated list of names of vector-like classes. By default only ``::std::vector`` is considered. + +.. option:: EnableProto + + When non-zero, the check will also warn on inefficient operations for proto + repeated fields. Otherwise, the check only warns on inefficient vector + operations. Default is `0`. diff --git a/clang-tools-extra/docs/clangd/Installation.rst b/clang-tools-extra/docs/clangd/Installation.rst index caf49a75d1105..18e1ae9817fc2 100644 --- a/clang-tools-extra/docs/clangd/Installation.rst +++ b/clang-tools-extra/docs/clangd/Installation.rst @@ -352,20 +352,28 @@ Clangd will assume the compile command is ``clang $FLAGS some_file.cc``. Creating this file by hand is a reasonable place to start if your project is quite simple. -Project-wide Index -================== - -By default clangd only has a view on symbols coming from files you are -currently editing. You can extend this view to whole project by providing a -project-wide index to clangd. There are two ways to do this. - -- Pass an experimental `-background-index` command line argument. With - this feature enabled, clangd incrementally builds an index of projects - that you work on and uses the just-built index automatically. - -- Generate an index file using `clangd-indexer - `__ - Then you can pass generated index file to clangd using - `-index-file=/path/to/index_file`. *Note that clangd-indexer isn't - included alongside clangd in the Debian clang-tools package. You will - likely have to build it from source to use this option.* +Background Indexing +=================== + +clangd builds an incremental index of your project (all files listed in the +compilation database). The index improves code navigation features (go-to-definition, +find-references) and code completion. + +- clangd only uses idle cores to build the index, you can limit the total + amount of cores by passing the `-j=` flag; +- the index is saved to the ``.clangd/index`` in the project root; index shards + for common headers e.g. STL will be stored in `$HOME/.clangd/index`; +- background indexing can be disabled by the ``--background-index=false`` flag; + Note that, disabling background-index will limit clangd's knowledge about your + codebase to files you are currently editing. + +Build Index Manually +==================== + +**DISCLAIMER: This is mainly for clangd developers.** + +There is a `clangd-indexer `__ +which generates an index file for your project. To use the index, pass the flag +`-index=file=/path/to/index_file` to clangd. *Note that clangd-indexer isn't +included alongside clangd in the Debian clang-tools package. You will likely +have to build it from source to use this option.* diff --git a/clang-tools-extra/modularize/Modularize.cpp b/clang-tools-extra/modularize/Modularize.cpp index 1905fdf4e2aa8..8d48324700e2e 100644 --- a/clang-tools-extra/modularize/Modularize.cpp +++ b/clang-tools-extra/modularize/Modularize.cpp @@ -585,6 +585,8 @@ class CollectEntitiesVisitor LinkageLabel = "extern \"C\" {}"; break; case LinkageSpecDecl::lang_cxx: + case LinkageSpecDecl::lang_cxx_11: + case LinkageSpecDecl::lang_cxx_14: LinkageLabel = "extern \"C++\" {}"; break; } diff --git a/clang-tools-extra/test/clang-tidy/bugprone-argument-comment.cpp b/clang-tools-extra/test/clang-tidy/bugprone-argument-comment.cpp index 08f87170c42a4..8a4d9e5b644ca 100644 --- a/clang-tools-extra/test/clang-tidy/bugprone-argument-comment.cpp +++ b/clang-tools-extra/test/clang-tidy/bugprone-argument-comment.cpp @@ -63,6 +63,28 @@ void ignores_underscores() { f3(/*With_Underscores=*/false); } +namespace IgnoresImplicit { +struct S { + S(int x); + int x; +}; + +struct T { + // Use two arguments (one defaulted) because simplistic check for implicit + // constructor looks for only one argument. We need to default the argument so + // that it will still be triggered implicitly. This is not contrived -- it + // comes up in real code, for example std::set(std::initializer_list...). + T(S s, int y = 0); +}; + +void k(T arg1); + +void mynewtest() { + int foo = 3; + k(/*arg1=*/S(foo)); +} +} // namespace IgnoresImplicit + namespace ThisEditDistanceAboveThreshold { void f4(int xxx); void g() { f4(/*xyz=*/0); } diff --git a/clang-tools-extra/test/clang-tidy/bugprone-infinite-loop.cpp b/clang-tools-extra/test/clang-tidy/bugprone-infinite-loop.cpp new file mode 100644 index 0000000000000..33d94820b0f69 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/bugprone-infinite-loop.cpp @@ -0,0 +1,320 @@ +// RUN: %check_clang_tidy %s bugprone-infinite-loop %t -- -- -fexceptions + +void simple_infinite_loop1() { + int i = 0; + int j = 0; + while (i < 10) { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop] + j++; + } + + do { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop] + j++; + } while (i < 10); + + for (i = 0; i < 10; ++j) { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop] + } +} + +void simple_infinite_loop2() { + int i = 0; + int j = 0; + int Limit = 10; + while (i < Limit) { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i, Limit) are updated in the loop body [bugprone-infinite-loop] + j++; + } + + do { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i, Limit) are updated in the loop body [bugprone-infinite-loop] + j++; + } while (i < Limit); + + for (i = 0; i < Limit; ++j) { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i, Limit) are updated in the loop body [bugprone-infinite-loop] + } +} + +void simple_not_infinite1() { + int i = 0; + int Limit = 100; + while (i < Limit) { + // Not an error since 'Limit' is updated. + Limit--; + } + do { + Limit--; + } while (i < Limit); + + for (i = 0; i < Limit; Limit--) { + } +} + +void simple_not_infinite2() { + for (int i = 10; i-- > 0;) { + // Not an error, since loop variable is modified in its condition part. + } +} + +int unknown_function(); + +void function_call() { + int i = 0; + while (i < unknown_function()) { + // Not an error, since the function may return different values. + } + + do { + // Not an error, since the function may return different values. + } while (i < unknown_function()); + + for (i = 0; i < unknown_function();) { + // Not an error, since the function may return different values. + } +} + +void escape_before1() { + int i = 0; + int Limit = 100; + int *p = &i; + while (i < Limit) { + // Not an error, since *p is alias of i. + (*p)++; + } + + do { + (*p)++; + } while (i < Limit); + + for (i = 0; i < Limit; ++(*p)) { + } +} + +void escape_before2() { + int i = 0; + int Limit = 100; + int &ii = i; + while (i < Limit) { + // Not an error, since ii is alias of i. + ii++; + } + + do { + ii++; + } while (i < Limit); + + for (i = 0; i < Limit; ++ii) { + } +} + +void escape_inside1() { + int i = 0; + int Limit = 100; + int *p = &i; + while (i < Limit) { + // Not an error, since *p is alias of i. + int *p = &i; + (*p)++; + } + + do { + int *p = &i; + (*p)++; + } while (i < Limit); +} + +void escape_inside2() { + int i = 0; + int Limit = 100; + while (i < Limit) { + // Not an error, since ii is alias of i. + int &ii = i; + ii++; + } + + do { + int &ii = i; + ii++; + } while (i < Limit); +} + +void escape_after1() { + int i = 0; + int j = 0; + int Limit = 10; + + while (i < Limit) { + // False negative, but difficult to detect without CFG-based analysis + } + int *p = &i; +} + +void escape_after2() { + int i = 0; + int j = 0; + int Limit = 10; + + while (i < Limit) { + // False negative, but difficult to detect without CFG-based analysis + } + int &ii = i; +} + +int glob; + +void global1(int &x) { + int i = 0, Limit = 100; + while (x < Limit) { + // Not an error since 'x' can be an alias of 'glob'. + glob++; + } +} + +void global2() { + int i = 0, Limit = 100; + while (glob < Limit) { + // Since 'glob' is declared out of the function we do not warn. + i++; + } +} + +struct X { + int m; + + void change_m(); + + void member_expr1(int i) { + while (i < m) { + // False negative: No warning, since skipping the case where a struct or + // class can be found in its condition. + ; + } + } + + void member_expr2(int i) { + while (i < m) { + --m; + } + } + + void member_expr3(int i) { + while (i < m) { + change_m(); + } + } +}; + +void array_index() { + int i = 0; + int v[10]; + while (i < 10) { + v[i++] = 0; + } + + i = 0; + do { + v[i++] = 0; + } while (i < 9); + + for (i = 0; i < 10;) { + v[i++] = 0; + } + + for (i = 0; i < 10; v[i++] = 0) { + } +} + +void no_loop_variable() { + while (0) + ; +} + +void volatile_in_condition() { + volatile int cond = 0; + while (!cond) { + } +} + +namespace std { +template class atomic { + T val; +public: + atomic(T v): val(v) {}; + operator T() { return val; }; +}; +} + +void atomic_in_condition() { + std::atomic cond = 0; + while (!cond) { + } +} + +void loop_exit1() { + int i = 0; + while (i) { + if (unknown_function()) + break; + } +} + +void loop_exit2() { + int i = 0; + while (i) { + if (unknown_function()) + return; + } +} + +void loop_exit3() { + int i = 0; + while (i) { + if (unknown_function()) + goto end; + } + end: + ; +} + +void loop_exit4() { + int i = 0; + while (i) { + if (unknown_function()) + throw 1; + } +} + +[[noreturn]] void exit(int); + +void loop_exit5() { + int i = 0; + while (i) { + if (unknown_function()) + exit(1); + } +} + +void loop_exit_in_lambda() { + int i = 0; + while (i) { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop] + auto l = []() { return 0; }; + } +} + +void lambda_capture() { + int i = 0; + int Limit = 100; + int *p = &i; + while (i < Limit) { + // Not an error, since i is captured by reference in a lambda. + auto l = [&i]() { ++i; }; + } + + do { + int *p = &i; + (*p)++; + } while (i < Limit); +} diff --git a/clang-tools-extra/test/clang-tidy/bugprone-posix-return.cpp b/clang-tools-extra/test/clang-tidy/bugprone-posix-return.cpp index 160f567cffaeb..271893c707069 100644 --- a/clang-tools-extra/test/clang-tidy/bugprone-posix-return.cpp +++ b/clang-tools-extra/test/clang-tidy/bugprone-posix-return.cpp @@ -9,6 +9,16 @@ typedef long off_t; typedef decltype(sizeof(int)) size_t; typedef struct __posix_spawn_file_actions* posix_spawn_file_actions_t; typedef struct __posix_spawnattr* posix_spawnattr_t; +# define __CPU_SETSIZE 1024 +# define __NCPUBITS (8 * sizeof (__cpu_mask)) +typedef unsigned long int __cpu_mask; +typedef struct +{ + __cpu_mask __bits[__CPU_SETSIZE / __NCPUBITS]; +} cpu_set_t; +typedef struct _opaque_pthread_t *__darwin_pthread_t; +typedef __darwin_pthread_t pthread_t; +typedef struct pthread_attr_t_ *pthread_attr_t; extern "C" int posix_fadvise(int fd, off_t offset, off_t len, int advice); extern "C" int posix_fallocate(int fd, off_t offset, off_t len); @@ -23,6 +33,12 @@ extern "C" int posix_spawnp(pid_t *pid, const char *file, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]); +extern "C" int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); +extern "C" int pthread_attr_setaffinity_np(pthread_attr_t *attr, size_t cpusetsize, const cpu_set_t *cpuset); +extern "C" int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy); +extern "C" int pthread_attr_init(pthread_attr_t *attr); +extern "C" int pthread_yield(void); + void warningLessThanZero() { if (posix_fadvise(0, 0, 0, 0) < 0) {} @@ -43,11 +59,38 @@ void warningLessThanZero() { if (posix_spawnp(NULL, NULL, NULL, NULL, {NULL}, {NULL}) < 0) {} // CHECK-MESSAGES: :[[@LINE-1]]:60: warning: // CHECK-FIXES: posix_spawnp(NULL, NULL, NULL, NULL, {NULL}, {NULL}) > 0 + if (pthread_create(NULL, NULL, NULL, NULL) < 0) {} + // CHECK-MESSAGES: :[[@LINE-1]]:46: warning: the comparison always evaluates to false because pthread_create always returns non-negative values + // CHECK-FIXES: pthread_create(NULL, NULL, NULL, NULL) > 0 + if (pthread_attr_setaffinity_np(NULL, 0, NULL) < 0) {} + // CHECK-MESSAGES: :[[@LINE-1]]:50: warning: + // CHECK-FIXES: pthread_attr_setaffinity_np(NULL, 0, NULL) > 0 + if (pthread_attr_setschedpolicy(NULL, 0) < 0) {} + // CHECK-MESSAGES: :[[@LINE-1]]:44: warning: + // CHECK-FIXES: pthread_attr_setschedpolicy(NULL, 0) > 0) + if (pthread_attr_init(NULL) < 0) {} + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: + // CHECK-FIXES: pthread_attr_init(NULL) > 0 + if (pthread_yield() < 0) {} + // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: + // CHECK-FIXES: pthread_yield() > 0 + } void warningAlwaysTrue() { if (posix_fadvise(0, 0, 0, 0) >= 0) {} // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: the comparison always evaluates to true because posix_fadvise always returns non-negative values + if (pthread_create(NULL, NULL, NULL, NULL) >= 0) {} + // CHECK-MESSAGES: :[[@LINE-1]]:46: warning: the comparison always evaluates to true because pthread_create always returns non-negative values + if (pthread_attr_setaffinity_np(NULL, 0, NULL) >= 0) {} + // CHECK-MESSAGES: :[[@LINE-1]]:50: warning: + if (pthread_attr_setschedpolicy(NULL, 0) >= 0) {} + // CHECK-MESSAGES: :[[@LINE-1]]:44: warning: + if (pthread_attr_init(NULL) >= 0) {} + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: + if (pthread_yield() >= 0) {} + // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: + } void warningEqualsNegative() { @@ -69,6 +112,15 @@ void warningEqualsNegative() { // CHECK-MESSAGES: :[[@LINE-1]]:59: warning: if (posix_spawnp(NULL, NULL, NULL, NULL, {NULL}, {NULL}) == -1) {} // CHECK-MESSAGES: :[[@LINE-1]]:60: warning: + if (pthread_create(NULL, NULL, NULL, NULL) == -1) {} + // CHECK-MESSAGES: :[[@LINE-1]]:46: warning: pthread_create + if (pthread_create(NULL, NULL, NULL, NULL) != -1) {} + // CHECK-MESSAGES: :[[@LINE-1]]:46: warning: + if (pthread_create(NULL, NULL, NULL, NULL) <= -1) {} + // CHECK-MESSAGES: :[[@LINE-1]]:46: warning: + if (pthread_create(NULL, NULL, NULL, NULL) < -1) {} + // CHECK-MESSAGES: :[[@LINE-1]]:46: warning: + } void WarningWithMacro() { @@ -85,6 +137,20 @@ void WarningWithMacro() { // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: if (posix_fadvise(0, 0, 0, 0) < NEGATIVE_ONE) {} // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: + if (pthread_create(NULL, NULL, NULL, NULL) < ZERO) {} + // CHECK-MESSAGES: :[[@LINE-1]]:46: warning: + // CHECK-FIXES: pthread_create(NULL, NULL, NULL, NULL) > ZERO + if (pthread_create(NULL, NULL, NULL, NULL) >= ZERO) {} + // CHECK-MESSAGES: :[[@LINE-1]]:46: warning: + if (pthread_create(NULL, NULL, NULL, NULL) == NEGATIVE_ONE) {} + // CHECK-MESSAGES: :[[@LINE-1]]:46: warning: + if (pthread_create(NULL, NULL, NULL, NULL) != NEGATIVE_ONE) {} + // CHECK-MESSAGES: :[[@LINE-1]]:46: warning: + if (pthread_create(NULL, NULL, NULL, NULL) <= NEGATIVE_ONE) {} + // CHECK-MESSAGES: :[[@LINE-1]]:46: warning: + if (pthread_create(NULL, NULL, NULL, NULL) < NEGATIVE_ONE) {} + // CHECK-MESSAGES: :[[@LINE-1]]:46: warning: + } void noWarning() { @@ -100,6 +166,7 @@ void noWarning() { namespace i { int posix_fadvise(int fd, off_t offset, off_t len, int advice); +int pthread_yield(void); void noWarning() { if (posix_fadvise(0, 0, 0, 0) < 0) {} @@ -108,6 +175,12 @@ void noWarning() { if (posix_fadvise(0, 0, 0, 0) != -1) {} if (posix_fadvise(0, 0, 0, 0) <= -1) {} if (posix_fadvise(0, 0, 0, 0) < -1) {} + if (pthread_yield() < 0) {} + if (pthread_yield() >= 0) {} + if (pthread_yield() == -1) {} + if (pthread_yield() != -1) {} + if (pthread_yield() <= -1) {} + if (pthread_yield() < -1) {} } } // namespace i @@ -115,6 +188,7 @@ void noWarning() { class G { public: int posix_fadvise(int fd, off_t offset, off_t len, int advice); + int pthread_yield(void); void noWarning() { if (posix_fadvise(0, 0, 0, 0) < 0) {} @@ -123,5 +197,11 @@ class G { if (posix_fadvise(0, 0, 0, 0) != -1) {} if (posix_fadvise(0, 0, 0, 0) <= -1) {} if (posix_fadvise(0, 0, 0, 0) < -1) {} + if (pthread_yield() < 0) {} + if (pthread_yield() >= 0) {} + if (pthread_yield() == -1) {} + if (pthread_yield() != -1) {} + if (pthread_yield() <= -1) {} + if (pthread_yield() < -1) {} } }; diff --git a/clang-tools-extra/test/clang-tidy/check_clang_tidy.py b/clang-tools-extra/test/clang-tidy/check_clang_tidy.py index aa6304baef813..368b674d2d327 100755 --- a/clang-tools-extra/test/clang-tidy/check_clang_tidy.py +++ b/clang-tools-extra/test/clang-tidy/check_clang_tidy.py @@ -69,7 +69,8 @@ def run_test_once(args, extra_args): clang_tidy_extra_args.append('-format-style=none') if extension in ['.m', '.mm']: - clang_extra_args = ['-fobjc-abi-version=2', '-fobjc-arc'] + clang_extra_args + clang_extra_args = ['-fobjc-abi-version=2', '-fobjc-arc', '-fblocks'] + \ + clang_extra_args if extension in ['.cpp', '.hpp', '.mm']: clang_extra_args.append('-std=' + std) diff --git a/clang-tools-extra/test/clang-tidy/cppcoreguidelines-init-variables.cpp b/clang-tools-extra/test/clang-tidy/cppcoreguidelines-init-variables.cpp new file mode 100644 index 0000000000000..893c1d2877983 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/cppcoreguidelines-init-variables.cpp @@ -0,0 +1,80 @@ +// RUN: %check_clang_tidy %s cppcoreguidelines-init-variables %t + +// Ensure that function declarations are not changed. +void some_func(int x, double d, bool b, const char *p); + +// Ensure that function arguments are not changed +int identity_function(int x) { + return x; +} + +int do_not_modify_me; + +static int should_not_be_initialized; +extern int should_not_be_initialized2; + +typedef struct { + int unaltered1; + int unaltered2; +} UnusedStruct; + +typedef int my_int_type; +#define MACRO_INT int +#define FULL_DECLARATION() int macrodecl; + +template +void template_test_function() { + T t; + int uninitialized; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: variable 'uninitialized' is not initialized [cppcoreguidelines-init-variables] + // CHECK-FIXES: {{^}} int uninitialized = 0;{{$}} +} + +void init_unit_tests() { + int x; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: variable 'x' is not initialized [cppcoreguidelines-init-variables] + // CHECK-FIXES: {{^}} int x = 0;{{$}} + my_int_type myint; + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: variable 'myint' is not initialized [cppcoreguidelines-init-variables] + // CHECK-FIXES: {{^}} my_int_type myint = 0;{{$}} + + MACRO_INT macroint; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: variable 'macroint' is not initialized [cppcoreguidelines-init-variables] + // CHECK-FIXES: {{^}} MACRO_INT macroint = 0;{{$}} + FULL_DECLARATION(); + + int x0 = 1, x1, x2 = 2; + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: variable 'x1' is not initialized [cppcoreguidelines-init-variables] + // CHECK-FIXES: {{^}} int x0 = 1, x1 = 0, x2 = 2;{{$}} + int y0, y1 = 1, y2; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: variable 'y0' is not initialized [cppcoreguidelines-init-variables] + // CHECK-MESSAGES: :[[@LINE-2]]:19: warning: variable 'y2' is not initialized [cppcoreguidelines-init-variables] + // CHECK-FIXES: {{^}} int y0 = 0, y1 = 1, y2 = 0;{{$}} + int hasval = 42; + + float f; + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: variable 'f' is not initialized [cppcoreguidelines-init-variables] + // CHECK-FIXES: {{^}} float f = NAN;{{$}} + float fval = 85.0; + double d; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: variable 'd' is not initialized [cppcoreguidelines-init-variables] + // CHECK-FIXES: {{^}} double d = NAN;{{$}} + double dval = 99.0; + + bool b; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: variable 'b' is not initialized [cppcoreguidelines-init-variables] + // CHECK-FIXES: {{^}} bool b = 0;{{$}} + bool bval = true; + + const char *ptr; + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: variable 'ptr' is not initialized [cppcoreguidelines-init-variables] + // CHECK-FIXES: {{^}} const char *ptr = nullptr;{{$}} + const char *ptrval = "a string"; + + UnusedStruct u; + + static int does_not_need_an_initializer; + extern int does_not_need_an_initializer2; + int parens(42); + int braces{42}; +} diff --git a/clang-tools-extra/test/clang-tidy/objc-avoid-spinlock.m b/clang-tools-extra/test/clang-tidy/darwin-avoid-spinlock.m similarity index 78% rename from clang-tools-extra/test/clang-tidy/objc-avoid-spinlock.m rename to clang-tools-extra/test/clang-tidy/darwin-avoid-spinlock.m index f9f05cb05a286..c870e0a0fed06 100644 --- a/clang-tools-extra/test/clang-tidy/objc-avoid-spinlock.m +++ b/clang-tools-extra/test/clang-tidy/darwin-avoid-spinlock.m @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy %s objc-avoid-spinlock %t +// RUN: %check_clang_tidy %s darwin-avoid-spinlock %t typedef int OSSpinLock; @@ -6,10 +6,10 @@ @implementation Foo - (void)f { int i = 1; OSSpinlockLock(&i); - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use os_unfair_lock_lock() or dispatch queue APIs instead of the deprecated OSSpinLock [objc-avoid-spinlock] + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use os_unfair_lock_lock() or dispatch queue APIs instead of the deprecated OSSpinLock [darwin-avoid-spinlock] OSSpinlockTry(&i); - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use os_unfair_lock_lock() or dispatch queue APIs instead of the deprecated OSSpinLock [objc-avoid-spinlock] + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use os_unfair_lock_lock() or dispatch queue APIs instead of the deprecated OSSpinLock [darwin-avoid-spinlock] OSSpinlockUnlock(&i); - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use os_unfair_lock_lock() or dispatch queue APIs instead of the deprecated OSSpinLock [objc-avoid-spinlock] + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use os_unfair_lock_lock() or dispatch queue APIs instead of the deprecated OSSpinLock [darwin-avoid-spinlock] } @end diff --git a/clang-tools-extra/test/clang-tidy/darwin-dispatch-once-nonstatic.mm b/clang-tools-extra/test/clang-tidy/darwin-dispatch-once-nonstatic.mm new file mode 100644 index 0000000000000..92d6f453c66df --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/darwin-dispatch-once-nonstatic.mm @@ -0,0 +1,48 @@ +// RUN: %check_clang_tidy %s darwin-dispatch-once-nonstatic %t + +typedef int dispatch_once_t; +extern void dispatch_once(dispatch_once_t *pred, void(^block)(void)); + + +void bad_dispatch_once(dispatch_once_t once, void(^block)(void)) {} +// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: dispatch_once_t variables must have static or global storage duration; function parameters should be pointer references [darwin-dispatch-once-nonstatic] + +// file-scope dispatch_once_ts have static storage duration. +dispatch_once_t global_once; +static dispatch_once_t file_static_once; +namespace { +dispatch_once_t anonymous_once; +} // end anonymous namespace + +int Correct(void) { + static int value; + static dispatch_once_t once; + dispatch_once(&once, ^{ + value = 1; + }); + return value; +} + +int Incorrect(void) { + static int value; + dispatch_once_t once; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: dispatch_once_t variables must have static or global storage duration [darwin-dispatch-once-nonstatic] + // CHECK-FIXES: static dispatch_once_t once; + dispatch_once(&once, ^{ + value = 1; + }); + return value; +} + +struct OnceStruct { + static dispatch_once_t staticOnce; // Allowed + int value; + dispatch_once_t once; // Allowed (at this time) +}; + +@interface MyObject { + dispatch_once_t _once; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: dispatch_once_t variables must have static or global storage duration and cannot be Objective-C instance variables [darwin-dispatch-once-nonstatic] + // CHECK-FIXES: dispatch_once_t _once; +} +@end diff --git a/clang-tools-extra/test/clang-tidy/file-filter-symlinks.cpp b/clang-tools-extra/test/clang-tidy/file-filter-symlinks.cpp new file mode 100644 index 0000000000000..7efa7d070f69f --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/file-filter-symlinks.cpp @@ -0,0 +1,19 @@ +// REQUIRES: shell + +// RUN: rm -rf %t +// RUN: mkdir -p %t/dir1/dir2 +// RUN: echo 'class A { A(int); };' > %t/dir1/header.h +// RUN: ln -s %t/dir1/header.h %t/dir1/header_alias.h +// +// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='dir1/dir2/\.\./header_alias\.h' %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER_ALIAS %s +// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='dir1/dir2/\.\./header_alias\.h' -quiet %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER_ALIAS %s +// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header_alias\.h' %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER_ALIAS %s +// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header_alias\.h' -quiet %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER_ALIAS %s +// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header\.h' %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER %s +// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header\.h' -quiet %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER %s + +// Check that `-header-filter` operates on the same file paths as paths in +// diagnostics printed by ClangTidy. +#include "dir1/dir2/../header_alias.h" +// CHECK_HEADER_ALIAS: dir1/dir2/../header_alias.h:1:11: warning: single-argument constructors +// CHECK_HEADER-NOT: warning: diff --git a/clang-tools-extra/test/clang-tidy/google-objc-global-variable-declaration.mm b/clang-tools-extra/test/clang-tidy/google-objc-global-variable-declaration.mm index a6b0f6ee47688..345edec1f979e 100644 --- a/clang-tools-extra/test/clang-tidy/google-objc-global-variable-declaration.mm +++ b/clang-tools-extra/test/clang-tidy/google-objc-global-variable-declaration.mm @@ -6,5 +6,5 @@ // CHECK-FIXES: static NSString* const kMyConstString = @"hello"; class MyTest { - static int not_objc_style; + static int not_objc_style; }; diff --git a/clang-tools-extra/test/clang-tidy/objc-missing-hash.m b/clang-tools-extra/test/clang-tidy/objc-missing-hash.m new file mode 100644 index 0000000000000..b9cc9d023ad13 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/objc-missing-hash.m @@ -0,0 +1,68 @@ +// RUN: %check_clang_tidy %s objc-missing-hash %t + +typedef _Bool BOOL; +#define YES 1 +#define NO 0 +typedef unsigned int NSUInteger; +typedef void *id; + +@interface NSObject +- (NSUInteger)hash; +- (BOOL)isEqual:(id)object; +@end + +@interface MissingHash : NSObject +@end + +@implementation MissingHash +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 'MissingHash' implements -isEqual: without implementing -hash [objc-missing-hash] + +- (BOOL)isEqual:(id)object { + return YES; +} + +@end + +@interface HasHash : NSObject +@end + +@implementation HasHash + +- (NSUInteger)hash { + return 0; +} + +- (BOOL)isEqual:(id)object { + return YES; +} + +@end + +@interface NSArray : NSObject +@end + +@interface MayHaveInheritedHash : NSArray +@end + +@implementation MayHaveInheritedHash + +- (BOOL)isEqual:(id)object { + return YES; +} + +@end + +@interface AnotherRootClass +@end + +@interface NotDerivedFromNSObject : AnotherRootClass +@end + +@implementation NotDerivedFromNSObject + +- (BOOL)isEqual:(id)object { + return NO; +} + +@end + diff --git a/clang-tools-extra/test/clang-tidy/performance-inefficient-vector-operation.cpp b/clang-tools-extra/test/clang-tidy/performance-inefficient-vector-operation.cpp index d5e38eb71f835..ffac24c2b9a82 100644 --- a/clang-tools-extra/test/clang-tidy/performance-inefficient-vector-operation.cpp +++ b/clang-tools-extra/test/clang-tidy/performance-inefficient-vector-operation.cpp @@ -1,4 +1,7 @@ -// RUN: %check_clang_tidy %s performance-inefficient-vector-operation %t -- -format-style=llvm +// RUN: %check_clang_tidy %s performance-inefficient-vector-operation %t -- \ +// RUN: -format-style=llvm \ +// RUN: -config='{CheckOptions: \ +// RUN: [{key: performance-inefficient-vector-operation.EnableProto, value: 1}]}' namespace std { @@ -62,13 +65,35 @@ class Bar { int Op(int); +namespace proto2 { +class MessageLite {}; +class Message : public MessageLite {}; +} // namespace proto2 + +class FooProto : public proto2::Message { + public: + int *add_x(); // repeated int x; + void add_x(int x); + void mutable_x(); + void mutable_y(); + int add_z() const; // optional int add_z; +}; + +class BarProto : public proto2::Message { + public: + int *add_x(); + void add_x(int x); + void mutable_x(); + void mutable_y(); +}; + void f(std::vector& t) { { std::vector v0; // CHECK-FIXES: v0.reserve(10); for (int i = 0; i < 10; ++i) v0.push_back(i); - // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called inside a loop; consider pre-allocating the vector capacity before the loop + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called inside a loop; consider pre-allocating the container capacity before the loop } { std::vector v1; @@ -162,6 +187,15 @@ void f(std::vector& t) { } } + { + FooProto foo; + // CHECK-FIXES: foo.mutable_x()->Reserve(5); + for (int i = 0; i < 5; i++) { + foo.add_x(i); + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'add_x' is called inside a loop; consider pre-allocating the container capacity before the loop + } + } + // ---- Non-fixed Cases ---- { std::vector z0; @@ -274,4 +308,54 @@ void f(std::vector& t) { z12.push_back(e); } } + + { + FooProto foo; + foo.mutable_x(); + // CHECK-FIXES-NOT: foo.mutable_x()->Reserve(5); + for (int i = 0; i < 5; i++) { + foo.add_x(i); + } + } + { + FooProto foo; + // CHECK-FIXES-NOT: foo.mutable_x()->Reserve(5); + for (int i = 0; i < 5; i++) { + foo.add_x(i); + foo.add_x(i); + } + } + { + FooProto foo; + // CHECK-FIXES-NOT: foo.mutable_x()->Reserve(5); + foo.add_x(-1); + for (int i = 0; i < 5; i++) { + foo.add_x(i); + } + } + { + FooProto foo; + BarProto bar; + bar.mutable_x(); + // CHECK-FIXES-NOT: foo.mutable_x()->Reserve(5); + for (int i = 0; i < 5; i++) { + foo.add_x(); + bar.add_x(); + } + } + { + FooProto foo; + foo.mutable_y(); + // CHECK-FIXES-NOT: foo.mutable_x()->Reserve(5); + for (int i = 0; i < 5; i++) { + foo.add_x(i); + } + } + { + FooProto foo; + // CHECK-FIXES-NOT: foo.mutable_z()->Reserve(5); + for (int i = 0; i < 5; i++) { + foo.add_z(); + } + } } diff --git a/clang-tools-extra/test/clang-tidy/readability-isolate-declaration-no-infinite-loop.cpp b/clang-tools-extra/test/clang-tidy/readability-isolate-declaration-no-infinite-loop.cpp new file mode 100644 index 0000000000000..78a8ec738800e --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/readability-isolate-declaration-no-infinite-loop.cpp @@ -0,0 +1,7 @@ +// RUN: %check_clang_tidy -expect-clang-tidy-error %s readability-isolate-declaration %t + +int main(){ + int a, b + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: multiple declarations in a single statement reduces readability + // CHECK-MESSAGES: [[@LINE-2]]:11: error: expected ';' at end of declaration [clang-diagnostic-error] +} diff --git a/clang-tools-extra/unittests/clang-tidy/LLVMModuleTest.cpp b/clang-tools-extra/unittests/clang-tidy/LLVMModuleTest.cpp index aed9f6d831ea3..b86f8e63fb899 100644 --- a/clang-tools-extra/unittests/clang-tidy/LLVMModuleTest.cpp +++ b/clang-tools-extra/unittests/clang-tidy/LLVMModuleTest.cpp @@ -211,6 +211,15 @@ TEST(LLVMHeaderGuardCheckTest, FixHeaderGuards) { "#endif /* LLVM_ADT_FOO_H\\ \n" " FOO */", "include/llvm/ADT/foo.h", None)); + + EXPECT_EQ("#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FOO_H\n" + "#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FOO_H\n" + "\n" + "\n" + "#endif\n", + runHeaderGuardCheck( + "", "/llvm-project/clang-tools-extra/clangd/foo.h", + StringRef("header is missing header guard"))); } #endif diff --git a/clang/docs/ClangCommandLineReference.rst b/clang/docs/ClangCommandLineReference.rst index c26a6e5ecfc8a..5f6bb9829f5af 100644 --- a/clang/docs/ClangCommandLineReference.rst +++ b/clang/docs/ClangCommandLineReference.rst @@ -86,8 +86,6 @@ Pass to the target offloading toolchain identified by . Run the static analyzer -.. option:: --analyze-auto - .. option:: --analyzer-no-default-checks .. option:: --analyzer-output @@ -1944,7 +1942,8 @@ Perform ThinLTO importing using provided function summary index .. option:: -ftime-trace -Turn on time profiler +Turn on time profiler. Results can be analyzed with chrome://tracing or +`Speedscope App `_ for flamegraph visualization .. option:: -ftime-trace-granularity= @@ -2194,7 +2193,7 @@ Set EABI type, e.g. 4, 5 or gnu (default depends on triple) .. option:: -mfentry -Insert calls to fentry at function entry (x86 only) +Insert calls to fentry at function entry (x86/SystemZ only) .. option:: -mfloat-abi= diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 7193e3ede290d..0803e65df2e22 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -1128,6 +1128,34 @@ the configuration (without a prefix: ``Auto``). B }; + * ``BS_Whitesmiths`` (in configuration: ``Whitesmiths``) + Like ``Allman`` but always indent braces and line up code with braces. + + .. code-block:: c++ + + try + { + foo(); + } + catch () + { + } + void foo() { bar(); } + class foo + { + }; + if (foo()) + { + } + else + { + } + enum X : int + { + A, + B + }; + * ``BS_GNU`` (in configuration: ``GNU``) Always break before braces and add an extra level of indentation to braces of control statements, not to those of class, function @@ -1483,6 +1511,13 @@ the configuration (without a prefix: ``Auto``). can also assign negative priorities if you have certain headers that always need to be first. + There is a third and optional field ``SortPriority`` which can used while + ``IncludeBloks = IBS_Regroup`` to define the priority in which ``#includes`` + should be ordered, and value of ``Priority`` defines the order of + ``#include blocks`` and also enables to group ``#includes`` of different + priority for order.``SortPriority`` is set to the value of ``Priority`` + as default if it is not assigned. + To configure this in the .clang-format file, use: .. code-block:: yaml @@ -1490,12 +1525,14 @@ the configuration (without a prefix: ``Auto``). IncludeCategories: - Regex: '^"(llvm|llvm-c|clang|clang-c)/' Priority: 2 + SortPriority: 2 - Regex: '^(<|"(gtest|gmock|isl|json)/)' Priority: 3 - Regex: '<[[:alnum:].]+>' Priority: 4 - Regex: '.*' Priority: 1 + SortPriority: 0 **IncludeIsMainRegex** (``std::string``) Specify a regular expression of suffixes that are allowed in the @@ -2251,22 +2288,38 @@ the configuration (without a prefix: ``Auto``). std::unique_ptr foo() {} // Won't be affected **Standard** (``LanguageStandard``) - Format compatible with this standard, e.g. use ``A >`` - instead of ``A>`` for ``LS_Cpp03``. + .. code-block:: c++ + + c++03: latest: + vector > x; vs. vector> x; + Parse and format C++ constructs compatible with this standard. Possible values: - * ``LS_Cpp03`` (in configuration: ``Cpp03``) + * ``LS_Cpp03`` (in configuration: ``c++03``) Use C++03-compatible syntax. - * ``LS_Cpp11`` (in configuration: ``Cpp11``) - Use features of C++11, C++14 and C++1z (e.g. ``A>`` instead of - ``A >``). + * ``LS_Cpp11`` (in configuration: ``c++11``) + Use C++11-compatible syntax. + + * ``LS_Cpp14`` (in configuration: ``c++14``) + Use C++14-compatible syntax. + + * ``LS_Cpp17`` (in configuration: ``c++17``) + Use C++17-compatible syntax. + + * ``LS_Cpp20`` (in configuration: ``c++20``) + Use C++20-compatible syntax. + + * ``LS_Latest`` (in configuration: ``Latest``) + Parse and format using the latest supported language version. * ``LS_Auto`` (in configuration: ``Auto``) Automatic detection based on the input. + * ``Cpp03``: deprecated alias for ``c++03`` + * ``Cpp11``: deprecated alias for ``Latest`` **StatementMacros** (``std::vector``) A vector of macros that should be interpreted as complete diff --git a/clang/docs/InternalsManual.rst b/clang/docs/InternalsManual.rst index b56abc1e1bc5d..59f3cba699dbb 100644 --- a/clang/docs/InternalsManual.rst +++ b/clang/docs/InternalsManual.rst @@ -859,7 +859,7 @@ benefits: There are unfortunately exceptions to this general approach, such as: - * A the first declaration of a redeclarable entity maintains a pointer to the + * The first declaration of a redeclarable entity maintains a pointer to the most recent declaration of that entity, which naturally needs to change as more declarations are parsed. * Name lookup tables in declaration contexts change after the namespace @@ -1913,7 +1913,7 @@ declarations like enums, classes, etc. if they are in anonymous namespaces. Therefore, we filter the lookup results and consider only those which have the same visibility as the declaration we currently import. -We consider two declarations in two anonymous namsepaces to have the same +We consider two declarations in two anonymous namespaces to have the same visibility only if they are imported from the same AST context. Strategies to Handle Conflicting Names @@ -2134,7 +2134,7 @@ about them. Finally, this is not just a problem for semantic analysis. The code generator and other clients have to be able to fold constants (e.g., to initialize global -variables) and has to handle a superset of what C99 allows. Further, these +variables) and have to handle a superset of what C99 allows. Further, these clients can benefit from extended information. For example, we know that "``foo() || 1``" always evaluates to ``true``, but we can't replace the expression with ``true`` because it has side effects. diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 3080f6dd07d6b..da3c85e78e57c 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1161,10 +1161,7 @@ The following type trait primitives are supported by Clang. Those traits marked * ``__is_sealed`` (Microsoft): Synonym for ``__is_final``. * ``__is_signed`` (C++, Embarcadero): - Note that this currently returns true for enumeration types if the underlying - type is signed, and returns false for floating-point types, in violation of - the requirements for ``std::is_signed``. This behavior is likely to change in - a future version of Clang. + Returns false for enumeration types, and returns true for floating-point types. Note, before Clang 10, returned true for enumeration types if the underlying type was signed, and returned false for floating-point types. * ``__is_standard_layout`` (C++, GNU, Microsoft, Embarcadero) * ``__is_trivial`` (C++, GNU, Microsoft, Embarcadero) * ``__is_trivially_assignable`` (C++, GNU, Microsoft) @@ -2377,12 +2374,14 @@ macro expansions, the entire macro expansion stack is expressed. array subscript access and structure/union member access are relocatable under bpf compile-once run-everywhere framework. Debuginfo (typically with ``-g``) is needed, otherwise, the compiler will exit with an error. +The return type for the intrinsic is the same as the type of the +argument. **Syntax**: .. code-block:: c - const void * __builtin_preserve_access_index(const void * ptr) + type __builtin_preserve_access_index(type arg) **Example of Use**: @@ -2397,7 +2396,8 @@ with ``-g``) is needed, otherwise, the compiler will exit with an error. } c[4]; }; struct t *v = ...; - const void *pb =__builtin_preserve_access_index(&v->c[3].b); + int *pb =__builtin_preserve_access_index(&v->c[3].b); + __builtin_preserve_access_index(v->j); Multiprecision Arithmetic Builtins ---------------------------------- diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 9716360a78d13..95dd51e634755 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -51,7 +51,11 @@ Major New Features Improvements to Clang's diagnostics ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- ... +- -Wtautological-overlap-compare will warn on negative numbers and non-int + types. +- -Wtautological-compare for self comparisons and + -Wtautological-overlap-compare will now look through member and array + access to determine if two operand expressions are the same. Non-comprehensive list of changes in this release ------------------------------------------------- @@ -98,7 +102,18 @@ Attribute Changes in Clang Windows Support --------------- -- ... +- Previous Clang versions contained a work-around to avoid an issue with the + standard library headers in Visual Studio 2019 versions prior to 16.3. This + work-around has now been removed, and users of Visual Studio 2019 are + encouraged to upgrade to 16.3 or later, otherwise they may see link errors as + below: + + .. code-block:: console + + error LNK2005: "bool const std::_Is_integral" (??$_Is_integral@H@std@@3_NB) already defined + + + C Language Changes in Clang --------------------------- @@ -113,7 +128,10 @@ C11 Feature Support C++ Language Changes in Clang ----------------------------- -- ... +- The behaviour of the `gnu_inline` attribute now matches GCC, for cases + where used without the `extern` keyword. As this is a change compared to + how it behaved in previous Clang versions, a warning is emitted for this + combination. C++1z Feature Support ^^^^^^^^^^^^^^^^^^^^^ @@ -192,7 +210,15 @@ AST Matchers clang-format ------------ -- ... +- The ``Standard`` style option specifies which version of C++ should be used + when parsing and formatting C++ code. The set of allowed values has changed: + - ``Latest`` will always enable new C++ language features. + - ``c++03``, ``c++11``, ``c++14``, ``c++17``, ``c++20`` will pin to exactly + that language version. + - ``Auto`` is the default and detects style from the code (this is unchanged). + The previous values of ``Cpp03`` and ``Cpp11`` are deprecated. Note that + ``Cpp11`` is treated as ``Latest``, as this was always clang-format's behavior. + (One motivation for this change is the new name describes the behavior better). libclang -------- diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index f45d2d5ac0e44..419714d38cdc4 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -1128,6 +1128,185 @@ number of cases where the compilation environment is tightly controlled and the precompiled header cannot be generated after headers have been installed. +.. _controlling-fp-behavior: + +Controlling Floating Point Behavior +----------------------------------- + +Clang provides a number of ways to control floating point behavior. The options +are listed below. + +.. option:: -ffast-math + + Enable fast-math mode. This option lets the + compiler make aggressive, potentially-lossy assumptions about + floating-point math. These include: + + * Floating-point math obeys regular algebraic rules for real numbers (e.g. + ``+`` and ``*`` are associative, ``x/y == x * (1/y)``, and + ``(a + b) * c == a * c + b * c``), + * Operands to floating-point operations are not equal to ``NaN`` and + ``Inf``, and + * ``+0`` and ``-0`` are interchangeable. + + ``-ffast-math`` also defines the ``__FAST_MATH__`` preprocessor + macro. Some math libraries recognize this macro and change their behavior. + With the exception of ``-ffp-contract=fast``, using any of the options + below to disable any of the individual optimizations in ``-ffast-math`` + will cause ``__FAST_MATH__`` to no longer be set. + + This option implies: + + * ``-fno-honor-infinities`` + + * ``-fno-honor-nans`` + + * ``-fno-math-errno`` + + * ``-ffinite-math`` + + * ``-fassociative-math`` + + * ``-freciprocal-math`` + + * ``-fno-signed-zeros`` + + * ``-fno-trapping-math`` + + * ``-ffp-contract=fast`` + +.. option:: -fdenormal-fp-math= + + Select which denormal numbers the code is permitted to require. + + Valid values are: + + * ``ieee`` - IEEE 754 denormal numbers + * ``preserve-sign`` - the sign of a flushed-to-zero number is preserved in the sign of 0 + * ``positive-zero`` - denormals are flushed to positive zero + + Defaults to ``ieee``. + +.. _opt_fstrict-float-cast-overflow: + +**-f[no-]strict-float-cast-overflow** + + When a floating-point value is not representable in a destination integer + type, the code has undefined behavior according to the language standard. + By default, Clang will not guarantee any particular result in that case. + With the 'no-strict' option, Clang attempts to match the overflowing behavior + of the target's native float-to-int conversion instructions. + +.. _opt_fmath-errno: + +**-f[no-]math-errno** + + Require math functions to indicate errors by setting errno. + The default varies by ToolChain. ``-fno-math-errno`` allows optimizations + that might cause standard C math functions to not set ``errno``. + For example, on some systems, the math function ``sqrt`` is specified + as setting ``errno`` to ``EDOM`` when the input is negative. On these + systems, the compiler cannot normally optimize a call to ``sqrt`` to use + inline code (e.g. the x86 ``sqrtsd`` instruction) without additional + checking to ensure that ``errno`` is set appropriately. + ``-fno-math-errno`` permits these transformations. + + On some targets, math library functions never set ``errno``, and so + ``-fno-math-errno`` is the default. This includes most BSD-derived + systems, including Darwin. + +.. _opt_ftrapping-math: + +**-f[no-]trapping-math** + + ``-fno-trapping-math`` allows optimizations that assume that + floating point operations cannot generate traps such as divide-by-zero, + overflow and underflow. Defaults to ``-ftrapping-math``. + Currently this option has no effect. + +.. option:: -ffp-contract= + + Specify when the compiler is permitted to form fused floating-point + operations, such as fused multiply-add (FMA). Fused operations are + permitted to produce more precise results than performing the same + operations separately. + + The C standard permits intermediate floating-point results within an + expression to be computed with more precision than their type would + normally allow. This permits operation fusing, and Clang takes advantage + of this by default. This behavior can be controlled with the + ``FP_CONTRACT`` pragma. Please refer to the pragma documentation for a + description of how the pragma interacts with this option. + + Valid values are: + + * ``fast`` (everywhere) + * ``on`` (according to FP_CONTRACT pragma, default) + * ``off`` (never fuse) + +.. _opt_fhonor-infinities: + +**-f[no-]honor-infinities** + + If both ``-fno-honor-infinities`` and ``-fno-honor-nans`` are used, + has the same effect as specifying ``-ffinite-math``. + +.. _opt_fhonor-nans: + +**-f[no-]honor-nans** + + If both ``-fno-honor-infinities`` and ``-fno-honor-nans`` are used, + has the same effect as specifying ``-ffinite-math``. + +.. _opt_fsigned-zeros: + +**-f[no-]signed-zeros** + + Allow optimizations that ignore the sign of floating point zeros. + Defaults to ``-fno-signed-zeros``. + +.. _opt_fassociative-math: + +**-f[no-]associative-math** + + Allow floating point operations to be reassociated. + Defaults to ``-fno-associative-math``. + +.. _opt_freciprocal-math: + +**-f[no-]reciprocal-math** + + Allow division operations to be transformed into multiplication by a + reciprocal. This can be significantly faster than an ordinary division + but can also have significantly less precision. Defaults to + ``-fno-reciprocal-math``. + +.. _opt_funsafe-math-optimizations: + +**-f[no-]unsafe-math-optimizations** + + Allow unsafe floating-point optimizations. Also implies: + + * ``-fassociative-math`` + * ``-freciprocal-math`` + * ``-fno-signed-zeroes`` + * ``-fno-trapping-math``. + + Defaults to ``-fno-unsafe-math-optimizations``. + +.. _opt_ffinite-math: + +**-f[no-]finite-math** + + Allow floating-point optimizations that assume arguments and results are + not NaNs or +-Inf. This defines the ``__FINITE_MATH_ONLY__`` preprocessor macro. + Also implies: + + * ``-fno-honor-infinities`` + * ``-fno-honor-nans`` + + Defaults to ``-fno-finite-math``. + .. _controlling-code-generation: Controlling Code Generation @@ -1266,36 +1445,6 @@ are listed below. This enables better devirtualization. Turned off by default, because it is still experimental. -.. option:: -ffast-math - - Enable fast-math mode. This defines the ``__FAST_MATH__`` preprocessor - macro, and lets the compiler make aggressive, potentially-lossy assumptions - about floating-point math. These include: - - * Floating-point math obeys regular algebraic rules for real numbers (e.g. - ``+`` and ``*`` are associative, ``x/y == x * (1/y)``, and - ``(a + b) * c == a * c + b * c``), - * operands to floating-point operations are not equal to ``NaN`` and - ``Inf``, and - * ``+0`` and ``-0`` are interchangeable. - -.. option:: -fdenormal-fp-math=[values] - - Select which denormal numbers the code is permitted to require. - - Valid values are: ``ieee``, ``preserve-sign``, and ``positive-zero``, - which correspond to IEEE 754 denormal numbers, the sign of a - flushed-to-zero number is preserved in the sign of 0, denormals are - flushed to positive zero, respectively. - -.. option:: -f[no-]strict-float-cast-overflow - - When a floating-point value is not representable in a destination integer - type, the code has undefined behavior according to the language standard. - By default, Clang will not guarantee any particular result in that case. - With the 'no-strict' option, Clang attempts to match the overflowing behavior - of the target's native float-to-int conversion instructions. - .. option:: -fwhole-program-vtables Enable whole-program vtable optimizations, such as single-implementation diff --git a/clang/include/clang-c/FatalErrorHandler.h b/clang/include/clang-c/FatalErrorHandler.h index 74c9a8fe98bbb..ce8ff2cae735f 100644 --- a/clang/include/clang-c/FatalErrorHandler.h +++ b/clang/include/clang-c/FatalErrorHandler.h @@ -18,14 +18,14 @@ extern "C" { * Installs error handler that prints error message to stderr and calls abort(). * Replaces currently installed error handler (if any). */ -void clang_install_aborting_llvm_fatal_error_handler(); +void clang_install_aborting_llvm_fatal_error_handler(void); /** * Removes currently installed error handler (if any). * If no error handler is intalled, the default strategy is to print error * message to stderr and call exit(1). */ -void clang_uninstall_llvm_fatal_error_handler(); +void clang_uninstall_llvm_fatal_error_handler(void); #ifdef __cplusplus } diff --git a/clang/include/clang/AST/APValue.h b/clang/include/clang/AST/APValue.h index 6943479831ecf..63359294ef632 100644 --- a/clang/include/clang/AST/APValue.h +++ b/clang/include/clang/AST/APValue.h @@ -53,6 +53,34 @@ class TypeInfoLValue { void print(llvm::raw_ostream &Out, const PrintingPolicy &Policy) const; }; + +/// Symbolic representation of a dynamic allocation. +class DynamicAllocLValue { + unsigned Index; + +public: + DynamicAllocLValue() : Index(0) {} + explicit DynamicAllocLValue(unsigned Index) : Index(Index + 1) {} + unsigned getIndex() { return Index - 1; } + + explicit operator bool() const { return Index != 0; } + + void *getOpaqueValue() { + return reinterpret_cast(static_cast(Index) + << NumLowBitsAvailable); + } + static DynamicAllocLValue getFromOpaqueValue(void *Value) { + DynamicAllocLValue V; + V.Index = reinterpret_cast(Value) >> NumLowBitsAvailable; + return V; + } + + static unsigned getMaxIndex() { + return (std::numeric_limits::max() >> NumLowBitsAvailable) - 1; + } + + static constexpr int NumLowBitsAvailable = 3; +}; } namespace llvm { @@ -67,6 +95,17 @@ template<> struct PointerLikeTypeTraits { // to include Type.h. static constexpr int NumLowBitsAvailable = 3; }; + +template<> struct PointerLikeTypeTraits { + static void *getAsVoidPointer(clang::DynamicAllocLValue V) { + return V.getOpaqueValue(); + } + static clang::DynamicAllocLValue getFromVoidPointer(void *P) { + return clang::DynamicAllocLValue::getFromOpaqueValue(P); + } + static constexpr int NumLowBitsAvailable = + clang::DynamicAllocLValue::NumLowBitsAvailable; +}; } namespace clang { @@ -97,13 +136,15 @@ class APValue { }; class LValueBase { - typedef llvm::PointerUnion + typedef llvm::PointerUnion PtrTy; public: LValueBase() : Local{} {} LValueBase(const ValueDecl *P, unsigned I = 0, unsigned V = 0); LValueBase(const Expr *P, unsigned I = 0, unsigned V = 0); + static LValueBase getDynamicAlloc(DynamicAllocLValue LV, QualType Type); static LValueBase getTypeInfo(TypeInfoLValue LV, QualType TypeInfo); template @@ -124,6 +165,7 @@ class APValue { unsigned getCallIndex() const; unsigned getVersion() const; QualType getTypeInfoType() const; + QualType getDynamicAllocType() const; friend bool operator==(const LValueBase &LHS, const LValueBase &RHS); friend bool operator!=(const LValueBase &LHS, const LValueBase &RHS) { @@ -140,6 +182,8 @@ class APValue { LocalState Local; /// The type std::type_info, if this is a TypeInfoLValue. void *TypeInfoType; + /// The QualType, if this is a DynamicAllocLValue. + void *DynamicAllocType; }; }; diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 689dfc76efd18..66bd4aa1e3ba0 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -2583,10 +2583,12 @@ class ASTContext : public RefCountedBase { return T == getObjCSelType(); } - bool ObjCQualifiedIdTypesAreCompatible(QualType LHS, QualType RHS, + bool ObjCQualifiedIdTypesAreCompatible(const ObjCObjectPointerType *LHS, + const ObjCObjectPointerType *RHS, bool ForCompare); - bool ObjCQualifiedClassTypesAreCompatible(QualType LHS, QualType RHS); + bool ObjCQualifiedClassTypesAreCompatible(const ObjCObjectPointerType *LHS, + const ObjCObjectPointerType *RHS); // Check the safety of assignment from LHS to RHS bool canAssignObjCInterfaces(const ObjCObjectPointerType *LHSOPT, diff --git a/clang/include/clang/AST/ASTFwd.h b/clang/include/clang/AST/ASTFwd.h index 93919bbdd52f5..25c3214854437 100644 --- a/clang/include/clang/AST/ASTFwd.h +++ b/clang/include/clang/AST/ASTFwd.h @@ -24,7 +24,7 @@ class Stmt; #include "clang/AST/StmtNodes.inc" class Type; #define TYPE(DERIVED, BASE) class DERIVED##Type; -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" class CXXCtorInitializer; } // end namespace clang diff --git a/clang/include/clang/AST/ASTTypeTraits.h b/clang/include/clang/AST/ASTTypeTraits.h index a29a04e5d242d..dd4ead2f0c2bd 100644 --- a/clang/include/clang/AST/ASTTypeTraits.h +++ b/clang/include/clang/AST/ASTTypeTraits.h @@ -148,7 +148,7 @@ class ASTNodeKind { #include "clang/AST/StmtNodes.inc" NKI_Type, #define TYPE(DERIVED, BASE) NKI_##DERIVED##Type, -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" NKI_OMPClause, #define OPENMP_CLAUSE(TextualSpelling, Class) NKI_##Class, #include "clang/Basic/OpenMPKinds.def" @@ -205,7 +205,7 @@ KIND_TO_KIND_ID(OMPClause) #define STMT(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED) #include "clang/AST/StmtNodes.inc" #define TYPE(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED##Type) -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" #define OPENMP_CLAUSE(TextualSpelling, Class) KIND_TO_KIND_ID(Class) #include "clang/Basic/OpenMPKinds.def" #undef KIND_TO_KIND_ID diff --git a/clang/include/clang/AST/CMakeLists.txt b/clang/include/clang/AST/CMakeLists.txt index da16987141c20..fc9839878131a 100644 --- a/clang/include/clang/AST/CMakeLists.txt +++ b/clang/include/clang/AST/CMakeLists.txt @@ -31,6 +31,10 @@ clang_tablegen(DeclNodes.inc -gen-clang-decl-nodes SOURCE ../Basic/DeclNodes.td TARGET ClangDeclNodes) +clang_tablegen(TypeNodes.inc -gen-clang-type-nodes + SOURCE ../Basic/TypeNodes.td + TARGET ClangTypeNodes) + clang_tablegen(CommentNodes.inc -gen-clang-comment-nodes SOURCE ../Basic/CommentNodes.td TARGET ClangCommentNodes) diff --git a/clang/include/clang/AST/CommentLexer.h b/clang/include/clang/AST/CommentLexer.h index 9ddbb7d31d99e..138fdaca0ff61 100644 --- a/clang/include/clang/AST/CommentLexer.h +++ b/clang/include/clang/AST/CommentLexer.h @@ -352,8 +352,7 @@ class Lexer { void lex(Token &T); - StringRef getSpelling(const Token &Tok, const SourceManager &SourceMgr, - bool *Invalid = nullptr) const; + StringRef getSpelling(const Token &Tok, const SourceManager &SourceMgr) const; }; } // end namespace comments diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 096df484cbe1f..cbac50daf14c7 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -310,6 +310,14 @@ class NamedDecl : public Decl { void printQualifiedName(raw_ostream &OS) const; void printQualifiedName(raw_ostream &OS, const PrintingPolicy &Policy) const; + /// Print only the nested name specifier part of a fully-qualified name, + /// including the '::' at the end. E.g. + /// when `printQualifiedName(D)` prints "A::B::i", + /// this function prints "A::B::". + void printNestedNameSpecifier(raw_ostream &OS) const; + void printNestedNameSpecifier(raw_ostream &OS, + const PrintingPolicy &Policy) const; + // FIXME: Remove string version. std::string getQualifiedNameAsString() const; @@ -800,12 +808,19 @@ struct EvaluatedStmt { /// valid if CheckedICE is true. bool IsICE : 1; + /// Whether this variable is known to have constant destruction. That is, + /// whether running the destructor on the initial value is a side-effect + /// (and doesn't inspect any state that might have changed during program + /// execution). This is currently only computed if the destructor is + /// non-trivial. + bool HasConstantDestruction : 1; + Stmt *Value; APValue Evaluated; - EvaluatedStmt() : WasEvaluated(false), IsEvaluating(false), CheckedICE(false), - CheckingICE(false), IsICE(false) {} - + EvaluatedStmt() + : WasEvaluated(false), IsEvaluating(false), CheckedICE(false), + CheckingICE(false), IsICE(false), HasConstantDestruction(false) {} }; /// Represents a variable declaration or definition. @@ -1259,6 +1274,14 @@ class VarDecl : public DeclaratorDecl, public Redeclarable { /// to untyped APValue if the value could not be evaluated. APValue *getEvaluatedValue() const; + /// Evaluate the destruction of this variable to determine if it constitutes + /// constant destruction. + /// + /// \pre isInitICE() + /// \return \c true if this variable has constant destruction, \c false if + /// not. + bool evaluateDestruction(SmallVectorImpl &Notes) const; + /// Determines whether it is already known whether the /// initializer is an integral constant expression or not. bool isInitKnownICE() const; @@ -1497,9 +1520,14 @@ class VarDecl : public DeclaratorDecl, public Redeclarable { // has no definition within this source file. bool isKnownToBeDefined() const; - /// Do we need to emit an exit-time destructor for this variable? + /// Is destruction of this variable entirely suppressed? If so, the variable + /// need not have a usable destructor at all. bool isNoDestroy(const ASTContext &) const; + /// Do we need to emit an exit-time destructor for this variable, and if so, + /// what kind? + QualType::DestructionKind needsDestruction(const ASTContext &Ctx) const; + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K >= firstVar && K <= lastVar; } diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index d64d0cb425db0..01c2f1809771b 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -959,7 +959,7 @@ class alignas(8) Decl { /// as this declaration, or NULL if there is no previous declaration. Decl *getPreviousDecl() { return getPreviousDeclImpl(); } - /// Retrieve the most recent declaration that declares the same entity + /// Retrieve the previous declaration that declares the same entity /// as this declaration, or NULL if there is no previous declaration. const Decl *getPreviousDecl() const { return const_cast(this)->getPreviousDeclImpl(); diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index f9668f1dce1f8..de0aa5f9b900f 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -42,6 +42,7 @@ #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/iterator_range.h" +#include "llvm/BinaryFormat/Dwarf.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/PointerLikeTypeTraits.h" @@ -445,6 +446,9 @@ class CXXRecordDecl : public RecordDecl { /// or an implicitly declared constexpr default constructor. unsigned HasConstexprDefaultConstructor : 1; + /// True if a defaulted destructor for this class would be constexpr. + unsigned DefaultedDestructorIsConstexpr : 1; + /// True when this class contains at least one non-static data /// member or base class of non-literal or volatile type. unsigned HasNonLiteralTypeFieldsOrBases : 1; @@ -1168,6 +1172,10 @@ class CXXRecordDecl : public RecordDecl { /// if this is a closure type. CXXMethodDecl *getLambdaCallOperator() const; + /// Retrieve the dependent lambda call operator of the closure type + /// if this is a templated closure type. + FunctionTemplateDecl *getDependentLambdaCallOperator() const; + /// Retrieve the lambda static invoker, the address of which /// is returned by the conversion operator, and the body of which /// is forwarded to the lambda call operator. @@ -1352,7 +1360,8 @@ class CXXRecordDecl : public RecordDecl { /// would be constexpr. bool defaultedDefaultConstructorIsConstexpr() const { return data().DefaultedDefaultConstructorIsConstexpr && - (!isUnion() || hasInClassInitializer() || !hasVariantMembers()); + (!isUnion() || hasInClassInitializer() || !hasVariantMembers() || + getASTContext().getLangOpts().CPlusPlus2a); } /// Determine whether this class has a constexpr default constructor. @@ -1440,6 +1449,16 @@ class CXXRecordDecl : public RecordDecl { !(data().HasTrivialSpecialMembers & SMF_MoveAssignment)); } + /// Determine whether a defaulted default constructor for this class + /// would be constexpr. + bool defaultedDestructorIsConstexpr() const { + return data().DefaultedDestructorIsConstexpr && + getASTContext().getLangOpts().CPlusPlus2a; + } + + /// Determine whether this class has a constexpr destructor. + bool hasConstexprDestructor() const; + /// Determine whether this class has a trivial destructor /// (C++ [class.dtor]p3) bool hasTrivialDestructor() const { @@ -1531,8 +1550,10 @@ class CXXRecordDecl : public RecordDecl { /// /// Only in C++17 and beyond, are lambdas literal types. bool isLiteral() const { - return hasTrivialDestructor() && - (!isLambda() || getASTContext().getLangOpts().CPlusPlus17) && + ASTContext &Ctx = getASTContext(); + return (Ctx.getLangOpts().CPlusPlus2a ? hasConstexprDestructor() + : hasTrivialDestructor()) && + (!isLambda() || Ctx.getLangOpts().CPlusPlus17) && !hasNonLiteralTypeFieldsOrBases() && (isAggregate() || isLambda() || hasConstexprNonCopyMoveConstructor() || @@ -2219,7 +2240,7 @@ class CXXMethodDecl : public FunctionDecl { const CXXRecordDecl *Decl); Qualifiers getMethodQualifiers() const { - return getType()->getAs()->getMethodQuals(); + return getType()->castAs()->getMethodQuals(); } /// Retrieve the ref-qualifier associated with this method. @@ -2234,7 +2255,7 @@ class CXXMethodDecl : public FunctionDecl { /// }; /// @endcode RefQualifierKind getRefQualifier() const { - return getType()->getAs()->getRefQualifier(); + return getType()->castAs()->getRefQualifier(); } bool hasInlineBody() const; @@ -2554,9 +2575,9 @@ class CXXConstructorDecl final ExplicitSpecifier getExplicitSpecifierInternal() const { if (CXXConstructorDeclBits.HasTrailingExplicitSpecifier) - return *getCanonicalDecl()->getTrailingObjects(); + return *getTrailingObjects(); return ExplicitSpecifier( - nullptr, getCanonicalDecl()->CXXConstructorDeclBits.IsSimpleExplicit + nullptr, CXXConstructorDeclBits.IsSimpleExplicit ? ExplicitSpecKind::ResolvedTrue : ExplicitSpecKind::ResolvedFalse); } @@ -2597,10 +2618,10 @@ class CXXConstructorDecl final InheritedConstructor Inherited = InheritedConstructor()); ExplicitSpecifier getExplicitSpecifier() { - return getExplicitSpecifierInternal(); + return getCanonicalDecl()->getExplicitSpecifierInternal(); } const ExplicitSpecifier getExplicitSpecifier() const { - return getExplicitSpecifierInternal(); + return getCanonicalDecl()->getExplicitSpecifierInternal(); } /// Return true if the declartion is already resolved to be explicit. @@ -2801,9 +2822,9 @@ class CXXDestructorDecl : public CXXMethodDecl { CXXDestructorDecl(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool isInline, - bool isImplicitlyDeclared) + bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind) : CXXMethodDecl(CXXDestructor, C, RD, StartLoc, NameInfo, T, TInfo, - SC_None, isInline, CSK_unspecified, SourceLocation()) { + SC_None, isInline, ConstexprKind, SourceLocation()) { setImplicit(isImplicitlyDeclared); } @@ -2813,9 +2834,9 @@ class CXXDestructorDecl : public CXXMethodDecl { static CXXDestructorDecl *Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, - QualType T, TypeSourceInfo* TInfo, - bool isInline, - bool isImplicitlyDeclared); + QualType T, TypeSourceInfo *TInfo, + bool isInline, bool isImplicitlyDeclared, + ConstexprSpecKind ConstexprKind); static CXXDestructorDecl *CreateDeserialized(ASTContext & C, unsigned ID); void setOperatorDelete(FunctionDecl *OD, Expr *ThisArg); @@ -2888,7 +2909,7 @@ class CXXConversionDecl : public CXXMethodDecl { /// Returns the type that this conversion function is converting to. QualType getConversionType() const { - return getType()->getAs()->getReturnType(); + return getType()->castAs()->getReturnType(); } /// Determine whether this conversion function is a conversion from @@ -2925,8 +2946,10 @@ class LinkageSpecDecl : public Decl, public DeclContext { /// ensure a stable ABI for this, we choose the DW_LANG_ encodings /// from the dwarf standard. enum LanguageIDs { - lang_c = /* DW_LANG_C */ 0x0002, - lang_cxx = /* DW_LANG_C_plus_plus */ 0x0004 + lang_c = llvm::dwarf::DW_LANG_C, + lang_cxx = llvm::dwarf::DW_LANG_C_plus_plus, + lang_cxx_11 = llvm::dwarf::DW_LANG_C_plus_plus_11, + lang_cxx_14 = llvm::dwarf::DW_LANG_C_plus_plus_14 }; private: diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 0b8ea50109061..33845d9f8a92b 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -906,6 +906,11 @@ class Expr : public ValueStmt { return skipRValueSubobjectAdjustments(CommaLHSs, Adjustments); } + /// Checks that the two Expr's will refer to the same value as a comparison + /// operand. The caller must ensure that the values referenced by the Expr's + /// are not modified between E1 and E2 or the result my be invalid. + static bool isSameComparisonOperand(const Expr* E1, const Expr* E2); + static bool classof(const Stmt *T) { return T->getStmtClass() >= firstExprConstant && T->getStmtClass() <= lastExprConstant; diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index e554968caba74..61e7a91d9ff77 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -1907,6 +1907,10 @@ class LambdaExpr final : public Expr, /// lambda expression. CXXMethodDecl *getCallOperator() const; + /// Retrieve the function template call operator associated with this + /// lambda expression. + FunctionTemplateDecl *getDependentCallOperator() const; + /// If this is a generic lambda expression, retrieve the template /// parameter list associated with it, or else return null. TemplateParameterList *getTemplateParameterList() const; @@ -2096,8 +2100,7 @@ class CXXNewExpr final bool IsParenTypeId); QualType getAllocatedType() const { - assert(getType()->isPointerType()); - return getType()->getAs()->getPointeeType(); + return getType()->castAs()->getPointeeType(); } TypeSourceInfo *getAllocatedTypeSourceInfo() const { @@ -2275,8 +2278,8 @@ class CXXDeleteExpr : public Expr { CXXDeleteExpr(QualType Ty, bool GlobalDelete, bool ArrayForm, bool ArrayFormAsWritten, bool UsualArrayDeleteWantsSize, FunctionDecl *OperatorDelete, Expr *Arg, SourceLocation Loc) - : Expr(CXXDeleteExprClass, Ty, VK_RValue, OK_Ordinary, false, false, - Arg->isInstantiationDependent(), + : Expr(CXXDeleteExprClass, Ty, VK_RValue, OK_Ordinary, false, + Arg->isValueDependent(), Arg->isInstantiationDependent(), Arg->containsUnexpandedParameterPack()), OperatorDelete(OperatorDelete), Argument(Arg) { CXXDeleteExprBits.GlobalDelete = GlobalDelete; diff --git a/clang/include/clang/AST/ExternalASTMerger.h b/clang/include/clang/AST/ExternalASTMerger.h index d89189da04f02..d3942ee0a4857 100644 --- a/clang/include/clang/AST/ExternalASTMerger.h +++ b/clang/include/clang/AST/ExternalASTMerger.h @@ -14,6 +14,7 @@ #define LLVM_CLANG_AST_EXTERNALASTMERGER_H #include "clang/AST/ASTImporter.h" +#include "clang/AST/ASTImporterSharedState.h" #include "clang/AST/ExternalASTSource.h" #include "llvm/Support/raw_ostream.h" @@ -79,15 +80,27 @@ class ExternalASTMerger : public ExternalASTSource { /// import SourceLocations properly. Additionally, when import occurs for /// a DeclContext whose origin has been overridden, then this /// ExternalASTMerger must be able to determine that. - struct ImporterSource { + class ImporterSource { ASTContext &AST; FileManager &FM; const OriginMap &OM; + + public: + ImporterSource(ASTContext &_AST, FileManager &_FM, const OriginMap &_OM) + : AST(_AST), FM(_FM), OM(_OM) {} + ASTContext &getASTContext() const { return AST; } + FileManager &getFileManager() const { return FM; } + const OriginMap &getOriginMap() const { return OM; } }; private: /// The target for this ExtenralASTMerger. ImporterTarget Target; + /// ExternalASTMerger has multiple ASTImporters that import into the same + /// TU. This is the shared state for all ASTImporters of this + /// ExternalASTMerger. + /// See also the CrossTranslationUnitContext that has a similar setup. + std::shared_ptr SharedState; public: ExternalASTMerger(const ImporterTarget &Target, diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index c0cb6aadc7d65..7d5faf2b8cad8 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -431,7 +431,7 @@ template class RecursiveASTVisitor { // Declare Traverse*() for all concrete Type classes. #define ABSTRACT_TYPE(CLASS, BASE) #define TYPE(CLASS, BASE) bool Traverse##CLASS##Type(CLASS##Type *T); -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" // The above header #undefs ABSTRACT_TYPE and TYPE upon exit. // Define WalkUpFrom*() and empty Visit*() for all Type classes. @@ -444,7 +444,7 @@ template class RecursiveASTVisitor { return true; \ } \ bool Visit##CLASS##Type(CLASS##Type *T) { return true; } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" // ---- Methods on TypeLocs ---- // FIXME: this currently just calls the matching Type methods @@ -460,7 +460,7 @@ template class RecursiveASTVisitor { bool VisitTypeLoc(TypeLoc TL) { return true; } // QualifiedTypeLoc and UnqualTypeLoc are not declared in - // TypeNodes.def and thus need to be handled specially. + // TypeNodes.inc and thus need to be handled specially. bool WalkUpFromQualifiedTypeLoc(QualifiedTypeLoc TL) { return getDerived().VisitUnqualTypeLoc(TL.getUnqualifiedLoc()); } @@ -478,7 +478,7 @@ template class RecursiveASTVisitor { return true; \ } \ bool Visit##CLASS##TypeLoc(CLASS##TypeLoc TL) { return true; } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" // ---- Methods on Decls ---- @@ -676,7 +676,7 @@ bool RecursiveASTVisitor::TraverseType(QualType T) { #define TYPE(CLASS, BASE) \ case Type::CLASS: \ DISPATCH(CLASS##Type, CLASS##Type, const_cast(T.getTypePtr())); -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } return true; @@ -2844,6 +2844,7 @@ bool RecursiveASTVisitor::TraverseOMPClause(OMPClause *C) { case OMPC_threadprivate: case OMPC_uniform: case OMPC_device_type: + case OMPC_match: case OMPC_unknown: break; } diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h index 4c2d0710963b2..0ff5a614a864d 100644 --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -146,8 +146,6 @@ class TextNodeDumper const comments::CommandTraits *Traits; - const ASTContext *Context; - const char *getCommandName(unsigned CommandID); public: diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 293f67c7fe9e9..ba835957ab52c 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -126,7 +126,7 @@ using CanQualType = CanQual; // Provide forward declarations for all of the *Type classes. #define TYPE(Class, Base) class Class##Type; -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" /// The collection of all-type qualifiers we support. /// Clang supports five independent qualifiers: @@ -1445,10 +1445,9 @@ class alignas(8) Type : public ExtQualsTypeCommonBase { public: enum TypeClass { #define TYPE(Class, Base) Class, -#define LAST_TYPE(Class) TypeLast = Class, +#define LAST_TYPE(Class) TypeLast = Class #define ABSTRACT_TYPE(Class, Base) -#include "clang/AST/TypeNodes.def" - TagFirst = Record, TagLast = Enum +#include "clang/AST/TypeNodes.inc" }; private: @@ -2064,6 +2063,7 @@ class alignas(8) Type : public ExtQualsTypeCommonBase { bool isCARCBridgableType() const; bool isTemplateTypeParmType() const; // C++ template type parameter bool isNullPtrType() const; // C++11 std::nullptr_t + bool isNothrowT() const; // C++ std::nothrow_t bool isAlignValT() const; // C++17 std::align_val_t bool isStdByteType() const; // C++17 std::byte bool isAtomicType() const; // C11 _Atomic() @@ -2427,7 +2427,7 @@ template <> inline const Class##Type *Type::getAs() const { \ template <> inline const Class##Type *Type::castAs() const { \ return cast(CanonicalType); \ } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" /// This class is used for builtin types like 'int'. Builtin /// types are always canonical and have a literal name field. @@ -4443,7 +4443,7 @@ class TagType : public Type { bool isBeingDefined() const; static bool classof(const Type *T) { - return T->getTypeClass() >= TagFirst && T->getTypeClass() <= TagLast; + return T->getTypeClass() == Enum || T->getTypeClass() == Record; } }; @@ -6361,6 +6361,7 @@ inline bool QualType::isCForbiddenLValueType() const { /// \returns True for types specified in C++0x [basic.fundamental]. inline bool Type::isFundamentalType() const { return isVoidType() || + isNullPtrType() || // FIXME: It's really annoying that we don't have an // 'isArithmeticType()' which agrees with the standard definition. (isArithmeticType() && !isEnumeralType()); diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h index 40d17f991f1fb..f305680d775cf 100644 --- a/clang/include/clang/AST/TypeLoc.h +++ b/clang/include/clang/AST/TypeLoc.h @@ -106,7 +106,7 @@ class TypeLoc { #define ABSTRACT_TYPE(Class, Base) #define TYPE(Class, Base) \ Class = Type::Class, -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" Qualified }; diff --git a/clang/include/clang/AST/TypeLocNodes.def b/clang/include/clang/AST/TypeLocNodes.def index c0dfe150d6cc0..81448c7e7ce5b 100644 --- a/clang/include/clang/AST/TypeLocNodes.def +++ b/clang/include/clang/AST/TypeLocNodes.def @@ -31,7 +31,7 @@ TYPELOC(Qualified, TypeLoc) #define TYPE(Class, Base) UNQUAL_TYPELOC(Class, Base##Loc) #define ABSTRACT_TYPE(Class, Base) ABSTRACT_TYPELOC(Class, Base##Loc) -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" #undef DECLARATOR_TYPELOC #undef TYPESPEC_TYPELOC diff --git a/clang/include/clang/AST/TypeNodes.def b/clang/include/clang/AST/TypeNodes.def deleted file mode 100644 index 58a5f880cbe61..0000000000000 --- a/clang/include/clang/AST/TypeNodes.def +++ /dev/null @@ -1,135 +0,0 @@ -//===-- TypeNodes.def - Metadata about Type AST nodes -----------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file defines the AST type info database. Each type node is -// enumerated by providing its name (e.g., "Builtin" or "Enum") and -// base class (e.g., "Type" or "TagType"). Depending on where in the -// abstract syntax tree the type will show up, the enumeration uses -// one of five different macros: -// -// TYPE(Class, Base) - A type that can show up anywhere in the AST, -// and might be dependent, canonical, or non-canonical. All clients -// will need to understand these types. -// -// ABSTRACT_TYPE(Class, Base) - An abstract class that shows up in -// the type hierarchy but has no concrete instances. -// -// NON_CANONICAL_TYPE(Class, Base) - A type that can show up -// anywhere in the AST but will never be a part of a canonical -// type. Clients that only need to deal with canonical types -// (ignoring, e.g., typedefs and other type aliases used for -// pretty-printing) can ignore these types. -// -// DEPENDENT_TYPE(Class, Base) - A type that will only show up -// within a C++ template that has not been instantiated, e.g., a -// type that is always dependent. Clients that do not need to deal -// with uninstantiated C++ templates can ignore these types. -// -// NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) - A type that -// is non-canonical unless it is dependent. Defaults to TYPE because -// it is neither reliably dependent nor reliably non-canonical. -// -// There is a sixth macro, independent of the others. Most clients -// will not need to use it. -// -// LEAF_TYPE(Class) - A type that never has inner types. Clients -// which can operate on such types more efficiently may wish to do so. -// -//===----------------------------------------------------------------------===// - -#ifndef ABSTRACT_TYPE -# define ABSTRACT_TYPE(Class, Base) TYPE(Class, Base) -#endif - -#ifndef NON_CANONICAL_TYPE -# define NON_CANONICAL_TYPE(Class, Base) TYPE(Class, Base) -#endif - -#ifndef DEPENDENT_TYPE -# define DEPENDENT_TYPE(Class, Base) TYPE(Class, Base) -#endif - -#ifndef NON_CANONICAL_UNLESS_DEPENDENT_TYPE -# define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) TYPE(Class, Base) -#endif - -TYPE(Builtin, Type) -TYPE(Complex, Type) -TYPE(Pointer, Type) -TYPE(BlockPointer, Type) -ABSTRACT_TYPE(Reference, Type) -TYPE(LValueReference, ReferenceType) -TYPE(RValueReference, ReferenceType) -TYPE(MemberPointer, Type) -ABSTRACT_TYPE(Array, Type) -TYPE(ConstantArray, ArrayType) -TYPE(IncompleteArray, ArrayType) -TYPE(VariableArray, ArrayType) -DEPENDENT_TYPE(DependentSizedArray, ArrayType) -DEPENDENT_TYPE(DependentSizedExtVector, Type) -DEPENDENT_TYPE(DependentAddressSpace, Type) -TYPE(Vector, Type) -DEPENDENT_TYPE(DependentVector, Type) -TYPE(ExtVector, VectorType) -ABSTRACT_TYPE(Function, Type) -TYPE(FunctionProto, FunctionType) -TYPE(FunctionNoProto, FunctionType) -DEPENDENT_TYPE(UnresolvedUsing, Type) -NON_CANONICAL_TYPE(Paren, Type) -NON_CANONICAL_TYPE(Typedef, Type) -NON_CANONICAL_TYPE(MacroQualified, Type) -NON_CANONICAL_TYPE(Adjusted, Type) -NON_CANONICAL_TYPE(Decayed, AdjustedType) -NON_CANONICAL_UNLESS_DEPENDENT_TYPE(TypeOfExpr, Type) -NON_CANONICAL_UNLESS_DEPENDENT_TYPE(TypeOf, Type) -NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Decltype, Type) -NON_CANONICAL_UNLESS_DEPENDENT_TYPE(UnaryTransform, Type) -ABSTRACT_TYPE(Tag, Type) -TYPE(Record, TagType) -TYPE(Enum, TagType) -NON_CANONICAL_TYPE(Elaborated, Type) -NON_CANONICAL_TYPE(Attributed, Type) -DEPENDENT_TYPE(TemplateTypeParm, Type) -NON_CANONICAL_TYPE(SubstTemplateTypeParm, Type) -DEPENDENT_TYPE(SubstTemplateTypeParmPack, Type) -NON_CANONICAL_UNLESS_DEPENDENT_TYPE(TemplateSpecialization, Type) -ABSTRACT_TYPE(Deduced, Type) -TYPE(Auto, DeducedType) -TYPE(DeducedTemplateSpecialization, DeducedType) -DEPENDENT_TYPE(InjectedClassName, Type) -DEPENDENT_TYPE(DependentName, Type) -DEPENDENT_TYPE(DependentTemplateSpecialization, Type) -NON_CANONICAL_UNLESS_DEPENDENT_TYPE(PackExpansion, Type) -NON_CANONICAL_TYPE(ObjCTypeParam, Type) -TYPE(ObjCObject, Type) -TYPE(ObjCInterface, ObjCObjectType) -TYPE(ObjCObjectPointer, Type) -TYPE(Pipe, Type) -TYPE(Atomic, Type) - -#ifdef LAST_TYPE -LAST_TYPE(Atomic) -#undef LAST_TYPE -#endif - -// These types are always leaves in the type hierarchy. -#ifdef LEAF_TYPE -LEAF_TYPE(Enum) -LEAF_TYPE(Builtin) -LEAF_TYPE(Record) -LEAF_TYPE(InjectedClassName) -LEAF_TYPE(ObjCInterface) -LEAF_TYPE(TemplateTypeParm) -#undef LEAF_TYPE -#endif - -#undef NON_CANONICAL_UNLESS_DEPENDENT_TYPE -#undef DEPENDENT_TYPE -#undef NON_CANONICAL_TYPE -#undef ABSTRACT_TYPE -#undef TYPE diff --git a/clang/include/clang/AST/TypeVisitor.h b/clang/include/clang/AST/TypeVisitor.h index 8930ec8539497..17301835fb187 100644 --- a/clang/include/clang/AST/TypeVisitor.h +++ b/clang/include/clang/AST/TypeVisitor.h @@ -70,7 +70,7 @@ class TypeVisitor { switch (T->getTypeClass()) { #define ABSTRACT_TYPE(CLASS, PARENT) #define TYPE(CLASS, PARENT) case Type::CLASS: DISPATCH(CLASS##Type); -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } llvm_unreachable("Unknown type class!"); } @@ -80,7 +80,7 @@ class TypeVisitor { #define TYPE(CLASS, PARENT) RetTy Visit##CLASS##Type(const CLASS##Type *T) { \ DISPATCH(PARENT); \ } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" /// Method called if \c ImpClass doesn't provide specific handler /// for some type class. diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index cc7fe78415a59..55932dc4ec8e7 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -724,9 +724,25 @@ def AVRSignal : InheritableAttr, TargetSpecificAttr { def AsmLabel : InheritableAttr { let Spellings = [Keyword<"asm">, Keyword<"__asm__">]; - let Args = [StringArgument<"Label">]; + let Args = [ + // Label specifies the mangled name for the decl. + StringArgument<"Label">, + + // IsLiteralLabel specifies whether the label is literal (i.e. suppresses + // the global C symbol prefix) or not. If not, the mangle-suppression prefix + // ('\01') is omitted from the decl name at the LLVM IR level. + // + // Non-literal labels are used by some external AST sources like LLDB. + BoolArgument<"IsLiteralLabel", /*optional=*/0, /*fake=*/1> + ]; let SemaHandler = 0; - let Documentation = [Undocumented]; + let Documentation = [AsmLabelDocs]; + let AdditionalMembers = +[{ +bool isEquivalent(AsmLabelAttr *Other) const { + return getLabel() == Other->getLabel() && getIsLiteralLabel() == Other->getIsLiteralLabel(); +} +}]; } def Availability : InheritableAttr { @@ -3526,6 +3542,78 @@ def OMPAllocateDecl : InheritableAttr { let Documentation = [Undocumented]; } +def OMPDeclareVariant : InheritableAttr { + let Spellings = [Pragma<"omp", "declare variant">]; + let Subjects = SubjectList<[Function]>; + let SemaHandler = 0; + let HasCustomParsing = 1; + let InheritEvenIfAlreadyPresent = 1; + let Documentation = [OMPDeclareVariantDocs]; + let Args = [ + ExprArgument<"VariantFuncRef">, + ExprArgument<"Score">, + EnumArgument<"CtxSelectorSet", "CtxSelectorSetType", + [ "", "implementation" + ], + [ + "CtxSetUnknown", "CtxSetImplementation" + ]>, + EnumArgument<"CtxScore", "ScoreType", + [ "", "score" + ], + [ + "ScoreUnknown", "ScoreSpecified" + ]>, + EnumArgument<"CtxSelector", "CtxSelectorType", + [ "", "vendor" + ], + [ + "CtxUnknown", "CtxVendor" + ]>, + StringArgument<"ImplVendor", 1> + ]; + let AdditionalMembers = [{ + void printScore(raw_ostream & OS, const PrintingPolicy &Policy) const { + if (const Expr *E = getScore()) { + OS << "score("; + E->printPretty(OS, nullptr, Policy); + OS << "):"; + } + } + void printPrettyPragma(raw_ostream & OS, const PrintingPolicy &Policy) + const { + assert(getCtxSelectorSet() != CtxSetUnknown && + getCtxSelector() != CtxUnknown && "Unknown context selector."); + if (const Expr *E = getVariantFuncRef()) { + OS << "("; + E->printPretty(OS, nullptr, Policy); + OS << ")"; + } + // TODO: add printing of real context selectors. + OS << " match("; + switch (getCtxSelectorSet()) { + case CtxSetImplementation: + OS << "implementation={"; + switch (getCtxSelector()) { + case CtxVendor: + OS << "vendor("; + printScore(OS, Policy); + OS << getImplVendor(); + OS << ")"; + break; + case CtxUnknown: + llvm_unreachable("Unknown context selector."); + } + OS << "}"; + break; + case CtxSetUnknown: + llvm_unreachable("Unknown context selector set."); + } + OS << ")"; + } + }]; +} + def InternalLinkage : InheritableAttr { let Spellings = [Clang<"internal_linkage">]; let Subjects = SubjectList<[Var, Function, CXXRecord]>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 5dff70e4b8a7f..ce78fcc139ea0 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -2727,6 +2727,30 @@ manipulating bits of the enumerator when issuing warnings. }]; } +def AsmLabelDocs : Documentation { + let Category = DocCatDecl; + let Content = [{ +This attribute can be used on a function or variable to specify its symbol name. + +On some targets, all C symbols are prefixed by default with a single character, typically ``_``. This was done historically to distinguish them from symbols used by other languages. (This prefix is also added to the standard Itanium C++ ABI prefix on "mangled" symbol names, so that e.g. on such targets the true symbol name for a C++ variable declared as ``int cppvar;`` would be ``__Z6cppvar``; note the two underscores.) This prefix is *not* added to the symbol names specified by the ``asm`` attribute; programmers wishing to match a C symbol name must compensate for this. + +For example, consider the following C code: + +.. code-block:: c + + int var1 asm("altvar") = 1; // "altvar" in symbol table. + int var2 = 1; // "_var2" in symbol table. + + void func1(void) asm("altfunc"); + void func1(void) {} // "altfunc" in symbol table. + void func2(void) {} // "_func2" in symbol table. + +Clang's implementation of this attribute is compatible with GCC's, `documented here `_. + +While it is possible to use this attribute to name a special symbol used internally by the compiler, such as an LLVM intrinsic, this is neither recommended nor supported and may cause the compiler to crash or miscompile. Users who wish to gain access to intrinsic behavior are strongly encouraged to request new builtin functions. + }]; +} + def EnumExtensibilityDocs : Documentation { let Category = DocCatDecl; let Content = [{ @@ -3410,6 +3434,34 @@ where clause is one of the following: }]; } +def OMPDeclareVariantDocs : Documentation { + let Category = DocCatFunction; + let Heading = "#pragma omp declare variant"; + let Content = [{ +The `declare variant` directive declares a specialized variant of a base + function and specifies the context in which that specialized variant is used. + The declare variant directive is a declarative directive. +The syntax of the `declare variant` construct is as follows: + + .. code-block:: none + + #pragma omp declare variant(variant-func-id) clause new-line + [#pragma omp declare variant(variant-func-id) clause new-line] + [...] + function definition or declaration + +where clause is one of the following: + + .. code-block:: none + + match(context-selector-specification) + +and where `variant-func-id` is the name of a function variant that is either a + base language identifier or, for C++, a template-id. + + }]; +} + def NoStackProtectorDocs : Documentation { let Category = DocCatFunction; let Content = [{ diff --git a/clang/include/clang/Basic/AttributeCommonInfo.h b/clang/include/clang/Basic/AttributeCommonInfo.h index c8fc0a5a044b8..545e7e9a2b47e 100644 --- a/clang/include/clang/Basic/AttributeCommonInfo.h +++ b/clang/include/clang/Basic/AttributeCommonInfo.h @@ -74,11 +74,11 @@ class AttributeCommonInfo { public: AttributeCommonInfo(SourceRange AttrRange) - : AttrRange(AttrRange), AttrKind(0), SyntaxUsed(0), + : AttrRange(AttrRange), ScopeLoc(), AttrKind(0), SyntaxUsed(0), SpellingIndex(SpellingNotCalculated) {} AttributeCommonInfo(SourceLocation AttrLoc) - : AttrRange(AttrLoc), AttrKind(0), SyntaxUsed(0), + : AttrRange(AttrLoc), ScopeLoc(), AttrKind(0), SyntaxUsed(0), SpellingIndex(SpellingNotCalculated) {} AttributeCommonInfo(const IdentifierInfo *AttrName, diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def index 808957573b698..b0f38fbccc365 100644 --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -461,7 +461,7 @@ BUILTIN(__builtin_rotateleft64, "UWiUWiUWi", "nc") BUILTIN(__builtin_rotateright8, "UcUcUc", "nc") BUILTIN(__builtin_rotateright16, "UsUsUs", "nc") BUILTIN(__builtin_rotateright32, "UZiUZiUZi", "nc") -BUILTIN(__builtin_rotateright64, "UWiUWiWi", "nc") +BUILTIN(__builtin_rotateright64, "UWiUWiUWi", "nc") // Random GCC builtins BUILTIN(__builtin_constant_p, "i.", "nctu") @@ -1005,9 +1005,7 @@ LIBBUILTIN(pthread_create, "", "fC<2,3>", "pthread.h", ALL_GNU_LANGUAGES) LIBBUILTIN(_setjmp, "iJ", "fj", "setjmp.h", ALL_LANGUAGES) LIBBUILTIN(__sigsetjmp, "iSJi", "fj", "setjmp.h", ALL_LANGUAGES) LIBBUILTIN(sigsetjmp, "iSJi", "fj", "setjmp.h", ALL_LANGUAGES) -LIBBUILTIN(setjmp_syscall, "iJ", "fj", "setjmp.h", ALL_LANGUAGES) LIBBUILTIN(savectx, "iJ", "fj", "setjmp.h", ALL_LANGUAGES) -LIBBUILTIN(qsetjmp, "iJ", "fj", "setjmp.h", ALL_LANGUAGES) LIBBUILTIN(getcontext, "iK*", "fj", "setjmp.h", ALL_LANGUAGES) LIBBUILTIN(_longjmp, "vJi", "fr", "setjmp.h", ALL_GNU_LANGUAGES) @@ -1470,7 +1468,7 @@ BUILTIN(__builtin_operator_new, "v*z", "tc") BUILTIN(__builtin_operator_delete, "vv*", "tn") BUILTIN(__builtin_char_memchr, "c*cC*iz", "n") BUILTIN(__builtin_dump_struct, "ivC*v*", "tn") -BUILTIN(__builtin_preserve_access_index, "vC*vC*", "nU") +BUILTIN(__builtin_preserve_access_index, "v.", "t") // Safestack builtins BUILTIN(__builtin___get_unsafe_stack_start, "v*", "Fn") diff --git a/clang/include/clang/Basic/BuiltinsWebAssembly.def b/clang/include/clang/Basic/BuiltinsWebAssembly.def index 37f945a319532..f825040d61428 100644 --- a/clang/include/clang/Basic/BuiltinsWebAssembly.def +++ b/clang/include/clang/Basic/BuiltinsWebAssembly.def @@ -118,5 +118,19 @@ TARGET_BUILTIN(__builtin_wasm_trunc_saturate_u_i32x4_f32x4, "V4iV4f", "nc", "sim TARGET_BUILTIN(__builtin_wasm_trunc_saturate_s_i64x2_f64x2, "V2LLiV2d", "nc", "unimplemented-simd128") TARGET_BUILTIN(__builtin_wasm_trunc_saturate_u_i64x2_f64x2, "V2LLiV2d", "nc", "unimplemented-simd128") +TARGET_BUILTIN(__builtin_wasm_narrow_s_i8x16_i16x8, "V16cV8sV8s", "nc", "simd128") +TARGET_BUILTIN(__builtin_wasm_narrow_u_i8x16_i16x8, "V16cV8sV8s", "nc", "simd128") +TARGET_BUILTIN(__builtin_wasm_narrow_s_i16x8_i32x4, "V8sV4iV4i", "nc", "simd128") +TARGET_BUILTIN(__builtin_wasm_narrow_u_i16x8_i32x4, "V8sV4iV4i", "nc", "simd128") + +TARGET_BUILTIN(__builtin_wasm_widen_low_s_i16x8_i8x16, "V8sV16c", "nc", "simd128") +TARGET_BUILTIN(__builtin_wasm_widen_high_s_i16x8_i8x16, "V8sV16c", "nc", "simd128") +TARGET_BUILTIN(__builtin_wasm_widen_low_u_i16x8_i8x16, "V8sV16c", "nc", "simd128") +TARGET_BUILTIN(__builtin_wasm_widen_high_u_i16x8_i8x16, "V8sV16c", "nc", "simd128") +TARGET_BUILTIN(__builtin_wasm_widen_low_s_i32x4_i16x8, "V4iV8s", "nc", "simd128") +TARGET_BUILTIN(__builtin_wasm_widen_high_s_i32x4_i16x8, "V4iV8s", "nc", "simd128") +TARGET_BUILTIN(__builtin_wasm_widen_low_u_i32x4_i16x8, "V4iV8s", "nc", "simd128") +TARGET_BUILTIN(__builtin_wasm_widen_high_u_i32x4_i16x8, "V4iV8s", "nc", "simd128") + #undef BUILTIN #undef TARGET_BUILTIN diff --git a/clang/include/clang/Basic/BuiltinsX86.def b/clang/include/clang/Basic/BuiltinsX86.def index a0ba0ecf36bb8..5ab9dc1c3ac3a 100644 --- a/clang/include/clang/Basic/BuiltinsX86.def +++ b/clang/include/clang/Basic/BuiltinsX86.def @@ -751,8 +751,8 @@ TARGET_BUILTIN(__builtin_ia32_bextri_u32, "UiUiIUi", "nc", "tbm") // LWP TARGET_BUILTIN(__builtin_ia32_llwpcb, "vv*", "n", "lwp") TARGET_BUILTIN(__builtin_ia32_slwpcb, "v*", "n", "lwp") -TARGET_BUILTIN(__builtin_ia32_lwpins32, "UcUiUiUi", "n", "lwp") -TARGET_BUILTIN(__builtin_ia32_lwpval32, "vUiUiUi", "n", "lwp") +TARGET_BUILTIN(__builtin_ia32_lwpins32, "UcUiUiIUi", "n", "lwp") +TARGET_BUILTIN(__builtin_ia32_lwpval32, "vUiUiIUi", "n", "lwp") // SHA TARGET_BUILTIN(__builtin_ia32_sha1rnds4, "V4iV4iV4iIc", "ncV:128:", "sha") diff --git a/clang/include/clang/Basic/BuiltinsX86_64.def b/clang/include/clang/Basic/BuiltinsX86_64.def index 56051af55e7d5..c535f43203e56 100644 --- a/clang/include/clang/Basic/BuiltinsX86_64.def +++ b/clang/include/clang/Basic/BuiltinsX86_64.def @@ -86,8 +86,8 @@ TARGET_BUILTIN(__builtin_ia32_bzhi_di, "UOiUOiUOi", "nc", "bmi2") TARGET_BUILTIN(__builtin_ia32_pdep_di, "UOiUOiUOi", "nc", "bmi2") TARGET_BUILTIN(__builtin_ia32_pext_di, "UOiUOiUOi", "nc", "bmi2") TARGET_BUILTIN(__builtin_ia32_bextri_u64, "UOiUOiIUOi", "nc", "tbm") -TARGET_BUILTIN(__builtin_ia32_lwpins64, "UcUOiUiUi", "n", "lwp") -TARGET_BUILTIN(__builtin_ia32_lwpval64, "vUOiUiUi", "n", "lwp") +TARGET_BUILTIN(__builtin_ia32_lwpins64, "UcUOiUiIUi", "n", "lwp") +TARGET_BUILTIN(__builtin_ia32_lwpval64, "vUOiUiIUi", "n", "lwp") TARGET_BUILTIN(__builtin_ia32_vcvtsd2si64, "OiV2dIi", "ncV:128:", "avx512f") TARGET_BUILTIN(__builtin_ia32_vcvtsd2usi64, "UOiV2dIi", "ncV:128:", "avx512f") TARGET_BUILTIN(__builtin_ia32_vcvtss2si64, "OiV4fIi", "ncV:128:", "avx512f") diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index 7732be56595a4..63207a0e2254a 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -37,8 +37,9 @@ def note_constexpr_virtual_call : Note< def note_constexpr_pure_virtual_call : Note< "pure virtual function %q0 called">; def note_constexpr_polymorphic_unknown_dynamic_type : Note< - "%select{||||virtual function called on|dynamic_cast applied to|" - "typeid applied to}0 object '%1' whose dynamic type is not constant">; + "%select{|||||virtual function called on|dynamic_cast applied to|" + "typeid applied to|construction of|destruction of}0 object '%1' " + "whose dynamic type is not constant">; def note_constexpr_dynamic_cast_to_reference_failed : Note< "reference dynamic_cast failed: %select{" "static type %1 of operand is a non-public base class of dynamic type %2|" @@ -53,6 +54,9 @@ def note_constexpr_nonliteral : Note< def note_constexpr_non_global : Note< "%select{pointer|reference}0 to %select{|subobject of }1" "%select{temporary|%3}2 is not a constant expression">; +def note_constexpr_dynamic_alloc : Note< + "%select{pointer|reference}0 to %select{|subobject of }1" + "heap-allocated object is not a constant expression">; def note_constexpr_uninitialized : Note< "%select{|sub}0object of type %1 is not initialized">; def note_constexpr_subobject_declared_here : Note< @@ -100,6 +104,7 @@ def note_constexpr_typeid_polymorphic : Note< def note_constexpr_void_comparison : Note< "comparison between unequal pointers to void has unspecified result">; def note_constexpr_temporary_here : Note<"temporary created here">; +def note_constexpr_dynamic_alloc_here : Note<"heap allocation performed here">; def note_constexpr_conditional_never_const : Note< "both arms of conditional operator are unable to produce a " "constant expression">; @@ -109,16 +114,19 @@ def note_constexpr_call_limit_exceeded : Note< "constexpr evaluation hit maximum call limit">; def note_constexpr_step_limit_exceeded : Note< "constexpr evaluation hit maximum step limit; possible infinite loop?">; +def note_constexpr_heap_alloc_limit_exceeded : Note< + "constexpr evaluation hit maximum heap allocation limit">; def note_constexpr_this : Note< "%select{|implicit }0use of 'this' pointer is only allowed within the " "evaluation of a call to a 'constexpr' member function">; def note_constexpr_lifetime_ended : Note< - "%select{read of|assignment to|increment of|decrement of|member call on|" - "dynamic_cast of|typeid applied to}0 " - "%select{temporary|variable}1 whose lifetime has ended">; + "%select{read of|read of|assignment to|increment of|decrement of|" + "member call on|dynamic_cast of|typeid applied to|construction of|" + "destruction of}0 %select{temporary|variable}1 whose " + "%plural{8:storage duration|:lifetime}0 has ended">; def note_constexpr_access_uninit : Note< - "%select{read of|assignment to|increment of|decrement of|member call on|" - "dynamic_cast of|typeid applied to}0 " + "%select{read of|read of|assignment to|increment of|decrement of|" + "member call on|dynamic_cast of|typeid applied to||destruction of}0 " "%select{object outside its lifetime|uninitialized object}1 " "is not allowed in a constant expression">; def note_constexpr_use_uninit_reference : Note< @@ -128,16 +136,21 @@ def note_constexpr_modify_const_type : Note< "modification of object of const-qualified type %0 is not allowed " "in a constant expression">; def note_constexpr_access_volatile_type : Note< - "%select{read of|assignment to|increment of|decrement of||}0 " + "%select{read of|read of|assignment to|increment of|decrement of|" + "|||}0 " "volatile-qualified type %1 is not allowed in a constant expression">; def note_constexpr_access_volatile_obj : Note< - "%select{read of|assignment to|increment of|decrement of||}0 " + "%select{read of|read of|assignment to|increment of|decrement of|" + "|||}0 " "volatile %select{temporary|object %2|member %2}1 is not allowed in " "a constant expression">; def note_constexpr_volatile_here : Note< "volatile %select{temporary created|object declared|member declared}0 here">; -def note_constexpr_ltor_mutable : Note< - "read of mutable member %0 is not allowed in a constant expression">; +def note_constexpr_access_mutable : Note< + "%select{read of|read of|assignment to|increment of|decrement of|" + "member call on|dynamic_cast of|typeid applied to|construction of|" + "destruction of}0 " + "mutable member %1 is not allowed in a constant expression">; def note_constexpr_ltor_non_const_int : Note< "read of non-const variable %0 is not allowed in a constant expression">; def note_constexpr_ltor_non_constexpr : Note< @@ -145,31 +158,44 @@ def note_constexpr_ltor_non_constexpr : Note< def note_constexpr_ltor_incomplete_type : Note< "read of incomplete type %0 is not allowed in a constant expression">; def note_constexpr_access_null : Note< - "%select{read of|assignment to|increment of|decrement of|member call on|" - "dynamic_cast of|typeid applied to}0 " + "%select{read of|read of|assignment to|increment of|decrement of|" + "member call on|dynamic_cast of|typeid applied to|construction of|" + "destruction of}0 " "dereferenced null pointer is not allowed in a constant expression">; def note_constexpr_access_past_end : Note< - "%select{read of|assignment to|increment of|decrement of|member call on|" - "dynamic_cast of|typeid applied to}0 " - "dereferenced one-past-the-end pointer is not allowed in a constant expression">; + "%select{read of|read of|assignment to|increment of|decrement of|" + "member call on|dynamic_cast of|typeid applied to|construction of|" + "destruction of}0 " + "dereferenced one-past-the-end pointer is not allowed " + "in a constant expression">; def note_constexpr_access_unsized_array : Note< - "%select{read of|assignment to|increment of|decrement of|member call on|" - "dynamic_cast of|typeid applied to}0 " + "%select{read of|read of|assignment to|increment of|decrement of|" + "member call on|dynamic_cast of|typeid applied to|construction of|" + "destruction of}0 " "element of array without known bound " "is not allowed in a constant expression">; def note_constexpr_access_inactive_union_member : Note< - "%select{read of|assignment to|increment of|decrement of|member call on|" - "dynamic_cast of|typeid applied to}0 " + "%select{read of|read of|assignment to|increment of|decrement of|" + "member call on|dynamic_cast of|typeid applied to|" + "construction of subobject of|destruction of}0 " "member %1 of union with %select{active member %3|no active member}2 " "is not allowed in a constant expression">; def note_constexpr_access_static_temporary : Note< - "%select{read of|assignment to|increment of|decrement of|member call on|" - "dynamic_cast of|typeid applied to}0 temporary " + "%select{read of|read of|assignment to|increment of|decrement of|" + "member call on|dynamic_cast of|typeid applied to|reconstruction of|" + "destruction of}0 temporary " "is not allowed in a constant expression outside the expression that " "created the temporary">; def note_constexpr_access_unreadable_object : Note< - "%select{read of|assignment to|increment of|decrement of|member call on|" - "dynamic_cast of|typeid applied to}0 object '%1' whose value is not known">; + "%select{read of|read of|assignment to|increment of|decrement of|" + "member call on|dynamic_cast of|typeid applied to|construction of|" + "destruction of}0 " + "object '%1' whose value is not known">; +def note_constexpr_access_deleted_object : Note< + "%select{read of|read of|assignment to|increment of|decrement of|" + "member call on|dynamic_cast of|typeid applied to|construction of|" + "destruction of}0 " + "heap allocated object that has been deleted">; def note_constexpr_modify_global : Note< "a constant expression cannot modify an object that is visible outside " "that expression">; @@ -189,6 +215,13 @@ def note_constexpr_baa_insufficient_alignment : Note< def note_constexpr_baa_value_insufficient_alignment : Note< "value of the aligned pointer (%0) is not a multiple of the asserted %1 " "%plural{1:byte|:bytes}1">; +def note_constexpr_destroy_out_of_lifetime : Note< + "destroying object '%0' whose lifetime has already ended">; +def note_constexpr_unsupported_destruction : Note< + "non-trivial destruction of type %0 in a constant expression is not supported">; +def note_constexpr_unsupported_tempoarary_nontrivial_dtor : Note< + "non-trivial destruction of lifetime-extended temporary with type %0 " + "used in the result of a constant expression is not yet supported">; def note_constexpr_unsupported_unsized_array : Note< "array-to-pointer decay of array member without known bound is not supported">; def note_constexpr_unsized_array_indexed : Note< @@ -228,6 +261,60 @@ def note_constexpr_bit_cast_invalid_subtype : Note< def note_constexpr_bit_cast_indet_dest : Note< "indeterminate value can only initialize an object of type 'unsigned char'" "%select{, 'char',|}1 or 'std::byte'; %0 is invalid">; +def note_constexpr_pseudo_destructor : Note< + "pseudo-destructor call is not permitted in constant expressions " + "until C++20">; +def note_constexpr_construct_complex_elem : Note< + "construction of individual component of complex number is not yet supported " + "in constant expressions">; +def note_constexpr_destroy_complex_elem : Note< + "destruction of individual component of complex number is not yet supported " + "in constant expressions">; +def note_constexpr_new : Note< + "dynamic memory allocation is not permitted in constant expressions " + "until C++20">; +def note_constexpr_new_non_replaceable : Note< + "call to %select{placement|class-specific}0 %1">; +def note_constexpr_new_placement : Note< + "this placement new expression is not yet supported in constant expressions">; +def note_constexpr_placement_new_wrong_type : Note< + "placement new would change type of storage from %0 to %1">; +def note_constexpr_new_negative : Note< + "cannot allocate array; evaluated array bound %0 is negative">; +def note_constexpr_new_too_large : Note< + "cannot allocate array; evaluated array bound %0 is too large">; +def note_constexpr_new_too_small : Note< + "cannot allocate array; evaluated array bound %0 is too small to hold " + "%1 explicitly initialized elements">; +def note_constexpr_new_untyped : Note< + "cannot allocate untyped memory in a constant expression; " + "use 'std::allocator::allocate' to allocate memory of type 'T'">; +def note_constexpr_new_not_complete_object_type : Note< + "cannot allocate memory of %select{incomplete|function}0 type %1">; +def note_constexpr_operator_new_bad_size : Note< + "allocated size %0 is not a multiple of size %1 of element type %2">; +def note_constexpr_delete_not_heap_alloc : Note< + "delete of pointer '%0' that does not point to a heap-allocated object">; +def note_constexpr_double_delete : Note< + "delete of pointer that has already been deleted">; +def note_constexpr_double_destroy : Note< + "destruction of object that is already being destroyed">; +def note_constexpr_new_delete_mismatch : Note< + "%plural{2:'delete' used to delete pointer to object " + "allocated with 'std::allocator<...>::allocate'|" + ":%select{non-array delete|array delete|'std::allocator<...>::deallocate'}0 " + "used to delete pointer to " + "%select{array object of type %2|non-array object of type %2|" + "object allocated with 'new'}0}1">; +def note_constexpr_delete_subobject : Note< + "delete of pointer%select{ to subobject|}1 '%0' " + "%select{|that does not point to complete object}1">; +def note_constexpr_delete_base_nonvirt_dtor : Note< + "delete of object with dynamic type %1 through pointer to " + "base class type %0 with non-virtual destructor">; +def note_constexpr_memory_leak : Note< + "allocation performed here was not deallocated" + "%plural{0:|: (along with %0 other memory leak%s0)}0">; def err_experimental_clang_interp_failed : Error< "the experimental clang interpreter failed to evaluate an expression">; @@ -279,7 +366,6 @@ def warn_odr_variable_multiple_def : Warning< "external variable %0 defined in multiple translation units">, InGroup; def note_odr_value_here : Note<"declared here with type %0">; -def note_odr_defined_here : Note<"also defined here">; def err_odr_function_type_inconsistent : Error< "external function %0 declared with incompatible types in different " "translation units (%1 vs. %2)">; diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td index 8196a4601c224..484cc317f9658 100644 --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -270,8 +270,6 @@ def err_target_unsupported_mcmse : Error< "-mcmse is not supported for %0">; def err_opt_not_valid_with_opt : Error< "option '%0' cannot be specified with '%1'">; -def err_opt_not_valid_without_opt : Error< - "option '%0' cannot be specified without '%1'">; def err_opt_not_valid_on_target : Error< "option '%0' cannot be specified on this target">; @@ -315,4 +313,9 @@ def err_unknown_analyzer_checker_or_package : Error< "no analyzer checkers or packages are associated with '%0'">; def note_suggest_disabling_all_checkers : Note< "use -analyzer-disable-all-checks to disable all static analyzer checkers">; + +// Poison system directories. +def warn_poison_system_directories : Warning < + "include location '%0' is unsafe for cross-compilation">, + InGroup>, DefaultIgnore; } diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index 5cc8f70b32f3d..c3f094c0590f7 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -188,9 +188,6 @@ def warn_drv_unknown_argument_clang_cl_with_suggestion : Warning< def warn_drv_ycyu_different_arg_clang_cl : Warning< "support for '/Yc' and '/Yu' with different filenames not implemented yet; flags ignored">, InGroup; -def warn_drv_ycyu_no_fi_arg_clang_cl : Warning< - "support for '%0' without a corresponding /FI flag not implemented yet; flag ignored">, - InGroup; def warn_drv_yc_multiple_inputs_clang_cl : Warning< "support for '/Yc' with more than one source file not implemented yet; flag ignored">, InGroup; diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index 3ab18913a457c..a798b498d4e9a 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -64,8 +64,6 @@ def err_fe_backend_unsupported : Error<"%0">, BackendInfo; def err_fe_invalid_code_complete_file : Error< "cannot locate code-completion file %0">, DefaultFatal; -def err_fe_stdout_binary : Error<"unable to change standard output to binary">, - DefaultFatal; def err_fe_dependency_file_requires_MT : Error< "-dependency-file requires at least one -MT or -MQ option">; def err_fe_invalid_plugin_name : Error< @@ -217,10 +215,6 @@ def err_modules_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_interface_stubs : Error<"clang-ifs (-emit-iterface-stubs): %0">; def err_test_module_file_extension_version : Error< "test module file extension '%0' has different version (%1.%2) than expected " diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 22a42001280d1..0f225bee24c06 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -61,9 +61,16 @@ def BoolConversion : DiagGroup<"bool-conversion", [PointerBoolConversion, UndefinedBoolConversion]>; def IntConversion : DiagGroup<"int-conversion">; def EnumConversion : DiagGroup<"enum-conversion">; -def ImplicitIntConversion : DiagGroup<"implicit-int-conversion">; +def ObjCSignedCharBoolImplicitIntConversion : + DiagGroup<"objc-signed-char-bool-implicit-int-conversion">; +def ImplicitIntConversion : DiagGroup<"implicit-int-conversion", + [ObjCSignedCharBoolImplicitIntConversion]>; def ImplicitIntFloatConversion : DiagGroup<"implicit-int-float-conversion">; -def ImplicitFloatConversion : DiagGroup<"implicit-float-conversion", [ImplicitIntFloatConversion]>; +def ObjCSignedCharBoolImplicitFloatConversion : + DiagGroup<"objc-signed-char-bool-implicit-float-conversion">; +def ImplicitFloatConversion : DiagGroup<"implicit-float-conversion", + [ImplicitIntFloatConversion, + ObjCSignedCharBoolImplicitFloatConversion]>; def ImplicitFixedPointConversion : DiagGroup<"implicit-fixed-point-conversion">; def FloatOverflowConversion : DiagGroup<"float-overflow-conversion">; @@ -492,6 +499,7 @@ def StringCompare : DiagGroup<"string-compare">; def StringPlusInt : DiagGroup<"string-plus-int">; def StringPlusChar : DiagGroup<"string-plus-char">; def StrncatSize : DiagGroup<"strncat-size">; +def IntInBoolContext : DiagGroup<"int-in-bool-context">; def TautologicalTypeLimitCompare : DiagGroup<"tautological-type-limit-compare">; def TautologicalUnsignedZeroCompare : DiagGroup<"tautological-unsigned-zero-compare">; def TautologicalUnsignedEnumZeroCompare : DiagGroup<"tautological-unsigned-enum-zero-compare">; @@ -555,6 +563,7 @@ def CoveredSwitchDefault : DiagGroup<"covered-switch-default">; def SwitchBool : DiagGroup<"switch-bool">; def SwitchEnum : DiagGroup<"switch-enum">; def Switch : DiagGroup<"switch">; +def EnumCompareConditional : DiagGroup<"enum-compare-conditional">; def EnumCompareSwitch : DiagGroup<"enum-compare-switch">; def EnumCompare : DiagGroup<"enum-compare", [EnumCompareSwitch]>; def ImplicitFallthroughPerFunction : @@ -814,6 +823,7 @@ def Most : DiagGroup<"most", [ Format, Implicit, InfiniteRecursion, + IntInBoolContext, MismatchedTags, MissingBraces, Move, @@ -1015,6 +1025,12 @@ def ObjCLiteralComparison : DiagGroup<"objc-literal-compare", [ ObjCStringComparison ]>; +def ObjCSignedCharBool : DiagGroup<"objc-signed-char-bool", + [ObjCSignedCharBoolImplicitIntConversion, + ObjCSignedCharBoolImplicitFloatConversion, + ObjCBoolConstantConversion, + TautologicalObjCBoolCompare]>; + // Inline ASM warnings. def ASMOperandWidths : DiagGroup<"asm-operand-widths">; def ASMIgnoredQualifier : DiagGroup<"asm-ignored-qualifier">; diff --git a/clang/include/clang/Basic/DiagnosticOptions.def b/clang/include/clang/Basic/DiagnosticOptions.def index baafd7ac723f6..6d1a1af92821b 100644 --- a/clang/include/clang/Basic/DiagnosticOptions.def +++ b/clang/include/clang/Basic/DiagnosticOptions.def @@ -49,6 +49,7 @@ DIAGOPT(Pedantic, 1, 0) /// -pedantic DIAGOPT(PedanticErrors, 1, 0) /// -pedantic-errors DIAGOPT(ShowColumn, 1, 1) /// Show column number on diagnostics. DIAGOPT(ShowLocation, 1, 1) /// Show source location information. +DIAGOPT(ShowLevel, 1, 1) /// Show diagnostic level. DIAGOPT(AbsolutePath, 1, 0) /// Use absolute paths. DIAGOPT(ShowCarets, 1, 1) /// Show carets in diagnostics. DIAGOPT(ShowFixits, 1, 1) /// Show fixit information. diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 50e7342a5cd60..a7dbcd08b8cd0 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -201,6 +201,7 @@ def err_invalid_token_after_declarator_suggest_equal : Error< "invalid %0 at end of declaration; did you mean '='?">; def err_expected_statement : Error<"expected statement">; def err_expected_lparen_after : Error<"expected '(' after '%0'">; +def err_expected_lbrace_after : Error<"expected '{' after '%0'">; def err_expected_rparen_after : Error<"expected ')' after '%0'">; def err_expected_punc : Error<"expected ')' or ',' after '%0'">; def err_expected_less_after : Error<"expected '<' after '%0'">; @@ -1177,8 +1178,8 @@ def err_omp_expected_identifier_for_critical : Error< "expected identifier specifying the name of the 'omp critical' directive">; def err_omp_expected_reduction_identifier : Error< "expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&', or '||'">; -def err_omp_decl_in_declare_simd : Error< - "function declaration is expected after 'declare simd' directive">; +def err_omp_decl_in_declare_simd_variant : Error< + "function declaration is expected after 'declare %select{simd|variant}0' directive">; def err_omp_unknown_map_type : Error< "incorrect map type, expected one of 'to', 'from', 'tofrom', 'alloc', 'release', or 'delete'">; def err_omp_unknown_map_type_modifier : Error< @@ -1199,6 +1200,17 @@ def err_omp_mapper_illegal_identifier : Error< "illegal OpenMP user-defined mapper identifier">; def err_omp_mapper_expected_declarator : Error< "expected declarator on 'omp declare mapper' directive">; +def err_omp_declare_variant_wrong_clause : Error< + "expected '%0' clause on 'omp declare variant' directive">; +def err_omp_declare_variant_no_ctx_selector : Error< + "expected context selector in '%0' clause on 'omp declare variant' directive">; +def err_omp_declare_variant_equal_expected : Error< + "expected '=' after '%0' context selector set name on 'omp declare variant' directive">; +def warn_omp_declare_variant_cs_name_expected : Warning< + "unknown context selector in '%0' context selector set of 'omp declare variant' directive, ignored">, + InGroup; +def err_omp_declare_variant_item_expected : Error< + "expected %0 in '%1' context selector of '%2' selector set of 'omp declare variant' directive">; def warn_omp_more_one_device_type_clause : Warning< "more than one 'device_type' clause is specified">, InGroup; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 9d36ff6f87d31..ca07d14dd594f 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2382,7 +2382,13 @@ def err_constexpr_tag : Error< "%select{class|struct|interface|union|enum}0 " "cannot be marked %sub{select_constexpr_spec_kind}1">; def err_constexpr_dtor : Error< - "destructor cannot be marked %sub{select_constexpr_spec_kind}0">; + "destructor cannot be declared %sub{select_constexpr_spec_kind}0">; +def err_constexpr_dtor_subobject : Error< + "destructor cannot be declared %sub{select_constexpr_spec_kind}0 because " + "%select{data member %2|base class %3}1 does not have a " + "constexpr destructor">; +def note_constexpr_dtor_subobject : Note< + "%select{data member %1|base class %2}0 declared here">; def err_constexpr_wrong_decl_kind : Error< "%sub{select_constexpr_spec_kind}0 can only be used " "in %select{|variable and function|function|variable}0 declarations">; @@ -2394,6 +2400,8 @@ def err_constexpr_var_non_literal : Error< "constexpr variable cannot have non-literal type %0">; def err_constexpr_var_requires_const_init : Error< "constexpr variable %0 must be initialized by a constant expression">; +def err_constexpr_var_requires_const_destruction : Error< + "constexpr variable %0 must have constant destruction">; def err_constexpr_redecl_mismatch : Error< "%select{non-constexpr|constexpr|consteval}1 declaration of %0" " follows %select{non-constexpr|constexpr|consteval}2 declaration">; @@ -2454,9 +2462,13 @@ def err_constexpr_local_var_static : Error< def err_constexpr_local_var_non_literal_type : Error< "variable of non-literal type %1 cannot be defined in a constexpr " "%select{function|constructor}0">; -def err_constexpr_local_var_no_init : Error< - "variables defined in a constexpr %select{function|constructor}0 must be " - "initialized">; +def ext_constexpr_local_var_no_init : ExtWarn< + "uninitialized variable in a constexpr %select{function|constructor}0 " + "is a C++20 extension">, InGroup; +def warn_cxx17_compat_constexpr_local_var_no_init : Warning< + "uninitialized variable in a constexpr %select{function|constructor}0 " + "is incompatible with C++ standards before C++20">, + InGroup, DefaultIgnore; def ext_constexpr_function_never_constant_expr : ExtWarn< "constexpr %select{function|constructor}0 never produces a " "constant expression">, InGroup>, DefaultError; @@ -2481,10 +2493,8 @@ def warn_cxx11_compat_constexpr_body_multiple_return : Warning< InGroup, DefaultIgnore; def note_constexpr_body_previous_return : Note< "previous return statement is here">; -def err_constexpr_function_try_block : Error< - "function try block not allowed in constexpr %select{function|constructor}0">; -// c++2a function try blocks in constexpr +// C++2a function try blocks in constexpr def ext_constexpr_function_try_block_cxx2a : ExtWarn< "function try block in constexpr %select{function|constructor}0 is " "a C++2a extension">, InGroup; @@ -2493,10 +2503,20 @@ def warn_cxx17_compat_constexpr_function_try_block : Warning< "incompatible with C++ standards before C++2a">, InGroup, DefaultIgnore; -def err_constexpr_union_ctor_no_init : Error< - "constexpr union constructor does not initialize any member">; -def err_constexpr_ctor_missing_init : Error< - "constexpr constructor must initialize all members">; +def ext_constexpr_union_ctor_no_init : ExtWarn< + "constexpr union constructor that does not initialize any member " + "is a C++20 extension">, InGroup; +def warn_cxx17_compat_constexpr_union_ctor_no_init : Warning< + "constexpr union constructor that does not initialize any member " + "is incompatible with C++ standards before C++20">, + InGroup, DefaultIgnore; +def ext_constexpr_ctor_missing_init : ExtWarn< + "constexpr constructor that does not initialize all members " + "is a C++20 extension">, InGroup; +def warn_cxx17_compat_constexpr_ctor_missing_init : Warning< + "constexpr constructor that does not initialize all members " + "is incompatible with C++ standards before C++20">, + InGroup, DefaultIgnore; def note_constexpr_ctor_missing_init : Note< "member not initialized by constructor">; def note_non_literal_no_constexpr_ctors : Note< @@ -2511,6 +2531,8 @@ def note_non_literal_user_provided_dtor : Note< "%0 is not literal because it has a user-provided destructor">; def note_non_literal_nontrivial_dtor : Note< "%0 is not literal because it has a non-trivial destructor">; +def note_non_literal_non_constexpr_dtor : Note< + "%0 is not literal because its destructor is not constexpr">; def note_non_literal_lambda : Note< "lambda closure types are non-literal types before C++17">; def warn_private_extern : Warning< @@ -3014,6 +3036,10 @@ def warn_gnu_inline_attribute_requires_inline : Warning< "'gnu_inline' attribute requires function to be marked 'inline'," " attribute ignored">, InGroup; +def warn_gnu_inline_cplusplus_without_extern : Warning< + "'gnu_inline' attribute without 'extern' in C++ treated as externally" + " available, this changed in Clang 10">, + InGroup>; def err_attribute_vecreturn_only_vector_member : Error< "the vecreturn attribute can only be used on a class or structure with one member, which must be a vector">; def err_attribute_vecreturn_only_pod_record : Error< @@ -3316,9 +3342,9 @@ def warn_impcast_integer_precision_constant : Warning< def warn_impcast_bitfield_precision_constant : Warning< "implicit truncation from %2 to bit-field changes value from %0 to %1">, InGroup; -def warn_impcast_constant_int_to_objc_bool : Warning< - "implicit conversion from constant value %0 to BOOL; " - "the only well defined values for BOOL are YES and NO">, +def warn_impcast_constant_value_to_objc_bool : Warning< + "implicit conversion from constant value %0 to 'BOOL'; " + "the only well defined values for 'BOOL' are YES and NO">, InGroup; def warn_impcast_fixed_point_range : Warning< @@ -3334,6 +3360,12 @@ def warn_impcast_literal_float_to_integer_out_of_range : Warning< def warn_impcast_float_integer : Warning< "implicit conversion turns floating-point number into integer: %0 to %1">, InGroup, DefaultIgnore; +def warn_impcast_float_to_objc_signed_char_bool : Warning< + "implicit conversion from floating-point type %0 to 'BOOL'">, + InGroup; +def warn_impcast_int_to_objc_signed_char_bool : Warning< + "implicit conversion from integral type %0 to 'BOOL'">, + InGroup, DefaultIgnore; // Implicit int -> float conversion precision loss warnings. def warn_impcast_integer_float_precision : Warning< @@ -5428,9 +5460,6 @@ def err_arc_mismatched_cast : Error< " to %3 is disallowed with ARC">; def err_arc_nolifetime_behavior : Error< "explicit ownership qualifier on cast result has no effect">; -def err_arc_objc_object_in_tag : Error< - "ARC forbids %select{Objective-C objects|blocks}0 in " - "%select{struct|interface|union|<>|enum}1">; def err_arc_objc_property_default_assign_on_object : Error< "ARC forbids synthesizing a property of an Objective-C object " "with unspecified ownership or storage attribute">; @@ -5723,6 +5752,12 @@ def warn_precedence_conditional : Warning< def note_precedence_conditional_first : Note< "place parentheses around the '?:' expression to evaluate it first">; +def warn_enum_constant_in_bool_context : Warning< + "converting the enum constant to a boolean">, + InGroup, DefaultIgnore; +def warn_left_shift_in_bool_context : Warning< + "converting the result of '<<' to a boolean; did you mean '(%0) != 0'?">, + InGroup, DefaultIgnore; def warn_logical_instead_of_bitwise : Warning< "use of logical '%0' with constant operand">, InGroup>; @@ -6135,8 +6170,8 @@ def warn_tautological_constant_compare : Warning< "%select{%1|%3}0 is always %4">, InGroup, DefaultIgnore; def warn_tautological_compare_objc_bool : Warning< - "result of comparison of constant %0 with expression of type BOOL" - " is always %1, as the only well defined values for BOOL are YES and NO">, + "result of comparison of constant %0 with expression of type 'BOOL'" + " is always %1, as the only well defined values for 'BOOL' are YES and NO">, InGroup; def warn_mixed_sign_comparison : Warning< @@ -6148,10 +6183,22 @@ def warn_out_of_range_compare : Warning< InGroup; def warn_tautological_bool_compare : Warning, InGroup; +def warn_integer_constants_in_conditional_always_true : Warning< + "converting the result of '?:' with integer constants to a boolean always " + "evaluates to 'true'">, + InGroup; +def warn_left_shift_always : Warning< + "converting the result of '<<' to a boolean always evaluates " + "to %select{false|true}0">, + InGroup; def warn_comparison_of_mixed_enum_types : Warning< "comparison of two values with different enumeration types" "%diff{ ($ and $)|}0,1">, InGroup; +def warn_conditional_mixed_enum_types : Warning< + "enumeration type mismatch in conditional expression" + "%diff{ ($ and $)|}0,1">, + InGroup, DefaultIgnore; def warn_comparison_of_mixed_enum_types_switch : Warning< "comparison of two values with different enumeration types in switch statement" "%diff{ ($ and $)|}0,1">, @@ -7621,8 +7668,6 @@ let CategoryName = "Inline Assembly Issue" in { "invalid lvalue in asm input for constraint '%0'">; def err_asm_invalid_input_constraint : Error< "invalid input constraint '%0' in asm">; - def err_asm_immediate_expected : Error<"constraint '%0' expects " - "an integer constant expression">; def err_asm_tying_incompatible_types : Error< "unsupported inline asm: input with type " "%diff{$ matching output with type $|}0,1">; @@ -8151,6 +8196,9 @@ def warn_printf_invalid_objc_flag: Warning< def warn_scanf_scanlist_incomplete : Warning< "no closing ']' for '%%[' in scanf format string">, InGroup; +def warn_format_bool_as_character : Warning< + "using '%0' format specifier, but argument has boolean value">, + InGroup; def note_format_string_defined : Note<"format string is defined here">; def note_format_fix_specifier : Note<"did you mean to use '%0'?">; def note_printf_c_str: Note<"did you mean to call the %0 method?">; @@ -9239,10 +9287,10 @@ def err_omp_single_copyprivate_with_nowait : Error< "the 'copyprivate' clause must not be used with the 'nowait' clause">; def note_omp_nowait_clause_here : Note< "'nowait' clause is here">; -def err_omp_single_decl_in_declare_simd : Error< - "single declaration is expected after 'declare simd' directive">; +def err_omp_single_decl_in_declare_simd_variant : Error< + "single declaration is expected after 'declare %select{simd|variant}0' directive">; def err_omp_function_expected : Error< - "'#pragma omp declare simd' can only be applied to functions">; + "'#pragma omp declare %select{simd|variant}0' can only be applied to functions">; def err_omp_wrong_cancel_region : Error< "one of 'for', 'parallel', 'sections' or 'taskgroup' is expected">; def err_omp_parent_cancel_region_nowait : Error< @@ -9434,6 +9482,34 @@ def note_omp_marked_device_type_here : Note<"marked as 'device_type(%0)' here">; def warn_omp_declare_target_after_first_use : Warning< "declaration marked as declare target after first use, it may lead to incorrect results">, InGroup; +def err_omp_declare_variant_incompat_attributes : Error< + "'#pragma omp declare variant' is not compatible with any target-specific attributes">; +def warn_omp_declare_variant_after_used : Warning< + "'#pragma omp declare variant' cannot be applied for function after first " + "usage; the original function might be used">, InGroup; +def warn_omp_declare_variant_after_emitted : Warning< + "'#pragma omp declare variant' cannot be applied to the function that was defined already;" + " the original function might be used">, InGroup; +def err_omp_declare_variant_noproto : Error< + "function with '#pragma omp declare variant' must have a prototype">; +def note_omp_declare_variant_specified_here : Note< + "'#pragma omp declare variant' for function specified here">; +def err_omp_declare_variant_doesnt_support : Error< + "'#pragma omp declare variant' does not " + "support %select{function templates|virtual functions|" + "deduced return types|constructors|destructors|deleted functions|" + "defaulted functions|constexpr functions|consteval function}0">; +def err_omp_declare_variant_diff : Error< + "function with '#pragma omp declare variant' has a different %select{calling convention" + "|return type|constexpr specification|inline specification|storage class|" + "linkage}0">; +def err_omp_declare_variant_incompat_types : Error< + "variant in '#pragma omp declare variant' with type %0 is incompatible with type %1" + >; +def warn_omp_declare_variant_marked_as_declare_variant : Warning< + "variant function in '#pragma omp declare variant' is itself marked as '#pragma omp declare variant'" + >, InGroup; +def note_omp_marked_declare_variant_here : Note<"marked as 'declare variant' here">; } // end of OpenMP category let CategoryName = "Related Result Type Issue" in { @@ -9883,8 +9959,6 @@ def err_memtag_arg_must_be_pointer : Error< "%0 argument of MTE builtin function must be a pointer (%1 invalid)">; def err_memtag_arg_must_be_integer : Error< "%0 argument of MTE builtin function must be an integer type (%1 invalid)">; -def err_memtag_arg_must_be_unsigned : Error< - "%0 argument of MTE builtin function must be an unsigned integer type (%1 invalid)">; def warn_dereference_of_noderef_type : Warning< "dereferencing %0; was declared with a 'noderef' type">, InGroup; diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index 57132f5c0ffb9..28eb694ba9a89 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -39,6 +39,8 @@ FEATURE(address_sanitizer, LangOpts.Sanitize.hasOneOf(SanitizerKind::Address | SanitizerKind::KernelAddress)) +FEATURE(leak_sanitizer, + LangOpts.Sanitize.has(SanitizerKind::Leak)) FEATURE(hwaddress_sanitizer, LangOpts.Sanitize.hasOneOf(SanitizerKind::HWAddress | SanitizerKind::KernelHWAddress)) diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index b39ab50a89d5a..eca8b7d0d822d 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -230,6 +230,8 @@ LANGOPT(SYCLIsHost , 1, 0, "SYCL host compilation") LANGOPT(SYCLAllowFuncPtr , 1, 0, "Allow function pointers in SYCL device code") LANGOPT(SYCLUnnamedLambda , 1, 0, "Allow unnamed lambda SYCL kernels") +LANGOPT(HIPUseNewLaunchAPI, 1, 0, "Use new kernel launching API for HIP") + LANGOPT(SizedDeallocation , 1, 0, "sized deallocation") LANGOPT(AlignedAllocation , 1, 0, "aligned allocation") LANGOPT(AlignedAllocationUnavailable, 1, 0, "aligned allocation functions are unavailable") diff --git a/clang/include/clang/Basic/OpenCLOptions.h b/clang/include/clang/Basic/OpenCLOptions.h index 47310da1d6d95..15661154eab54 100644 --- a/clang/include/clang/Basic/OpenCLOptions.h +++ b/clang/include/clang/Basic/OpenCLOptions.h @@ -42,7 +42,7 @@ class OpenCLOptions { // Is supported as either an extension or an (optional) core feature for // OpenCL version \p CLVer. - bool isSupported(llvm::StringRef Ext, LangOptions LO) const { + bool isSupported(llvm::StringRef Ext, const LangOptions &LO) const { // In C++ mode all extensions should work at least as in v2.0. auto CLVer = LO.OpenCLCPlusPlus ? 200 : LO.OpenCLVersion; auto I = OptMap.find(Ext)->getValue(); @@ -51,7 +51,7 @@ class OpenCLOptions { // Is supported (optional) OpenCL core features for OpenCL version \p CLVer. // For supported extension, return false. - bool isSupportedCore(llvm::StringRef Ext, LangOptions LO) const { + bool isSupportedCore(llvm::StringRef Ext, const LangOptions &LO) const { // In C++ mode all extensions should work at least as in v2.0. auto CLVer = LO.OpenCLCPlusPlus ? 200 : LO.OpenCLVersion; auto I = OptMap.find(Ext)->getValue(); @@ -60,7 +60,7 @@ class OpenCLOptions { // Is supported OpenCL extension for OpenCL version \p CLVer. // For supported (optional) core feature, return false. - bool isSupportedExtension(llvm::StringRef Ext, LangOptions LO) const { + bool isSupportedExtension(llvm::StringRef Ext, const LangOptions &LO) const { // In C++ mode all extensions should work at least as in v2.0. auto CLVer = LO.OpenCLCPlusPlus ? 200 : LO.OpenCLVersion; auto I = OptMap.find(Ext)->getValue(); diff --git a/clang/include/clang/Basic/OpenMPKinds.def b/clang/include/clang/Basic/OpenMPKinds.def index 9a9592a7627b4..e8a40de3e3cdb 100644 --- a/clang/include/clang/Basic/OpenMPKinds.def +++ b/clang/include/clang/Basic/OpenMPKinds.def @@ -194,6 +194,12 @@ #ifndef OPENMP_DEVICE_TYPE_KIND #define OPENMP_DEVICE_TYPE_KIND(Name) #endif +#ifndef OPENMP_DECLARE_VARIANT_CLAUSE +#define OPENMP_DECLARE_VARIANT_CLAUSE(Name) +#endif +#ifndef OPENMP_MATCH_KIND +#define OPENMP_MATCH_KIND(Name) +#endif // OpenMP directives. OPENMP_DIRECTIVE(threadprivate) @@ -251,6 +257,7 @@ OPENMP_DIRECTIVE_EXT(target_teams_distribute_parallel_for, "target teams distrib OPENMP_DIRECTIVE_EXT(target_teams_distribute_parallel_for_simd, "target teams distribute parallel for simd") OPENMP_DIRECTIVE_EXT(target_teams_distribute_simd, "target teams distribute simd") OPENMP_DIRECTIVE(allocate) +OPENMP_DIRECTIVE_EXT(declare_variant, "declare variant") // OpenMP clauses. OPENMP_CLAUSE(allocator, OMPAllocatorClause) @@ -958,6 +965,15 @@ OPENMP_DEVICE_TYPE_KIND(host) OPENMP_DEVICE_TYPE_KIND(nohost) OPENMP_DEVICE_TYPE_KIND(any) +// Clauses allowed for OpenMP directive 'declare variant'. +OPENMP_DECLARE_VARIANT_CLAUSE(match) + +// Context selectors for 'match' clause. +// TODO: add other context selectors. +OPENMP_MATCH_KIND(implementation) + +#undef OPENMP_MATCH_KIND +#undef OPENMP_DECLARE_VARIANT_CLAUSE #undef OPENMP_DEVICE_TYPE_KIND #undef OPENMP_ALLOCATE_CLAUSE #undef OPENMP_DECLARE_MAPPER_CLAUSE diff --git a/clang/include/clang/Basic/OpenMPKinds.h b/clang/include/clang/Basic/OpenMPKinds.h index 003737b0ab783..fe0ba51becade 100644 --- a/clang/include/clang/Basic/OpenMPKinds.h +++ b/clang/include/clang/Basic/OpenMPKinds.h @@ -36,6 +36,7 @@ enum OpenMPClauseKind { OMPC_threadprivate, OMPC_uniform, OMPC_device_type, + OMPC_match, OMPC_unknown }; diff --git a/clang/include/clang/Basic/TypeNodes.td b/clang/include/clang/Basic/TypeNodes.td new file mode 100644 index 0000000000000..b2554de24aaf4 --- /dev/null +++ b/clang/include/clang/Basic/TypeNodes.td @@ -0,0 +1,106 @@ +class Type { + bit Abstract = abstract; +} + +class DerivedType : Type { + Type Base = base; +} + +/// A type node that is only used to represent dependent types in C++. For +/// example, DependentTemplateSpecializationType is used to represent types +/// where the base template-id is dependent (such as `T::foo`). Code +/// that only works with non-dependent types can ignore these type nodes. +class AlwaysDependent {} + +/// A type node that is never used to represent a canonical type, which is to +/// say that it always represents some sort of type "sugar" which can +/// (supposedly) be erased without affecting the formal behavior of the +/// language. For example, in standard C/C++, typedefs do not introduce new +/// types and do not affect the semantics of the program. Code that only +/// works with canonical types can ignore these type nodes. +/// +/// Note that this simple story about non-canonical types is not the whole +/// truth. Languages and extensions often have formation rules which differ +/// based on how a type is spelled and which therefore are not consistent +/// with immediately stipping away type sugar. More critically, attributes on +/// typedefs can have semantic impacts in ways that are only reflected in our +/// AST by preserving the typedef sugar; for example, we do not otherwise +/// represent the alignment attribute on typedefs, and so it is necessary to +/// preserve typedef structure into most parts of IR generation. +class NeverCanonical {} + +/// A type node that only represents a canonical type in some dependent cases. +/// For example, `std::vector` (a TemplateSpecializationType) is +/// considered to be a non-canonical representation for the RecordType +/// referencing the concrete ClassTemplateSpecializationDecl; but +/// `std::vector` cannot be resolved to a concrete specialization +/// and so remains canonical. Code which only works with non-dependent +/// canonical types can ignore these nodes. +class NeverCanonicalUnlessDependent {} + +/// A type node which never has component type structure. Some code may be +/// able to operate on leaf types faster than they can on non-leaf types. +/// +/// For example, the function type `void (int)` is not a leaf type because it +/// is structurally composed of component types (`void` and `int`). +/// +/// A struct type is a leaf type because its field types are not part of its +/// type-expression. +/// +/// Nodes like `TypedefType` which are syntactically leaves but can desugar +/// to types that may not be leaves should not declare this. +class LeafType {} + +def BuiltinType : Type, LeafType; +def ComplexType : Type; +def PointerType : Type; +def BlockPointerType : Type; +def ReferenceType : Type<1>; +def LValueReferenceType : DerivedType; +def RValueReferenceType : DerivedType; +def MemberPointerType : Type; +def ArrayType : Type<1>; +def ConstantArrayType : DerivedType; +def IncompleteArrayType : DerivedType; +def VariableArrayType : DerivedType; +def DependentSizedArrayType : DerivedType, AlwaysDependent; +def DependentSizedExtVectorType : Type, AlwaysDependent; +def DependentAddressSpaceType : Type, AlwaysDependent; +def VectorType : Type; +def DependentVectorType : Type, AlwaysDependent; +def ExtVectorType : DerivedType; +def FunctionType : Type<1>; +def FunctionProtoType : DerivedType; +def FunctionNoProtoType : DerivedType; +def UnresolvedUsingType : Type, AlwaysDependent; +def ParenType : Type, NeverCanonical; +def TypedefType : Type, NeverCanonical; +def MacroQualifiedType : Type, NeverCanonical; +def AdjustedType : Type, NeverCanonical; +def DecayedType : DerivedType, NeverCanonical; +def TypeOfExprType : Type, NeverCanonicalUnlessDependent; +def TypeOfType : Type, NeverCanonicalUnlessDependent; +def DecltypeType : Type, NeverCanonicalUnlessDependent; +def UnaryTransformType : Type, NeverCanonicalUnlessDependent; +def TagType : Type<1>; +def RecordType : DerivedType, LeafType; +def EnumType : DerivedType, LeafType; +def ElaboratedType : Type, NeverCanonical; +def AttributedType : Type, NeverCanonical; +def TemplateTypeParmType : Type, AlwaysDependent, LeafType; +def SubstTemplateTypeParmType : Type, NeverCanonical; +def SubstTemplateTypeParmPackType : Type, AlwaysDependent; +def TemplateSpecializationType : Type, NeverCanonicalUnlessDependent; +def DeducedType : Type<1>; +def AutoType : DerivedType; +def DeducedTemplateSpecializationType : DerivedType; +def InjectedClassNameType : Type, AlwaysDependent, LeafType; +def DependentNameType : Type, AlwaysDependent; +def DependentTemplateSpecializationType : Type, AlwaysDependent; +def PackExpansionType : Type, NeverCanonicalUnlessDependent; +def ObjCTypeParamType : Type, NeverCanonical; +def ObjCObjectType : Type; +def ObjCInterfaceType : DerivedType, LeafType; +def ObjCObjectPointerType : Type; +def PipeType : Type; +def AtomicType : Type; diff --git a/clang/include/clang/CodeGen/CGFunctionInfo.h b/clang/include/clang/CodeGen/CGFunctionInfo.h index 1f81072e23d00..5069d9af42a3c 100644 --- a/clang/include/clang/CodeGen/CGFunctionInfo.h +++ b/clang/include/clang/CodeGen/CGFunctionInfo.h @@ -109,14 +109,12 @@ class ABIArgInfo { UnpaddedCoerceAndExpandType = T; } - ABIArgInfo(Kind K) - : TheKind(K), PaddingInReg(false), InReg(false) { - } - public: - ABIArgInfo() + ABIArgInfo(Kind K = Direct) : TypeData(nullptr), PaddingType(nullptr), DirectOffset(0), - TheKind(Direct), PaddingInReg(false), InReg(false) {} + TheKind(K), PaddingInReg(false), InAllocaSRet(false), + IndirectByVal(false), IndirectRealign(false), SRetAfterThis(false), + InReg(false), CanBeFlattened(false), SignExt(false) {} static ABIArgInfo getDirect(llvm::Type *T = nullptr, unsigned Offset = 0, llvm::Type *Padding = nullptr, diff --git a/clang/include/clang/Driver/CC1Options.td b/clang/include/clang/Driver/CC1Options.td index ad6d1f32363dc..35063f8f40b5d 100644 --- a/clang/include/clang/Driver/CC1Options.td +++ b/clang/include/clang/Driver/CC1Options.td @@ -691,7 +691,7 @@ let Flags = [CC1Option, CC1AsOption, NoDriverOption] in { def version : Flag<["-"], "version">, HelpText<"Print the compiler version">; def main_file_name : Separate<["-"], "main-file-name">, - HelpText<"Main file name to use for debug info">; + HelpText<"Main file name to use for debug info and source if missing">; def split_dwarf_output : Separate<["-"], "split-dwarf-output">, HelpText<"File name to use for split dwarf debug info output">; diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 7c3b2de84f601..0d84508947d14 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -621,6 +621,9 @@ def hip_device_lib_EQ : Joined<["--"], "hip-device-lib=">, Group, HelpText<"HIP device library">; def fhip_dump_offload_linker_script : Flag<["-"], "fhip-dump-offload-linker-script">, Group, Flags<[NoArgumentUnused, HelpHidden]>; +def fhip_new_launch_api : Flag<["-"], "fhip-new-launch-api">, + Flags<[CC1Option]>, HelpText<"Use new kernel launching API for HIP.">; +def fno_hip_new_launch_api : Flag<["-"], "fno-hip-new-launch-api">; def libomptarget_nvptx_path_EQ : Joined<["--"], "libomptarget-nvptx-path=">, Group, HelpText<"Path to libomptarget-nvptx libraries">; def dD : Flag<["-"], "dD">, Group, Flags<[CC1Option]>, @@ -2227,6 +2230,12 @@ def msave_restore : Flag<["-"], "msave-restore">, Group, HelpText<"Enable using library calls for save and restore">; def mno_save_restore : Flag<["-"], "mno-save-restore">, Group, HelpText<"Disable using library calls for save and restore">; +def mcmodel_EQ_medlow : Flag<["-"], "mcmodel=medlow">, Group, + Flags<[CC1Option]>, Alias, AliasArgs<["small"]>, + HelpText<"Equivalent to -mcmodel=small, compatible with RISC-V gcc.">; +def mcmodel_EQ_medany : Flag<["-"], "mcmodel=medany">, Group, + Flags<[CC1Option]>, Alias, AliasArgs<["medium"]>, + HelpText<"Equivalent to -mcmodel=medium, compatible with RISC-V gcc.">; def munaligned_access : Flag<["-"], "munaligned-access">, Group, HelpText<"Allow memory accesses to be unaligned (AArch32/AArch64 only)">; @@ -2431,7 +2440,7 @@ def mpie_copy_relocations : Flag<["-"], "mpie-copy-relocations">, Group Flags<[CC1Option]>, HelpText<"Use copy relocations support for PIE builds">; def mno_pie_copy_relocations : Flag<["-"], "mno-pie-copy-relocations">, Group; -def mfentry : Flag<["-"], "mfentry">, HelpText<"Insert calls to fentry at function entry (x86 only)">, +def mfentry : Flag<["-"], "mfentry">, HelpText<"Insert calls to fentry at function entry (x86/SystemZ only)">, Flags<[CC1Option]>, Group; def mips16 : Flag<["-"], "mips16">, Group; def mno_mips16 : Flag<["-"], "mno-mips16">, Group; @@ -2806,7 +2815,6 @@ def _mhwdiv : Separate<["--"], "mhwdiv">, Alias; def _CLASSPATH_EQ : Joined<["--"], "CLASSPATH=">, Alias; def _CLASSPATH : Separate<["--"], "CLASSPATH">, Alias; def _all_warnings : Flag<["--"], "all-warnings">, Alias; -def _analyze_auto : Flag<["--"], "analyze-auto">, Flags<[DriverOption]>; def _analyzer_no_default_checks : Flag<["--"], "analyzer-no-default-checks">, Flags<[DriverOption]>; def _analyzer_output : JoinedOrSeparate<["--"], "analyzer-output">, Flags<[DriverOption]>, HelpText<"Static analyzer report output format (html|plist|plist-multi-file|plist-html|text).">; diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 07b2df912da4f..6389e498e483f 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -733,6 +733,32 @@ struct FormatStyle { /// B /// }; /// \endcode + BS_Whitesmiths, + /// Like ``Allman`` but always indent braces and line up code with braces. + /// \code + /// try + /// { + /// foo(); + /// } + /// catch () + /// { + /// } + /// void foo() { bar(); } + /// class foo + /// { + /// }; + /// if (foo()) + /// { + /// } + /// else + /// { + /// } + /// enum X : int + /// { + /// A, + /// B + /// }; + /// \endcode BS_GNU, /// Like ``Attach``, but break before functions. /// \code @@ -1919,15 +1945,32 @@ struct FormatStyle { /// \endcode bool SpacesInSquareBrackets; - /// Supported language standards. + /// Supported language standards for parsing and formatting C++ constructs. + /// \code + /// Latest: vector> + /// c++03 vs. vector > + /// \endcode + /// + /// The correct way to spell a specific language version is e.g. ``c++11``. + /// The historical aliases ``Cpp03`` and ``Cpp11`` are deprecated. enum LanguageStandard { - /// Use C++03-compatible syntax. + /// c++03: Parse and format as C++03. LS_Cpp03, - /// Use features of C++11, C++14 and C++1z (e.g. ``A>`` instead of - /// ``A >``). + /// c++11: Parse and format as C++11. LS_Cpp11, - /// Automatic detection based on the input. - LS_Auto + /// c++14: Parse and format as C++14. + LS_Cpp14, + /// c++17: Parse and format as C++17. + LS_Cpp17, + /// c++20: Parse and format as C++20. + LS_Cpp20, + /// Latest: Parse and format using the latest supported language version. + /// 'Cpp11' is an alias for LS_Latest for historical reasons. + LS_Latest, + + /// Auto: Automatic detection based on the input. + /// Parse using the latest language version. Format based on detected input. + LS_Auto, }; /// Format compatible with this standard, e.g. use ``A >`` diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index d800ee1289eb2..1bdd2be04c0ef 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -2208,7 +2208,7 @@ class Preprocessor { SourceLocation FilenameLoc, CharSourceRange FilenameRange, const Token &FilenameTok, bool &IsFrameworkFound, bool IsImportDecl, bool &IsMapped, const DirectoryLookup *LookupFrom, - const FileEntry *LookupFromFile, SmallString<128> &NormalizedPath, + const FileEntry *LookupFromFile, StringRef LookupFilename, SmallVectorImpl &RelativePath, SmallVectorImpl &SearchPath, ModuleMap::KnownHeader &SuggestedModule, bool isAngled); diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index bc9686ab350e1..b391eb6fd4ff4 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2843,6 +2843,17 @@ class Parser : public CodeCompletionHandler { DeclGroupPtrTy ParseOMPDeclareSimdClauses(DeclGroupPtrTy Ptr, CachedTokens &Toks, SourceLocation Loc); + /// Parses OpenMP context selectors and calls \p Callback for each + /// successfully parsed context selector. + bool parseOpenMPContextSelectors( + SourceLocation Loc, + llvm::function_ref< + void(SourceRange, const Sema::OpenMPDeclareVariantCtsSelectorData &)> + Callback); + + /// Parse clauses for '#pragma omp declare variant'. + void ParseOMPDeclareVariantClauses(DeclGroupPtrTy Ptr, CachedTokens &Toks, + SourceLocation Loc); /// Parse clauses for '#pragma omp declare target'. DeclGroupPtrTy ParseOMPDeclareTargetClauses(); /// Parse '#pragma omp end declare target'. @@ -2936,7 +2947,8 @@ class Parser : public CodeCompletionHandler { /// Parses simple expression in parens for single-expression clauses of OpenMP /// constructs. /// \param RLoc Returned location of right paren. - ExprResult ParseOpenMPParensExpr(StringRef ClauseName, SourceLocation &RLoc); + ExprResult ParseOpenMPParensExpr(StringRef ClauseName, SourceLocation &RLoc, + bool IsAddressOfOperand = false); /// Data used for parsing list of variables in OpenMP clauses. struct OpenMPVarListDataTy { diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 435af4e8cb1f4..aa5a3d1289f97 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4767,6 +4767,12 @@ class Sema { MultiExprArg ArgExprs, SourceLocation RParenLoc, Expr *ExecConfig = nullptr, bool IsExecConfig = false); + enum class AtomicArgumentOrder { API, AST }; + ExprResult + BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange, + SourceLocation RParenLoc, MultiExprArg Args, + AtomicExpr::AtomicOp Op, + AtomicArgumentOrder ArgOrder = AtomicArgumentOrder::API); ExprResult BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, SourceLocation LParenLoc, ArrayRef Arg, SourceLocation RParenLoc, @@ -9228,7 +9234,39 @@ class Sema { MapT &Map, unsigned Selector = 0, SourceRange SrcRange = SourceRange()); + /// Marks all the functions that might be required for the currently active + /// OpenMP context. + void markOpenMPDeclareVariantFuncsReferenced(SourceLocation Loc, + FunctionDecl *Func, + bool MightBeOdrUse); + public: + /// Struct to store the context selectors info for declare variant directive. + struct OpenMPDeclareVariantCtsSelectorData { + OMPDeclareVariantAttr::CtxSelectorSetType CtxSet = + OMPDeclareVariantAttr::CtxSetUnknown; + OMPDeclareVariantAttr::CtxSelectorType Ctx = + OMPDeclareVariantAttr::CtxUnknown; + StringRef ImplVendor; + ExprResult CtxScore; + explicit OpenMPDeclareVariantCtsSelectorData() = default; + explicit OpenMPDeclareVariantCtsSelectorData( + OMPDeclareVariantAttr::CtxSelectorSetType CtxSet, + OMPDeclareVariantAttr::CtxSelectorType Ctx, StringRef ImplVendor, + ExprResult CtxScore) + : CtxSet(CtxSet), Ctx(Ctx), ImplVendor(ImplVendor), CtxScore(CtxScore) { + } + }; + + /// Checks if the variant/multiversion functions are compatible. + bool areMultiversionVariantFunctionsCompatible( + const FunctionDecl *OldFD, const FunctionDecl *NewFD, + const PartialDiagnostic &NoProtoDiagID, + const PartialDiagnosticAt &NoteCausedDiagIDAt, + const PartialDiagnosticAt &NoSupportDiagIDAt, + const PartialDiagnosticAt &DiffDiagIDAt, bool TemplatesSupported, + bool ConstexprSupported); + /// Function tries to capture lambda's captured variables in the OpenMP region /// before the original lambda is captured. void tryCaptureOpenMPLambdas(ValueDecl *V); @@ -9653,6 +9691,29 @@ class Sema { ArrayRef Alignments, ArrayRef Linears, ArrayRef LinModifiers, ArrayRef Steps, SourceRange SR); + /// Checks '\#pragma omp declare variant' variant function and original + /// functions after parsing of the associated method/function. + /// \param DG Function declaration to which declare variant directive is + /// applied to. + /// \param VariantRef Expression that references the variant function, which + /// must be used instead of the original one, specified in \p DG. + /// \returns None, if the function/variant function are not compatible with + /// the pragma, pair of original function/variant ref expression otherwise. + Optional> checkOpenMPDeclareVariantFunction( + DeclGroupPtrTy DG, Expr *VariantRef, SourceRange SR); + + /// Called on well-formed '\#pragma omp declare variant' after parsing of + /// the associated method/function. + /// \param FD Function declaration to which declare variant directive is + /// applied to. + /// \param VariantRef Expression that references the variant function, which + /// must be used instead of the original one, specified in \p DG. + /// \param Data Set of context-specific data for the specified context + /// selector. + void ActOnOpenMPDeclareVariantDirective( + FunctionDecl *FD, Expr *VariantRef, SourceRange SR, + const Sema::OpenMPDeclareVariantCtsSelectorData &Data); + OMPClause *ActOnOpenMPSingleExprClause(OpenMPClauseKind Kind, Expr *Expr, SourceLocation StartLoc, diff --git a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h index 04d611a94a7f5..ce16095e10c0b 100644 --- a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -278,13 +278,13 @@ class AnalyzerOptions : public RefCountedBase { // Create an array of all -analyzer-config command line options. Sort it in // the constructor. - std::vector AnalyzerConfigCmdFlags = { + std::vector AnalyzerConfigCmdFlags = { #define ANALYZER_OPTION_DEPENDS_ON_USER_MODE(TYPE, NAME, CMDFLAG, DESC, \ SHALLOW_VAL, DEEP_VAL) \ ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, SHALLOW_VAL) #define ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL) \ - CMDFLAG, + llvm::StringLiteral(CMDFLAG), #include "clang/StaticAnalyzer/Core/AnalyzerOptions.def" #undef ANALYZER_OPTION @@ -415,9 +415,10 @@ inline UserModeKind AnalyzerOptions::getUserMode() const { inline std::vector AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental) { - static const StringRef StaticAnalyzerCheckerNames[] = { + static constexpr llvm::StringLiteral StaticAnalyzerCheckerNames[] = { #define GET_CHECKERS -#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) FULLNAME, +#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \ + llvm::StringLiteral(FULLNAME), #include "clang/StaticAnalyzer/Checkers/Checkers.inc" #undef CHECKER #undef GET_CHECKERS @@ -433,9 +434,9 @@ AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental) { inline std::vector AnalyzerOptions::getRegisteredPackages(bool IncludeExperimental) { - static const StringRef StaticAnalyzerPackageNames[] = { + static constexpr llvm::StringLiteral StaticAnalyzerPackageNames[] = { #define GET_PACKAGES -#define PACKAGE(FULLNAME) FULLNAME, +#define PACKAGE(FULLNAME) llvm::StringLiteral(FULLNAME), #include "clang/StaticAnalyzer/Checkers/Checkers.inc" #undef PACKAGE #undef GET_PACKAGES diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index 8d1a672bc6129..fc1cc91388266 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -347,7 +347,7 @@ class CallEvent { ProgramStateRef invalidateRegions(unsigned BlockCount, ProgramStateRef Orig = nullptr) const; - using FrameBindingTy = std::pair; + using FrameBindingTy = std::pair; using BindingsTy = SmallVectorImpl; /// Populates the given SmallVector with the bindings in the callee's stack diff --git a/clang/include/clang/Tooling/ArgumentsAdjusters.h b/clang/include/clang/Tooling/ArgumentsAdjusters.h index bf0886034324a..c48a8725aae90 100644 --- a/clang/include/clang/Tooling/ArgumentsAdjusters.h +++ b/clang/include/clang/Tooling/ArgumentsAdjusters.h @@ -43,6 +43,10 @@ ArgumentsAdjuster getClangSyntaxOnlyAdjuster(); /// arguments. ArgumentsAdjuster getClangStripOutputAdjuster(); +/// Gets an argument adjuster which removes command line arguments related to +/// diagnostic serialization. +ArgumentsAdjuster getClangStripSerializeDiagnosticAdjuster(); + /// Gets an argument adjuster which removes dependency-file /// related command line arguments. ArgumentsAdjuster getClangStripDependencyFileAdjuster(); diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h index 94b370520093b..1d0d26589e04d 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h @@ -56,6 +56,9 @@ class CachedFileSystemEntry { /// \returns True if the entry is valid. bool isValid() const { return !MaybeStat || MaybeStat->isStatusKnown(); } + /// \returns True if the current entry points to a directory. + bool isDirectory() const { return MaybeStat && MaybeStat->isDirectory(); } + /// \returns The error or the file's contents. llvm::ErrorOr getContents() const { if (!MaybeStat) diff --git a/clang/include/clang/Tooling/Inclusions/HeaderIncludes.h b/clang/include/clang/Tooling/Inclusions/HeaderIncludes.h index ec6f0ea45ffed..6e6d6d8fb024b 100644 --- a/clang/include/clang/Tooling/Inclusions/HeaderIncludes.h +++ b/clang/include/clang/Tooling/Inclusions/HeaderIncludes.h @@ -32,6 +32,7 @@ class IncludeCategoryManager { /// 0. Otherwise, returns the priority of the matching category or INT_MAX. /// NOTE: this API is not thread-safe! int getIncludePriority(StringRef IncludeName, bool CheckMainHeader) const; + int getSortIncludePriority(StringRef IncludeName, bool CheckMainHeader) const; private: bool isMainHeader(StringRef IncludeName) const; diff --git a/clang/include/clang/Tooling/Inclusions/IncludeStyle.h b/clang/include/clang/Tooling/Inclusions/IncludeStyle.h index a0f236e6fc461..266763a5b1bd0 100644 --- a/clang/include/clang/Tooling/Inclusions/IncludeStyle.h +++ b/clang/include/clang/Tooling/Inclusions/IncludeStyle.h @@ -58,6 +58,8 @@ struct IncludeStyle { std::string Regex; /// The priority to assign to this category. int Priority; + /// The custom priority to sort before grouping. + int SortPriority; bool operator==(const IncludeCategory &Other) const { return Regex == Other.Regex && Priority == Other.Priority; } diff --git a/clang/include/clang/Tooling/Refactoring/MatchConsumer.h b/clang/include/clang/Tooling/Refactoring/MatchConsumer.h new file mode 100644 index 0000000000000..d516550d0f02b --- /dev/null +++ b/clang/include/clang/Tooling/Refactoring/MatchConsumer.h @@ -0,0 +1,58 @@ +//===--- MatchConsumer.h - MatchConsumer abstraction ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file This file defines the *MatchConsumer* abstraction: a computation over +/// match results, specifically the `ast_matchers::MatchFinder::MatchResult` +/// class. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_MATCH_CONSUMER_H_ +#define LLVM_CLANG_TOOLING_REFACTOR_MATCH_CONSUMER_H_ + +#include "clang/AST/ASTTypeTraits.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" + +namespace clang { +namespace tooling { + +/// A failable computation over nodes bound by AST matchers. +/// +/// The computation should report any errors though its return value (rather +/// than terminating the program) to enable usage in interactive scenarios like +/// clang-query. +/// +/// This is a central abstraction of the Transformer framework. +template +using MatchConsumer = + std::function(const ast_matchers::MatchFinder::MatchResult &)>; + +/// Creates an error that signals that a `MatchConsumer` expected a certain node +/// to be bound by AST matchers, but it was not actually bound. +inline llvm::Error notBoundError(llvm::StringRef Id) { + return llvm::make_error(llvm::errc::invalid_argument, + "Id not bound: " + Id); +} + +/// Chooses between the two consumers, based on whether \p ID is bound in the +/// match. +template +MatchConsumer ifBound(std::string ID, MatchConsumer TrueC, + MatchConsumer FalseC) { + return [=](const ast_matchers::MatchFinder::MatchResult &Result) { + auto &Map = Result.Nodes.getMap(); + return (Map.find(ID) != Map.end() ? TrueC : FalseC)(Result); + }; +} + +} // namespace tooling +} // namespace clang +#endif // LLVM_CLANG_TOOLING_REFACTOR_MATCH_CONSUMER_H_ diff --git a/clang/include/clang/Tooling/Refactoring/RangeSelector.h b/clang/include/clang/Tooling/Refactoring/RangeSelector.h index b117e4d82ad46..d5b5c8fbd8a5b 100644 --- a/clang/include/clang/Tooling/Refactoring/RangeSelector.h +++ b/clang/include/clang/Tooling/Refactoring/RangeSelector.h @@ -17,14 +17,14 @@ #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Basic/SourceLocation.h" +#include "clang/Tooling/Refactoring/MatchConsumer.h" #include "llvm/Support/Error.h" #include #include namespace clang { namespace tooling { -using RangeSelector = std::function( - const ast_matchers::MatchFinder::MatchResult &)>; +using RangeSelector = MatchConsumer; inline RangeSelector charRange(CharSourceRange R) { return [R](const ast_matchers::MatchFinder::MatchResult &) @@ -79,6 +79,10 @@ RangeSelector statements(std::string ID); // (all source between the braces). RangeSelector initListElements(std::string ID); +/// Given an \IfStmt (bound to \p ID), selects the range of the else branch, +/// starting from the \c else keyword. +RangeSelector elseBranch(std::string ID); + /// Selects the range from which `S` was expanded (possibly along with other /// source), if `S` is an expansion, and `S` itself, otherwise. Corresponds to /// `SourceManager::getExpansionRange`. diff --git a/clang/include/clang/Tooling/Refactoring/RecursiveSymbolVisitor.h b/clang/include/clang/Tooling/Refactoring/RecursiveSymbolVisitor.h index 41a448f035a40..c0f995d85c14c 100644 --- a/clang/include/clang/Tooling/Refactoring/RecursiveSymbolVisitor.h +++ b/clang/include/clang/Tooling/Refactoring/RecursiveSymbolVisitor.h @@ -98,7 +98,17 @@ class RecursiveSymbolVisitor TypeBeginLoc, TypeEndLoc)) return false; } - return visit(Loc.getType()->getAsCXXRecordDecl(), TypeBeginLoc, TypeEndLoc); + if (const Type *TP = Loc.getTypePtr()) { + if (TP->getTypeClass() == clang::Type::Record) + return visit(TP->getAsCXXRecordDecl(), TypeBeginLoc, TypeEndLoc); + } + return true; + } + + bool VisitTypedefTypeLoc(TypedefTypeLoc TL) { + const SourceLocation TypeEndLoc = + Lexer::getLocForEndOfToken(TL.getBeginLoc(), 0, SM, LangOpts); + return visit(TL.getTypedefNameDecl(), TL.getBeginLoc(), TypeEndLoc); } bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) { @@ -122,8 +132,7 @@ class RecursiveSymbolVisitor ND, SourceRange(BeginLoc, EndLoc)); } bool visit(const NamedDecl *ND, SourceLocation Loc) { - return visit(ND, Loc, - Loc.getLocWithOffset(ND->getNameAsString().length() - 1)); + return visit(ND, Loc, Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts)); } }; diff --git a/clang/include/clang/Tooling/Refactoring/SourceCodeBuilders.h b/clang/include/clang/Tooling/Refactoring/SourceCodeBuilders.h new file mode 100644 index 0000000000000..797046f3ecffb --- /dev/null +++ b/clang/include/clang/Tooling/Refactoring/SourceCodeBuilders.h @@ -0,0 +1,86 @@ +//===--- SourceCodeBuilders.h - Source-code building facilities -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file collects facilities for generating source code strings. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_SOURCE_CODE_BUILDERS_H_ +#define LLVM_CLANG_TOOLING_REFACTOR_SOURCE_CODE_BUILDERS_H_ + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include + +namespace clang { +namespace tooling { + +/// \name Code analysis utilities. +/// @{ +/// Ignores implicit object-construction expressions in addition to the normal +/// implicit expressions that are ignored. +const Expr *reallyIgnoreImplicit(const Expr &E); + +/// Determines whether printing this expression in *any* expression requires +/// parentheses to preserve its meaning. This analyses is necessarily +/// conservative because it lacks information about the target context. +bool mayEverNeedParens(const Expr &E); + +/// Determines whether printing this expression to the left of a dot or arrow +/// operator requires a parentheses to preserve its meaning. Given that +/// dot/arrow are (effectively) the highest precedence, this is equivalent to +/// asking whether it ever needs parens. +inline bool needParensBeforeDotOrArrow(const Expr &E) { + return mayEverNeedParens(E); +} + +/// Determines whether printing this expression to the right of a unary operator +/// requires a parentheses to preserve its meaning. +bool needParensAfterUnaryOperator(const Expr &E); +/// @} + +/// \name Basic code-string generation utilities. +/// @{ + +/// Builds source for an expression, adding parens if needed for unambiguous +/// parsing. +llvm::Optional buildParens(const Expr &E, + const ASTContext &Context); + +/// Builds idiomatic source for the dereferencing of `E`: prefix with `*` but +/// simplify when it already begins with `&`. \returns empty string on failure. +llvm::Optional buildDereference(const Expr &E, + const ASTContext &Context); + +/// Builds idiomatic source for taking the address of `E`: prefix with `&` but +/// simplify when it already begins with `*`. \returns empty string on failure. +llvm::Optional buildAddressOf(const Expr &E, + const ASTContext &Context); + +/// Adds a dot to the end of the given expression, but adds parentheses when +/// needed by the syntax, and simplifies to `->` when possible, e.g.: +/// +/// `x` becomes `x.` +/// `*a` becomes `a->` +/// `a+b` becomes `(a+b).` +llvm::Optional buildDot(const Expr &E, const ASTContext &Context); + +/// Adds an arrow to the end of the given expression, but adds parentheses +/// when needed by the syntax, and simplifies to `.` when possible, e.g.: +/// +/// `x` becomes `x->` +/// `&a` becomes `a.` +/// `a+b` becomes `(a+b)->` +llvm::Optional buildArrow(const Expr &E, + const ASTContext &Context); +/// @} + +} // namespace tooling +} // namespace clang +#endif // LLVM_CLANG_TOOLING_REFACTOR_SOURCE_CODE_BUILDERS_H_ diff --git a/clang/include/clang/Tooling/Refactoring/Stencil.h b/clang/include/clang/Tooling/Refactoring/Stencil.h index e57a576e55755..3bd66e578b857 100644 --- a/clang/include/clang/Tooling/Refactoring/Stencil.h +++ b/clang/include/clang/Tooling/Refactoring/Stencil.h @@ -23,6 +23,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/ASTTypeTraits.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Tooling/Refactoring/MatchConsumer.h" #include "clang/Tooling/Refactoring/RangeSelector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" @@ -153,16 +154,32 @@ inline StencilPart node(llvm::StringRef Id) { return selection(tooling::node(Id)); } -/// Variant of \c node() that identifies the node as a statement, for purposes -/// of deciding whether to include any trailing semicolon. Only relevant for -/// Expr nodes, which, by default, are *not* considered as statements. -/// \returns the source corresponding to the identified node, considered as a -/// statement. -/// FIXME: Deprecated. Write `selection(statement(Id))` instead. -inline StencilPart sNode(llvm::StringRef Id) { - return selection(tooling::statement(Id)); +/// Constructs a `MemberExpr` that accesses the named member (\p Member) of the +/// object bound to \p BaseId. The access is constructed idiomatically: if \p +/// BaseId is bound to `e` and \p Member identifies member `m`, then returns +/// `e->m`, when e is a pointer, `e2->m` when e = `*e2` and `e.m` otherwise. +/// Additionally, `e` is wrapped in parentheses, if needed. +StencilPart access(llvm::StringRef BaseId, StencilPart Member); +inline StencilPart access(llvm::StringRef BaseId, llvm::StringRef Member) { + return access(BaseId, text(Member)); } +/// Chooses between the two stencil parts, based on whether \p ID is bound in +/// the match. +StencilPart ifBound(llvm::StringRef Id, StencilPart TruePart, + StencilPart FalsePart); + +/// Chooses between the two strings, based on whether \p ID is bound in the +/// match. +inline StencilPart ifBound(llvm::StringRef Id, llvm::StringRef TrueText, + llvm::StringRef FalseText) { + return ifBound(Id, text(TrueText), text(FalseText)); +} + +/// Wraps a MatchConsumer in a StencilPart, so that it can be used in a Stencil. +/// This supports user-defined extensions to the Stencil language. +StencilPart run(MatchConsumer C); + /// For debug use only; semantics are not guaranteed. /// /// \returns the string resulting from calling the node's print() method. diff --git a/clang/include/clang/Tooling/Refactoring/Transformer.h b/clang/include/clang/Tooling/Refactoring/Transformer.h index d5d4adf1f0e89..0971cc3e66793 100644 --- a/clang/include/clang/Tooling/Refactoring/Transformer.h +++ b/clang/include/clang/Tooling/Refactoring/Transformer.h @@ -19,6 +19,7 @@ #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/ASTMatchers/ASTMatchersInternal.h" #include "clang/Tooling/Refactoring/AtomicChange.h" +#include "clang/Tooling/Refactoring/MatchConsumer.h" #include "clang/Tooling/Refactoring/RangeSelector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" @@ -32,11 +33,7 @@ namespace clang { namespace tooling { -// Note that \p TextGenerator is allowed to fail, e.g. when trying to access a -// matched node that was not bound. Allowing this to fail simplifies error -// handling for interactive tools like clang-query. -using TextGenerator = std::function( - const ast_matchers::MatchFinder::MatchResult &)>; +using TextGenerator = MatchConsumer; /// Wraps a string as a TextGenerator. inline TextGenerator text(std::string M) { @@ -254,6 +251,12 @@ ast_matchers::internal::DynTypedMatcher buildMatcher(const RewriteRule &Rule); std::vector buildMatchers(const RewriteRule &Rule); +/// Gets the beginning location of the source matched by a rewrite rule. If the +/// match occurs within a macro expansion, returns the beginning of the +/// expansion point. `Result` must come from the matching of a rewrite rule. +SourceLocation +getRuleMatchLoc(const ast_matchers::MatchFinder::MatchResult &Result); + /// Returns the \c Case of \c Rule that was selected in the match result. /// Assumes a matcher built with \c buildMatcher. const RewriteRule::Case & diff --git a/clang/include/clang/module.modulemap b/clang/include/clang/module.modulemap index fc4a2de2f99e3..f7bf482fbbeaf 100644 --- a/clang/include/clang/module.modulemap +++ b/clang/include/clang/module.modulemap @@ -20,7 +20,6 @@ module Clang_AST { textual header "AST/BuiltinTypes.def" textual header "AST/OperationKinds.def" textual header "AST/TypeLocNodes.def" - textual header "AST/TypeNodes.def" module * { export * } } diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp index 1993bba9bd1a1..f29d6f5554c54 100644 --- a/clang/lib/AST/APValue.cpp +++ b/clang/lib/AST/APValue.cpp @@ -42,6 +42,14 @@ APValue::LValueBase::LValueBase(const ValueDecl *P, unsigned I, unsigned V) APValue::LValueBase::LValueBase(const Expr *P, unsigned I, unsigned V) : Ptr(P), Local{I, V} {} +APValue::LValueBase APValue::LValueBase::getDynamicAlloc(DynamicAllocLValue LV, + QualType Type) { + LValueBase Base; + Base.Ptr = LV; + Base.DynamicAllocType = Type.getAsOpaquePtr(); + return Base; +} + APValue::LValueBase APValue::LValueBase::getTypeInfo(TypeInfoLValue LV, QualType TypeInfo) { LValueBase Base; @@ -51,11 +59,12 @@ APValue::LValueBase APValue::LValueBase::getTypeInfo(TypeInfoLValue LV, } unsigned APValue::LValueBase::getCallIndex() const { - return is() ? 0 : Local.CallIndex; + return (is() || is()) ? 0 + : Local.CallIndex; } unsigned APValue::LValueBase::getVersion() const { - return is() ? 0 : Local.Version; + return (is() || is()) ? 0 : Local.Version; } QualType APValue::LValueBase::getTypeInfoType() const { @@ -63,6 +72,11 @@ QualType APValue::LValueBase::getTypeInfoType() const { return QualType::getFromOpaquePtr(TypeInfoType); } +QualType APValue::LValueBase::getDynamicAllocType() const { + assert(is() && "not a dynamic allocation lvalue"); + return QualType::getFromOpaquePtr(DynamicAllocType); +} + namespace clang { bool operator==(const APValue::LValueBase &LHS, const APValue::LValueBase &RHS) { @@ -111,7 +125,7 @@ llvm::DenseMapInfo::getTombstoneKey() { namespace clang { llvm::hash_code hash_value(const APValue::LValueBase &Base) { - if (Base.is()) + if (Base.is() || Base.is()) return llvm::hash_value(Base.getOpaqueValue()); return llvm::hash_combine(Base.getOpaqueValue(), Base.getCallIndex(), Base.getVersion()); @@ -479,7 +493,7 @@ void APValue::printPretty(raw_ostream &Out, const ASTContext &Ctx, return; case APValue::Vector: { Out << '{'; - QualType ElemTy = Ty->getAs()->getElementType(); + QualType ElemTy = Ty->castAs()->getElementType(); getVectorElt(0).printPretty(Out, Ctx, ElemTy); for (unsigned i = 1; i != getVectorLength(); ++i) { Out << ", "; @@ -528,13 +542,18 @@ void APValue::printPretty(raw_ostream &Out, const ASTContext &Ctx, S = CharUnits::One(); } Out << '&'; - } else if (!IsReference) + } else if (!IsReference) { Out << '&'; + } if (const ValueDecl *VD = Base.dyn_cast()) Out << *VD; else if (TypeInfoLValue TI = Base.dyn_cast()) { TI.print(Out, Ctx.getPrintingPolicy()); + } else if (DynamicAllocLValue DA = Base.dyn_cast()) { + Out << "{*new " + << Base.getDynamicAllocType().stream(Ctx.getPrintingPolicy()) << "#" + << DA.getIndex() << "}"; } else { assert(Base.get() != nullptr && "Expecting non-null Expr"); @@ -563,10 +582,17 @@ void APValue::printPretty(raw_ostream &Out, const ASTContext &Ctx, } else if (TypeInfoLValue TI = Base.dyn_cast()) { TI.print(Out, Ctx.getPrintingPolicy()); ElemTy = Base.getTypeInfoType(); + } else if (DynamicAllocLValue DA = Base.dyn_cast()) { + Out << "{*new " + << Base.getDynamicAllocType().stream(Ctx.getPrintingPolicy()) << "#" + << DA.getIndex() << "}"; + ElemTy = Base.getDynamicAllocType(); } else { const Expr *E = Base.get(); assert(E != nullptr && "Expecting non-null Expr"); E->printPretty(Out, nullptr, Ctx.getPrintingPolicy()); + // FIXME: This is wrong if E is a MaterializeTemporaryExpr with an lvalue + // adjustment. ElemTy = E->getType(); } diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 588f12fd963fb..2443447d3f3ea 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -982,7 +982,7 @@ void ASTContext::PrintStats() const { unsigned counts[] = { #define TYPE(Name, Parent) 0, #define ABSTRACT_TYPE(Name, Parent) -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" 0 // Extra }; @@ -1002,7 +1002,7 @@ void ASTContext::PrintStats() const { TotalBytes += counts[Idx] * sizeof(Name##Type); \ ++Idx; #define ABSTRACT_TYPE(Name, Parent) -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm::errs() << "Total bytes = " << TotalBytes << "\n"; @@ -1814,7 +1814,7 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { case Type::Class: \ assert(!T->isDependentType() && "should not see dependent types here"); \ return getTypeInfo(cast(T)->desugar().getTypePtr()); -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm_unreachable("Should not see dependent types"); case Type::FunctionNoProto: @@ -2448,7 +2448,7 @@ structHasUniqueObjectRepresentations(const ASTContext &Context, // have tail padding, so just make sure there isn't an error. if (!isStructEmpty(Base.getType())) { llvm::Optional Size = structHasUniqueObjectRepresentations( - Context, Base.getType()->getAs()->getDecl()); + Context, Base.getType()->castAs()->getDecl()); if (!Size) return llvm::None; Bases.emplace_back(Base.getType(), Size.getValue()); @@ -2539,7 +2539,7 @@ bool ASTContext::hasUniqueObjectRepresentations(QualType Ty) const { } if (Ty->isRecordType()) { - const RecordDecl *Record = Ty->getAs()->getDecl(); + const RecordDecl *Record = Ty->castAs()->getDecl(); if (Record->isInvalidDecl()) return false; @@ -3227,7 +3227,7 @@ QualType ASTContext::getVariableArrayDecayedType(QualType type) const { #define TYPE(Class, Base) #define ABSTRACT_TYPE(Class, Base) #define NON_CANONICAL_TYPE(Class, Base) case Type::Class: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm_unreachable("didn't desugar past all non-canonical types?"); // These types should never be variably-modified. @@ -5684,7 +5684,7 @@ static FloatingRank getFloatingRank(QualType T) { return getFloatingRank(CT->getElementType()); assert(T->getAs() && "getFloatingRank(): not a floating type"); - switch (T->getAs()->getKind()) { + switch (T->castAs()->getKind()) { default: llvm_unreachable("getFloatingRank(): not a floating type"); case BuiltinType::Float16: return Float16Rank; case BuiltinType::Half: return HalfRank; @@ -6322,14 +6322,14 @@ std::string ASTContext::getObjCEncodingForBlock(const BlockExpr *Expr) const { const BlockDecl *Decl = Expr->getBlockDecl(); QualType BlockTy = - Expr->getType()->getAs()->getPointeeType(); + Expr->getType()->castAs()->getPointeeType(); + QualType BlockReturnTy = BlockTy->castAs()->getReturnType(); // Encode result type. if (getLangOpts().EncodeExtendedBlockSig) - getObjCEncodingForMethodParameter( - Decl::OBJC_TQ_None, BlockTy->getAs()->getReturnType(), S, - true /*Extended*/); + getObjCEncodingForMethodParameter(Decl::OBJC_TQ_None, BlockReturnTy, S, + true /*Extended*/); else - getObjCEncodingForType(BlockTy->getAs()->getReturnType(), S); + getObjCEncodingForType(BlockReturnTy, S); // Compute size of all parameters. // Start with computing size of a pointer in number of bytes. // FIXME: There might(should) be a better way of doing this computation! @@ -7129,7 +7129,7 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string &S, case Type::KIND: #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(KIND, BASE) \ case Type::KIND: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm_unreachable("@encode for dependent type!"); } llvm_unreachable("bad type kind!"); @@ -7912,7 +7912,7 @@ Qualifiers::GC ASTContext::getObjCGCAttrKind(QualType Ty) const { if (Ty->isObjCObjectPointerType() || Ty->isBlockPointerType()) return Qualifiers::Strong; else if (Ty->isPointerType()) - return getObjCGCAttrKind(Ty->getAs()->getPointeeType()); + return getObjCGCAttrKind(Ty->castAs()->getPointeeType()); } else { // It's not valid to set GC attributes on anything that isn't a // pointer. @@ -7949,8 +7949,8 @@ bool ASTContext::areCompatibleVectorTypes(QualType FirstVec, // Treat Neon vector types and most AltiVec vector types as if they are the // equivalent GCC vector types. - const auto *First = FirstVec->getAs(); - const auto *Second = SecondVec->getAs(); + const auto *First = FirstVec->castAs(); + const auto *Second = SecondVec->castAs(); if (First->getNumElements() == Second->getNumElements() && hasSameType(First->getElementType(), Second->getElementType()) && First->getVectorKind() != VectorType::AltiVecPixel && @@ -8003,15 +8003,11 @@ ASTContext::ProtocolCompatibleWithProtocol(ObjCProtocolDecl *lProto, /// ObjCQualifiedClassTypesAreCompatible - compare Class and /// Class. -bool ASTContext::ObjCQualifiedClassTypesAreCompatible(QualType lhs, - QualType rhs) { - const auto *lhsQID = lhs->getAs(); - const auto *rhsOPT = rhs->getAs(); - assert((lhsQID && rhsOPT) && "ObjCQualifiedClassTypesAreCompatible"); - - for (auto *lhsProto : lhsQID->quals()) { +bool ASTContext::ObjCQualifiedClassTypesAreCompatible( + const ObjCObjectPointerType *lhs, const ObjCObjectPointerType *rhs) { + for (auto *lhsProto : lhs->quals()) { bool match = false; - for (auto *rhsProto : rhsOPT->quals()) { + for (auto *rhsProto : rhs->quals()) { if (ProtocolCompatibleWithProtocol(lhsProto, rhsProto)) { match = true; break; @@ -8025,8 +8021,9 @@ bool ASTContext::ObjCQualifiedClassTypesAreCompatible(QualType lhs, /// ObjCQualifiedIdTypesAreCompatible - We know that one of lhs/rhs is an /// ObjCQualifiedIDType. -bool ASTContext::ObjCQualifiedIdTypesAreCompatible(QualType lhs, QualType rhs, - bool compare) { +bool ASTContext::ObjCQualifiedIdTypesAreCompatible( + const ObjCObjectPointerType *lhs, const ObjCObjectPointerType *rhs, + bool compare) { // Allow id and an 'id' or void* type in all cases. if (lhs->isVoidPointerType() || lhs->isObjCIdType() || lhs->isObjCClassType()) @@ -8035,16 +8032,12 @@ bool ASTContext::ObjCQualifiedIdTypesAreCompatible(QualType lhs, QualType rhs, rhs->isObjCIdType() || rhs->isObjCClassType()) return true; - if (const ObjCObjectPointerType *lhsQID = lhs->getAsObjCQualifiedIdType()) { - const auto *rhsOPT = rhs->getAs(); - - if (!rhsOPT) return false; - - if (rhsOPT->qual_empty()) { + if (lhs->isObjCQualifiedIdType()) { + if (rhs->qual_empty()) { // If the RHS is a unqualified interface pointer "NSString*", // make sure we check the class hierarchy. - if (ObjCInterfaceDecl *rhsID = rhsOPT->getInterfaceDecl()) { - for (auto *I : lhsQID->quals()) { + if (ObjCInterfaceDecl *rhsID = rhs->getInterfaceDecl()) { + for (auto *I : lhs->quals()) { // when comparing an id

on lhs with a static type on rhs, // see if static class implements all of id's protocols, directly or // through its super class and categories. @@ -8056,13 +8049,13 @@ bool ASTContext::ObjCQualifiedIdTypesAreCompatible(QualType lhs, QualType rhs, return true; } // Both the right and left sides have qualifiers. - for (auto *lhsProto : lhsQID->quals()) { + for (auto *lhsProto : lhs->quals()) { bool match = false; // when comparing an id

on lhs with a static type on rhs, // see if static class implements all of id's protocols, directly or // through its super class and categories. - for (auto *rhsProto : rhsOPT->quals()) { + for (auto *rhsProto : rhs->quals()) { if (ProtocolCompatibleWithProtocol(lhsProto, rhsProto) || (compare && ProtocolCompatibleWithProtocol(rhsProto, lhsProto))) { match = true; @@ -8071,8 +8064,8 @@ bool ASTContext::ObjCQualifiedIdTypesAreCompatible(QualType lhs, QualType rhs, } // If the RHS is a qualified interface pointer "NSString

*", // make sure we check the class hierarchy. - if (ObjCInterfaceDecl *rhsID = rhsOPT->getInterfaceDecl()) { - for (auto *I : lhsQID->quals()) { + if (ObjCInterfaceDecl *rhsID = rhs->getInterfaceDecl()) { + for (auto *I : lhs->quals()) { // when comparing an id

on lhs with a static type on rhs, // see if static class implements all of id's protocols, directly or // through its super class and categories. @@ -8089,13 +8082,11 @@ bool ASTContext::ObjCQualifiedIdTypesAreCompatible(QualType lhs, QualType rhs, return true; } - const ObjCObjectPointerType *rhsQID = rhs->getAsObjCQualifiedIdType(); - assert(rhsQID && "One of the LHS/RHS should be id"); + assert(rhs->isObjCQualifiedIdType() && "One of the LHS/RHS should be id"); - if (const ObjCObjectPointerType *lhsOPT = - lhs->getAsObjCInterfacePointerType()) { + if (lhs->getInterfaceType()) { // If both the right and left sides have qualifiers. - for (auto *lhsProto : lhsOPT->quals()) { + for (auto *lhsProto : lhs->quals()) { bool match = false; // when comparing an id

on rhs with a static type on lhs, @@ -8103,7 +8094,7 @@ bool ASTContext::ObjCQualifiedIdTypesAreCompatible(QualType lhs, QualType rhs, // through its super class and categories. // First, lhs protocols in the qualifier list must be found, direct // or indirect in rhs's qualifier list or it is a mismatch. - for (auto *rhsProto : rhsQID->quals()) { + for (auto *rhsProto : rhs->quals()) { if (ProtocolCompatibleWithProtocol(lhsProto, rhsProto) || (compare && ProtocolCompatibleWithProtocol(rhsProto, lhsProto))) { match = true; @@ -8116,17 +8107,17 @@ bool ASTContext::ObjCQualifiedIdTypesAreCompatible(QualType lhs, QualType rhs, // Static class's protocols, or its super class or category protocols // must be found, direct or indirect in rhs's qualifier list or it is a mismatch. - if (ObjCInterfaceDecl *lhsID = lhsOPT->getInterfaceDecl()) { + if (ObjCInterfaceDecl *lhsID = lhs->getInterfaceDecl()) { llvm::SmallPtrSet LHSInheritedProtocols; CollectInheritedProtocols(lhsID, LHSInheritedProtocols); // This is rather dubious but matches gcc's behavior. If lhs has // no type qualifier and its class has no static protocol(s) // assume that it is mismatch. - if (LHSInheritedProtocols.empty() && lhsOPT->qual_empty()) + if (LHSInheritedProtocols.empty() && lhs->qual_empty()) return false; for (auto *lhsProto : LHSInheritedProtocols) { bool match = false; - for (auto *rhsProto : rhsQID->quals()) { + for (auto *rhsProto : rhs->quals()) { if (ProtocolCompatibleWithProtocol(lhsProto, rhsProto) || (compare && ProtocolCompatibleWithProtocol(rhsProto, lhsProto))) { match = true; @@ -8171,14 +8162,11 @@ bool ASTContext::canAssignObjCInterfaces(const ObjCObjectPointerType *LHSOPT, }; if (LHS->isObjCQualifiedId() || RHS->isObjCQualifiedId()) { - return finish(ObjCQualifiedIdTypesAreCompatible(QualType(LHSOPT,0), - QualType(RHSOPT,0), - false)); + return finish(ObjCQualifiedIdTypesAreCompatible(LHSOPT, RHSOPT, false)); } if (LHS->isObjCQualifiedClass() && RHS->isObjCQualifiedClass()) { - return finish(ObjCQualifiedClassTypesAreCompatible(QualType(LHSOPT,0), - QualType(RHSOPT,0))); + return finish(ObjCQualifiedClassTypesAreCompatible(LHSOPT, RHSOPT)); } // If we have 2 user-defined types, fall into that path. @@ -8227,8 +8215,8 @@ bool ASTContext::canAssignObjCInterfacesInBlockPointer( if (LHSOPT->isObjCQualifiedIdType() || RHSOPT->isObjCQualifiedIdType()) return finish(ObjCQualifiedIdTypesAreCompatible( - QualType(BlockReturnType ? LHSOPT : RHSOPT, 0), - QualType(BlockReturnType ? RHSOPT : LHSOPT, 0), false)); + (BlockReturnType ? LHSOPT : RHSOPT), + (BlockReturnType ? RHSOPT : LHSOPT), false)); const ObjCInterfaceType* LHS = LHSOPT->getInterfaceType(); const ObjCInterfaceType* RHS = RHSOPT->getInterfaceType(); @@ -8952,7 +8940,7 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) case Type::Class: #define NON_CANONICAL_TYPE(Class, Base) case Type::Class: #define DEPENDENT_TYPE(Class, Base) case Type::Class: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm_unreachable("Non-canonical and dependent types shouldn't get here"); case Type::Auto: @@ -8972,8 +8960,8 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, case Type::Pointer: { // Merge two pointer types, while trying to preserve typedef info - QualType LHSPointee = LHS->getAs()->getPointeeType(); - QualType RHSPointee = RHS->getAs()->getPointeeType(); + QualType LHSPointee = LHS->castAs()->getPointeeType(); + QualType RHSPointee = RHS->castAs()->getPointeeType(); if (Unqualified) { LHSPointee = LHSPointee.getUnqualifiedType(); RHSPointee = RHSPointee.getUnqualifiedType(); @@ -8991,8 +8979,8 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, case Type::BlockPointer: { // Merge two block pointer types, while trying to preserve typedef info - QualType LHSPointee = LHS->getAs()->getPointeeType(); - QualType RHSPointee = RHS->getAs()->getPointeeType(); + QualType LHSPointee = LHS->castAs()->getPointeeType(); + QualType RHSPointee = RHS->castAs()->getPointeeType(); if (Unqualified) { LHSPointee = LHSPointee.getUnqualifiedType(); RHSPointee = RHSPointee.getUnqualifiedType(); @@ -9024,8 +9012,8 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, case Type::Atomic: { // Merge two pointer types, while trying to preserve typedef info - QualType LHSValue = LHS->getAs()->getValueType(); - QualType RHSValue = RHS->getAs()->getValueType(); + QualType LHSValue = LHS->castAs()->getValueType(); + QualType RHSValue = RHS->castAs()->getValueType(); if (Unqualified) { LHSValue = LHSValue.getUnqualifiedType(); RHSValue = RHSValue.getUnqualifiedType(); @@ -9284,8 +9272,8 @@ QualType ASTContext::mergeObjCGCQualifiers(QualType LHS, QualType RHS) { } if (LHSCan->isObjCObjectPointerType() && RHSCan->isObjCObjectPointerType()) { - QualType LHSBaseQT = LHS->getAs()->getPointeeType(); - QualType RHSBaseQT = RHS->getAs()->getPointeeType(); + QualType LHSBaseQT = LHS->castAs()->getPointeeType(); + QualType RHSBaseQT = RHS->castAs()->getPointeeType(); QualType ResQT = mergeObjCGCQualifiers(LHSBaseQT, RHSBaseQT); if (ResQT == LHSBaseQT) return LHS; @@ -9936,25 +9924,10 @@ static GVALinkage basicGVALinkageForVariable(const ASTContext &Context, return StrongLinkage; case TSK_ExplicitSpecialization: - if (Context.getTargetInfo().getCXXABI().isMicrosoft()) { - // If this is a fully specialized constexpr variable template, pretend it - // was marked inline. MSVC 14.21.27702 headers define _Is_integral in a - // header this way, and we don't want to emit non-discardable definitions - // of these variables in every TU that includes . This - // behavior is non-conforming, since another TU could use an extern - // template declaration for this variable, but for constexpr variables, - // it's unlikely for a user to want to do that. This behavior can be - // removed if the headers change to explicitly mark such variable template - // specializations inline. - if (isa(VD) && VD->isConstexpr()) - return GVA_DiscardableODR; - - // Use ODR linkage for static data members of fully specialized templates - // to prevent duplicate definition errors with MSVC. - if (VD->isStaticDataMember()) - return GVA_StrongODR; - } - return StrongLinkage; + return Context.getTargetInfo().getCXXABI().isMicrosoft() && + VD->isStaticDataMember() + ? GVA_StrongODR + : StrongLinkage; case TSK_ExplicitInstantiationDefinition: return GVA_StrongODR; @@ -10100,7 +10073,7 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) { return false; // Variables that have destruction with side-effects are required. - if (VD->getType().isDestructedType()) + if (VD->needsDestruction(*this)) return true; // Variables that have initialization with side-effects are required. @@ -10583,8 +10556,7 @@ QualType ASTContext::getCorrespondingSaturatedType(QualType Ty) const { if (Ty->isSaturatedFixedPointType()) return Ty; - const auto &BT = Ty->getAs(); - switch (BT->getKind()) { + switch (Ty->castAs()->getKind()) { default: llvm_unreachable("Not a fixed point type!"); case BuiltinType::ShortAccum: diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp index 15df865852941..30985441031de 100644 --- a/clang/lib/AST/ASTDiagnostic.cpp +++ b/clang/lib/AST/ASTDiagnostic.cpp @@ -154,7 +154,7 @@ Underlying = CTy->desugar(); \ } \ break; \ } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } // If it wasn't sugared, we're done. diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 7be3502108341..057444ac99290 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -3244,7 +3244,7 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { if (GetImportedOrCreateDecl( ToFunction, D, Importer.getToContext(), cast(DC), ToInnerLocStart, NameInfo, T, TInfo, D->isInlineSpecified(), - D->isImplicit())) + D->isImplicit(), D->getConstexprKind())) return ToFunction; CXXDestructorDecl *ToDtor = cast(ToFunction); diff --git a/clang/lib/AST/ASTTypeTraits.cpp b/clang/lib/AST/ASTTypeTraits.cpp index 8615dee4e7958..6b7f6ec51086f 100644 --- a/clang/lib/AST/ASTTypeTraits.cpp +++ b/clang/lib/AST/ASTTypeTraits.cpp @@ -37,7 +37,7 @@ const ASTNodeKind::KindInfo ASTNodeKind::AllKindInfo[] = { #include "clang/AST/StmtNodes.inc" { NKI_None, "Type" }, #define TYPE(DERIVED, BASE) { NKI_##BASE, #DERIVED "Type" }, -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" { NKI_None, "OMPClause" }, #define OPENMP_CLAUSE(TextualSpelling, Class) {NKI_OMPClause, #Class}, #include "clang/Basic/OpenMPKinds.def" @@ -104,7 +104,7 @@ ASTNodeKind ASTNodeKind::getFromNode(const Type &T) { #define TYPE(Class, Base) \ case Type::Class: return ASTNodeKind(NKI_##Class##Type); #define ABSTRACT_TYPE(Class, Base) -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } llvm_unreachable("invalid type kind"); } @@ -117,6 +117,7 @@ ASTNodeKind ASTNodeKind::getFromNode(const OMPClause &C) { case OMPC_threadprivate: case OMPC_uniform: case OMPC_device_type: + case OMPC_match: case OMPC_unknown: llvm_unreachable("unexpected OpenMP clause kind"); } diff --git a/clang/lib/AST/CommentLexer.cpp b/clang/lib/AST/CommentLexer.cpp index 19485f6018c02..c1ea3eab075e2 100644 --- a/clang/lib/AST/CommentLexer.cpp +++ b/clang/lib/AST/CommentLexer.cpp @@ -850,17 +850,14 @@ void Lexer::lex(Token &T) { } StringRef Lexer::getSpelling(const Token &Tok, - const SourceManager &SourceMgr, - bool *Invalid) const { + const SourceManager &SourceMgr) const { SourceLocation Loc = Tok.getLocation(); std::pair LocInfo = SourceMgr.getDecomposedLoc(Loc); bool InvalidTemp = false; StringRef File = SourceMgr.getBufferData(LocInfo.first, &InvalidTemp); - if (InvalidTemp) { - *Invalid = true; + if (InvalidTemp) return StringRef(); - } const char *Begin = File.data() + LocInfo.second; return StringRef(Begin, Tok.getLength()); diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index b27ac907b8bc2..9ebf1c32629fe 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -1558,6 +1558,24 @@ void NamedDecl::printQualifiedName(raw_ostream &OS) const { void NamedDecl::printQualifiedName(raw_ostream &OS, const PrintingPolicy &P) const { + if (getDeclContext()->isFunctionOrMethod()) { + // We do not print '(anonymous)' for function parameters without name. + printName(OS); + return; + } + printNestedNameSpecifier(OS, P); + if (getDeclName() || isa(this)) + OS << *this; + else + OS << "(anonymous)"; +} + +void NamedDecl::printNestedNameSpecifier(raw_ostream &OS) const { + printNestedNameSpecifier(OS, getASTContext().getPrintingPolicy()); +} + +void NamedDecl::printNestedNameSpecifier(raw_ostream &OS, + const PrintingPolicy &P) const { const DeclContext *Ctx = getDeclContext(); // For ObjC methods and properties, look through categories and use the @@ -1571,10 +1589,8 @@ void NamedDecl::printQualifiedName(raw_ostream &OS, Ctx = ID; } - if (Ctx->isFunctionOrMethod()) { - printName(OS); + if (Ctx->isFunctionOrMethod()) return; - } using ContextsTy = SmallVector; ContextsTy Contexts; @@ -1644,11 +1660,6 @@ void NamedDecl::printQualifiedName(raw_ostream &OS, } OS << "::"; } - - if (getDeclName() || isa(this)) - OS << *this; - else - OS << "(anonymous)"; } void NamedDecl::getNameForDiagnostic(raw_ostream &OS, @@ -2581,6 +2592,18 @@ bool VarDecl::isNoDestroy(const ASTContext &Ctx) const { !hasAttr())); } +QualType::DestructionKind +VarDecl::needsDestruction(const ASTContext &Ctx) const { + if (EvaluatedStmt *Eval = Init.dyn_cast()) + if (Eval->HasConstantDestruction) + return QualType::DK_none; + + if (isNoDestroy(Ctx)) + return QualType::DK_none; + + return getType().isDestructedType(); +} + MemberSpecializationInfo *VarDecl::getMemberSpecializationInfo() const { if (isStaticDataMember()) // FIXME: Remove ? @@ -2966,8 +2989,7 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction(bool *IsAligned) const Ty = Ty->getPointeeType(); if (Ty.getCVRQualifiers() != Qualifiers::Const) return false; - const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl(); - if (RD && isNamed(RD, "nothrow_t") && RD->isInStdNamespace()) + if (Ty->isNothrowT()) Consume(); } @@ -3251,6 +3273,9 @@ bool FunctionDecl::doesDeclarationForceExternallyVisibleDefinition() const { return true; } + if (Context.getLangOpts().CPlusPlus) + return false; + if (Context.getLangOpts().GNUInline || hasAttr()) { // With GNU inlining, a declaration with 'inline' but not 'extern', forces // an externally visible definition. @@ -3279,9 +3304,6 @@ bool FunctionDecl::doesDeclarationForceExternallyVisibleDefinition() const { return FoundBody; } - if (Context.getLangOpts().CPlusPlus) - return false; - // C99 6.7.4p6: // [...] If all of the file scope declarations for a function in a // translation unit include the inline function specifier without extern, @@ -3361,6 +3383,8 @@ bool FunctionDecl::isInlineDefinitionExternallyVisible() const { // If it's not the case that both 'inline' and 'extern' are // specified on the definition, then this inline definition is // externally visible. + if (Context.getLangOpts().CPlusPlus) + return false; if (!(isInlineSpecified() && getStorageClass() == SC_Extern)) return true; diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 4939c37edc7e8..95b14e494a3b5 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -95,6 +95,7 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) HasDefaultedDefaultConstructor(false), DefaultedDefaultConstructorIsConstexpr(true), HasConstexprDefaultConstructor(false), + DefaultedDestructorIsConstexpr(true), HasNonLiteralTypeFieldsOrBases(false), ComputedVisibleConversions(false), UserProvidedDefaultConstructor(false), DeclaredSpecialMembers(0), ImplicitCopyConstructorCanHaveConstParamForVBase(true), @@ -325,10 +326,12 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, data().IsStandardLayout = false; data().IsCXX11StandardLayout = false; - // C++11 [dcl.constexpr]p4: - // In the definition of a constexpr constructor [...] - // -- the class shall not have any virtual base classes + // C++20 [dcl.constexpr]p3: + // In the definition of a constexpr function [...] + // -- if the function is a constructor or destructor, + // its class shall not have any virtual base classes data().DefaultedDefaultConstructorIsConstexpr = false; + data().DefaultedDestructorIsConstexpr = false; // C++1z [class.copy]p8: // The implicitly-declared copy constructor for a class X will have @@ -520,6 +523,19 @@ void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) { data().NeedOverloadResolutionForMoveConstructor = true; data().NeedOverloadResolutionForDestructor = true; } + + // C++2a [dcl.constexpr]p4: + // The definition of a constexpr destructor [shall] satisfy the + // following requirement: + // -- for every subobject of class type or (possibly multi-dimensional) + // array thereof, that class type shall have a constexpr destructor + if (!Subobj->hasConstexprDestructor()) + data().DefaultedDestructorIsConstexpr = false; +} + +bool CXXRecordDecl::hasConstexprDestructor() const { + auto *Dtor = getDestructor(); + return Dtor ? Dtor->isConstexpr() : defaultedDestructorIsConstexpr(); } bool CXXRecordDecl::hasAnyDependentBases() const { @@ -1263,7 +1279,8 @@ void CXXRecordDecl::addedMember(Decl *D) { } else { // Base element type of field is a non-class type. if (!T->isLiteralType(Context) || - (!Field->hasInClassInitializer() && !isUnion())) + (!Field->hasInClassInitializer() && !isUnion() && + !Context.getLangOpts().CPlusPlus2a)) data().DefaultedDefaultConstructorIsConstexpr = false; // C++11 [class.copy]p23: @@ -1382,17 +1399,29 @@ static bool allLookupResultsAreTheSame(const DeclContext::lookup_result &R) { } #endif -CXXMethodDecl* CXXRecordDecl::getLambdaCallOperator() const { - if (!isLambda()) return nullptr; +static NamedDecl* getLambdaCallOperatorHelper(const CXXRecordDecl &RD) { + if (!RD.isLambda()) return nullptr; DeclarationName Name = - getASTContext().DeclarationNames.getCXXOperatorName(OO_Call); - DeclContext::lookup_result Calls = lookup(Name); + RD.getASTContext().DeclarationNames.getCXXOperatorName(OO_Call); + DeclContext::lookup_result Calls = RD.lookup(Name); assert(!Calls.empty() && "Missing lambda call operator!"); assert(allLookupResultsAreTheSame(Calls) && "More than one lambda call operator!"); + return Calls.front(); +} + +FunctionTemplateDecl* CXXRecordDecl::getDependentLambdaCallOperator() const { + NamedDecl *CallOp = getLambdaCallOperatorHelper(*this); + return dyn_cast_or_null(CallOp); +} + +CXXMethodDecl *CXXRecordDecl::getLambdaCallOperator() const { + NamedDecl *CallOp = getLambdaCallOperatorHelper(*this); + + if (CallOp == nullptr) + return nullptr; - NamedDecl *CallOp = Calls.front(); if (const auto *CallOpTmpl = dyn_cast(CallOp)) return cast(CallOpTmpl->getTemplatedDecl()); @@ -2570,20 +2599,19 @@ CXXDestructorDecl * CXXDestructorDecl::CreateDeserialized(ASTContext &C, unsigned ID) { return new (C, ID) CXXDestructorDecl(C, nullptr, SourceLocation(), DeclarationNameInfo(), - QualType(), nullptr, false, false); + QualType(), nullptr, false, false, CSK_unspecified); } -CXXDestructorDecl * -CXXDestructorDecl::Create(ASTContext &C, CXXRecordDecl *RD, - SourceLocation StartLoc, - const DeclarationNameInfo &NameInfo, - QualType T, TypeSourceInfo *TInfo, - bool isInline, bool isImplicitlyDeclared) { +CXXDestructorDecl *CXXDestructorDecl::Create( + ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, + const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, + bool isInline, bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind) { assert(NameInfo.getName().getNameKind() == DeclarationName::CXXDestructorName && "Name must refer to a destructor"); - return new (C, RD) CXXDestructorDecl(C, RD, StartLoc, NameInfo, T, TInfo, - isInline, isImplicitlyDeclared); + return new (C, RD) + CXXDestructorDecl(C, RD, StartLoc, NameInfo, T, TInfo, isInline, + isImplicitlyDeclared, ConstexprKind); } void CXXDestructorDecl::setOperatorDelete(FunctionDecl *OD, Expr *ThisArg) { diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index d055d9cd31fbb..3d93588a43ba1 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -1007,12 +1007,19 @@ void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) { void DeclPrinter::VisitLinkageSpecDecl(LinkageSpecDecl *D) { const char *l; - if (D->getLanguage() == LinkageSpecDecl::lang_c) + switch (D->getLanguage()) { + case LinkageSpecDecl::lang_c: l = "C"; - else { - assert(D->getLanguage() == LinkageSpecDecl::lang_cxx && - "unknown language in linkage specification"); + break; + case LinkageSpecDecl::lang_cxx_14: + l = "C++14"; + break; + case LinkageSpecDecl::lang_cxx_11: + l = "C++11"; + break; + case LinkageSpecDecl::lang_cxx: l = "C++"; + break; } Out << "extern \"" << l << "\" "; diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 40c39c845db63..c03ae22fb5d89 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -510,8 +510,12 @@ SourceRange TemplateTypeParmDecl::getSourceRange() const { if (hasDefaultArgument() && !defaultArgumentWasInherited()) return SourceRange(getBeginLoc(), getDefaultArgumentInfo()->getTypeLoc().getEndLoc()); - else - return TypeDecl::getSourceRange(); + // TypeDecl::getSourceRange returns a range containing name location, which is + // wrong for unnamed template parameters. e.g: + // it will return <[[typename>]] instead of <[[typename]]> + else if (getDeclName().isEmpty()) + return SourceRange(getBeginLoc()); + return TypeDecl::getSourceRange(); } unsigned TemplateTypeParmDecl::getDepth() const { diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index e11224a91ae7b..efc050496e23f 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -185,6 +185,12 @@ bool Expr::isKnownToHaveBooleanValue() const { return CO->getTrueExpr()->isKnownToHaveBooleanValue() && CO->getFalseExpr()->isKnownToHaveBooleanValue(); + if (isa(E)) + return true; + + if (const auto *OVE = dyn_cast(E)) + return OVE->getSourceExpr()->isKnownToHaveBooleanValue(); + return false; } @@ -3991,6 +3997,112 @@ bool Expr::refersToGlobalRegisterVar() const { return false; } +bool Expr::isSameComparisonOperand(const Expr* E1, const Expr* E2) { + E1 = E1->IgnoreParens(); + E2 = E2->IgnoreParens(); + + if (E1->getStmtClass() != E2->getStmtClass()) + return false; + + switch (E1->getStmtClass()) { + default: + return false; + case CXXThisExprClass: + return true; + case DeclRefExprClass: { + // DeclRefExpr without an ImplicitCastExpr can happen for integral + // template parameters. + const auto *DRE1 = cast(E1); + const auto *DRE2 = cast(E2); + return DRE1->isRValue() && DRE2->isRValue() && + DRE1->getDecl() == DRE2->getDecl(); + } + case ImplicitCastExprClass: { + // Peel off implicit casts. + while (true) { + const auto *ICE1 = dyn_cast(E1); + const auto *ICE2 = dyn_cast(E2); + if (!ICE1 || !ICE2) + return false; + if (ICE1->getCastKind() != ICE2->getCastKind()) + return false; + E1 = ICE1->getSubExpr()->IgnoreParens(); + E2 = ICE2->getSubExpr()->IgnoreParens(); + // The final cast must be one of these types. + if (ICE1->getCastKind() == CK_LValueToRValue || + ICE1->getCastKind() == CK_ArrayToPointerDecay || + ICE1->getCastKind() == CK_FunctionToPointerDecay) { + break; + } + } + + const auto *DRE1 = dyn_cast(E1); + const auto *DRE2 = dyn_cast(E2); + if (DRE1 && DRE2) + return declaresSameEntity(DRE1->getDecl(), DRE2->getDecl()); + + const auto *Ivar1 = dyn_cast(E1); + const auto *Ivar2 = dyn_cast(E2); + if (Ivar1 && Ivar2) { + return Ivar1->isFreeIvar() && Ivar2->isFreeIvar() && + declaresSameEntity(Ivar1->getDecl(), Ivar2->getDecl()); + } + + const auto *Array1 = dyn_cast(E1); + const auto *Array2 = dyn_cast(E2); + if (Array1 && Array2) { + if (!isSameComparisonOperand(Array1->getBase(), Array2->getBase())) + return false; + + auto Idx1 = Array1->getIdx(); + auto Idx2 = Array2->getIdx(); + const auto Integer1 = dyn_cast(Idx1); + const auto Integer2 = dyn_cast(Idx2); + if (Integer1 && Integer2) { + if (!llvm::APInt::isSameValue(Integer1->getValue(), + Integer2->getValue())) + return false; + } else { + if (!isSameComparisonOperand(Idx1, Idx2)) + return false; + } + + return true; + } + + // Walk the MemberExpr chain. + while (isa(E1) && isa(E2)) { + const auto *ME1 = cast(E1); + const auto *ME2 = cast(E2); + if (!declaresSameEntity(ME1->getMemberDecl(), ME2->getMemberDecl())) + return false; + if (const auto *D = dyn_cast(ME1->getMemberDecl())) + if (D->isStaticDataMember()) + return true; + E1 = ME1->getBase()->IgnoreParenImpCasts(); + E2 = ME2->getBase()->IgnoreParenImpCasts(); + } + + if (isa(E1) && isa(E2)) + return true; + + // A static member variable can end the MemberExpr chain with either + // a MemberExpr or a DeclRefExpr. + auto getAnyDecl = [](const Expr *E) -> const ValueDecl * { + if (const auto *DRE = dyn_cast(E)) + return DRE->getDecl(); + if (const auto *ME = dyn_cast(E)) + return ME->getMemberDecl(); + return nullptr; + }; + + const ValueDecl *VD1 = getAnyDecl(E1); + const ValueDecl *VD2 = getAnyDecl(E2); + return declaresSameEntity(VD1, VD2); + } + } +} + /// isArrow - Return true if the base expression is a pointer to vector, /// return false if the base expression is a vector. bool ExtVectorElementExpr::isArrow() const { diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index c5f86a4cc12b4..1d5fd80d0d470 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -124,6 +124,8 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew, if (ArraySize) { if (Expr *SizeExpr = *ArraySize) { + if (SizeExpr->isValueDependent()) + ExprBits.ValueDependent = true; if (SizeExpr->isInstantiationDependent()) ExprBits.InstantiationDependent = true; if (SizeExpr->containsUnexpandedParameterPack()) @@ -134,6 +136,8 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew, } if (Initializer) { + if (Initializer->isValueDependent()) + ExprBits.ValueDependent = true; if (Initializer->isInstantiationDependent()) ExprBits.InstantiationDependent = true; if (Initializer->containsUnexpandedParameterPack()) @@ -143,6 +147,8 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew, } for (unsigned I = 0; I != PlacementArgs.size(); ++I) { + if (PlacementArgs[I]->isValueDependent()) + ExprBits.ValueDependent = true; if (PlacementArgs[I]->isInstantiationDependent()) ExprBits.InstantiationDependent = true; if (PlacementArgs[I]->containsUnexpandedParameterPack()) @@ -1212,6 +1218,11 @@ CXXMethodDecl *LambdaExpr::getCallOperator() const { return Record->getLambdaCallOperator(); } +FunctionTemplateDecl *LambdaExpr::getDependentCallOperator() const { + CXXRecordDecl *Record = getLambdaClass(); + return Record->getDependentLambdaCallOperator(); +} + TemplateParameterList *LambdaExpr::getTemplateParameterList() const { CXXRecordDecl *Record = getLambdaClass(); return Record->getGenericLambdaTemplateParameterList(); diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index a4c247370ee1b..908e801234045 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -66,8 +66,6 @@ using llvm::APSInt; using llvm::APFloat; using llvm::Optional; -static bool IsGlobalLValue(APValue::LValueBase B); - namespace { struct LValue; class CallStackFrame; @@ -98,6 +96,9 @@ namespace { if (B.is()) return B.getTypeInfoType(); + if (B.is()) + return B.getDynamicAllocType(); + const Expr *Base = B.get(); // For a materialized temporary, the type of the temporary we materialized @@ -134,6 +135,14 @@ namespace { return E.getAsBaseOrMember().getInt(); } + /// Given an expression, determine the type used to store the result of + /// evaluating that expression. + static QualType getStorageType(ASTContext &Ctx, Expr *E) { + if (E->isRValue()) + return E->getType(); + return Ctx.getLValueReferenceType(E->getType()); + } + /// Given a CallExpr, try to get the alloc_size attribute. May return null. static const AllocSizeAttr *getAllocSizeAttr(const CallExpr *CE) { const FunctionDecl *Callee = CE->getDirectCallee(); @@ -572,13 +581,26 @@ namespace { return 0; } - APValue &createTemporary(const void *Key, bool IsLifetimeExtended); + /// Allocate storage for an object of type T in this stack frame. + /// Populates LV with a handle to the created object. Key identifies + /// the temporary within the stack frame, and must not be reused without + /// bumping the temporary version number. + template + APValue &createTemporary(const KeyT *Key, QualType T, + bool IsLifetimeExtended, LValue &LV); void describe(llvm::raw_ostream &OS) override; Frame *getCaller() const override { return Caller; } SourceLocation getCallLocation() const override { return CallLoc; } const FunctionDecl *getCallee() const override { return Callee; } + + bool isStdFunction() const { + for (const DeclContext *DC = Callee; DC; DC = DC->getParent()) + if (DC->isStdNamespace()) + return true; + return false; + } }; /// Temporarily override 'this'. @@ -596,18 +618,42 @@ namespace { CallStackFrame &Frame; const LValue *OldThis; }; +} + +static bool HandleDestruction(EvalInfo &Info, const Expr *E, + const LValue &This, QualType ThisType); +static bool HandleDestruction(EvalInfo &Info, SourceLocation Loc, + APValue::LValueBase LVBase, APValue &Value, + QualType T); +namespace { /// A cleanup, and a flag indicating whether it is lifetime-extended. class Cleanup { llvm::PointerIntPair Value; + APValue::LValueBase Base; + QualType T; public: - Cleanup(APValue *Val, bool IsLifetimeExtended) - : Value(Val, IsLifetimeExtended) {} + Cleanup(APValue *Val, APValue::LValueBase Base, QualType T, + bool IsLifetimeExtended) + : Value(Val, IsLifetimeExtended), Base(Base), T(T) {} bool isLifetimeExtended() const { return Value.getInt(); } - void endLifetime() { + bool endLifetime(EvalInfo &Info, bool RunDestructors) { + if (RunDestructors) { + SourceLocation Loc; + if (const ValueDecl *VD = Base.dyn_cast()) + Loc = VD->getLocation(); + else if (const Expr *E = Base.dyn_cast()) + Loc = E->getExprLoc(); + return HandleDestruction(Info, Loc, Base, *Value.getPointer(), T); + } *Value.getPointer() = APValue(); + return true; + } + + bool hasSideEffect() { + return T.isDestructedType(); } }; @@ -623,7 +669,13 @@ namespace { return llvm::hash_combine(Obj.Base, Obj.Path); } }; - enum class ConstructionPhase { None, Bases, AfterBases }; + enum class ConstructionPhase { + None, + Bases, + AfterBases, + Destroying, + DestroyingBases + }; } namespace llvm { @@ -645,6 +697,37 @@ template<> struct DenseMapInfo { } namespace { + /// A dynamically-allocated heap object. + struct DynAlloc { + /// The value of this heap-allocated object. + APValue Value; + /// The allocating expression; used for diagnostics. Either a CXXNewExpr + /// or a CallExpr (the latter is for direct calls to operator new inside + /// std::allocator::allocate). + const Expr *AllocExpr = nullptr; + + enum Kind { + New, + ArrayNew, + StdAllocator + }; + + /// Get the kind of the allocation. This must match between allocation + /// and deallocation. + Kind getKind() const { + if (auto *NE = dyn_cast(AllocExpr)) + return NE->isArray() ? ArrayNew : New; + assert(isa(AllocExpr)); + return StdAllocator; + } + }; + + struct DynAllocOrder { + bool operator()(DynamicAllocLValue L, DynamicAllocLValue R) const { + return L.getIndex() < R.getIndex(); + } + }; + /// EvalInfo - This is a private struct used by the evaluator to capture /// information about a subexpression as it is folded. It retains information /// about the AST context, but also maintains information about the folded @@ -699,6 +782,15 @@ namespace { /// evaluated, if any. APValue::LValueBase EvaluatingDecl; + enum class EvaluatingDeclKind { + None, + /// We're evaluating the construction of EvaluatingDecl. + Ctor, + /// We're evaluating the destruction of EvaluatingDecl. + Dtor, + }; + EvaluatingDeclKind IsEvaluatingDecl = EvaluatingDeclKind::None; + /// EvaluatingDeclValue - This is the value being constructed for the /// declaration whose initializer is being evaluated, if any. APValue *EvaluatingDeclValue; @@ -707,6 +799,14 @@ namespace { llvm::DenseMap ObjectsUnderConstruction; + /// Current heap allocations, along with the location where each was + /// allocated. We use std::map here because we need stable addresses + /// for the stored APValues. + std::map HeapAllocs; + + /// The number of heap allocations performed so far in this evaluation. + unsigned NumHeapAllocs = 0; + struct EvaluatingConstructorRAII { EvalInfo &EI; ObjectUnderConstruction Object; @@ -728,9 +828,29 @@ namespace { } }; + struct EvaluatingDestructorRAII { + EvalInfo &EI; + ObjectUnderConstruction Object; + bool DidInsert; + EvaluatingDestructorRAII(EvalInfo &EI, ObjectUnderConstruction Object) + : EI(EI), Object(Object) { + DidInsert = EI.ObjectsUnderConstruction + .insert({Object, ConstructionPhase::Destroying}) + .second; + } + void startedDestroyingBases() { + EI.ObjectsUnderConstruction[Object] = + ConstructionPhase::DestroyingBases; + } + ~EvaluatingDestructorRAII() { + if (DidInsert) + EI.ObjectsUnderConstruction.erase(Object); + } + }; + ConstructionPhase - isEvaluatingConstructor(APValue::LValueBase Base, - ArrayRef Path) { + isEvaluatingCtorDtor(APValue::LValueBase Base, + ArrayRef Path) { return ObjectsUnderConstruction.lookup({Base, Path}); } @@ -811,8 +931,14 @@ namespace { HasFoldFailureDiagnostic(false), InConstantContext(false), EvalMode(Mode) {} - void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) { + ~EvalInfo() { + discardCleanups(); + } + + void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value, + EvaluatingDeclKind EDK = EvaluatingDeclKind::Ctor) { EvaluatingDecl = Base; + IsEvaluatingDecl = EDK; EvaluatingDeclValue = &Value; } @@ -858,6 +984,68 @@ namespace { return true; } + APValue *createHeapAlloc(const Expr *E, QualType T, LValue &LV); + + Optional lookupDynamicAlloc(DynamicAllocLValue DA) { + Optional Result; + auto It = HeapAllocs.find(DA); + if (It != HeapAllocs.end()) + Result = &It->second; + return Result; + } + + /// Information about a stack frame for std::allocator::[de]allocate. + struct StdAllocatorCaller { + unsigned FrameIndex; + QualType ElemType; + explicit operator bool() const { return FrameIndex != 0; }; + }; + + StdAllocatorCaller getStdAllocatorCaller(StringRef FnName) const { + for (const CallStackFrame *Call = CurrentCall; Call != &BottomFrame; + Call = Call->Caller) { + const auto *MD = dyn_cast_or_null(Call->Callee); + if (!MD) + continue; + const IdentifierInfo *FnII = MD->getIdentifier(); + if (!FnII || !FnII->isStr(FnName)) + continue; + + const auto *CTSD = + dyn_cast(MD->getParent()); + if (!CTSD) + continue; + + const IdentifierInfo *ClassII = CTSD->getIdentifier(); + const TemplateArgumentList &TAL = CTSD->getTemplateArgs(); + if (CTSD->isInStdNamespace() && ClassII && + ClassII->isStr("allocator") && TAL.size() >= 1 && + TAL[0].getKind() == TemplateArgument::Type) + return {Call->Index, TAL[0].getAsType()}; + } + + return {}; + } + + void performLifetimeExtension() { + // Disable the cleanups for lifetime-extended temporaries. + CleanupStack.erase( + std::remove_if(CleanupStack.begin(), CleanupStack.end(), + [](Cleanup &C) { return C.isLifetimeExtended(); }), + CleanupStack.end()); + } + + /// Throw away any remaining cleanups at the end of evaluation. If any + /// cleanups would have had a side-effect, note that as an unmodeled + /// side-effect and return false. Otherwise, return true. + bool discardCleanups() { + for (Cleanup &C : CleanupStack) + if (C.hasSideEffect()) + if (!noteSideEffect()) + return false; + return true; + } + private: interp::Frame *getCurrentFrame() override { return CurrentCall; } const interp::Frame *getBottomFrame() const override { return &BottomFrame; } @@ -1101,29 +1289,45 @@ namespace { // temporaries created in different iterations of a loop. Info.CurrentCall->pushTempVersion(); } + bool destroy(bool RunDestructors = true) { + bool OK = cleanup(Info, RunDestructors, OldStackSize); + OldStackSize = -1U; + return OK; + } ~ScopeRAII() { + if (OldStackSize != -1U) + destroy(false); // Body moved to a static method to encourage the compiler to inline away // instances of this class. - cleanup(Info, OldStackSize); Info.CurrentCall->popTempVersion(); } private: - static void cleanup(EvalInfo &Info, unsigned OldStackSize) { - unsigned NewEnd = OldStackSize; - for (unsigned I = OldStackSize, N = Info.CleanupStack.size(); - I != N; ++I) { - if (IsFullExpression && Info.CleanupStack[I].isLifetimeExtended()) { - // Full-expression cleanup of a lifetime-extended temporary: nothing - // to do, just move this cleanup to the right place in the stack. - std::swap(Info.CleanupStack[I], Info.CleanupStack[NewEnd]); - ++NewEnd; - } else { - // End the lifetime of the object. - Info.CleanupStack[I].endLifetime(); + static bool cleanup(EvalInfo &Info, bool RunDestructors, + unsigned OldStackSize) { + assert(OldStackSize <= Info.CleanupStack.size() && + "running cleanups out of order?"); + + // Run all cleanups for a block scope, and non-lifetime-extended cleanups + // for a full-expression scope. + bool Success = true; + for (unsigned I = Info.CleanupStack.size(); I > OldStackSize; --I) { + if (!(IsFullExpression && + Info.CleanupStack[I - 1].isLifetimeExtended())) { + if (!Info.CleanupStack[I - 1].endLifetime(Info, RunDestructors)) { + Success = false; + break; + } } } - Info.CleanupStack.erase(Info.CleanupStack.begin() + NewEnd, - Info.CleanupStack.end()); + + // Compact lifetime-extended cleanups. + auto NewEnd = Info.CleanupStack.begin() + OldStackSize; + if (IsFullExpression) + NewEnd = + std::remove_if(NewEnd, Info.CleanupStack.end(), + [](Cleanup &C) { return !C.isLifetimeExtended(); }); + Info.CleanupStack.erase(NewEnd, Info.CleanupStack.end()); + return Success; } }; typedef ScopeRAII BlockScopeRAII; @@ -1183,18 +1387,14 @@ CallStackFrame::~CallStackFrame() { Info.CurrentCall = Caller; } -APValue &CallStackFrame::createTemporary(const void *Key, - bool IsLifetimeExtended) { - unsigned Version = Info.CurrentCall->getTempVersion(); - APValue &Result = Temporaries[MapKeyTy(Key, Version)]; - assert(Result.isAbsent() && "temporary created multiple times"); - Info.CleanupStack.push_back(Cleanup(&Result, IsLifetimeExtended)); - return Result; +static bool isRead(AccessKinds AK) { + return AK == AK_Read || AK == AK_ReadObjectRepresentation; } static bool isModification(AccessKinds AK) { switch (AK) { case AK_Read: + case AK_ReadObjectRepresentation: case AK_MemberCall: case AK_DynamicCast: case AK_TypeId: @@ -1202,14 +1402,20 @@ static bool isModification(AccessKinds AK) { case AK_Assign: case AK_Increment: case AK_Decrement: + case AK_Construct: + case AK_Destroy: return true; } llvm_unreachable("unknown access kind"); } +static bool isAnyAccess(AccessKinds AK) { + return isRead(AK) || isModification(AK); +} + /// Is this an access per the C++ definition? static bool isFormalAccess(AccessKinds AK) { - return AK == AK_Read || isModification(AK); + return isAnyAccess(AK) && AK != AK_Construct && AK != AK_Destroy; } namespace { @@ -1305,9 +1511,10 @@ namespace { IsNullPtr = false; } - void setNull(QualType PointerTy, uint64_t TargetVal) { + void setNull(ASTContext &Ctx, QualType PointerTy) { Base = (Expr *)nullptr; - Offset = CharUnits::fromQuantity(TargetVal); + Offset = + CharUnits::fromQuantity(Ctx.getTargetNullPointerValue(PointerTy)); InvalidBase = false; Designator = SubobjectDesignator(PointerTy->getPointeeType()); IsNullPtr = true; @@ -1317,6 +1524,12 @@ namespace { set(B, true); } + std::string toString(ASTContext &Ctx, QualType T) const { + APValue Printable; + moveInto(Printable); + return Printable.getAsString(Ctx, T); + } + private: // Check that this LValue is not based on a null pointer. If it is, produce // a diagnostic and mark the designator as invalid. @@ -1539,15 +1752,6 @@ static bool EvaluateFixedPoint(const Expr *E, APFixedPoint &Result, // Misc utilities //===----------------------------------------------------------------------===// -/// A helper function to create a temporary and set an LValue. -template -static APValue &createTemporary(const KeyTy *Key, bool IsLifetimeExtended, - LValue &LV, CallStackFrame &Frame) { - LV.set({Key, Frame.Info.CurrentCall->Index, - Frame.Info.CurrentCall->getTempVersion()}); - return Frame.createTemporary(Key, IsLifetimeExtended); -} - /// Negate an APSInt in place, converting it to a signed form if necessary, and /// preserving its value (by extending by up to one bit as needed). static void negateAsSigned(APSInt &Int) { @@ -1558,6 +1762,43 @@ static void negateAsSigned(APSInt &Int) { Int = -Int; } +template +APValue &CallStackFrame::createTemporary(const KeyT *Key, QualType T, + bool IsLifetimeExtended, LValue &LV) { + unsigned Version = getTempVersion(); + APValue::LValueBase Base(Key, Index, Version); + LV.set(Base); + APValue &Result = Temporaries[MapKeyTy(Key, Version)]; + assert(Result.isAbsent() && "temporary created multiple times"); + + // If we're creating a temporary immediately in the operand of a speculative + // evaluation, don't register a cleanup to be run outside the speculative + // evaluation context, since we won't actually be able to initialize this + // object. + if (Index <= Info.SpeculativeEvaluationDepth) { + if (T.isDestructedType()) + Info.noteSideEffect(); + } else { + Info.CleanupStack.push_back(Cleanup(&Result, Base, T, IsLifetimeExtended)); + } + return Result; +} + +APValue *EvalInfo::createHeapAlloc(const Expr *E, QualType T, LValue &LV) { + if (NumHeapAllocs > DynamicAllocLValue::getMaxIndex()) { + FFDiag(E, diag::note_constexpr_heap_alloc_limit_exceeded); + return nullptr; + } + + DynamicAllocLValue DA(NumHeapAllocs++); + LV.set(APValue::LValueBase::getDynamicAlloc(DA, T)); + auto Result = HeapAllocs.emplace(std::piecewise_construct, + std::forward_as_tuple(DA), std::tuple<>()); + assert(Result.second && "reused a heap alloc index?"); + Result.first->second.AllocExpr = E; + return &Result.first->second.Value; +} + /// Produce a string describing the given constexpr call. void CallStackFrame::describe(raw_ostream &Out) { unsigned ArgIndex = 0; @@ -1628,7 +1869,7 @@ static bool IsGlobalLValue(APValue::LValueBase B) { return isa(D); } - if (B.is()) + if (B.is() || B.is()) return true; const Expr *E = B.get(); @@ -1727,15 +1968,39 @@ static void NoteLValueLocation(EvalInfo &Info, APValue::LValueBase Base) { Info.Note(VD->getLocation(), diag::note_declared_at); else if (const Expr *E = Base.dyn_cast()) Info.Note(E->getExprLoc(), diag::note_constexpr_temporary_here); + else if (DynamicAllocLValue DA = Base.dyn_cast()) { + // FIXME: Produce a note for dangling pointers too. + if (Optional Alloc = Info.lookupDynamicAlloc(DA)) + Info.Note((*Alloc)->AllocExpr->getExprLoc(), + diag::note_constexpr_dynamic_alloc_here); + } // We have no information to show for a typeid(T) object. } +enum class CheckEvaluationResultKind { + ConstantExpression, + FullyInitialized, +}; + +/// Materialized temporaries that we've already checked to determine if they're +/// initializsed by a constant expression. +using CheckedTemporaries = + llvm::SmallPtrSet; + +static bool CheckEvaluationResult(CheckEvaluationResultKind CERK, + EvalInfo &Info, SourceLocation DiagLoc, + QualType Type, const APValue &Value, + Expr::ConstExprUsage Usage, + SourceLocation SubobjectLoc, + CheckedTemporaries &CheckedTemps); + /// Check that this reference or pointer core constant expression is a valid /// value for an address or reference constant expression. Return true if we /// can fold this expression, whether or not it's a constant expression. static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, QualType Type, const LValue &LVal, - Expr::ConstExprUsage Usage) { + Expr::ConstExprUsage Usage, + CheckedTemporaries &CheckedTemps) { bool IsReferenceType = Type->isReferenceType(); APValue::LValueBase Base = LVal.getLValueBase(); @@ -1761,14 +2026,23 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, LVal.getLValueCallIndex() == 0) && "have call index for global lvalue"); + if (Base.is()) { + Info.FFDiag(Loc, diag::note_constexpr_dynamic_alloc) + << IsReferenceType << !Designator.Entries.empty(); + NoteLValueLocation(Info, Base); + return false; + } + if (const ValueDecl *VD = Base.dyn_cast()) { if (const VarDecl *Var = dyn_cast(VD)) { // Check if this is a thread-local variable. if (Var->getTLSKind()) + // FIXME: Diagnostic! return false; // A dllimport variable never acts like a constant. if (Usage == Expr::EvaluateForCodeGen && Var->hasAttr()) + // FIXME: Diagnostic! return false; } if (const auto *FD = dyn_cast(VD)) { @@ -1784,6 +2058,25 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, // perform initialization with the address of the thunk. if (Info.getLangOpts().CPlusPlus && Usage == Expr::EvaluateForCodeGen && FD->hasAttr()) + // FIXME: Diagnostic! + return false; + } + } else if (const auto *MTE = dyn_cast_or_null( + Base.dyn_cast())) { + if (CheckedTemps.insert(MTE).second) { + QualType TempType = getType(Base); + if (TempType.isDestructedType()) { + Info.FFDiag(MTE->getExprLoc(), + diag::note_constexpr_unsupported_tempoarary_nontrivial_dtor) + << TempType; + return false; + } + + APValue *V = Info.Ctx.getMaterializedTemporaryValue(MTE, false); + assert(V && "evasluation result refers to uninitialised temporary"); + if (!CheckEvaluationResult(CheckEvaluationResultKind::ConstantExpression, + Info, MTE->getExprLoc(), TempType, *V, + Usage, SourceLocation(), CheckedTemps)) return false; } } @@ -1858,14 +2151,12 @@ static bool CheckLiteralType(EvalInfo &Info, const Expr *E, return false; } -/// Check that this core constant expression value is a valid value for a -/// constant expression. If not, report an appropriate diagnostic. Does not -/// check that the expression is of literal type. -static bool -CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, QualType Type, - const APValue &Value, - Expr::ConstExprUsage Usage = Expr::EvaluateForCodeGen, - SourceLocation SubobjectLoc = SourceLocation()) { +static bool CheckEvaluationResult(CheckEvaluationResultKind CERK, + EvalInfo &Info, SourceLocation DiagLoc, + QualType Type, const APValue &Value, + Expr::ConstExprUsage Usage, + SourceLocation SubobjectLoc, + CheckedTemporaries &CheckedTemps) { if (!Value.hasValue()) { Info.FFDiag(DiagLoc, diag::note_constexpr_uninitialized) << true << Type; @@ -1885,30 +2176,31 @@ CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, QualType Type, if (Value.isArray()) { QualType EltTy = Type->castAsArrayTypeUnsafe()->getElementType(); for (unsigned I = 0, N = Value.getArrayInitializedElts(); I != N; ++I) { - if (!CheckConstantExpression(Info, DiagLoc, EltTy, - Value.getArrayInitializedElt(I), Usage, - SubobjectLoc)) + if (!CheckEvaluationResult(CERK, Info, DiagLoc, EltTy, + Value.getArrayInitializedElt(I), Usage, + SubobjectLoc, CheckedTemps)) return false; } if (!Value.hasArrayFiller()) return true; - return CheckConstantExpression(Info, DiagLoc, EltTy, Value.getArrayFiller(), - Usage, SubobjectLoc); + return CheckEvaluationResult(CERK, Info, DiagLoc, EltTy, + Value.getArrayFiller(), Usage, SubobjectLoc, + CheckedTemps); } if (Value.isUnion() && Value.getUnionField()) { - return CheckConstantExpression(Info, DiagLoc, - Value.getUnionField()->getType(), - Value.getUnionValue(), Usage, - Value.getUnionField()->getLocation()); + return CheckEvaluationResult( + CERK, Info, DiagLoc, Value.getUnionField()->getType(), + Value.getUnionValue(), Usage, Value.getUnionField()->getLocation(), + CheckedTemps); } if (Value.isStruct()) { RecordDecl *RD = Type->castAs()->getDecl(); if (const CXXRecordDecl *CD = dyn_cast(RD)) { unsigned BaseIndex = 0; for (const CXXBaseSpecifier &BS : CD->bases()) { - if (!CheckConstantExpression(Info, DiagLoc, BS.getType(), - Value.getStructBase(BaseIndex), Usage, - BS.getBeginLoc())) + if (!CheckEvaluationResult(CERK, Info, DiagLoc, BS.getType(), + Value.getStructBase(BaseIndex), Usage, + BS.getBeginLoc(), CheckedTemps)) return false; ++BaseIndex; } @@ -1917,26 +2209,66 @@ CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, QualType Type, if (I->isUnnamedBitfield()) continue; - if (!CheckConstantExpression(Info, DiagLoc, I->getType(), - Value.getStructField(I->getFieldIndex()), - Usage, I->getLocation())) + if (!CheckEvaluationResult(CERK, Info, DiagLoc, I->getType(), + Value.getStructField(I->getFieldIndex()), + Usage, I->getLocation(), CheckedTemps)) return false; } } - if (Value.isLValue()) { + if (Value.isLValue() && + CERK == CheckEvaluationResultKind::ConstantExpression) { LValue LVal; LVal.setFrom(Info.Ctx, Value); - return CheckLValueConstantExpression(Info, DiagLoc, Type, LVal, Usage); + return CheckLValueConstantExpression(Info, DiagLoc, Type, LVal, Usage, + CheckedTemps); } - if (Value.isMemberPointer()) + if (Value.isMemberPointer() && + CERK == CheckEvaluationResultKind::ConstantExpression) return CheckMemberPointerConstantExpression(Info, DiagLoc, Type, Value, Usage); // Everything else is fine. return true; } +/// Check that this core constant expression value is a valid value for a +/// constant expression. If not, report an appropriate diagnostic. Does not +/// check that the expression is of literal type. +static bool +CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, QualType Type, + const APValue &Value, + Expr::ConstExprUsage Usage = Expr::EvaluateForCodeGen) { + CheckedTemporaries CheckedTemps; + return CheckEvaluationResult(CheckEvaluationResultKind::ConstantExpression, + Info, DiagLoc, Type, Value, Usage, + SourceLocation(), CheckedTemps); +} + +/// Check that this evaluated value is fully-initialized and can be loaded by +/// an lvalue-to-rvalue conversion. +static bool CheckFullyInitialized(EvalInfo &Info, SourceLocation DiagLoc, + QualType Type, const APValue &Value) { + CheckedTemporaries CheckedTemps; + return CheckEvaluationResult( + CheckEvaluationResultKind::FullyInitialized, Info, DiagLoc, Type, Value, + Expr::EvaluateForCodeGen, SourceLocation(), CheckedTemps); +} + +/// Enforce C++2a [expr.const]/4.17, which disallows new-expressions unless +/// "the allocated storage is deallocated within the evaluation". +static bool CheckMemoryLeaks(EvalInfo &Info) { + if (!Info.HeapAllocs.empty()) { + // We can still fold to a constant despite a compile-time memory leak, + // so long as the heap allocation isn't referenced in the result (we check + // that in CheckConstantExpression). + Info.CCEDiag(Info.HeapAllocs.begin()->second.AllocExpr, + diag::note_constexpr_memory_leak) + << unsigned(Info.HeapAllocs.size() - 1); + } + return true; +} + static bool EvalPointerValueAsBool(const APValue &Value, bool &Result) { // A null base expression indicates a null pointer. These are always // evaluatable, and they are false unless the offset is zero. @@ -2628,9 +2960,10 @@ static APSInt extractStringLiteralCharacter(EvalInfo &Info, const Expr *Lit, // FIXME: This is inefficient; we should probably introduce something similar // to the LLVM ConstantDataArray to make this cheaper. static void expandStringLiteral(EvalInfo &Info, const StringLiteral *S, - APValue &Result) { - const ConstantArrayType *CAT = - Info.Ctx.getAsConstantArrayType(S->getType()); + APValue &Result, + QualType AllocType = QualType()) { + const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType( + AllocType.isNull() ? S->getType() : AllocType); assert(CAT && "string literal isn't an array"); QualType CharType = CAT->getElementType(); assert(CharType->isIntegerType() && "unexpected character type"); @@ -2694,8 +3027,8 @@ static bool isReadByLvalueToRvalueConversion(QualType T) { /// Diagnose an attempt to read from any unreadable field within the specified /// type, which might be a class type. -static bool diagnoseUnreadableFields(EvalInfo &Info, const Expr *E, - QualType T) { +static bool diagnoseMutableFields(EvalInfo &Info, const Expr *E, AccessKinds AK, + QualType T) { CXXRecordDecl *RD = T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl(); if (!RD) return false; @@ -2710,17 +3043,17 @@ static bool diagnoseUnreadableFields(EvalInfo &Info, const Expr *E, // FIXME: Add core issue number for the union case. if (Field->isMutable() && (RD->isUnion() || isReadByLvalueToRvalueConversion(Field->getType()))) { - Info.FFDiag(E, diag::note_constexpr_ltor_mutable, 1) << Field; + Info.FFDiag(E, diag::note_constexpr_access_mutable, 1) << AK << Field; Info.Note(Field->getLocation(), diag::note_declared_at); return true; } - if (diagnoseUnreadableFields(Info, E, Field->getType())) + if (diagnoseMutableFields(Info, E, AK, Field->getType())) return true; } for (auto &BaseSpec : RD->bases()) - if (diagnoseUnreadableFields(Info, E, BaseSpec.getType())) + if (diagnoseMutableFields(Info, E, AK, BaseSpec.getType())) return true; // All mutable fields were empty, and thus not actually read. @@ -2728,7 +3061,8 @@ static bool diagnoseUnreadableFields(EvalInfo &Info, const Expr *E, } static bool lifetimeStartedInEvaluation(EvalInfo &Info, - APValue::LValueBase Base) { + APValue::LValueBase Base, + bool MutableSubobject = false) { // A temporary we created. if (Base.getCallIndex()) return true; @@ -2737,19 +3071,42 @@ static bool lifetimeStartedInEvaluation(EvalInfo &Info, if (!Evaluating) return false; - // The variable whose initializer we're evaluating. - if (auto *BaseD = Base.dyn_cast()) - if (declaresSameEntity(Evaluating, BaseD)) - return true; + auto *BaseD = Base.dyn_cast(); - // A temporary lifetime-extended by the variable whose initializer we're - // evaluating. - if (auto *BaseE = Base.dyn_cast()) - if (auto *BaseMTE = dyn_cast(BaseE)) - if (declaresSameEntity(BaseMTE->getExtendingDecl(), Evaluating)) - return true; + switch (Info.IsEvaluatingDecl) { + case EvalInfo::EvaluatingDeclKind::None: + return false; - return false; + case EvalInfo::EvaluatingDeclKind::Ctor: + // The variable whose initializer we're evaluating. + if (BaseD) + return declaresSameEntity(Evaluating, BaseD); + + // A temporary lifetime-extended by the variable whose initializer we're + // evaluating. + if (auto *BaseE = Base.dyn_cast()) + if (auto *BaseMTE = dyn_cast(BaseE)) + return declaresSameEntity(BaseMTE->getExtendingDecl(), Evaluating); + return false; + + case EvalInfo::EvaluatingDeclKind::Dtor: + // C++2a [expr.const]p6: + // [during constant destruction] the lifetime of a and its non-mutable + // subobjects (but not its mutable subobjects) [are] considered to start + // within e. + // + // FIXME: We can meaningfully extend this to cover non-const objects, but + // we will need special handling: we should be able to access only + // subobjects of such objects that are themselves declared const. + if (!BaseD || + !(BaseD->getType().isConstQualified() || + BaseD->getType()->isReferenceType()) || + MutableSubobject) + return false; + return declaresSameEntity(Evaluating, BaseD); + } + + llvm_unreachable("unknown evaluating decl kind"); } namespace { @@ -2767,13 +3124,13 @@ struct CompleteObject { CompleteObject(APValue::LValueBase Base, APValue *Value, QualType Type) : Base(Base), Value(Value), Type(Type) {} - bool mayReadMutableMembers(EvalInfo &Info) const { + bool mayAccessMutableMembers(EvalInfo &Info, AccessKinds AK) const { // In C++14 onwards, it is permitted to read a mutable member whose // lifetime began within the evaluation. // FIXME: Should we also allow this in C++11? if (!Info.getLangOpts().CPlusPlus14) return false; - return lifetimeStartedInEvaluation(Info, Base); + return lifetimeStartedInEvaluation(Info, Base, /*MutableSubobject*/true); } explicit operator bool() const { return !Type.isNull(); } @@ -2821,19 +3178,22 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, // Walk the designator's path to find the subobject. for (unsigned I = 0, N = Sub.Entries.size(); /**/; ++I) { // Reading an indeterminate value is undefined, but assigning over one is OK. - if (O->isAbsent() || (O->isIndeterminate() && handler.AccessKind != AK_Assign)) { + if ((O->isAbsent() && handler.AccessKind != AK_Construct) || + (O->isIndeterminate() && handler.AccessKind != AK_Construct && + handler.AccessKind != AK_Assign && + handler.AccessKind != AK_ReadObjectRepresentation)) { if (!Info.checkingPotentialConstantExpression()) Info.FFDiag(E, diag::note_constexpr_access_uninit) << handler.AccessKind << O->isIndeterminate(); return handler.failed(); } - // C++ [class.ctor]p5: + // C++ [class.ctor]p5, C++ [class.dtor]p5: // const and volatile semantics are not applied on an object under - // construction. + // {con,de}struction. if ((ObjType.isConstQualified() || ObjType.isVolatileQualified()) && ObjType->isRecordType() && - Info.isEvaluatingConstructor( + Info.isEvaluatingCtorDtor( Obj.Base, llvm::makeArrayRef(Sub.Entries.begin(), Sub.Entries.begin() + I)) != ConstructionPhase::None) { @@ -2876,9 +3236,9 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, // things we need to check: if there are any mutable subobjects, we // cannot perform this read. (This only happens when performing a trivial // copy or assignment.) - if (ObjType->isRecordType() && handler.AccessKind == AK_Read && - !Obj.mayReadMutableMembers(Info) && - diagnoseUnreadableFields(Info, E, ObjType)) + if (ObjType->isRecordType() && + !Obj.mayAccessMutableMembers(Info, handler.AccessKind) && + diagnoseMutableFields(Info, E, handler.AccessKind, ObjType)) return handler.failed(); } @@ -2916,7 +3276,7 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, if (O->getArrayInitializedElts() > Index) O = &O->getArrayInitializedElt(Index); - else if (handler.AccessKind != AK_Read) { + else if (!isRead(handler.AccessKind)) { expandArray(*O, Index); O = &O->getArrayInitializedElt(Index); } else @@ -2946,10 +3306,10 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, : O->getComplexFloatReal(), ObjType); } } else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) { - if (Field->isMutable() && handler.AccessKind == AK_Read && - !Obj.mayReadMutableMembers(Info)) { - Info.FFDiag(E, diag::note_constexpr_ltor_mutable, 1) - << Field; + if (Field->isMutable() && + !Obj.mayAccessMutableMembers(Info, handler.AccessKind)) { + Info.FFDiag(E, diag::note_constexpr_access_mutable, 1) + << handler.AccessKind << Field; Info.Note(Field->getLocation(), diag::note_declared_at); return handler.failed(); } @@ -2960,9 +3320,18 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, const FieldDecl *UnionField = O->getUnionField(); if (!UnionField || UnionField->getCanonicalDecl() != Field->getCanonicalDecl()) { - Info.FFDiag(E, diag::note_constexpr_access_inactive_union_member) - << handler.AccessKind << Field << !UnionField << UnionField; - return handler.failed(); + if (I == N - 1 && handler.AccessKind == AK_Construct) { + // Placement new onto an inactive union member makes it active. + O->setUnion(Field, APValue()); + } else { + // FIXME: If O->getUnionValue() is absent, report that there's no + // active union member rather than reporting the prior active union + // member. We'll need to fix nullptr_t to not use APValue() as its + // representation first. + Info.FFDiag(E, diag::note_constexpr_access_inactive_union_member) + << handler.AccessKind << Field << !UnionField << UnionField; + return handler.failed(); + } } O = &O->getUnionValue(); } else @@ -2986,15 +3355,17 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, namespace { struct ExtractSubobjectHandler { EvalInfo &Info; + const Expr *E; APValue &Result; - - static const AccessKinds AccessKind = AK_Read; + const AccessKinds AccessKind; typedef bool result_type; bool failed() { return false; } bool found(APValue &Subobj, QualType SubobjType) { Result = Subobj; - return true; + if (AccessKind == AK_ReadObjectRepresentation) + return true; + return CheckFullyInitialized(Info, E->getExprLoc(), SubobjType, Result); } bool found(APSInt &Value, QualType SubobjType) { Result = APValue(Value); @@ -3007,14 +3378,13 @@ struct ExtractSubobjectHandler { }; } // end anonymous namespace -const AccessKinds ExtractSubobjectHandler::AccessKind; - /// Extract the designated sub-object of an rvalue. static bool extractSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, - const SubobjectDesignator &Sub, - APValue &Result) { - ExtractSubobjectHandler Handler = { Info, Result }; + const SubobjectDesignator &Sub, APValue &Result, + AccessKinds AK = AK_Read) { + assert(AK == AK_Read || AK == AK_ReadObjectRepresentation); + ExtractSubobjectHandler Handler = {Info, E, Result, AK}; return findSubobject(Info, E, Obj, Sub, Handler); } @@ -3160,13 +3530,13 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, } } - bool IsAccess = isFormalAccess(AK); + bool IsAccess = isAnyAccess(AK); // C++11 DR1311: An lvalue-to-rvalue conversion on a volatile-qualified type // is not a constant expression (even if the object is non-volatile). We also // apply this rule to C++98, in order to conform to the expected 'volatile' // semantics. - if (IsAccess && LValType.isVolatileQualified()) { + if (isFormalAccess(AK) && LValType.isVolatileQualified()) { if (Info.getLangOpts().CPlusPlus) Info.FFDiag(E, diag::note_constexpr_access_volatile_type) << AK << LValType; @@ -3201,8 +3571,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, // the variable we're reading must be const. if (!Frame) { if (Info.getLangOpts().CPlusPlus14 && - declaresSameEntity( - VD, Info.EvaluatingDecl.dyn_cast())) { + lifetimeStartedInEvaluation(Info, LVal.Base)) { // OK, we can read and modify an object if we're in the process of // evaluating its initializer, because its lifetime began in this // evaluation. @@ -3261,6 +3630,14 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, if (!evaluateVarDeclInit(Info, E, VD, Frame, BaseVal, &LVal)) return CompleteObject(); + } else if (DynamicAllocLValue DA = LVal.Base.dyn_cast()) { + Optional Alloc = Info.lookupDynamicAlloc(DA); + if (!Alloc) { + Info.FFDiag(E, diag::note_constexpr_access_deleted_object) << AK; + return CompleteObject(); + } + return CompleteObject(LVal.Base, &(*Alloc)->Value, + LVal.Base.getDynamicAllocType()); } else { const Expr *Base = LVal.Base.dyn_cast(); @@ -3284,11 +3661,14 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, // int x = ++r; // constexpr int k = r; // Therefore we use the C++14 rules in C++11 too. - const ValueDecl *VD = Info.EvaluatingDecl.dyn_cast(); - const ValueDecl *ED = MTE->getExtendingDecl(); + // + // Note that temporaries whose lifetimes began while evaluating a + // variable's constructor are not usable while evaluating the + // corresponding destructor, not even if they're of const-qualified + // types. if (!(BaseType.isConstQualified() && BaseType->isIntegralOrEnumerationType()) && - !(VD && VD->getCanonicalDecl() == ED->getCanonicalDecl())) { + !lifetimeStartedInEvaluation(Info, LVal.Base)) { if (!IsAccess) return CompleteObject(LVal.getLValueBase(), nullptr, BaseType); Info.FFDiag(E, diag::note_constexpr_access_static_temporary, 1) << AK; @@ -3340,15 +3720,22 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, /// case of a non-class type). /// \param LVal - The glvalue on which we are attempting to perform this action. /// \param RVal - The produced value will be placed here. -static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, - QualType Type, - const LValue &LVal, APValue &RVal) { +/// \param WantObjectRepresentation - If true, we're looking for the object +/// representation rather than the value, and in particular, +/// there is no requirement that the result be fully initialized. +static bool +handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, QualType Type, + const LValue &LVal, APValue &RVal, + bool WantObjectRepresentation = false) { if (LVal.Designator.Invalid) return false; // Check for special cases where there is no existing APValue to look at. const Expr *Base = LVal.Base.dyn_cast(); + AccessKinds AK = + WantObjectRepresentation ? AK_ReadObjectRepresentation : AK_Read; + if (Base && !LVal.getLValueCallIndex() && !Type.isVolatileQualified()) { if (const CompoundLiteralExpr *CLE = dyn_cast(Base)) { // In C99, a CompoundLiteralExpr is an lvalue, and we defer evaluating the @@ -3362,7 +3749,7 @@ static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, if (!Evaluate(Lit, Info, CLE->getInitializer())) return false; CompleteObject LitObj(LVal.Base, &Lit, Base->getType()); - return extractSubobject(Info, Conv, LitObj, LVal.Designator, RVal); + return extractSubobject(Info, Conv, LitObj, LVal.Designator, RVal, AK); } else if (isa(Base) || isa(Base)) { // Special-case character extraction so we don't have to construct an // APValue for the whole string. @@ -3377,7 +3764,7 @@ static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, } if (LVal.Designator.isOnePastTheEnd()) { if (Info.getLangOpts().CPlusPlus11) - Info.FFDiag(Conv, diag::note_constexpr_access_past_end) << AK_Read; + Info.FFDiag(Conv, diag::note_constexpr_access_past_end) << AK; else Info.FFDiag(Conv); return false; @@ -3388,8 +3775,8 @@ static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, } } - CompleteObject Obj = findCompleteObject(Info, Conv, AK_Read, LVal, Type); - return Obj && extractSubobject(Info, Conv, Obj, LVal.Designator, RVal); + CompleteObject Obj = findCompleteObject(Info, Conv, AK, LVal, Type); + return Obj && extractSubobject(Info, Conv, Obj, LVal.Designator, RVal, AK); } /// Perform an assignment of Val to LVal. Takes ownership of Val. @@ -3681,7 +4068,7 @@ static bool handleIncDec(EvalInfo &Info, const Expr *E, const LValue &LVal, /// Build an lvalue for the object argument of a member function call. static bool EvaluateObjectArgument(EvalInfo &Info, const Expr *Object, LValue &This) { - if (Object->getType()->isPointerType()) + if (Object->getType()->isPointerType() && Object->isRValue()) return EvaluatePointer(Object, This, Info); if (Object->isGLValue()) @@ -3843,6 +4230,40 @@ static bool HandleBaseToDerivedCast(EvalInfo &Info, const CastExpr *E, return CastToDerivedClass(Info, E, Result, TargetType, NewEntriesSize); } +/// Get the value to use for a default-initialized object of type T. +static APValue getDefaultInitValue(QualType T) { + if (auto *RD = T->getAsCXXRecordDecl()) { + if (RD->isUnion()) + return APValue((const FieldDecl*)nullptr); + + APValue Struct(APValue::UninitStruct(), RD->getNumBases(), + std::distance(RD->field_begin(), RD->field_end())); + + unsigned Index = 0; + for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(), + End = RD->bases_end(); I != End; ++I, ++Index) + Struct.getStructBase(Index) = getDefaultInitValue(I->getType()); + + for (const auto *I : RD->fields()) { + if (I->isUnnamedBitfield()) + continue; + Struct.getStructField(I->getFieldIndex()) = + getDefaultInitValue(I->getType()); + } + return Struct; + } + + if (auto *AT = + dyn_cast_or_null(T->getAsArrayTypeUnsafe())) { + APValue Array(APValue::UninitArray(), 0, AT->getSize().getZExtValue()); + if (Array.hasArrayFiller()) + Array.getArrayFiller() = getDefaultInitValue(AT->getElementType()); + return Array; + } + + return APValue::IndeterminateValue(); +} + namespace { enum EvalStmtResult { /// Evaluation failed. @@ -3866,14 +4287,13 @@ static bool EvaluateVarDecl(EvalInfo &Info, const VarDecl *VD) { return true; LValue Result; - APValue &Val = createTemporary(VD, true, Result, *Info.CurrentCall); + APValue &Val = + Info.CurrentCall->createTemporary(VD, VD->getType(), true, Result); const Expr *InitE = VD->getInit(); if (!InitE) { - Info.FFDiag(VD->getBeginLoc(), diag::note_constexpr_uninitialized) - << false << VD->getType(); - Val = APValue(); - return false; + Val = getDefaultInitValue(VD->getType()); + return true; } if (InitE->isValueDependent()) @@ -3910,7 +4330,9 @@ static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl, FullExpressionRAII Scope(Info); if (CondDecl && !EvaluateDecl(Info, CondDecl)) return false; - return EvaluateAsBooleanCondition(Cond, Result, Info); + if (!EvaluateAsBooleanCondition(Cond, Result, Info)) + return false; + return Scope.destroy(); } namespace { @@ -3946,7 +4368,12 @@ static EvalStmtResult EvaluateLoopBody(StmtResult &Result, EvalInfo &Info, const Stmt *Body, const SwitchCase *Case = nullptr) { BlockScopeRAII Scope(Info); - switch (EvalStmtResult ESR = EvaluateStmt(Result, Info, Body, Case)) { + + EvalStmtResult ESR = EvaluateStmt(Result, Info, Body, Case); + if (ESR != ESR_Failed && ESR != ESR_CaseNotFound && !Scope.destroy()) + ESR = ESR_Failed; + + switch (ESR) { case ESR_Break: return ESR_Succeeded; case ESR_Succeeded: @@ -3968,17 +4395,23 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info, // Evaluate the switch condition. APSInt Value; { - FullExpressionRAII Scope(Info); if (const Stmt *Init = SS->getInit()) { EvalStmtResult ESR = EvaluateStmt(Result, Info, Init); - if (ESR != ESR_Succeeded) + if (ESR != ESR_Succeeded) { + if (ESR != ESR_Failed && !Scope.destroy()) + ESR = ESR_Failed; return ESR; + } } + + FullExpressionRAII CondScope(Info); if (SS->getConditionVariable() && !EvaluateDecl(Info, SS->getConditionVariable())) return ESR_Failed; if (!EvaluateInteger(SS->getCond(), Value, Info)) return ESR_Failed; + if (!CondScope.destroy()) + return ESR_Failed; } // Find the switch case corresponding to the value of the condition. @@ -4002,10 +4435,14 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info, } if (!Found) - return ESR_Succeeded; + return Scope.destroy() ? ESR_Failed : ESR_Succeeded; // Search the switch body for the switch case and evaluate it from there. - switch (EvalStmtResult ESR = EvaluateStmt(Result, Info, SS->getBody(), Found)) { + EvalStmtResult ESR = EvaluateStmt(Result, Info, SS->getBody(), Found); + if (ESR != ESR_Failed && ESR != ESR_CaseNotFound && !Scope.destroy()) + return ESR_Failed; + + switch (ESR) { case ESR_Break: return ESR_Succeeded; case ESR_Succeeded: @@ -4032,10 +4469,6 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, // If we're hunting down a 'case' or 'default' label, recurse through // substatements until we hit the label. if (Case) { - // FIXME: We don't start the lifetime of objects whose initialization we - // jump over. However, such objects must be of class type with a trivial - // default constructor that initialize all subobjects, so must be empty, - // so this almost never matters. switch (S->getStmtClass()) { case Stmt::CompoundStmtClass: // FIXME: Precompute which substatement of a compound statement we @@ -4061,10 +4494,35 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, // preceded by our switch label. BlockScopeRAII Scope(Info); + // Step into the init statement in case it brings an (uninitialized) + // variable into scope. + if (const Stmt *Init = IS->getInit()) { + EvalStmtResult ESR = EvaluateStmt(Result, Info, Init, Case); + if (ESR != ESR_CaseNotFound) { + assert(ESR != ESR_Succeeded); + return ESR; + } + } + + // Condition variable must be initialized if it exists. + // FIXME: We can skip evaluating the body if there's a condition + // variable, as there can't be any case labels within it. + // (The same is true for 'for' statements.) + EvalStmtResult ESR = EvaluateStmt(Result, Info, IS->getThen(), Case); - if (ESR != ESR_CaseNotFound || !IS->getElse()) + if (ESR == ESR_Failed) + return ESR; + if (ESR != ESR_CaseNotFound) + return Scope.destroy() ? ESR : ESR_Failed; + if (!IS->getElse()) + return ESR_CaseNotFound; + + ESR = EvaluateStmt(Result, Info, IS->getElse(), Case); + if (ESR == ESR_Failed) return ESR; - return EvaluateStmt(Result, Info, IS->getElse(), Case); + if (ESR != ESR_CaseNotFound) + return Scope.destroy() ? ESR : ESR_Failed; + return ESR_CaseNotFound; } case Stmt::WhileStmtClass: { @@ -4077,21 +4535,47 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, case Stmt::ForStmtClass: { const ForStmt *FS = cast(S); + BlockScopeRAII Scope(Info); + + // Step into the init statement in case it brings an (uninitialized) + // variable into scope. + if (const Stmt *Init = FS->getInit()) { + EvalStmtResult ESR = EvaluateStmt(Result, Info, Init, Case); + if (ESR != ESR_CaseNotFound) { + assert(ESR != ESR_Succeeded); + return ESR; + } + } + EvalStmtResult ESR = EvaluateLoopBody(Result, Info, FS->getBody(), Case); if (ESR != ESR_Continue) return ESR; if (FS->getInc()) { FullExpressionRAII IncScope(Info); - if (!EvaluateIgnoredValue(Info, FS->getInc())) + if (!EvaluateIgnoredValue(Info, FS->getInc()) || !IncScope.destroy()) return ESR_Failed; } break; } - case Stmt::DeclStmtClass: - // FIXME: If the variable has initialization that can't be jumped over, - // bail out of any immediately-surrounding compound-statement too. + case Stmt::DeclStmtClass: { + // Start the lifetime of any uninitialized variables we encounter. They + // might be used by the selected branch of the switch. + const DeclStmt *DS = cast(S); + for (const auto *D : DS->decls()) { + if (const auto *VD = dyn_cast(D)) { + if (VD->hasLocalStorage() && !VD->getInit()) + if (!EvaluateVarDecl(Info, VD)) + return ESR_Failed; + // FIXME: If the variable has initialization that can't be jumped + // over, bail out of any immediately-surrounding compound-statement + // too. There can't be any case labels here. + } + } + return ESR_CaseNotFound; + } + default: return ESR_CaseNotFound; } @@ -4102,8 +4586,10 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, if (const Expr *E = dyn_cast(S)) { // Don't bother evaluating beyond an expression-statement which couldn't // be evaluated. + // FIXME: Do we need the FullExpressionRAII object here? + // VisitExprWithCleanups should create one when necessary. FullExpressionRAII Scope(Info); - if (!EvaluateIgnoredValue(Info, E)) + if (!EvaluateIgnoredValue(Info, E) || !Scope.destroy()) return ESR_Failed; return ESR_Succeeded; } @@ -4116,12 +4602,12 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, case Stmt::DeclStmtClass: { const DeclStmt *DS = cast(S); - for (const auto *DclIt : DS->decls()) { + for (const auto *D : DS->decls()) { // Each declaration initialization is its own full-expression. - // FIXME: This isn't quite right; if we're performing aggregate - // initialization, each braced subexpression is its own full-expression. FullExpressionRAII Scope(Info); - if (!EvaluateDecl(Info, DclIt) && !Info.noteFailure()) + if (!EvaluateDecl(Info, D) && !Info.noteFailure()) + return ESR_Failed; + if (!Scope.destroy()) return ESR_Failed; } return ESR_Succeeded; @@ -4135,7 +4621,7 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, ? EvaluateInPlace(Result.Value, Info, *Result.Slot, RetExpr) : Evaluate(Result.Value, Info, RetExpr))) return ESR_Failed; - return ESR_Returned; + return Scope.destroy() ? ESR_Returned : ESR_Failed; } case Stmt::CompoundStmtClass: { @@ -4146,10 +4632,15 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, EvalStmtResult ESR = EvaluateStmt(Result, Info, BI, Case); if (ESR == ESR_Succeeded) Case = nullptr; - else if (ESR != ESR_CaseNotFound) + else if (ESR != ESR_CaseNotFound) { + if (ESR != ESR_Failed && !Scope.destroy()) + return ESR_Failed; return ESR; + } } - return Case ? ESR_CaseNotFound : ESR_Succeeded; + if (Case) + return ESR_CaseNotFound; + return Scope.destroy() ? ESR_Succeeded : ESR_Failed; } case Stmt::IfStmtClass: { @@ -4159,8 +4650,11 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, BlockScopeRAII Scope(Info); if (const Stmt *Init = IS->getInit()) { EvalStmtResult ESR = EvaluateStmt(Result, Info, Init); - if (ESR != ESR_Succeeded) + if (ESR != ESR_Succeeded) { + if (ESR != ESR_Failed && !Scope.destroy()) + return ESR_Failed; return ESR; + } } bool Cond; if (!EvaluateCond(Info, IS->getConditionVariable(), IS->getCond(), Cond)) @@ -4168,10 +4662,13 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, if (const Stmt *SubStmt = Cond ? IS->getThen() : IS->getElse()) { EvalStmtResult ESR = EvaluateStmt(Result, Info, SubStmt); - if (ESR != ESR_Succeeded) + if (ESR != ESR_Succeeded) { + if (ESR != ESR_Failed && !Scope.destroy()) + return ESR_Failed; return ESR; + } } - return ESR_Succeeded; + return Scope.destroy() ? ESR_Succeeded : ESR_Failed; } case Stmt::WhileStmtClass: { @@ -4186,8 +4683,13 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, break; EvalStmtResult ESR = EvaluateLoopBody(Result, Info, WS->getBody()); - if (ESR != ESR_Continue) + if (ESR != ESR_Continue) { + if (ESR != ESR_Failed && !Scope.destroy()) + return ESR_Failed; return ESR; + } + if (!Scope.destroy()) + return ESR_Failed; } return ESR_Succeeded; } @@ -4202,7 +4704,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, Case = nullptr; FullExpressionRAII CondScope(Info); - if (!EvaluateAsBooleanCondition(DS->getCond(), Continue, Info)) + if (!EvaluateAsBooleanCondition(DS->getCond(), Continue, Info) || + !CondScope.destroy()) return ESR_Failed; } while (Continue); return ESR_Succeeded; @@ -4210,14 +4713,17 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, case Stmt::ForStmtClass: { const ForStmt *FS = cast(S); - BlockScopeRAII Scope(Info); + BlockScopeRAII ForScope(Info); if (FS->getInit()) { EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getInit()); - if (ESR != ESR_Succeeded) + if (ESR != ESR_Succeeded) { + if (ESR != ESR_Failed && !ForScope.destroy()) + return ESR_Failed; return ESR; + } } while (true) { - BlockScopeRAII Scope(Info); + BlockScopeRAII IterScope(Info); bool Continue = true; if (FS->getCond() && !EvaluateCond(Info, FS->getConditionVariable(), FS->getCond(), Continue)) @@ -4226,16 +4732,22 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, break; EvalStmtResult ESR = EvaluateLoopBody(Result, Info, FS->getBody()); - if (ESR != ESR_Continue) + if (ESR != ESR_Continue) { + if (ESR != ESR_Failed && (!IterScope.destroy() || !ForScope.destroy())) + return ESR_Failed; return ESR; + } if (FS->getInc()) { FullExpressionRAII IncScope(Info); - if (!EvaluateIgnoredValue(Info, FS->getInc())) + if (!EvaluateIgnoredValue(Info, FS->getInc()) || !IncScope.destroy()) return ESR_Failed; } + + if (!IterScope.destroy()) + return ESR_Failed; } - return ESR_Succeeded; + return ForScope.destroy() ? ESR_Succeeded : ESR_Failed; } case Stmt::CXXForRangeStmtClass: { @@ -4245,22 +4757,34 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, // Evaluate the init-statement if present. if (FS->getInit()) { EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getInit()); - if (ESR != ESR_Succeeded) + if (ESR != ESR_Succeeded) { + if (ESR != ESR_Failed && !Scope.destroy()) + return ESR_Failed; return ESR; + } } // Initialize the __range variable. EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getRangeStmt()); - if (ESR != ESR_Succeeded) + if (ESR != ESR_Succeeded) { + if (ESR != ESR_Failed && !Scope.destroy()) + return ESR_Failed; return ESR; + } // Create the __begin and __end iterators. ESR = EvaluateStmt(Result, Info, FS->getBeginStmt()); - if (ESR != ESR_Succeeded) + if (ESR != ESR_Succeeded) { + if (ESR != ESR_Failed && !Scope.destroy()) + return ESR_Failed; return ESR; + } ESR = EvaluateStmt(Result, Info, FS->getEndStmt()); - if (ESR != ESR_Succeeded) + if (ESR != ESR_Succeeded) { + if (ESR != ESR_Failed && !Scope.destroy()) + return ESR_Failed; return ESR; + } while (true) { // Condition: __begin != __end. @@ -4276,20 +4800,29 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, // User's variable declaration, initialized by *__begin. BlockScopeRAII InnerScope(Info); ESR = EvaluateStmt(Result, Info, FS->getLoopVarStmt()); - if (ESR != ESR_Succeeded) + if (ESR != ESR_Succeeded) { + if (ESR != ESR_Failed && (!InnerScope.destroy() || !Scope.destroy())) + return ESR_Failed; return ESR; + } // Loop body. ESR = EvaluateLoopBody(Result, Info, FS->getBody()); - if (ESR != ESR_Continue) + if (ESR != ESR_Continue) { + if (ESR != ESR_Failed && (!InnerScope.destroy() || !Scope.destroy())) + return ESR_Failed; return ESR; + } // Increment: ++__begin if (!EvaluateIgnoredValue(Info, FS->getInc())) return ESR_Failed; + + if (!InnerScope.destroy()) + return ESR_Failed; } - return ESR_Succeeded; + return Scope.destroy() ? ESR_Succeeded : ESR_Failed; } case Stmt::SwitchStmtClass: @@ -4464,9 +4997,13 @@ static bool checkDynamicType(EvalInfo &Info, const Expr *E, const LValue &This, /// Check that the pointee of the 'this' pointer in a member function call is /// either within its lifetime or in its period of construction or destruction. -static bool checkNonVirtualMemberCallThisPointer(EvalInfo &Info, const Expr *E, - const LValue &This) { - return checkDynamicType(Info, E, This, AK_MemberCall, false); +static bool +checkNonVirtualMemberCallThisPointer(EvalInfo &Info, const Expr *E, + const LValue &This, + const CXXMethodDecl *NamedMember) { + return checkDynamicType( + Info, E, This, + isa(NamedMember) ? AK_Destroy : AK_MemberCall, false); } struct DynamicType { @@ -4514,16 +5051,19 @@ static Optional ComputeDynamicType(EvalInfo &Info, const Expr *E, ArrayRef Path = This.Designator.Entries; for (unsigned PathLength = This.Designator.MostDerivedPathLength; PathLength <= Path.size(); ++PathLength) { - switch (Info.isEvaluatingConstructor(This.getLValueBase(), - Path.slice(0, PathLength))) { + switch (Info.isEvaluatingCtorDtor(This.getLValueBase(), + Path.slice(0, PathLength))) { case ConstructionPhase::Bases: - // We're constructing a base class. This is not the dynamic type. + case ConstructionPhase::DestroyingBases: + // We're constructing or destroying a base class. This is not the dynamic + // type. break; case ConstructionPhase::None: case ConstructionPhase::AfterBases: - // We've finished constructing the base classes, so this is the dynamic - // type. + case ConstructionPhase::Destroying: + // We've finished constructing the base classes and not yet started + // destroying them again, so this is the dynamic type. return DynamicType{getBaseClassType(This.Designator, PathLength), PathLength}; } @@ -4540,8 +5080,9 @@ static Optional ComputeDynamicType(EvalInfo &Info, const Expr *E, static const CXXMethodDecl *HandleVirtualDispatch( EvalInfo &Info, const Expr *E, LValue &This, const CXXMethodDecl *Found, llvm::SmallVectorImpl &CovariantAdjustmentPath) { - Optional DynType = - ComputeDynamicType(Info, E, This, AK_MemberCall); + Optional DynType = ComputeDynamicType( + Info, E, This, + isa(Found) ? AK_Destroy : AK_MemberCall); if (!DynType) return nullptr; @@ -4677,8 +5218,7 @@ static bool HandleDynamicCast(EvalInfo &Info, const ExplicitCastExpr *E, if (!E->isGLValue()) { // The value of a failed cast to pointer type is the null pointer value // of the required result type. - auto TargetVal = Info.Ctx.getTargetNullPointerValue(E->getType()); - Ptr.setNull(E->getType(), TargetVal); + Ptr.setNull(Info.Ctx, E->getType()); return true; } @@ -4743,39 +5283,6 @@ struct StartLifetimeOfUnionMemberHandler { static const AccessKinds AccessKind = AK_Assign; - APValue getDefaultInitValue(QualType SubobjType) { - if (auto *RD = SubobjType->getAsCXXRecordDecl()) { - if (RD->isUnion()) - return APValue((const FieldDecl*)nullptr); - - APValue Struct(APValue::UninitStruct(), RD->getNumBases(), - std::distance(RD->field_begin(), RD->field_end())); - - unsigned Index = 0; - for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(), - End = RD->bases_end(); I != End; ++I, ++Index) - Struct.getStructBase(Index) = getDefaultInitValue(I->getType()); - - for (const auto *I : RD->fields()) { - if (I->isUnnamedBitfield()) - continue; - Struct.getStructField(I->getFieldIndex()) = - getDefaultInitValue(I->getType()); - } - return Struct; - } - - if (auto *AT = dyn_cast_or_null( - SubobjType->getAsArrayTypeUnsafe())) { - APValue Array(APValue::UninitArray(), 0, AT->getSize().getZExtValue()); - if (Array.hasArrayFiller()) - Array.getArrayFiller() = getDefaultInitValue(AT->getElementType()); - return Array; - } - - return APValue::IndeterminateValue(); - } - typedef bool result_type; bool failed() { return false; } bool found(APValue &Subobj, QualType SubobjType) { @@ -4788,7 +5295,8 @@ struct StartLifetimeOfUnionMemberHandler { // * No variant members' lifetimes begin // * All scalar subobjects whose lifetimes begin have indeterminate values assert(SubobjType->isUnionType()); - if (!declaresSameEntity(Subobj.getUnionField(), Field)) + if (!declaresSameEntity(Subobj.getUnionField(), Field) || + !Subobj.getUnionValue().hasValue()) Subobj.setUnion(Field, getDefaultInitValue(Field->getType())); return true; } @@ -4820,7 +5328,9 @@ static bool HandleUnionActiveMemberChange(EvalInfo &Info, const Expr *LHSExpr, // -- If E is of the form A.B, S(E) contains the elements of S(A)... if (auto *ME = dyn_cast(E)) { auto *FD = dyn_cast(ME->getMemberDecl()); - if (!FD) + // Note that we can't implicitly start the lifetime of a reference, + // so we don't need to proceed any further if we reach one. + if (!FD || FD->getType()->isReferenceType()) break; // ... and also contains A.B if B names a union member @@ -4981,8 +5491,8 @@ static bool HandleFunctionCall(SourceLocation CallLoc, LValue RHS; RHS.setFrom(Info.Ctx, ArgValues[0]); APValue RHSValue; - if (!handleLValueToRValueConversion(Info, Args[0], Args[0]->getType(), - RHS, RHSValue)) + if (!handleLValueToRValueConversion(Info, Args[0], Args[0]->getType(), RHS, + RHSValue, MD->getParent()->isUnion())) return false; if (Info.getLangOpts().CPlusPlus2a && MD->isTrivial() && !HandleUnionActiveMemberChange(Info, Args[0], *This)) @@ -5045,7 +5555,8 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, CXXConstructorDecl::init_const_iterator I = Definition->init_begin(); { FullExpressionRAII InitScope(Info); - if (!EvaluateInPlace(Result, Info, This, (*I)->getInit())) + if (!EvaluateInPlace(Result, Info, This, (*I)->getInit()) || + !InitScope.destroy()) return false; } return EvaluateStmt(Ret, Info, Definition->getBody()) != ESR_Failed; @@ -5066,7 +5577,7 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, RHS.setFrom(Info.Ctx, ArgValues[0]); return handleLValueToRValueConversion( Info, E, Definition->getParamDecl(0)->getType().getNonReferenceType(), - RHS, Result); + RHS, Result, Definition->getParent()->isUnion()); } // Reserve space for the struct members. @@ -5085,6 +5596,25 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, #ifndef NDEBUG CXXRecordDecl::base_class_const_iterator BaseIt = RD->bases_begin(); #endif + CXXRecordDecl::field_iterator FieldIt = RD->field_begin(); + auto SkipToField = [&](FieldDecl *FD, bool Indirect) { + // We might be initializing the same field again if this is an indirect + // field initialization. + if (FieldIt == RD->field_end() || + FieldIt->getFieldIndex() > FD->getFieldIndex()) { + assert(Indirect && "fields out of order?"); + return; + } + + // Default-initialize any fields with no explicit initializer. + for (; !declaresSameEntity(*FieldIt, FD); ++FieldIt) { + assert(FieldIt != RD->field_end() && "missing field?"); + if (!FieldIt->isUnnamedBitfield()) + Result.getStructField(FieldIt->getFieldIndex()) = + getDefaultInitValue(FieldIt->getType()); + } + ++FieldIt; + }; for (const auto *I : Definition->inits()) { LValue Subobject = This; LValue SubobjectParent = This; @@ -5113,6 +5643,7 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, Result = APValue(FD); Value = &Result.getUnionValue(); } else { + SkipToField(FD, false); Value = &Result.getStructField(FD->getFieldIndex()); } } else if (IndirectFieldDecl *IFD = I->getIndirectMember()) { @@ -5132,8 +5663,10 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, if (CD->isUnion()) *Value = APValue(FD); else - *Value = APValue(APValue::UninitStruct(), CD->getNumBases(), - std::distance(CD->field_begin(), CD->field_end())); + // FIXME: This immediately starts the lifetime of all members of an + // anonymous struct. It would be preferable to strictly start member + // lifetime in initialization order. + *Value = getDefaultInitValue(Info.Ctx.getRecordType(CD)); } // Store Subobject as its parent before updating it for the last element // in the chain. @@ -5143,50 +5676,431 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, return false; if (CD->isUnion()) Value = &Value->getUnionValue(); - else + else { + if (C == IndirectFieldChain.front() && !RD->isUnion()) + SkipToField(FD, true); Value = &Value->getStructField(FD->getFieldIndex()); + } } } else { llvm_unreachable("unknown base initializer kind"); } - // Need to override This for implicit field initializers as in this case - // This refers to innermost anonymous struct/union containing initializer, - // not to currently constructed class. - const Expr *Init = I->getInit(); - ThisOverrideRAII ThisOverride(*Info.CurrentCall, &SubobjectParent, - isa(Init)); - FullExpressionRAII InitScope(Info); - if (!EvaluateInPlace(*Value, Info, Subobject, Init) || - (FD && FD->isBitField() && - !truncateBitfieldValue(Info, Init, *Value, FD))) { - // If we're checking for a potential constant expression, evaluate all - // initializers even if some of them fail. - if (!Info.noteFailure()) - return false; - Success = false; + // Need to override This for implicit field initializers as in this case + // This refers to innermost anonymous struct/union containing initializer, + // not to currently constructed class. + const Expr *Init = I->getInit(); + ThisOverrideRAII ThisOverride(*Info.CurrentCall, &SubobjectParent, + isa(Init)); + FullExpressionRAII InitScope(Info); + if (!EvaluateInPlace(*Value, Info, Subobject, Init) || + (FD && FD->isBitField() && + !truncateBitfieldValue(Info, Init, *Value, FD))) { + // If we're checking for a potential constant expression, evaluate all + // initializers even if some of them fail. + if (!Info.noteFailure()) + return false; + Success = false; + } + + // This is the point at which the dynamic type of the object becomes this + // class type. + if (I->isBaseInitializer() && BasesSeen == RD->getNumBases()) + EvalObj.finishedConstructingBases(); + } + + // Default-initialize any remaining fields. + if (!RD->isUnion()) { + for (; FieldIt != RD->field_end(); ++FieldIt) { + if (!FieldIt->isUnnamedBitfield()) + Result.getStructField(FieldIt->getFieldIndex()) = + getDefaultInitValue(FieldIt->getType()); + } + } + + return Success && + EvaluateStmt(Ret, Info, Definition->getBody()) != ESR_Failed && + LifetimeExtendedScope.destroy(); +} + +static bool HandleConstructorCall(const Expr *E, const LValue &This, + ArrayRef Args, + const CXXConstructorDecl *Definition, + EvalInfo &Info, APValue &Result) { + ArgVector ArgValues(Args.size()); + if (!EvaluateArgs(Args, ArgValues, Info, Definition)) + return false; + + return HandleConstructorCall(E, This, ArgValues.data(), Definition, + Info, Result); +} + +static bool HandleDestructionImpl(EvalInfo &Info, SourceLocation CallLoc, + const LValue &This, APValue &Value, + QualType T) { + // Objects can only be destroyed while they're within their lifetimes. + // FIXME: We have no representation for whether an object of type nullptr_t + // is in its lifetime; it usually doesn't matter. Perhaps we should model it + // as indeterminate instead? + if (Value.isAbsent() && !T->isNullPtrType()) { + APValue Printable; + This.moveInto(Printable); + Info.FFDiag(CallLoc, diag::note_constexpr_destroy_out_of_lifetime) + << Printable.getAsString(Info.Ctx, Info.Ctx.getLValueReferenceType(T)); + return false; + } + + // Invent an expression for location purposes. + // FIXME: We shouldn't need to do this. + OpaqueValueExpr LocE(CallLoc, Info.Ctx.IntTy, VK_RValue); + + // For arrays, destroy elements right-to-left. + if (const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(T)) { + uint64_t Size = CAT->getSize().getZExtValue(); + QualType ElemT = CAT->getElementType(); + + LValue ElemLV = This; + ElemLV.addArray(Info, &LocE, CAT); + if (!HandleLValueArrayAdjustment(Info, &LocE, ElemLV, ElemT, Size)) + return false; + + // Ensure that we have actual array elements available to destroy; the + // destructors might mutate the value, so we can't run them on the array + // filler. + if (Size && Size > Value.getArrayInitializedElts()) + expandArray(Value, Value.getArraySize() - 1); + + for (; Size != 0; --Size) { + APValue &Elem = Value.getArrayInitializedElt(Size - 1); + if (!HandleLValueArrayAdjustment(Info, &LocE, ElemLV, ElemT, -1) || + !HandleDestructionImpl(Info, CallLoc, ElemLV, Elem, ElemT)) + return false; + } + + // End the lifetime of this array now. + Value = APValue(); + return true; + } + + const CXXRecordDecl *RD = T->getAsCXXRecordDecl(); + if (!RD) { + if (T.isDestructedType()) { + Info.FFDiag(CallLoc, diag::note_constexpr_unsupported_destruction) << T; + return false; + } + + Value = APValue(); + return true; + } + + if (RD->getNumVBases()) { + Info.FFDiag(CallLoc, diag::note_constexpr_virtual_base) << RD; + return false; + } + + const CXXDestructorDecl *DD = RD->getDestructor(); + if (!DD && !RD->hasTrivialDestructor()) { + Info.FFDiag(CallLoc); + return false; + } + + if (!DD || DD->isTrivial() || + (RD->isAnonymousStructOrUnion() && RD->isUnion())) { + // A trivial destructor just ends the lifetime of the object. Check for + // this case before checking for a body, because we might not bother + // building a body for a trivial destructor. Note that it doesn't matter + // whether the destructor is constexpr in this case; all trivial + // destructors are constexpr. + // + // If an anonymous union would be destroyed, some enclosing destructor must + // have been explicitly defined, and the anonymous union destruction should + // have no effect. + Value = APValue(); + return true; + } + + if (!Info.CheckCallLimit(CallLoc)) + return false; + + const FunctionDecl *Definition = nullptr; + const Stmt *Body = DD->getBody(Definition); + + if (!CheckConstexprFunction(Info, CallLoc, DD, Definition, Body)) + return false; + + CallStackFrame Frame(Info, CallLoc, Definition, &This, nullptr); + + // We're now in the period of destruction of this object. + unsigned BasesLeft = RD->getNumBases(); + EvalInfo::EvaluatingDestructorRAII EvalObj( + Info, + ObjectUnderConstruction{This.getLValueBase(), This.Designator.Entries}); + if (!EvalObj.DidInsert) { + // C++2a [class.dtor]p19: + // the behavior is undefined if the destructor is invoked for an object + // whose lifetime has ended + // (Note that formally the lifetime ends when the period of destruction + // begins, even though certain uses of the object remain valid until the + // period of destruction ends.) + Info.FFDiag(CallLoc, diag::note_constexpr_double_destroy); + return false; + } + + // FIXME: Creating an APValue just to hold a nonexistent return value is + // wasteful. + APValue RetVal; + StmtResult Ret = {RetVal, nullptr}; + if (EvaluateStmt(Ret, Info, Definition->getBody()) == ESR_Failed) + return false; + + // A union destructor does not implicitly destroy its members. + if (RD->isUnion()) + return true; + + const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); + + // We don't have a good way to iterate fields in reverse, so collect all the + // fields first and then walk them backwards. + SmallVector Fields(RD->field_begin(), RD->field_end()); + for (const FieldDecl *FD : llvm::reverse(Fields)) { + if (FD->isUnnamedBitfield()) + continue; + + LValue Subobject = This; + if (!HandleLValueMember(Info, &LocE, Subobject, FD, &Layout)) + return false; + + APValue *SubobjectValue = &Value.getStructField(FD->getFieldIndex()); + if (!HandleDestructionImpl(Info, CallLoc, Subobject, *SubobjectValue, + FD->getType())) + return false; + } + + if (BasesLeft != 0) + EvalObj.startedDestroyingBases(); + + // Destroy base classes in reverse order. + for (const CXXBaseSpecifier &Base : llvm::reverse(RD->bases())) { + --BasesLeft; + + QualType BaseType = Base.getType(); + LValue Subobject = This; + if (!HandleLValueDirectBase(Info, &LocE, Subobject, RD, + BaseType->getAsCXXRecordDecl(), &Layout)) + return false; + + APValue *SubobjectValue = &Value.getStructBase(BasesLeft); + if (!HandleDestructionImpl(Info, CallLoc, Subobject, *SubobjectValue, + BaseType)) + return false; + } + assert(BasesLeft == 0 && "NumBases was wrong?"); + + // The period of destruction ends now. The object is gone. + Value = APValue(); + return true; +} + +namespace { +struct DestroyObjectHandler { + EvalInfo &Info; + const Expr *E; + const LValue &This; + const AccessKinds AccessKind; + + typedef bool result_type; + bool failed() { return false; } + bool found(APValue &Subobj, QualType SubobjType) { + return HandleDestructionImpl(Info, E->getExprLoc(), This, Subobj, + SubobjType); + } + bool found(APSInt &Value, QualType SubobjType) { + Info.FFDiag(E, diag::note_constexpr_destroy_complex_elem); + return false; + } + bool found(APFloat &Value, QualType SubobjType) { + Info.FFDiag(E, diag::note_constexpr_destroy_complex_elem); + return false; + } +}; +} + +/// Perform a destructor or pseudo-destructor call on the given object, which +/// might in general not be a complete object. +static bool HandleDestruction(EvalInfo &Info, const Expr *E, + const LValue &This, QualType ThisType) { + CompleteObject Obj = findCompleteObject(Info, E, AK_Destroy, This, ThisType); + DestroyObjectHandler Handler = {Info, E, This, AK_Destroy}; + return Obj && findSubobject(Info, E, Obj, This.Designator, Handler); +} + +/// Destroy and end the lifetime of the given complete object. +static bool HandleDestruction(EvalInfo &Info, SourceLocation Loc, + APValue::LValueBase LVBase, APValue &Value, + QualType T) { + // If we've had an unmodeled side-effect, we can't rely on mutable state + // (such as the object we're about to destroy) being correct. + if (Info.EvalStatus.HasSideEffects) + return false; + + LValue LV; + LV.set({LVBase}); + return HandleDestructionImpl(Info, Loc, LV, Value, T); +} + +/// Perform a call to 'perator new' or to `__builtin_operator_new'. +static bool HandleOperatorNewCall(EvalInfo &Info, const CallExpr *E, + LValue &Result) { + if (Info.checkingPotentialConstantExpression() || + Info.SpeculativeEvaluationDepth) + return false; + + // This is permitted only within a call to std::allocator::allocate. + auto Caller = Info.getStdAllocatorCaller("allocate"); + if (!Caller) { + Info.FFDiag(E->getExprLoc(), Info.getLangOpts().CPlusPlus2a + ? diag::note_constexpr_new_untyped + : diag::note_constexpr_new); + return false; + } + + QualType ElemType = Caller.ElemType; + if (ElemType->isIncompleteType() || ElemType->isFunctionType()) { + Info.FFDiag(E->getExprLoc(), + diag::note_constexpr_new_not_complete_object_type) + << (ElemType->isIncompleteType() ? 0 : 1) << ElemType; + return false; + } + + APSInt ByteSize; + if (!EvaluateInteger(E->getArg(0), ByteSize, Info)) + return false; + bool IsNothrow = false; + for (unsigned I = 1, N = E->getNumArgs(); I != N; ++I) { + EvaluateIgnoredValue(Info, E->getArg(I)); + IsNothrow |= E->getType()->isNothrowT(); + } + + CharUnits ElemSize; + if (!HandleSizeof(Info, E->getExprLoc(), ElemType, ElemSize)) + return false; + APInt Size, Remainder; + APInt ElemSizeAP(ByteSize.getBitWidth(), ElemSize.getQuantity()); + APInt::udivrem(ByteSize, ElemSizeAP, Size, Remainder); + if (Remainder != 0) { + // This likely indicates a bug in the implementation of 'std::allocator'. + Info.FFDiag(E->getExprLoc(), diag::note_constexpr_operator_new_bad_size) + << ByteSize << APSInt(ElemSizeAP, true) << ElemType; + return false; + } + + if (ByteSize.getActiveBits() > ConstantArrayType::getMaxSizeBits(Info.Ctx)) { + if (IsNothrow) { + Result.setNull(Info.Ctx, E->getType()); + return true; } - // This is the point at which the dynamic type of the object becomes this - // class type. - if (I->isBaseInitializer() && BasesSeen == RD->getNumBases()) - EvalObj.finishedConstructingBases(); + Info.FFDiag(E, diag::note_constexpr_new_too_large) << APSInt(Size, true); + return false; } - return Success && - EvaluateStmt(Ret, Info, Definition->getBody()) != ESR_Failed; + QualType AllocType = + Info.Ctx.getConstantArrayType(ElemType, Size, ArrayType::Normal, 0); + APValue *Val = Info.createHeapAlloc(E, AllocType, Result); + *Val = APValue(APValue::UninitArray(), 0, Size.getZExtValue()); + Result.addArray(Info, E, cast(AllocType)); + return true; } -static bool HandleConstructorCall(const Expr *E, const LValue &This, - ArrayRef Args, - const CXXConstructorDecl *Definition, - EvalInfo &Info, APValue &Result) { - ArgVector ArgValues(Args.size()); - if (!EvaluateArgs(Args, ArgValues, Info, Definition)) +static bool hasVirtualDestructor(QualType T) { + if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) + if (CXXDestructorDecl *DD = RD->getDestructor()) + return DD->isVirtual(); + return false; +} + +/// Check that the given object is a suitable pointer to a heap allocation that +/// still exists and is of the right kind for the purpose of a deletion. +/// +/// On success, returns the heap allocation to deallocate. On failure, produces +/// a diagnostic and returns None. +static Optional CheckDeleteKind(EvalInfo &Info, const Expr *E, + const LValue &Pointer, + DynAlloc::Kind DeallocKind) { + auto PointerAsString = [&] { + return Pointer.toString(Info.Ctx, Info.Ctx.VoidPtrTy); + }; + + DynamicAllocLValue DA = Pointer.Base.dyn_cast(); + if (!DA) { + Info.FFDiag(E, diag::note_constexpr_delete_not_heap_alloc) + << PointerAsString(); + if (Pointer.Base) + NoteLValueLocation(Info, Pointer.Base); + return None; + } + + Optional Alloc = Info.lookupDynamicAlloc(DA); + if (!Alloc) { + Info.FFDiag(E, diag::note_constexpr_double_delete); + return None; + } + + QualType AllocType = Pointer.Base.getDynamicAllocType(); + if (DeallocKind != (*Alloc)->getKind()) { + Info.FFDiag(E, diag::note_constexpr_new_delete_mismatch) + << DeallocKind << (*Alloc)->getKind() << AllocType; + NoteLValueLocation(Info, Pointer.Base); + return None; + } + + bool Subobject = false; + if (DeallocKind == DynAlloc::New) { + Subobject = Pointer.Designator.MostDerivedPathLength != 0 || + Pointer.Designator.isOnePastTheEnd(); + } else { + Subobject = Pointer.Designator.Entries.size() != 1 || + Pointer.Designator.Entries[0].getAsArrayIndex() != 0; + } + if (Subobject) { + Info.FFDiag(E, diag::note_constexpr_delete_subobject) + << PointerAsString() << Pointer.Designator.isOnePastTheEnd(); + return None; + } + + return Alloc; +} + +// Perform a call to 'operator delete' or '__builtin_operator_delete'. +bool HandleOperatorDeleteCall(EvalInfo &Info, const CallExpr *E) { + if (Info.checkingPotentialConstantExpression() || + Info.SpeculativeEvaluationDepth) return false; - return HandleConstructorCall(E, This, ArgValues.data(), Definition, - Info, Result); + // This is permitted only within a call to std::allocator::deallocate. + if (!Info.getStdAllocatorCaller("deallocate")) { + Info.FFDiag(E->getExprLoc()); + return true; + } + + LValue Pointer; + if (!EvaluatePointer(E->getArg(0), Pointer, Info)) + return false; + for (unsigned I = 1, N = E->getNumArgs(); I != N; ++I) + EvaluateIgnoredValue(Info, E->getArg(I)); + + if (Pointer.Designator.Invalid) + return false; + + // Deleting a null pointer has no effect. + if (Pointer.isNullPointer()) + return true; + + if (!CheckDeleteKind(Info, E, Pointer, DynAlloc::StdAllocator)) + return false; + + Info.HeapAllocs.erase(Pointer.Base.get()); + return true; } //===----------------------------------------------------------------------===// @@ -5567,7 +6481,7 @@ class BufferToAPValueConverter { #define NON_CANONICAL_UNLESS_DEPENDENT(Class, Base) \ case Type::Class: \ llvm_unreachable("either dependent or not canonical!"); -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } llvm_unreachable("Unhandled Type::TypeClass"); } @@ -5658,9 +6572,9 @@ static bool handleLValueToRValueBitCast(EvalInfo &Info, APValue &DestValue, LValue SourceLValue; APValue SourceRValue; SourceLValue.setFrom(Info.Ctx, SourceValue); - if (!handleLValueToRValueConversion(Info, BCE, - BCE->getSubExpr()->getType().withConst(), - SourceLValue, SourceRValue)) + if (!handleLValueToRValueConversion( + Info, BCE, BCE->getSubExpr()->getType().withConst(), SourceLValue, + SourceRValue, /*WantObjectRepresentation=*/true)) return false; // Read out SourceValue into a char buffer. @@ -5799,10 +6713,16 @@ class ExprEvaluatorBase return StmtVisitorTy::Visit(E->getExpr()); } - // We cannot create any objects for which cleanups are required, so there is - // nothing to do here; all cleanups must come from unevaluated subexpressions. - bool VisitExprWithCleanups(const ExprWithCleanups *E) - { return StmtVisitorTy::Visit(E->getSubExpr()); } + bool VisitExprWithCleanups(const ExprWithCleanups *E) { + FullExpressionRAII Scope(Info); + return StmtVisitorTy::Visit(E->getSubExpr()) && Scope.destroy(); + } + + // Temporaries are registered when created, so we don't care about + // CXXBindTemporaryExpr. + bool VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *E) { + return StmtVisitorTy::Visit(E->getSubExpr()); + } bool VisitCXXReinterpretCastExpr(const CXXReinterpretCastExpr *E) { CCEDiag(E, diag::note_constexpr_invalid_cast) << 0; @@ -5842,7 +6762,11 @@ class ExprEvaluatorBase bool VisitBinaryConditionalOperator(const BinaryConditionalOperator *E) { // Evaluate and cache the common expression. We treat it as a temporary, // even though it's not quite the same thing. - if (!Evaluate(Info.CurrentCall->createTemporary(E->getOpaqueValue(), false), + LValue CommonLV; + if (!Evaluate(Info.CurrentCall->createTemporary( + E->getOpaqueValue(), + getStorageType(Info.Ctx, E->getOpaqueValue()), false, + CommonLV), Info, E->getCommon())) return false; @@ -5921,11 +6845,21 @@ class ExprEvaluatorBase HasQualifier = ME->hasQualifier(); } else if (const BinaryOperator *BE = dyn_cast(Callee)) { // Indirect bound member calls ('.*' or '->*'). - Member = dyn_cast_or_null( - HandleMemberPointerAccess(Info, BE, ThisVal, false)); + const ValueDecl *D = + HandleMemberPointerAccess(Info, BE, ThisVal, false); + if (!D) + return false; + Member = dyn_cast(D); if (!Member) return Error(Callee); This = &ThisVal; + } else if (const auto *PDE = dyn_cast(Callee)) { + if (!Info.getLangOpts().CPlusPlus2a) + Info.CCEDiag(PDE, diag::note_constexpr_pseudo_destructor); + // FIXME: If pseudo-destructor calls ever start ending the lifetime of + // their callee, we should start calling HandleDestruction here. + // For now, we just evaluate the object argument and discard it. + return EvaluateObjectArgument(Info, PDE->getBase(), ThisVal); } else return Error(Callee); FD = Member; @@ -5994,6 +6928,17 @@ class ExprEvaluatorBase FD = cast(CorrespondingCallOpSpecialization); } else FD = LambdaCallOp; + } else if (FD->isReplaceableGlobalAllocationFunction()) { + if (FD->getDeclName().getCXXOverloadedOperator() == OO_New || + FD->getDeclName().getCXXOverloadedOperator() == OO_Array_New) { + LValue Ptr; + if (!HandleOperatorNewCall(Info, E, Ptr)) + return false; + Ptr.moveInto(Result); + return true; + } else { + return HandleOperatorDeleteCall(Info, E); + } } } else return Error(E); @@ -6009,11 +6954,20 @@ class ExprEvaluatorBase return false; } else { // Check that the 'this' pointer points to an object of the right type. - if (!checkNonVirtualMemberCallThisPointer(Info, E, *This)) + // FIXME: If this is an assignment operator call, we may need to change + // the active union member before we check this. + if (!checkNonVirtualMemberCallThisPointer(Info, E, *This, NamedMember)) return false; } } + // Destructor calls are different enough that they have their own codepath. + if (auto *DD = dyn_cast(FD)) { + assert(This && "no 'this' pointer for destructor call"); + return HandleDestruction(Info, E, *This, + Info.Ctx.getRecordType(DD->getParent())); + } + const FunctionDecl *Definition = nullptr; Stmt *Body = FD->getBody(Definition); @@ -6149,11 +7103,11 @@ class ExprEvaluatorBase if (Info.checkingForUndefinedBehavior()) return Error(E); - BlockScopeRAII Scope(Info); const CompoundStmt *CS = E->getSubStmt(); if (CS->body_empty()) return true; + BlockScopeRAII Scope(Info); for (CompoundStmt::const_body_iterator BI = CS->body_begin(), BE = CS->body_end(); /**/; ++BI) { @@ -6164,7 +7118,7 @@ class ExprEvaluatorBase diag::note_constexpr_stmt_expr_unsupported); return false; } - return this->Visit(FinalExpr); + return this->Visit(FinalExpr) && Scope.destroy(); } APValue ReturnValue; @@ -6513,16 +7467,14 @@ bool LValueExprEvaluator::VisitMaterializeTemporaryExpr( *Value = APValue(); Result.set(E); } else { - Value = &createTemporary(E, E->getStorageDuration() == SD_Automatic, Result, - *Info.CurrentCall); + Value = &Info.CurrentCall->createTemporary( + E, E->getType(), E->getStorageDuration() == SD_Automatic, Result); } QualType Type = Inner->getType(); // Materialize the temporary itself. - if (!EvaluateInPlace(*Value, Info, Result, Inner) || - (E->getStorageDuration() == SD_Static && - !CheckConstantExpression(Info, E->getExprLoc(), Type, *Value))) { + if (!EvaluateInPlace(*Value, Info, Result, Inner)) { *Value = APValue(); return false; } @@ -6852,8 +7804,7 @@ class PointerExprEvaluator return true; } bool ZeroInitialization(const Expr *E) { - auto TargetVal = Info.Ctx.getTargetNullPointerValue(E->getType()); - Result.setNull(E->getType(), TargetVal); + Result.setNull(Info.Ctx, E->getType()); return true; } @@ -6914,6 +7865,8 @@ class PointerExprEvaluator return true; } + bool VisitCXXNewExpr(const CXXNewExpr *E); + bool VisitSourceLocExpr(const SourceLocExpr *E) { assert(E->isStringType() && "SourceLocExpr isn't a pointer type?"); APValue LValResult = E->EvaluateInContext( @@ -6978,12 +7931,22 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) { // permitted in constant expressions in C++11. Bitcasts from cv void* are // also static_casts, but we disallow them as a resolution to DR1312. if (!E->getType()->isVoidPointerType()) { - Result.Designator.setInvalid(); - if (SubExpr->getType()->isVoidPointerType()) - CCEDiag(E, diag::note_constexpr_invalid_cast) - << 3 << SubExpr->getType(); - else - CCEDiag(E, diag::note_constexpr_invalid_cast) << 2; + if (!Result.InvalidBase && !Result.Designator.Invalid && + !Result.IsNullPtr && + Info.Ctx.hasSameUnqualifiedType(Result.Designator.getType(Info.Ctx), + E->getType()->getPointeeType()) && + Info.getStdAllocatorCaller("allocate")) { + // Inside a call to std::allocator::allocate and friends, we permit + // casting from void* back to cv1 T* for a pointer that points to a + // cv2 T. + } else { + Result.Designator.setInvalid(); + if (SubExpr->getType()->isVoidPointerType()) + CCEDiag(E, diag::note_constexpr_invalid_cast) + << 3 << SubExpr->getType(); + else + CCEDiag(E, diag::note_constexpr_invalid_cast) << 2; + } } if (E->getCastKind() == CK_AddressSpaceConversion && Result.IsNullPtr) ZeroInitialization(E); @@ -7046,8 +8009,8 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) { if (!evaluateLValue(SubExpr, Result)) return false; } else { - APValue &Value = createTemporary(SubExpr, false, Result, - *Info.CurrentCall); + APValue &Value = Info.CurrentCall->createTemporary( + SubExpr, SubExpr->getType(), false, Result); if (!EvaluateInPlace(Value, Info, Result, SubExpr)) return false; } @@ -7220,6 +8183,8 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, return true; } + case Builtin::BI__builtin_operator_new: + return HandleOperatorNewCall(Info, E, Result); case Builtin::BI__builtin_launder: return evaluatePointer(E->getArg(0), Result); case Builtin::BIstrchr: @@ -7455,6 +8420,8 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, while (true) { APValue Val; + // FIXME: Set WantObjectRepresentation to true if we're copying a + // char-like type? if (!handleLValueToRValueConversion(Info, E, T, Src, Val) || !handleAssignment(Info, E, Dest, T, Val)) return false; @@ -7469,10 +8436,208 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, } default: - return visitNonBuiltinCallExpr(E); + break; } + + return visitNonBuiltinCallExpr(E); } +static bool EvaluateArrayNewInitList(EvalInfo &Info, LValue &This, + APValue &Result, const InitListExpr *ILE, + QualType AllocType); + +bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) { + if (!Info.getLangOpts().CPlusPlus2a) + Info.CCEDiag(E, diag::note_constexpr_new); + + // We cannot speculatively evaluate a delete expression. + if (Info.SpeculativeEvaluationDepth) + return false; + + FunctionDecl *OperatorNew = E->getOperatorNew(); + + bool IsNothrow = false; + bool IsPlacement = false; + if (OperatorNew->isReservedGlobalPlacementOperator() && + Info.CurrentCall->isStdFunction() && !E->isArray()) { + // FIXME Support array placement new. + assert(E->getNumPlacementArgs() == 1); + if (!EvaluatePointer(E->getPlacementArg(0), Result, Info)) + return false; + if (Result.Designator.Invalid) + return false; + IsPlacement = true; + } else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) { + Info.FFDiag(E, diag::note_constexpr_new_non_replaceable) + << isa(OperatorNew) << OperatorNew; + return false; + } else if (E->getNumPlacementArgs()) { + // The only new-placement list we support is of the form (std::nothrow). + // + // FIXME: There is no restriction on this, but it's not clear that any + // other form makes any sense. We get here for cases such as: + // + // new (std::align_val_t{N}) X(int) + // + // (which should presumably be valid only if N is a multiple of + // alignof(int), and in any case can't be deallocated unless N is + // alignof(X) and X has new-extended alignment). + if (E->getNumPlacementArgs() != 1 || + !E->getPlacementArg(0)->getType()->isNothrowT()) + return Error(E, diag::note_constexpr_new_placement); + + LValue Nothrow; + if (!EvaluateLValue(E->getPlacementArg(0), Nothrow, Info)) + return false; + IsNothrow = true; + } + + const Expr *Init = E->getInitializer(); + const InitListExpr *ResizedArrayILE = nullptr; + + QualType AllocType = E->getAllocatedType(); + if (Optional ArraySize = E->getArraySize()) { + const Expr *Stripped = *ArraySize; + for (; auto *ICE = dyn_cast(Stripped); + Stripped = ICE->getSubExpr()) + if (ICE->getCastKind() != CK_NoOp && + ICE->getCastKind() != CK_IntegralCast) + break; + + llvm::APSInt ArrayBound; + if (!EvaluateInteger(Stripped, ArrayBound, Info)) + return false; + + // C++ [expr.new]p9: + // The expression is erroneous if: + // -- [...] its value before converting to size_t [or] applying the + // second standard conversion sequence is less than zero + if (ArrayBound.isSigned() && ArrayBound.isNegative()) { + if (IsNothrow) + return ZeroInitialization(E); + + Info.FFDiag(*ArraySize, diag::note_constexpr_new_negative) + << ArrayBound << (*ArraySize)->getSourceRange(); + return false; + } + + // -- its value is such that the size of the allocated object would + // exceed the implementation-defined limit + if (ConstantArrayType::getNumAddressingBits(Info.Ctx, AllocType, + ArrayBound) > + ConstantArrayType::getMaxSizeBits(Info.Ctx)) { + if (IsNothrow) + return ZeroInitialization(E); + + Info.FFDiag(*ArraySize, diag::note_constexpr_new_too_large) + << ArrayBound << (*ArraySize)->getSourceRange(); + return false; + } + + // -- the new-initializer is a braced-init-list and the number of + // array elements for which initializers are provided [...] + // exceeds the number of elements to initialize + if (Init) { + auto *CAT = Info.Ctx.getAsConstantArrayType(Init->getType()); + assert(CAT && "unexpected type for array initializer"); + + unsigned Bits = + std::max(CAT->getSize().getBitWidth(), ArrayBound.getBitWidth()); + llvm::APInt InitBound = CAT->getSize().zextOrSelf(Bits); + llvm::APInt AllocBound = ArrayBound.zextOrSelf(Bits); + if (InitBound.ugt(AllocBound)) { + if (IsNothrow) + return ZeroInitialization(E); + + Info.FFDiag(*ArraySize, diag::note_constexpr_new_too_small) + << AllocBound.toString(10, /*Signed=*/false) + << InitBound.toString(10, /*Signed=*/false) + << (*ArraySize)->getSourceRange(); + return false; + } + + // If the sizes differ, we must have an initializer list, and we need + // special handling for this case when we initialize. + if (InitBound != AllocBound) + ResizedArrayILE = cast(Init); + } + + AllocType = Info.Ctx.getConstantArrayType(AllocType, ArrayBound, + ArrayType::Normal, 0); + } else { + assert(!AllocType->isArrayType() && + "array allocation with non-array new"); + } + + APValue *Val; + if (IsPlacement) { + AccessKinds AK = AK_Construct; + struct FindObjectHandler { + EvalInfo &Info; + const Expr *E; + QualType AllocType; + const AccessKinds AccessKind; + APValue *Value; + + typedef bool result_type; + bool failed() { return false; } + bool found(APValue &Subobj, QualType SubobjType) { + // FIXME: Reject the cases where [basic.life]p8 would not permit the + // old name of the object to be used to name the new object. + if (!Info.Ctx.hasSameUnqualifiedType(SubobjType, AllocType)) { + Info.FFDiag(E, diag::note_constexpr_placement_new_wrong_type) << + SubobjType << AllocType; + return false; + } + Value = &Subobj; + return true; + } + bool found(APSInt &Value, QualType SubobjType) { + Info.FFDiag(E, diag::note_constexpr_construct_complex_elem); + return false; + } + bool found(APFloat &Value, QualType SubobjType) { + Info.FFDiag(E, diag::note_constexpr_construct_complex_elem); + return false; + } + } Handler = {Info, E, AllocType, AK, nullptr}; + + CompleteObject Obj = findCompleteObject(Info, E, AK, Result, AllocType); + if (!Obj || !findSubobject(Info, E, Obj, Result.Designator, Handler)) + return false; + + Val = Handler.Value; + + // [basic.life]p1: + // The lifetime of an object o of type T ends when [...] the storage + // which the object occupies is [...] reused by an object that is not + // nested within o (6.6.2). + *Val = APValue(); + } else { + // Perform the allocation and obtain a pointer to the resulting object. + Val = Info.createHeapAlloc(E, AllocType, Result); + if (!Val) + return false; + } + + if (ResizedArrayILE) { + if (!EvaluateArrayNewInitList(Info, Result, *Val, ResizedArrayILE, + AllocType)) + return false; + } else if (Init) { + if (!EvaluateInPlace(*Val, Info, Result, Init)) + return false; + } else { + *Val = getDefaultInitValue(AllocType); + } + + // Array new returns a pointer to the first element, not a pointer to the + // array. + if (auto *AT = AllocType->getAsArrayTypeUnsafe()) + Result.addArray(Info, E, cast(AT)); + + return true; +} //===----------------------------------------------------------------------===// // Member Pointer Evaluation //===----------------------------------------------------------------------===// @@ -7596,7 +8761,6 @@ namespace { bool VisitCXXInheritedCtorInitExpr(const CXXInheritedCtorInitExpr *E); bool VisitCXXConstructExpr(const CXXConstructExpr *E, QualType T); bool VisitCXXStdInitializerListExpr(const CXXStdInitializerListExpr *E); - bool VisitBinCmp(const BinaryOperator *E); }; } @@ -7830,15 +8994,11 @@ bool RecordExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E, if (Result.hasValue()) return true; - // We can get here in two different ways: - // 1) We're performing value-initialization, and should zero-initialize - // the object, or - // 2) We're performing default-initialization of an object with a trivial - // constexpr default constructor, in which case we should start the - // lifetimes of all the base subobjects (there can be no data member - // subobjects in this case) per [basic.life]p1. - // Either way, ZeroInitialization is appropriate. - return ZeroInitialization(E, T); + if (ZeroInit) + return ZeroInitialization(E, T); + + Result = getDefaultInitValue(T); + return true; } const FunctionDecl *Definition = nullptr; @@ -7938,9 +9098,8 @@ bool RecordExprEvaluator::VisitCXXStdInitializerListExpr( bool RecordExprEvaluator::VisitLambdaExpr(const LambdaExpr *E) { const CXXRecordDecl *ClosureClass = E->getLambdaClass(); - if (ClosureClass->isInvalidDecl()) return false; - - if (Info.checkingPotentialConstantExpression()) return true; + if (ClosureClass->isInvalidDecl()) + return false; const size_t NumFields = std::distance(ClosureClass->field_begin(), ClosureClass->field_end()); @@ -8000,7 +9159,8 @@ class TemporaryExprEvaluator /// Visit an expression which constructs the value of this temporary. bool VisitConstructExpr(const Expr *E) { - APValue &Value = createTemporary(E, false, Result, *Info.CurrentCall); + APValue &Value = + Info.CurrentCall->createTemporary(E, E->getType(), false, Result); return EvaluateInPlace(Value, Info, Result, E); } @@ -8258,14 +9418,16 @@ namespace { bool VisitCallExpr(const CallExpr *E) { return handleCallExpr(E, Result, &This); } - bool VisitInitListExpr(const InitListExpr *E); + bool VisitInitListExpr(const InitListExpr *E, + QualType AllocType = QualType()); bool VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E); bool VisitCXXConstructExpr(const CXXConstructExpr *E); bool VisitCXXConstructExpr(const CXXConstructExpr *E, const LValue &Subobject, APValue *Value, QualType Type); - bool VisitStringLiteral(const StringLiteral *E) { - expandStringLiteral(Info, E, Result); + bool VisitStringLiteral(const StringLiteral *E, + QualType AllocType = QualType()) { + expandStringLiteral(Info, E, Result, AllocType); return true; } }; @@ -8277,6 +9439,15 @@ static bool EvaluateArray(const Expr *E, const LValue &This, return ArrayExprEvaluator(Info, This, Result).Visit(E); } +static bool EvaluateArrayNewInitList(EvalInfo &Info, LValue &This, + APValue &Result, const InitListExpr *ILE, + QualType AllocType) { + assert(ILE->isRValue() && ILE->getType()->isArrayType() && + "not an array rvalue"); + return ArrayExprEvaluator(Info, This, Result) + .VisitInitListExpr(ILE, AllocType); +} + // Return true iff the given array filler may depend on the element index. static bool MaybeElementDependentArrayFiller(const Expr *FillerExpr) { // For now, just whitelist non-class value-initialization and initialization @@ -8293,15 +9464,23 @@ static bool MaybeElementDependentArrayFiller(const Expr *FillerExpr) { return true; } -bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) { - const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(E->getType()); +bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E, + QualType AllocType) { + const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType( + AllocType.isNull() ? E->getType() : AllocType); if (!CAT) return Error(E); // C++11 [dcl.init.string]p1: A char array [...] can be initialized by [...] // an appropriately-typed string literal enclosed in braces. - if (E->isStringLiteralInit()) - return Visit(E->getInit(0)); + if (E->isStringLiteralInit()) { + auto *SL = dyn_cast(E->getInit(0)->IgnoreParens()); + // FIXME: Support ObjCEncodeExpr here once we support it in + // ArrayExprEvaluator generally. + if (!SL) + return Error(E); + return VisitStringLiteral(SL, AllocType); + } bool Success = true; @@ -8360,8 +9539,12 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) { } bool ArrayExprEvaluator::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E) { + LValue CommonLV; if (E->getCommonExpr() && - !Evaluate(Info.CurrentCall->createTemporary(E->getCommonExpr(), false), + !Evaluate(Info.CurrentCall->createTemporary( + E->getCommonExpr(), + getStorageType(Info.Ctx, E->getCommonExpr()), false, + CommonLV), Info, E->getCommonExpr()->getSourceExpr())) return false; @@ -8761,7 +9944,7 @@ EvaluateBuiltinClassifyType(QualType T, const LangOptions &LangOpts) { #define DEPENDENT_TYPE(ID, BASE) case Type::ID: #define NON_CANONICAL_TYPE(ID, BASE) case Type::ID: #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(ID, BASE) case Type::ID: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" case Type::Auto: case Type::DeducedTemplateSpecialization: llvm_unreachable("unexpected non-canonical or dependent type"); @@ -8981,6 +10164,8 @@ static QualType getObjectType(APValue::LValueBase B) { return E->getType(); } else if (B.is()) { return B.getTypeInfoType(); + } else if (B.is()) { + return B.getDynamicAllocType(); } return QualType(); @@ -11960,17 +13145,86 @@ class VoidExprEvaluator bool VisitCallExpr(const CallExpr *E) { switch (E->getBuiltinCallee()) { - default: - return ExprEvaluatorBaseTy::VisitCallExpr(E); case Builtin::BI__assume: case Builtin::BI__builtin_assume: // The argument is not evaluated! return true; + + case Builtin::BI__builtin_operator_delete: + return HandleOperatorDeleteCall(Info, E); + + default: + break; } + + return ExprEvaluatorBaseTy::VisitCallExpr(E); } + + bool VisitCXXDeleteExpr(const CXXDeleteExpr *E); }; } // end anonymous namespace +bool VoidExprEvaluator::VisitCXXDeleteExpr(const CXXDeleteExpr *E) { + // We cannot speculatively evaluate a delete expression. + if (Info.SpeculativeEvaluationDepth) + return false; + + FunctionDecl *OperatorDelete = E->getOperatorDelete(); + if (!OperatorDelete->isReplaceableGlobalAllocationFunction()) { + Info.FFDiag(E, diag::note_constexpr_new_non_replaceable) + << isa(OperatorDelete) << OperatorDelete; + return false; + } + + const Expr *Arg = E->getArgument(); + + LValue Pointer; + if (!EvaluatePointer(Arg, Pointer, Info)) + return false; + if (Pointer.Designator.Invalid) + return false; + + // Deleting a null pointer has no effect. + if (Pointer.isNullPointer()) { + // This is the only case where we need to produce an extension warning: + // the only other way we can succeed is if we find a dynamic allocation, + // and we will have warned when we allocated it in that case. + if (!Info.getLangOpts().CPlusPlus2a) + Info.CCEDiag(E, diag::note_constexpr_new); + return true; + } + + Optional Alloc = CheckDeleteKind( + Info, E, Pointer, E->isArrayForm() ? DynAlloc::ArrayNew : DynAlloc::New); + if (!Alloc) + return false; + QualType AllocType = Pointer.Base.getDynamicAllocType(); + + // For the non-array case, the designator must be empty if the static type + // does not have a virtual destructor. + if (!E->isArrayForm() && Pointer.Designator.Entries.size() != 0 && + !hasVirtualDestructor(Arg->getType()->getPointeeType())) { + Info.FFDiag(E, diag::note_constexpr_delete_base_nonvirt_dtor) + << Arg->getType()->getPointeeType() << AllocType; + return false; + } + + if (!HandleDestruction(Info, E->getExprLoc(), Pointer.getLValueBase(), + (*Alloc)->Value, AllocType)) + return false; + + if (!Info.HeapAllocs.erase(Pointer.Base.dyn_cast())) { + // The element was already erased. This means the destructor call also + // deleted the object. + // FIXME: This probably results in undefined behavior before we get this + // far, and should be diagnosed elsewhere first. + Info.FFDiag(E, diag::note_constexpr_double_delete); + return false; + } + + return true; +} + static bool EvaluateVoid(const Expr *E, EvalInfo &Info) { assert(E->isRValue() && E->getType()->isVoidType()); return VoidExprEvaluator(Info).Visit(E); @@ -12020,13 +13274,14 @@ static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) { return true; } else if (T->isArrayType()) { LValue LV; - APValue &Value = createTemporary(E, false, LV, *Info.CurrentCall); + APValue &Value = + Info.CurrentCall->createTemporary(E, T, false, LV); if (!EvaluateArray(E, LV, Value, Info)) return false; Result = Value; } else if (T->isRecordType()) { LValue LV; - APValue &Value = createTemporary(E, false, LV, *Info.CurrentCall); + APValue &Value = Info.CurrentCall->createTemporary(E, T, false, LV); if (!EvaluateRecord(E, LV, Value, Info)) return false; Result = Value; @@ -12040,7 +13295,7 @@ static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) { QualType Unqual = T.getAtomicUnqualifiedType(); if (Unqual->isArrayType() || Unqual->isRecordType()) { LValue LV; - APValue &Value = createTemporary(E, false, LV, *Info.CurrentCall); + APValue &Value = Info.CurrentCall->createTemporary(E, Unqual, false, LV); if (!EvaluateAtomic(E, &LV, Value, Info)) return false; } else { @@ -12119,7 +13374,8 @@ static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result) { } // Check this core constant expression is a constant expression. - return CheckConstantExpression(Info, E->getExprLoc(), E->getType(), Result); + return CheckConstantExpression(Info, E->getExprLoc(), E->getType(), Result) && + CheckMemoryLeaks(Info); } static bool FastEvaluateAsRValue(const Expr *Exp, Expr::EvalResult &Result, @@ -12268,10 +13524,12 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx, EvalInfo Info(Ctx, Result, EvalInfo::EM_ConstantFold); Info.InConstantContext = InConstantContext; LValue LV; - if (!EvaluateLValue(this, LV, Info) || Result.HasSideEffects || + CheckedTemporaries CheckedTemps; + if (!EvaluateLValue(this, LV, Info) || !Info.discardCleanups() || + Result.HasSideEffects || !CheckLValueConstantExpression(Info, getExprLoc(), Ctx.getLValueReferenceType(getType()), LV, - Expr::EvaluateForCodeGen)) + Expr::EvaluateForCodeGen, CheckedTemps)) return false; LV.moveInto(Result.Val); @@ -12287,11 +13545,15 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, ConstExprUsage Usage, EvalInfo Info(Ctx, Result, EM); Info.InConstantContext = true; - if (!::Evaluate(Result.Val, Info, this)) + if (!::Evaluate(Result.Val, Info, this) || Result.HasSideEffects) return false; + if (!Info.discardCleanups()) + llvm_unreachable("Unhandled cleanup; missing full expression marker?"); + return CheckConstantExpression(Info, getExprLoc(), getType(), Result.Val, - Usage); + Usage) && + CheckMemoryLeaks(Info); } bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, @@ -12353,7 +13615,50 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, EStatus.HasSideEffects) return false; - return CheckConstantExpression(Info, DeclLoc, DeclTy, Value); + // At this point, any lifetime-extended temporaries are completely + // initialized. + Info.performLifetimeExtension(); + + if (!Info.discardCleanups()) + llvm_unreachable("Unhandled cleanup; missing full expression marker?"); + + return CheckConstantExpression(Info, DeclLoc, DeclTy, Value) && + CheckMemoryLeaks(Info); +} + +bool VarDecl::evaluateDestruction( + SmallVectorImpl &Notes) const { + assert(getEvaluatedValue() && !getEvaluatedValue()->isAbsent() && + "cannot evaluate destruction of non-constant-initialized variable"); + + Expr::EvalStatus EStatus; + EStatus.Diag = &Notes; + + // Make a copy of the value for the destructor to mutate. + APValue DestroyedValue = *getEvaluatedValue(); + + EvalInfo Info(getASTContext(), EStatus, EvalInfo::EM_ConstantExpression); + Info.setEvaluatingDecl(this, DestroyedValue, + EvalInfo::EvaluatingDeclKind::Dtor); + Info.InConstantContext = true; + + SourceLocation DeclLoc = getLocation(); + QualType DeclTy = getType(); + + LValue LVal; + LVal.set(this); + + // FIXME: Consider storing whether this variable has constant destruction in + // the EvaluatedStmt so that CodeGen can query it. + if (!HandleDestruction(Info, DeclLoc, LVal.Base, DestroyedValue, DeclTy) || + EStatus.HasSideEffects) + return false; + + if (!Info.discardCleanups()) + llvm_unreachable("Unhandled cleanup; missing full expression marker?"); + + ensureEvaluatedStmt()->HasConstantDestruction = true; + return true; } /// isEvaluatable - Call EvaluateAsRValue to see if this expression can be @@ -12959,7 +14264,11 @@ bool Expr::isCXX11ConstantExpr(const ASTContext &Ctx, APValue *Result, EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpression); APValue Scratch; - bool IsConstExpr = ::EvaluateAsRValue(Info, this, Result ? *Result : Scratch); + bool IsConstExpr = + ::EvaluateAsRValue(Info, this, Result ? *Result : Scratch) && + // FIXME: We don't produce a diagnostic for this, but the callers that + // call us on arbitrary full-expressions should generally not care. + Info.discardCleanups() && !Status.HasSideEffects; if (!Diags.empty()) { IsConstExpr = false; @@ -13011,7 +14320,8 @@ bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx, // Build fake call to Callee. CallStackFrame Frame(Info, Callee->getLocation(), Callee, ThisPtr, ArgValues.data()); - return Evaluate(Value, Info, this) && !Info.EvalStatus.HasSideEffects; + return Evaluate(Value, Info, this) && Info.discardCleanups() && + !Info.EvalStatus.HasSideEffects; } bool Expr::isPotentialConstantExpr(const FunctionDecl *FD, diff --git a/clang/lib/AST/ExternalASTMerger.cpp b/clang/lib/AST/ExternalASTMerger.cpp index 4dc89f0f31a0a..01161723ac0aa 100644 --- a/clang/lib/AST/ExternalASTMerger.cpp +++ b/clang/lib/AST/ExternalASTMerger.cpp @@ -105,13 +105,16 @@ class LazyASTImporter : public ASTImporter { llvm::raw_ostream &logs() { return Parent.logs(); } public: LazyASTImporter(ExternalASTMerger &_Parent, ASTContext &ToContext, - FileManager &ToFileManager, ASTContext &FromContext, - FileManager &FromFileManager, - const ExternalASTMerger::OriginMap &_FromOrigins) - : ASTImporter(ToContext, ToFileManager, FromContext, FromFileManager, - /*MinimalImport=*/true), - Parent(_Parent), Reverse(FromContext, FromFileManager, ToContext, - ToFileManager, /*MinimalImport=*/true), FromOrigins(_FromOrigins) {} + FileManager &ToFileManager, + const ExternalASTMerger::ImporterSource &_Source, + std::shared_ptr SharedState) + : ASTImporter(ToContext, ToFileManager, _Source.getASTContext(), + _Source.getFileManager(), + /*MinimalImport=*/true, SharedState), + Parent(_Parent), + Reverse(_Source.getASTContext(), _Source.getFileManager(), ToContext, + ToFileManager, /*MinimalImport=*/true), + FromOrigins(_Source.getOriginMap()) {} /// Whenever a DeclContext is imported, ensure that ExternalASTSource's origin /// map is kept up to date. Also set the appropriate flags. @@ -314,28 +317,30 @@ void ExternalASTMerger::RecordOriginImpl(const DeclContext *ToDC, DCOrigin Origi ExternalASTMerger::ExternalASTMerger(const ImporterTarget &Target, llvm::ArrayRef Sources) : LogStream(&llvm::nulls()), Target(Target) { + SharedState = std::make_shared( + *Target.AST.getTranslationUnitDecl()); AddSources(Sources); } void ExternalASTMerger::AddSources(llvm::ArrayRef Sources) { for (const ImporterSource &S : Sources) { - assert(&S.AST != &Target.AST); + assert(&S.getASTContext() != &Target.AST); Importers.push_back(std::make_unique( - *this, Target.AST, Target.FM, S.AST, S.FM, S.OM)); + *this, Target.AST, Target.FM, S, SharedState)); } } void ExternalASTMerger::RemoveSources(llvm::ArrayRef Sources) { if (LoggingEnabled()) for (const ImporterSource &S : Sources) - logs() << "(ExternalASTMerger*)" << (void*)this - << " removing source (ASTContext*)" << (void*)&S.AST + logs() << "(ExternalASTMerger*)" << (void *)this + << " removing source (ASTContext*)" << (void *)&S.getASTContext() << "\n"; Importers.erase( std::remove_if(Importers.begin(), Importers.end(), [&Sources](std::unique_ptr &Importer) -> bool { for (const ImporterSource &S : Sources) { - if (&Importer->getFromContext() == &S.AST) + if (&Importer->getFromContext() == &S.getASTContext()) return true; } return false; @@ -345,7 +350,7 @@ void ExternalASTMerger::RemoveSources(llvm::ArrayRef Sources) { std::pair Origin = *OI; bool Erase = false; for (const ImporterSource &S : Sources) { - if (&S.AST == Origin.second.AST) { + if (&S.getASTContext() == Origin.second.AST) { Erase = true; break; } diff --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp index ff439f9921c9b..3c96c80e4ccc8 100644 --- a/clang/lib/AST/FormatString.cpp +++ b/clang/lib/AST/FormatString.cpp @@ -359,6 +359,7 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const { case BuiltinType::SChar: case BuiltinType::UChar: case BuiltinType::Char_U: + case BuiltinType::Bool: return Match; } return NoMatch; @@ -386,6 +387,7 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const { case BuiltinType::SChar: case BuiltinType::Char_U: case BuiltinType::UChar: + case BuiltinType::Bool: if (T == C.UnsignedShortTy || T == C.ShortTy) return NoMatchPedantic; return T == C.UnsignedCharTy || T == C.SignedCharTy ? Match diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp index c9ace131bbd5a..1a8109cedf769 100644 --- a/clang/lib/AST/Interp/Interp.cpp +++ b/clang/lib/AST/Interp/Interp.cpp @@ -275,7 +275,7 @@ bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { const SourceInfo &Loc = S.Current->getSource(OpPC); const FieldDecl *Field = Ptr.getField(); - S.FFDiag(Loc, diag::note_constexpr_ltor_mutable, 1) << Field; + S.FFDiag(Loc, diag::note_constexpr_access_mutable, 1) << AK_Read << Field; S.Note(Field->getLocation(), diag::note_declared_at); return false; } diff --git a/clang/lib/AST/Interp/InterpStack.cpp b/clang/lib/AST/Interp/InterpStack.cpp index f159fe1955f81..5c803f3d94244 100644 --- a/clang/lib/AST/Interp/InterpStack.cpp +++ b/clang/lib/AST/Interp/InterpStack.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include +#include #include "InterpStack.h" using namespace clang; diff --git a/clang/lib/AST/Interp/State.h b/clang/lib/AST/Interp/State.h index 49af3d18c9bf5..d9a645a3eb3eb 100644 --- a/clang/lib/AST/Interp/State.h +++ b/clang/lib/AST/Interp/State.h @@ -25,12 +25,15 @@ namespace clang { /// same set of semantic restrictions. enum AccessKinds { AK_Read, + AK_ReadObjectRepresentation, AK_Assign, AK_Increment, AK_Decrement, AK_MemberCall, AK_DynamicCast, AK_TypeId, + AK_Construct, + AK_Destroy, }; // The order of this enum is important for diagnostics. diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index a54a767f45aea..d740b89fb9764 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -520,7 +520,7 @@ class CXXNameMangler { #define ABSTRACT_TYPE(CLASS, PARENT) #define NON_CANONICAL_TYPE(CLASS, PARENT) #define TYPE(CLASS, PARENT) void mangleType(const CLASS##Type *T); -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" void mangleType(const TagType*); void mangleType(TemplateName); @@ -2541,7 +2541,7 @@ void CXXNameMangler::mangleType(QualType T) { case Type::CLASS: \ mangleType(static_cast(ty)); \ break; -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } } diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp index b7e8c882244e2..049ce0d6ff24b 100644 --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -850,6 +850,12 @@ void JSONNodeDumper::VisitLinkageSpecDecl(const LinkageSpecDecl *LSD) { switch (LSD->getLanguage()) { case LinkageSpecDecl::lang_c: Lang = "C"; break; case LinkageSpecDecl::lang_cxx: Lang = "C++"; break; + case LinkageSpecDecl::lang_cxx_11: + Lang = "C++11"; + break; + case LinkageSpecDecl::lang_cxx_14: + Lang = "C++14"; + break; } JOS.attribute("language", Lang); attributeOnlyIfTrue("hasBraces", LSD->hasBraces()); diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp index 28de87fde9e15..b158fe85a475c 100644 --- a/clang/lib/AST/Mangle.cpp +++ b/clang/lib/AST/Mangle.cpp @@ -122,15 +122,21 @@ void MangleContext::mangleName(const NamedDecl *D, raw_ostream &Out) { if (const AsmLabelAttr *ALA = D->getAttr()) { // If we have an asm name, then we use it as the mangling. + // If the label isn't literal, or if this is an alias for an LLVM intrinsic, + // do not add a "\01" prefix. + if (!ALA->getIsLiteralLabel() || ALA->getLabel().startswith("llvm.")) { + Out << ALA->getLabel(); + return; + } + // Adding the prefix can cause problems when one file has a "foo" and // another has a "\01foo". That is known to happen on ELF with the // tricks normally used for producing aliases (PR9177). Fortunately the // llvm mangler on ELF is a nop, so we can just avoid adding the \01 - // marker. We also avoid adding the marker if this is an alias for an - // LLVM intrinsic. + // marker. char GlobalPrefix = getASTContext().getTargetInfo().getDataLayout().getGlobalPrefix(); - if (GlobalPrefix && !ALA->getLabel().startswith("llvm.")) + if (GlobalPrefix) Out << '\01'; // LLVM IR Marker for __asm("foo") Out << ALA->getLabel(); diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index 7ad8b52f7fd59..c80388be1eaad 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -364,7 +364,7 @@ class MicrosoftCXXNameMangler { #define TYPE(CLASS, PARENT) void mangleType(const CLASS##Type *T, \ Qualifiers Quals, \ SourceRange Range); -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" #undef ABSTRACT_TYPE #undef NON_CANONICAL_TYPE #undef TYPE @@ -615,6 +615,8 @@ void MicrosoftCXXNameMangler::mangleMemberDataPointer(const CXXRecordDecl *RD, case MSInheritanceAttr::Keyword_multiple_inheritance: Code = '0'; break; case MSInheritanceAttr::Keyword_virtual_inheritance: Code = 'F'; break; case MSInheritanceAttr::Keyword_unspecified_inheritance: Code = 'G'; break; + case MSInheritanceAttr::SpellingNotCalculated: + llvm_unreachable("not reachable"); } Out << '$' << Code; @@ -646,6 +648,8 @@ MicrosoftCXXNameMangler::mangleMemberFunctionPointer(const CXXRecordDecl *RD, case MSInheritanceAttr::Keyword_multiple_inheritance: Code = 'H'; break; case MSInheritanceAttr::Keyword_virtual_inheritance: Code = 'I'; break; case MSInheritanceAttr::Keyword_unspecified_inheritance: Code = 'J'; break; + case MSInheritanceAttr::SpellingNotCalculated: + llvm_unreachable("not reachable"); } // If non-virtual, mangle the name. If virtual, mangle as a virtual memptr @@ -1937,7 +1941,7 @@ void MicrosoftCXXNameMangler::mangleType(QualType T, SourceRange Range, case Type::CLASS: \ mangleType(cast(ty), Quals, Range); \ break; -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" #undef ABSTRACT_TYPE #undef NON_CANONICAL_TYPE #undef TYPE diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp index 0312fe6b76967..bfe272b1d935d 100644 --- a/clang/lib/AST/OpenMPClause.cpp +++ b/clang/lib/AST/OpenMPClause.cpp @@ -44,6 +44,7 @@ OMPClause::child_range OMPClause::used_children() { case OMPC_threadprivate: case OMPC_uniform: case OMPC_device_type: + case OMPC_match: case OMPC_unknown: break; } @@ -129,6 +130,7 @@ const OMPClauseWithPreInit *OMPClauseWithPreInit::get(const OMPClause *C) { case OMPC_dynamic_allocators: case OMPC_atomic_default_mem_order: case OMPC_device_type: + case OMPC_match: break; } @@ -206,6 +208,7 @@ const OMPClauseWithPostUpdate *OMPClauseWithPostUpdate::get(const OMPClause *C) case OMPC_dynamic_allocators: case OMPC_atomic_default_mem_order: case OMPC_device_type: + case OMPC_match: break; } diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index df3d149f2978e..a540346ad459f 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -223,7 +223,6 @@ void TextNodeDumper::Visit(const Decl *D) { return; } - Context = &D->getASTContext(); { ColorScope Color(OS, ShowColors, DeclKindNameColor); OS << D->getDeclKindName() << "Decl"; @@ -688,7 +687,7 @@ void TextNodeDumper::VisitConstantExpr(const ConstantExpr *Node) { if (Node->getResultAPValueKind() != APValue::None) { ColorScope Color(OS, ShowColors, ValueColor); OS << " "; - Node->getAPValueResult().printPretty(OS, *Context, Node->getType()); + Node->getAPValueResult().dump(OS); } } @@ -1385,6 +1384,8 @@ void TextNodeDumper::VisitVarDecl(const VarDecl *D) { break; } } + if (D->needsDestruction(D->getASTContext())) + OS << " destroyed"; if (D->isParameterPack()) OS << " pack"; } @@ -1767,6 +1768,12 @@ void TextNodeDumper::VisitLinkageSpecDecl(const LinkageSpecDecl *D) { case LinkageSpecDecl::lang_cxx: OS << " C++"; break; + case LinkageSpecDecl::lang_cxx_11: + OS << " C++11"; + break; + case LinkageSpecDecl::lang_cxx_14: + OS << " C++14"; + break; } } diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 411cb749a7b8d..8ab2f1e271efa 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -75,11 +75,11 @@ const IdentifierInfo* QualType::getBaseTypeIdentifier() const { if (ty->isPointerType() || ty->isReferenceType()) return ty->getPointeeType().getBaseTypeIdentifier(); else if (ty->isRecordType()) - ND = ty->getAs()->getDecl(); + ND = ty->castAs()->getDecl(); else if (ty->isEnumeralType()) - ND = ty->getAs()->getDecl(); + ND = ty->castAs()->getDecl(); else if (ty->getTypeClass() == Type::Typedef) - ND = ty->getAs()->getDecl(); + ND = ty->castAs()->getDecl(); else if (ty->isArrayType()) return ty->castAsArrayTypeUnsafe()-> getElementType().getBaseTypeIdentifier(); @@ -298,7 +298,7 @@ QualType QualType::getSingleStepDesugaredTypeImpl(QualType type, #define TYPE(CLASS, BASE) \ static_assert(!std::is_polymorphic::value, \ #CLASS "Type should not be polymorphic!"); -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" // Check that no type class has a non-trival destructor. Types are // allocated with the BumpPtrAllocator from ASTContext and therefore @@ -310,7 +310,7 @@ QualType QualType::getSingleStepDesugaredTypeImpl(QualType type, static_assert(std::is_trivially_destructible::value || \ std::is_same::value, \ #CLASS "Type should be trivially destructible!"); -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" QualType Type::getLocallyUnqualifiedSingleStepDesugaredType() const { switch (getTypeClass()) { @@ -321,7 +321,7 @@ QualType Type::getLocallyUnqualifiedSingleStepDesugaredType() const { if (!ty->isSugared()) return QualType(ty, 0); \ return ty->desugar(); \ } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } llvm_unreachable("bad type kind!"); } @@ -342,7 +342,7 @@ SplitQualType QualType::getSplitDesugaredType(QualType T) { Cur = Ty->desugar(); \ break; \ } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } } } @@ -370,7 +370,7 @@ SplitQualType QualType::getSplitUnqualifiedTypeImpl(QualType type) { next = ty->desugar(); \ break; \ } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } // Otherwise, split the underlying type. If that yields qualifiers, @@ -409,7 +409,7 @@ template static const T *getAsSugar(const Type *Cur) { Cur = Ty->desugar().getTypePtr(); \ break; \ } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } } } @@ -442,7 +442,7 @@ const Type *Type::getUnqualifiedDesugaredType() const { Cur = Ty->desugar().getTypePtr(); \ break; \ } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } } } @@ -766,7 +766,7 @@ struct SimpleTransformVisitor : public TypeVisitor { #define TYPE(Class, Base) #define DEPENDENT_TYPE(Class, Base) \ QualType Visit##Class##Type(const Class##Type *T) { return QualType(T, 0); } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" #define TRIVIAL_TYPE_CLASS(Class) \ QualType Visit##Class##Type(const Class##Type *T) { return QualType(T, 0); } @@ -2490,6 +2490,15 @@ bool QualType::isCXX11PODType(const ASTContext &Context) const { return false; } +bool Type::isNothrowT() const { + if (const auto *RD = getAsCXXRecordDecl()) { + IdentifierInfo *II = RD->getIdentifier(); + if (II && II->isStr("nothrow_t") && RD->isInStdNamespace()) + return true; + } + return false; +} + bool Type::isAlignValT() const { if (const auto *ET = getAs()) { IdentifierInfo *II = ET->getDecl()->getIdentifier(); @@ -2703,7 +2712,7 @@ const char *Type::getTypeClassName() const { switch (TypeBits.TC) { #define ABSTRACT_TYPE(Derived, Base) #define TYPE(Derived, Base) case Derived: return #Derived; -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } llvm_unreachable("Invalid type class."); @@ -3573,13 +3582,13 @@ static CachedProperties computeCachedProperties(const Type *T) { switch (T->getTypeClass()) { #define TYPE(Class,Base) #define NON_CANONICAL_TYPE(Class,Base) case Type::Class: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm_unreachable("didn't expect a non-canonical type here"); #define TYPE(Class,Base) #define DEPENDENT_TYPE(Class,Base) case Type::Class: #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class,Base) case Type::Class: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" // Treat instantiation-dependent types as external. assert(T->isInstantiationDependentType()); return CachedProperties(ExternalLinkage, false); @@ -3676,13 +3685,13 @@ LinkageInfo LinkageComputer::computeTypeLinkageInfo(const Type *T) { switch (T->getTypeClass()) { #define TYPE(Class,Base) #define NON_CANONICAL_TYPE(Class,Base) case Type::Class: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm_unreachable("didn't expect a non-canonical type here"); #define TYPE(Class,Base) #define DEPENDENT_TYPE(Class,Base) case Type::Class: #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class,Base) case Type::Class: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" // Treat instantiation-dependent types as external. assert(T->isInstantiationDependentType()); return LinkageInfo::external(); @@ -3791,7 +3800,7 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const { case Type::Class: \ llvm_unreachable("non-canonical type"); #define TYPE(Class, Parent) -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" // Pointer types. case Type::Pointer: diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 6e707fb7d52e1..5ae3340eeb5f2 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -125,7 +125,7 @@ namespace { #define TYPE(CLASS, PARENT) \ void print##CLASS##Before(const CLASS##Type *T, raw_ostream &OS); \ void print##CLASS##After(const CLASS##Type *T, raw_ostream &OS); -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" private: void printBefore(const Type *ty, Qualifiers qs, raw_ostream &OS); @@ -329,7 +329,7 @@ void TypePrinter::printBefore(const Type *T,Qualifiers Quals, raw_ostream &OS) { #define TYPE(CLASS, PARENT) case Type::CLASS: \ print##CLASS##Before(cast(T), OS); \ break; -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } if (hasAfterQuals) { @@ -355,7 +355,7 @@ void TypePrinter::printAfter(const Type *T, Qualifiers Quals, raw_ostream &OS) { #define TYPE(CLASS, PARENT) case Type::CLASS: \ print##CLASS##After(cast(T), OS); \ break; -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } } diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index 1a45c9f532d92..687cf57059138 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -70,23 +70,47 @@ static SourceLocation GetEndLoc(Decl *D) { return D->getLocation(); } +/// Returns true on constant values based around a single IntegerLiteral. +/// Allow for use of parentheses, integer casts, and negative signs. +static bool IsIntegerLiteralConstantExpr(const Expr *E) { + // Allow parentheses + E = E->IgnoreParens(); + + // Allow conversions to different integer kind. + if (const auto *CE = dyn_cast(E)) { + if (CE->getCastKind() != CK_IntegralCast) + return false; + E = CE->getSubExpr(); + } + + // Allow negative numbers. + if (const auto *UO = dyn_cast(E)) { + if (UO->getOpcode() != UO_Minus) + return false; + E = UO->getSubExpr(); + } + + return isa(E); +} + /// Helper for tryNormalizeBinaryOperator. Attempts to extract an IntegerLiteral -/// or EnumConstantDecl from the given Expr. If it fails, returns nullptr. +/// constant expression or EnumConstantDecl from the given Expr. If it fails, +/// returns nullptr. static const Expr *tryTransformToIntOrEnumConstant(const Expr *E) { E = E->IgnoreParens(); - if (isa(E)) + if (IsIntegerLiteralConstantExpr(E)) return E; if (auto *DR = dyn_cast(E->IgnoreParenImpCasts())) return isa(DR->getDecl()) ? DR : nullptr; return nullptr; } -/// Tries to interpret a binary operator into `Decl Op Expr` form, if Expr is -/// an integer literal or an enum constant. +/// Tries to interpret a binary operator into `Expr Op NumExpr` form, if +/// NumExpr is an integer literal or an enum constant. /// /// If this fails, at least one of the returned DeclRefExpr or Expr will be /// null. -static std::tuple +static std::tuple tryNormalizeBinaryOperator(const BinaryOperator *B) { BinaryOperatorKind Op = B->getOpcode(); @@ -108,8 +132,7 @@ tryNormalizeBinaryOperator(const BinaryOperator *B) { Constant = tryTransformToIntOrEnumConstant(B->getLHS()); } - auto *D = dyn_cast(MaybeDecl->IgnoreParenImpCasts()); - return std::make_tuple(D, Op, Constant); + return std::make_tuple(MaybeDecl, Op, Constant); } /// For an expression `x == Foo && x == Bar`, this determines whether the @@ -121,11 +144,11 @@ tryNormalizeBinaryOperator(const BinaryOperator *B) { static bool areExprTypesCompatible(const Expr *E1, const Expr *E2) { // User intent isn't clear if they're mixing int literals with enum // constants. - if (isa(E1) != isa(E2)) + if (isa(E1) != isa(E2)) return false; // Integer literal comparisons, regardless of literal type, are acceptable. - if (isa(E1)) + if (!isa(E1)) return true; // IntegerLiterals are handled above and only EnumConstantDecls are expected @@ -1021,34 +1044,34 @@ class CFGBuilder { if (!LHS->isComparisonOp() || !RHS->isComparisonOp()) return {}; - const DeclRefExpr *Decl1; - const Expr *Expr1; + const Expr *DeclExpr1; + const Expr *NumExpr1; BinaryOperatorKind BO1; - std::tie(Decl1, BO1, Expr1) = tryNormalizeBinaryOperator(LHS); + std::tie(DeclExpr1, BO1, NumExpr1) = tryNormalizeBinaryOperator(LHS); - if (!Decl1 || !Expr1) + if (!DeclExpr1 || !NumExpr1) return {}; - const DeclRefExpr *Decl2; - const Expr *Expr2; + const Expr *DeclExpr2; + const Expr *NumExpr2; BinaryOperatorKind BO2; - std::tie(Decl2, BO2, Expr2) = tryNormalizeBinaryOperator(RHS); + std::tie(DeclExpr2, BO2, NumExpr2) = tryNormalizeBinaryOperator(RHS); - if (!Decl2 || !Expr2) + if (!DeclExpr2 || !NumExpr2) return {}; // Check that it is the same variable on both sides. - if (Decl1->getDecl() != Decl2->getDecl()) + if (!Expr::isSameComparisonOperand(DeclExpr1, DeclExpr2)) return {}; // Make sure the user's intent is clear (e.g. they're comparing against two // int literals, or two things from the same enum) - if (!areExprTypesCompatible(Expr1, Expr2)) + if (!areExprTypesCompatible(NumExpr1, NumExpr2)) return {}; Expr::EvalResult L1Result, L2Result; - if (!Expr1->EvaluateAsInt(L1Result, *Context) || - !Expr2->EvaluateAsInt(L2Result, *Context)) + if (!NumExpr1->EvaluateAsInt(L1Result, *Context) || + !NumExpr2->EvaluateAsInt(L2Result, *Context)) return {}; llvm::APSInt L1 = L1Result.Val.getInt(); @@ -1081,6 +1104,10 @@ class CFGBuilder { // * Variable x is equal to the largest literal. // * Variable x is greater than largest literal. bool AlwaysTrue = true, AlwaysFalse = true; + // Track value of both subexpressions. If either side is always + // true/false, another warning should have already been emitted. + bool LHSAlwaysTrue = true, LHSAlwaysFalse = true; + bool RHSAlwaysTrue = true, RHSAlwaysFalse = true; for (const llvm::APSInt &Value : Values) { TryResult Res1, Res2; Res1 = analyzeLogicOperatorCondition(BO1, Value, L1); @@ -1096,10 +1123,16 @@ class CFGBuilder { AlwaysTrue &= (Res1.isTrue() || Res2.isTrue()); AlwaysFalse &= !(Res1.isTrue() || Res2.isTrue()); } + + LHSAlwaysTrue &= Res1.isTrue(); + LHSAlwaysFalse &= Res1.isFalse(); + RHSAlwaysTrue &= Res2.isTrue(); + RHSAlwaysFalse &= Res2.isFalse(); } if (AlwaysTrue || AlwaysFalse) { - if (BuildOpts.Observer) + if (!LHSAlwaysTrue && !LHSAlwaysFalse && !RHSAlwaysTrue && + !RHSAlwaysFalse && BuildOpts.Observer) BuildOpts.Observer->compareAlwaysTrue(B, AlwaysTrue); return TryResult(AlwaysTrue); } diff --git a/clang/lib/Analysis/CallGraph.cpp b/clang/lib/Analysis/CallGraph.cpp index 6aa8bf395b63f..63f7a8878e02b 100644 --- a/clang/lib/Analysis/CallGraph.cpp +++ b/clang/lib/Analysis/CallGraph.cpp @@ -80,7 +80,10 @@ class CGBuilder : public StmtVisitor { } void VisitLambdaExpr(LambdaExpr *LE) { - if (CXXMethodDecl *MD = LE->getCallOperator()) + if (FunctionTemplateDecl *FTD = LE->getDependentCallOperator()) + for (FunctionDecl *FD : FTD->specializations()) + G->VisitFunctionDecl(FD); + else if (CXXMethodDecl *MD = LE->getCallOperator()) G->VisitFunctionDecl(MD); } diff --git a/clang/lib/Analysis/Consumed.cpp b/clang/lib/Analysis/Consumed.cpp index 17bc29ba88342..cde753e8ec576 100644 --- a/clang/lib/Analysis/Consumed.cpp +++ b/clang/lib/Analysis/Consumed.cpp @@ -644,10 +644,10 @@ bool ConsumedStmtVisitor::handleCall(const CallExpr *Call, const Expr *ObjArg, continue; // Adjust state on the caller side. - if (isRValueRef(ParamType)) - setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Consumed); - else if (ReturnTypestateAttr *RT = Param->getAttr()) + if (ReturnTypestateAttr *RT = Param->getAttr()) setStateForVarOrTmp(StateMap, PInfo, mapReturnTypestateAttrState(RT)); + else if (isRValueRef(ParamType) || isConsumableType(ParamType)) + setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Consumed); else if (isPointerOrRef(ParamType) && (!ParamType->getPointeeType().isConstQualified() || isSetOnReadPtrType(ParamType))) diff --git a/clang/lib/Analysis/PathDiagnostic.cpp b/clang/lib/Analysis/PathDiagnostic.cpp index 764cb8ed0e4f8..53235ba076994 100644 --- a/clang/lib/Analysis/PathDiagnostic.cpp +++ b/clang/lib/Analysis/PathDiagnostic.cpp @@ -695,14 +695,18 @@ PathDiagnosticLocation::create(const ProgramPoint& P, return PathDiagnosticLocation( CEB->getLocationContext()->getDecl()->getSourceRange().getEnd(), SMng); } else if (Optional BE = P.getAs()) { - CFGElement BlockFront = BE->getBlock()->front(); - if (auto StmtElt = BlockFront.getAs()) { - return PathDiagnosticLocation(StmtElt->getStmt()->getBeginLoc(), SMng); - } else if (auto NewAllocElt = BlockFront.getAs()) { - return PathDiagnosticLocation( - NewAllocElt->getAllocatorExpr()->getBeginLoc(), SMng); + if (Optional BlockFront = BE->getFirstElement()) { + if (auto StmtElt = BlockFront->getAs()) { + return PathDiagnosticLocation(StmtElt->getStmt()->getBeginLoc(), SMng); + } else if (auto NewAllocElt = BlockFront->getAs()) { + return PathDiagnosticLocation( + NewAllocElt->getAllocatorExpr()->getBeginLoc(), SMng); + } + llvm_unreachable("Unexpected CFG element at front of block"); } - llvm_unreachable("Unexpected CFG element at front of block"); + + return PathDiagnosticLocation( + BE->getBlock()->getTerminatorStmt()->getBeginLoc(), SMng); } else if (Optional FE = P.getAs()) { return PathDiagnosticLocation(FE->getStmt(), SMng, FE->getLocationContext()); diff --git a/clang/lib/Analysis/ReachableCode.cpp b/clang/lib/Analysis/ReachableCode.cpp index 2fea88ea2eff4..1dab8e309f599 100644 --- a/clang/lib/Analysis/ReachableCode.cpp +++ b/clang/lib/Analysis/ReachableCode.cpp @@ -247,7 +247,7 @@ static bool isConfigurationValue(const Stmt *S, } case Stmt::UnaryOperatorClass: { const UnaryOperator *UO = cast(S); - if (UO->getOpcode() != UO_LNot) + if (UO->getOpcode() != UO_LNot && UO->getOpcode() != UO_Minus) return false; bool SilenceableCondValNotSet = SilenceableCondVal && SilenceableCondVal->getBegin().isInvalid(); diff --git a/clang/lib/Analysis/plugins/CheckerDependencyHandling/CMakeLists.txt b/clang/lib/Analysis/plugins/CheckerDependencyHandling/CMakeLists.txt index dde2cca133be7..229de54814926 100644 --- a/clang/lib/Analysis/plugins/CheckerDependencyHandling/CMakeLists.txt +++ b/clang/lib/Analysis/plugins/CheckerDependencyHandling/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS ) set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/CheckerDependencyHandlingAnalyzerPlugin.exports) -add_llvm_library(CheckerDependencyHandlingAnalyzerPlugin MODULE CheckerDependencyHandling.cpp PLUGIN_TOOL clang) +add_llvm_library(CheckerDependencyHandlingAnalyzerPlugin MODULE BUILDTREE_ONLY CheckerDependencyHandling.cpp PLUGIN_TOOL clang) clang_target_link_libraries(CheckerDependencyHandlingAnalyzerPlugin PRIVATE clangAnalysis diff --git a/clang/lib/Analysis/plugins/CheckerOptionHandling/CMakeLists.txt b/clang/lib/Analysis/plugins/CheckerOptionHandling/CMakeLists.txt index 744164782d915..432383efba5cc 100644 --- a/clang/lib/Analysis/plugins/CheckerOptionHandling/CMakeLists.txt +++ b/clang/lib/Analysis/plugins/CheckerOptionHandling/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS ) set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/CheckerOptionHandlingAnalyzerPlugin.exports) -add_llvm_library(CheckerOptionHandlingAnalyzerPlugin MODULE CheckerOptionHandling.cpp PLUGIN_TOOL clang) +add_llvm_library(CheckerOptionHandlingAnalyzerPlugin MODULE BUILDTREE_ONLY CheckerOptionHandling.cpp PLUGIN_TOOL clang) clang_target_link_libraries(CheckerOptionHandlingAnalyzerPlugin PRIVATE clangAnalysis diff --git a/clang/lib/Analysis/plugins/SampleAnalyzer/CMakeLists.txt b/clang/lib/Analysis/plugins/SampleAnalyzer/CMakeLists.txt index 0db1780ca7c98..d9b3f05cbd1bb 100644 --- a/clang/lib/Analysis/plugins/SampleAnalyzer/CMakeLists.txt +++ b/clang/lib/Analysis/plugins/SampleAnalyzer/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS ) set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/SampleAnalyzerPlugin.exports) -add_llvm_library(SampleAnalyzerPlugin MODULE MainCallChecker.cpp PLUGIN_TOOL clang) +add_llvm_library(SampleAnalyzerPlugin MODULE BUILDTREE_ONLY MainCallChecker.cpp PLUGIN_TOOL clang) clang_target_link_libraries(SampleAnalyzerPlugin PRIVATE clangAnalysis diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp index 761df3b55db6b..f69efe1aaaba6 100644 --- a/clang/lib/Basic/OpenMPKinds.cpp +++ b/clang/lib/Basic/OpenMPKinds.cpp @@ -56,6 +56,7 @@ OpenMPClauseKind clang::getOpenMPClauseKind(StringRef Str) { #include "clang/Basic/OpenMPKinds.def" .Case("uniform", OMPC_uniform) .Case("device_type", OMPC_device_type) + .Case("match", OMPC_match) .Default(OMPC_unknown); } @@ -74,6 +75,8 @@ const char *clang::getOpenMPClauseName(OpenMPClauseKind Kind) { return "threadprivate or thread local"; case OMPC_device_type: return "device_type"; + case OMPC_match: + return "match"; } llvm_unreachable("Invalid OpenMP clause kind"); } @@ -200,6 +203,7 @@ unsigned clang::getOpenMPSimpleClauseType(OpenMPClauseKind Kind, case OMPC_unified_shared_memory: case OMPC_reverse_offload: case OMPC_dynamic_allocators: + case OMPC_match: break; } llvm_unreachable("Invalid OpenMP simple clause kind"); @@ -393,6 +397,7 @@ const char *clang::getOpenMPSimpleClauseTypeName(OpenMPClauseKind Kind, case OMPC_unified_shared_memory: case OMPC_reverse_offload: case OMPC_dynamic_allocators: + case OMPC_match: break; } llvm_unreachable("Invalid OpenMP simple clause kind"); @@ -606,8 +611,6 @@ bool clang::isAllowedClauseForDirective(OpenMPDirectiveKind DKind, break; } break; - case OMPD_declare_simd: - break; case OMPD_cancel: switch (CKind) { #define OPENMP_CANCEL_CLAUSE(Name) \ @@ -833,6 +836,16 @@ bool clang::isAllowedClauseForDirective(OpenMPDirectiveKind DKind, #define OPENMP_ALLOCATE_CLAUSE(Name) \ case OMPC_##Name: \ return true; +#include "clang/Basic/OpenMPKinds.def" + default: + break; + } + break; + case OMPD_declare_variant: + switch (CKind) { +#define OPENMP_DECLARE_VARIANT_CLAUSE(Name) \ + case OMPC_##Name: \ + return true; #include "clang/Basic/OpenMPKinds.def" default: break; @@ -849,6 +862,7 @@ bool clang::isAllowedClauseForDirective(OpenMPDirectiveKind DKind, case OMPD_taskwait: case OMPD_cancellation_point: case OMPD_declare_reduction: + case OMPD_declare_simd: break; } return false; @@ -1078,6 +1092,7 @@ void clang::getOpenMPCaptureRegions( case OMPD_declare_target: case OMPD_end_declare_target: case OMPD_requires: + case OMPD_declare_variant: llvm_unreachable("OpenMP Directive is not allowed"); case OMPD_unknown: llvm_unreachable("Unknown OpenMP directive"); diff --git a/clang/lib/Basic/Targets/ARM.cpp b/clang/lib/Basic/Targets/ARM.cpp index 2d23b050bbc9e..437a77afdc998 100644 --- a/clang/lib/Basic/Targets/ARM.cpp +++ b/clang/lib/Basic/Targets/ARM.cpp @@ -428,11 +428,10 @@ bool ARMTargetInfo::handleTargetFeatures(std::vector &Features, for (const auto &Feature : Features) { if (Feature == "+soft-float") { SoftFloat = true; - } else if (Feature == "+vfp2sp" || Feature == "+vfp2d16sp" || - Feature == "+vfp2" || Feature == "+vfp2d16") { + } else if (Feature == "+vfp2sp" || Feature == "+vfp2") { FPU |= VFP2FPU; HW_FP |= HW_FP_SP; - if (Feature == "+vfp2" || Feature == "+vfp2d16") + if (Feature == "+vfp2") HW_FP |= HW_FP_DP; } else if (Feature == "+vfp3sp" || Feature == "+vfp3d16sp" || Feature == "+vfp3" || Feature == "+vfp3d16") { diff --git a/clang/lib/Basic/Targets/RISCV.cpp b/clang/lib/Basic/Targets/RISCV.cpp index 0a8df86fc88e3..ab8272c034fd3 100644 --- a/clang/lib/Basic/Targets/RISCV.cpp +++ b/clang/lib/Basic/Targets/RISCV.cpp @@ -88,8 +88,14 @@ void RISCVTargetInfo::getTargetDefines(const LangOptions &Opts, Builder.defineMacro("__riscv"); bool Is64Bit = getTriple().getArch() == llvm::Triple::riscv64; Builder.defineMacro("__riscv_xlen", Is64Bit ? "64" : "32"); - // TODO: modify when more code models are supported. - Builder.defineMacro("__riscv_cmodel_medlow"); + StringRef CodeModel = getTargetOpts().CodeModel; + if (CodeModel == "default") + CodeModel = "small"; + + if (CodeModel == "small") + Builder.defineMacro("__riscv_cmodel_medlow"); + else if (CodeModel == "medium") + Builder.defineMacro("__riscv_cmodel_medany"); StringRef ABIName = getABI(); if (ABIName == "ilp32f" || ABIName == "lp64f") diff --git a/clang/lib/Basic/Targets/SystemZ.cpp b/clang/lib/Basic/Targets/SystemZ.cpp index d86928a6333b0..ad3915e4d5dd0 100644 --- a/clang/lib/Basic/Targets/SystemZ.cpp +++ b/clang/lib/Basic/Targets/SystemZ.cpp @@ -92,7 +92,7 @@ static constexpr ISANameRevision ISARevisions[] = { {{"arch10"}, 10}, {{"zEC12"}, 10}, {{"arch11"}, 11}, {{"z13"}, 11}, {{"arch12"}, 12}, {{"z14"}, 12}, - {{"arch13"}, 13}, + {{"arch13"}, 13}, {{"z15"}, 13} }; int SystemZTargetInfo::getISARevision(StringRef Name) const { diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 522bef8ed6f92..150264a40587a 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -911,6 +911,7 @@ void EmitAssemblyHelper::EmitAssembly(BackendAction Action, { PrettyStackTraceString CrashInfo("Per-function optimization"); + llvm::TimeTraceScope TimeScope("PerFunctionPasses", StringRef("")); PerFunctionPasses.doInitialization(); for (Function &F : *TheModule) @@ -921,11 +922,13 @@ void EmitAssemblyHelper::EmitAssembly(BackendAction Action, { PrettyStackTraceString CrashInfo("Per-module optimization passes"); + llvm::TimeTraceScope TimeScope("PerModulePasses", StringRef("")); PerModulePasses.run(*TheModule); } { PrettyStackTraceString CrashInfo("Code generation"); + llvm::TimeTraceScope TimeScope("CodeGenPasses", StringRef("")); CodeGenPasses.run(*TheModule); } diff --git a/clang/lib/CodeGen/CGAtomic.cpp b/clang/lib/CodeGen/CGAtomic.cpp index a95cd12c2d648..f9915a1a8f5f0 100644 --- a/clang/lib/CodeGen/CGAtomic.cpp +++ b/clang/lib/CodeGen/CGAtomic.cpp @@ -107,7 +107,7 @@ namespace { } AtomicAlign = ValueAlign = lvalue.getAlignment(); } else if (lvalue.isVectorElt()) { - ValueTy = lvalue.getType()->getAs()->getElementType(); + ValueTy = lvalue.getType()->castAs()->getElementType(); ValueSizeInBits = C.getTypeSize(ValueTy); AtomicTy = lvalue.getType(); AtomicSizeInBits = C.getTypeSize(AtomicTy); diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index d3b7033e5671a..37be19275809b 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -2366,7 +2366,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, .toCharUnitsFromBits(TI.getSuitableAlign()) .getQuantity(); AllocaInst *AI = Builder.CreateAlloca(Builder.getInt8Ty(), Size); - AI->setAlignment(SuitableAlignmentInBytes); + AI->setAlignment(MaybeAlign(SuitableAlignmentInBytes)); initializeAlloca(*this, AI, Size, SuitableAlignmentInBytes); return RValue::get(AI); } @@ -2379,7 +2379,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, unsigned AlignmentInBytes = CGM.getContext().toCharUnitsFromBits(AlignmentInBits).getQuantity(); AllocaInst *AI = Builder.CreateAlloca(Builder.getInt8Ty(), Size); - AI->setAlignment(AlignmentInBytes); + AI->setAlignment(MaybeAlign(AlignmentInBytes)); initializeAlloca(*this, AI, Size, AlignmentInBytes); return RValue::get(AI); } @@ -14200,6 +14200,63 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID, Function *Callee = CGM.getIntrinsic(IntNo, A->getType()); return Builder.CreateCall(Callee, {A, B, C}); } + case WebAssembly::BI__builtin_wasm_narrow_s_i8x16_i16x8: + case WebAssembly::BI__builtin_wasm_narrow_u_i8x16_i16x8: + case WebAssembly::BI__builtin_wasm_narrow_s_i16x8_i32x4: + case WebAssembly::BI__builtin_wasm_narrow_u_i16x8_i32x4: { + Value *Low = EmitScalarExpr(E->getArg(0)); + Value *High = EmitScalarExpr(E->getArg(1)); + unsigned IntNo; + switch (BuiltinID) { + case WebAssembly::BI__builtin_wasm_narrow_s_i8x16_i16x8: + case WebAssembly::BI__builtin_wasm_narrow_s_i16x8_i32x4: + IntNo = Intrinsic::wasm_narrow_signed; + break; + case WebAssembly::BI__builtin_wasm_narrow_u_i8x16_i16x8: + case WebAssembly::BI__builtin_wasm_narrow_u_i16x8_i32x4: + IntNo = Intrinsic::wasm_narrow_unsigned; + break; + default: + llvm_unreachable("unexpected builtin ID"); + } + Function *Callee = + CGM.getIntrinsic(IntNo, {ConvertType(E->getType()), Low->getType()}); + return Builder.CreateCall(Callee, {Low, High}); + } + case WebAssembly::BI__builtin_wasm_widen_low_s_i16x8_i8x16: + case WebAssembly::BI__builtin_wasm_widen_high_s_i16x8_i8x16: + case WebAssembly::BI__builtin_wasm_widen_low_u_i16x8_i8x16: + case WebAssembly::BI__builtin_wasm_widen_high_u_i16x8_i8x16: + case WebAssembly::BI__builtin_wasm_widen_low_s_i32x4_i16x8: + case WebAssembly::BI__builtin_wasm_widen_high_s_i32x4_i16x8: + case WebAssembly::BI__builtin_wasm_widen_low_u_i32x4_i16x8: + case WebAssembly::BI__builtin_wasm_widen_high_u_i32x4_i16x8: { + Value *Vec = EmitScalarExpr(E->getArg(0)); + unsigned IntNo; + switch (BuiltinID) { + case WebAssembly::BI__builtin_wasm_widen_low_s_i16x8_i8x16: + case WebAssembly::BI__builtin_wasm_widen_low_s_i32x4_i16x8: + IntNo = Intrinsic::wasm_widen_low_signed; + break; + case WebAssembly::BI__builtin_wasm_widen_high_s_i16x8_i8x16: + case WebAssembly::BI__builtin_wasm_widen_high_s_i32x4_i16x8: + IntNo = Intrinsic::wasm_widen_high_signed; + break; + case WebAssembly::BI__builtin_wasm_widen_low_u_i16x8_i8x16: + case WebAssembly::BI__builtin_wasm_widen_low_u_i32x4_i16x8: + IntNo = Intrinsic::wasm_widen_low_unsigned; + break; + case WebAssembly::BI__builtin_wasm_widen_high_u_i16x8_i8x16: + case WebAssembly::BI__builtin_wasm_widen_high_u_i32x4_i16x8: + IntNo = Intrinsic::wasm_widen_high_unsigned; + break; + default: + llvm_unreachable("unexpected builtin ID"); + } + Function *Callee = + CGM.getIntrinsic(IntNo, {ConvertType(E->getType()), Vec->getType()}); + return Builder.CreateCall(Callee, Vec); + } default: return nullptr; } diff --git a/clang/lib/CodeGen/CGCUDANV.cpp b/clang/lib/CodeGen/CGCUDANV.cpp index 4d4038dae9cfc..05aeef4194d4d 100644 --- a/clang/lib/CodeGen/CGCUDANV.cpp +++ b/clang/lib/CodeGen/CGCUDANV.cpp @@ -236,7 +236,8 @@ void CGNVCUDARuntime::emitDeviceStub(CodeGenFunction &CGF, EmittedKernels.push_back({CGF.CurFn, CGF.CurFuncDecl}); if (CudaFeatureEnabled(CGM.getTarget().getSDKVersion(), - CudaFeature::CUDA_USES_NEW_LAUNCH)) + CudaFeature::CUDA_USES_NEW_LAUNCH) || + CGF.getLangOpts().HIPUseNewLaunchAPI) emitDeviceStubBodyNew(CGF, Args); else emitDeviceStubBodyLegacy(CGF, Args); @@ -264,14 +265,18 @@ void CGNVCUDARuntime::emitDeviceStubBodyNew(CodeGenFunction &CGF, llvm::BasicBlock *EndBlock = CGF.createBasicBlock("setup.end"); - // Lookup cudaLaunchKernel function. + // Lookup cudaLaunchKernel/hipLaunchKernel function. // cudaError_t cudaLaunchKernel(const void *func, dim3 gridDim, dim3 blockDim, // void **args, size_t sharedMem, // cudaStream_t stream); + // hipError_t hipLaunchKernel(const void *func, dim3 gridDim, dim3 blockDim, + // void **args, size_t sharedMem, + // hipStream_t stream); TranslationUnitDecl *TUDecl = CGM.getContext().getTranslationUnitDecl(); DeclContext *DC = TranslationUnitDecl::castToDeclContext(TUDecl); + auto LaunchKernelName = addPrefixToName("LaunchKernel"); IdentifierInfo &cudaLaunchKernelII = - CGM.getContext().Idents.get("cudaLaunchKernel"); + CGM.getContext().Idents.get(LaunchKernelName); FunctionDecl *cudaLaunchKernelFD = nullptr; for (const auto &Result : DC->lookup(&cudaLaunchKernelII)) { if (FunctionDecl *FD = dyn_cast(Result)) @@ -280,7 +285,7 @@ void CGNVCUDARuntime::emitDeviceStubBodyNew(CodeGenFunction &CGF, if (cudaLaunchKernelFD == nullptr) { CGM.Error(CGF.CurFuncDecl->getLocation(), - "Can't find declaration for cudaLaunchKernel()"); + "Can't find declaration for " + LaunchKernelName); return; } // Create temporary dim3 grid_dim, block_dim. @@ -301,7 +306,7 @@ void CGNVCUDARuntime::emitDeviceStubBodyNew(CodeGenFunction &CGF, /*ShmemSize=*/ShmemSize.getType(), /*Stream=*/Stream.getType()}, /*isVarArg=*/false), - "__cudaPopCallConfiguration"); + addUnderscoredPrefixToName("PopCallConfiguration")); CGF.EmitRuntimeCallOrInvoke(cudaPopConfigFn, {GridDim.getPointer(), BlockDim.getPointer(), @@ -329,7 +334,7 @@ void CGNVCUDARuntime::emitDeviceStubBodyNew(CodeGenFunction &CGF, const CGFunctionInfo &FI = CGM.getTypes().arrangeFunctionDeclaration(cudaLaunchKernelFD); llvm::FunctionCallee cudaLaunchKernelFn = - CGM.CreateRuntimeFunction(FTy, "cudaLaunchKernel"); + CGM.CreateRuntimeFunction(FTy, LaunchKernelName); CGF.EmitCall(FI, CGCallee::forDirect(cudaLaunchKernelFn), ReturnValueSlot(), LaunchKernelArgs); CGF.EmitBranch(EndBlock); diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 97d50fe709714..43da978a56b10 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -3093,7 +3093,7 @@ void CodeGenFunction::EmitDelegateCallArg(CallArgList &args, // Deactivate the cleanup for the callee-destructed param that was pushed. if (hasAggregateEvaluationKind(type) && !CurFuncIsThunk && type->getAs()->getDecl()->isParamDestroyedInCallee() && - type.isDestructedType()) { + param->needsDestruction(getContext())) { EHScopeStack::stable_iterator cleanup = CalleeDestructedParamCleanups.lookup(cast(param)); assert(cleanup.isValid() && @@ -3841,7 +3841,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, AI = CreateTempAlloca(ArgStruct, "argmem"); } auto Align = CallInfo.getArgStructAlignment(); - AI->setAlignment(Align.getQuantity()); + AI->setAlignment(llvm::MaybeAlign(Align.getQuantity())); AI->setUsedWithInAlloca(true); assert(AI->isUsedWithInAlloca() && !AI->isStaticAlloca()); ArgMemory = Address(AI, Align); @@ -4143,11 +4143,12 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, auto scalarAlign = CGM.getDataLayout().getPrefTypeAlignment(scalarType); // Materialize to a temporary. - addr = CreateTempAlloca(RV.getScalarVal()->getType(), - CharUnits::fromQuantity(std::max( - layout->getAlignment(), scalarAlign)), - "tmp", - /*ArraySize=*/nullptr, &AllocaAddr); + addr = CreateTempAlloca( + RV.getScalarVal()->getType(), + CharUnits::fromQuantity(std::max( + (unsigned)layout->getAlignment().value(), scalarAlign)), + "tmp", + /*ArraySize=*/nullptr, &AllocaAddr); tempSize = EmitLifetimeStart(scalarSize, AllocaAddr.getPointer()); Builder.CreateStore(RV.getScalarVal(), addr); diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index b4fedb0836ec1..3312a279c1571 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -2085,7 +2085,7 @@ static bool canEmitDelegateCallArgs(CodeGenFunction &CGF, if (CGF.getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee()) { // If the parameters are callee-cleanup, it's not safe to forward. for (auto *P : Ctor->parameters()) - if (P->getType().isDestructedType()) + if (P->needsDestruction(CGF.getContext())) return false; // Likewise if they're inalloca. diff --git a/clang/lib/CodeGen/CGCleanup.cpp b/clang/lib/CodeGen/CGCleanup.cpp index 3cf366e4635d5..1280bdb1932e5 100644 --- a/clang/lib/CodeGen/CGCleanup.cpp +++ b/clang/lib/CodeGen/CGCleanup.cpp @@ -310,7 +310,7 @@ static void createStoreInstBefore(llvm::Value *value, Address addr, static llvm::LoadInst *createLoadInstBefore(Address addr, const Twine &name, llvm::Instruction *beforeInst) { auto load = new llvm::LoadInst(addr.getPointer(), name, beforeInst); - load->setAlignment(addr.getAlignment().getQuantity()); + load->setAlignment(llvm::MaybeAlign(addr.getAlignment().getQuantity())); return load; } diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index e36f6932f86e7..71d99bc551c48 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -561,6 +561,10 @@ void CGDebugInfo::CreateCompileUnit() { if (LO.CPlusPlus) { if (LO.ObjC) LangTag = llvm::dwarf::DW_LANG_ObjC_plus_plus; + else if (LO.CPlusPlus14) + LangTag = llvm::dwarf::DW_LANG_C_plus_plus_14; + else if (LO.CPlusPlus11) + LangTag = llvm::dwarf::DW_LANG_C_plus_plus_11; else LangTag = llvm::dwarf::DW_LANG_C_plus_plus; } else if (LO.ObjC) { @@ -878,6 +882,8 @@ llvm::DIType *CGDebugInfo::CreateType(const PointerType *Ty, static bool hasCXXMangling(const TagDecl *TD, llvm::DICompileUnit *TheCU) { switch (TheCU->getSourceLanguage()) { case llvm::dwarf::DW_LANG_C_plus_plus: + case llvm::dwarf::DW_LANG_C_plus_plus_11: + case llvm::dwarf::DW_LANG_C_plus_plus_14: return true; case llvm::dwarf::DW_LANG_ObjC_plus_plus: return isa(TD) || isa(TD); @@ -2711,6 +2717,8 @@ llvm::DIType *CGDebugInfo::CreateType(const MemberPointerType *Ty, break; case MSInheritanceAttr::Keyword_unspecified_inheritance: break; + case MSInheritanceAttr::SpellingNotCalculated: + llvm_unreachable("Spelling not yet calculated"); } } } @@ -2994,7 +3002,7 @@ llvm::DIType *CGDebugInfo::CreateTypeNode(QualType Ty, llvm::DIFile *Unit) { #define ABSTRACT_TYPE(Class, Base) #define NON_CANONICAL_TYPE(Class, Base) #define DEPENDENT_TYPE(Class, Base) case Type::Class: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm_unreachable("Dependent types cannot show up in debug information"); case Type::ExtVector: diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 08d71252ef0b5..d0a0f20d2f823 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -309,14 +309,6 @@ llvm::Constant *CodeGenModule::getOrCreateStaticVarDecl( return Addr; } -/// hasNontrivialDestruction - Determine whether a type's destruction is -/// non-trivial. If so, and the variable uses static initialization, we must -/// register its destructor to run on exit. -static bool hasNontrivialDestruction(QualType T) { - CXXRecordDecl *RD = T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl(); - return RD && !RD->hasTrivialDestructor(); -} - /// AddInitializerToStaticVarDecl - Add the initializer for 'D' to the /// global variable that has already been created for it. If the initializer /// has a different type than GV does, this may free GV and return a different @@ -395,7 +387,7 @@ CodeGenFunction::AddInitializerToStaticVarDecl(const VarDecl &D, emitter.finalize(GV); - if (hasNontrivialDestruction(D.getType()) && HaveInsertPoint()) { + if (D.needsDestruction(getContext()) && HaveInsertPoint()) { // We have a constant initializer, but a nontrivial destructor. We still // need to perform a guarded "initialization" in order to register the // destructor. @@ -2037,7 +2029,7 @@ void CodeGenFunction::EmitAutoVarCleanups(const AutoVarEmission &emission) { const VarDecl &D = *emission.Variable; // Check the type for a cleanup. - if (QualType::DestructionKind dtorKind = D.getType().isDestructedType()) + if (QualType::DestructionKind dtorKind = D.needsDestruction(getContext())) emitAutoVarTypeCleanup(emission, dtorKind); // In GC mode, honor objc_precise_lifetime. @@ -2447,7 +2439,8 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, ParamValue Arg, // cleanup. if (hasAggregateEvaluationKind(Ty) && !CurFuncIsThunk && Ty->getAs()->getDecl()->isParamDestroyedInCallee()) { - if (QualType::DestructionKind DtorKind = Ty.isDestructedType()) { + if (QualType::DestructionKind DtorKind = + D.needsDestruction(getContext())) { assert((DtorKind == QualType::DK_cxx_destructor || DtorKind == QualType::DK_nontrivial_c_struct) && "unexpected destructor type"); diff --git a/clang/lib/CodeGen/CGDeclCXX.cpp b/clang/lib/CodeGen/CGDeclCXX.cpp index 0bc256a78d357..358c856c5fc04 100644 --- a/clang/lib/CodeGen/CGDeclCXX.cpp +++ b/clang/lib/CodeGen/CGDeclCXX.cpp @@ -75,16 +75,10 @@ static void EmitDeclDestroy(CodeGenFunction &CGF, const VarDecl &D, // that isn't balanced out by a destructor call as intended by the // attribute. This also checks for -fno-c++-static-destructors and // bails even if the attribute is not present. - if (D.isNoDestroy(CGF.getContext())) - return; - - CodeGenModule &CGM = CGF.CGM; + QualType::DestructionKind DtorKind = D.needsDestruction(CGF.getContext()); // FIXME: __attribute__((cleanup)) ? - QualType Type = D.getType(); - QualType::DestructionKind DtorKind = Type.isDestructedType(); - switch (DtorKind) { case QualType::DK_none: return; @@ -103,6 +97,9 @@ static void EmitDeclDestroy(CodeGenFunction &CGF, const VarDecl &D, llvm::FunctionCallee Func; llvm::Constant *Argument; + CodeGenModule &CGM = CGF.CGM; + QualType Type = D.getType(); + // Special-case non-array C++ destructors, if they have the right signature. // Under some ABIs, destructors return this instead of void, and cannot be // passed directly to __cxa_atexit if the target does not allow this diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index b442178f03a89..175ec8c5ceb92 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -66,7 +66,7 @@ Address CodeGenFunction::CreateTempAllocaWithoutCast(llvm::Type *Ty, const Twine &Name, llvm::Value *ArraySize) { auto Alloca = CreateTempAlloca(Ty, Name, ArraySize); - Alloca->setAlignment(Align.getQuantity()); + Alloca->setAlignment(llvm::MaybeAlign(Align.getQuantity())); return Address(Alloca, Align); } diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index 0a57870a7c583..9610a04be343a 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -1759,7 +1759,7 @@ static CharUnits GetNumNonZeroBytesInInit(const Expr *E, CodeGenFunction &CGF) { // referencee. InitListExprs for unions and arrays can't have references. if (const RecordType *RT = E->getType()->getAs()) { if (!RT->isUnionType()) { - RecordDecl *SD = E->getType()->getAs()->getDecl(); + RecordDecl *SD = RT->getDecl(); CharUnits NumNonZeroBytes = CharUnits::Zero(); unsigned ILEElement = 0; diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index e6bf6d7720765..7d01fbf27c4db 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -1657,8 +1657,8 @@ Value *ScalarExprEmitter::VisitConvertVectorExpr(ConvertVectorExpr *E) { if (SrcTy == DstTy) return Src; - QualType SrcEltType = SrcType->getAs()->getElementType(), - DstEltType = DstType->getAs()->getElementType(); + QualType SrcEltType = SrcType->castAs()->getElementType(), + DstEltType = DstType->castAs()->getElementType(); assert(SrcTy->isVectorTy() && "ConvertVector source IR type must be a vector"); @@ -3815,7 +3815,7 @@ Value *ScalarExprEmitter::EmitCompare(const BinaryOperator *E, Value *FirstVecArg = LHS, *SecondVecArg = RHS; - QualType ElTy = LHSTy->getAs()->getElementType(); + QualType ElTy = LHSTy->castAs()->getElementType(); const BuiltinType *BTy = ElTy->getAs(); BuiltinType::Kind ElementKind = BTy->getKind(); diff --git a/clang/lib/CodeGen/CGObjCGNU.cpp b/clang/lib/CodeGen/CGObjCGNU.cpp index ee5c12aa35bd7..ee0fd184c64d4 100644 --- a/clang/lib/CodeGen/CGObjCGNU.cpp +++ b/clang/lib/CodeGen/CGObjCGNU.cpp @@ -1854,7 +1854,8 @@ class CGObjCGNUstep2 : public CGObjCGNUstep { ivarBuilder.addInt(Int32Ty, CGM.getContext().getTypeSizeInChars(ivarTy).getQuantity()); // Alignment will be stored as a base-2 log of the alignment. - int align = llvm::Log2_32(Context.getTypeAlignInChars(ivarTy).getQuantity()); + unsigned align = + llvm::Log2_32(Context.getTypeAlignInChars(ivarTy).getQuantity()); // Objects that require more than 2^64-byte alignment should be impossible! assert(align < 64); // uint32_t flags; diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 12880fecbadff..2b92c4b9e6df0 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -2517,14 +2517,12 @@ void CGObjCCommonMac::BuildRCRecordLayout(const llvm::StructLayout *RecLayout, } if (const ArrayType *Array = CGM.getContext().getAsArrayType(FQT)) { - const ConstantArrayType *CArray = - dyn_cast_or_null(Array); + auto *CArray = cast(Array); uint64_t ElCount = CArray->getSize().getZExtValue(); assert(CArray && "only array with known element size is supported"); FQT = CArray->getElementType(); while (const ArrayType *Array = CGM.getContext().getAsArrayType(FQT)) { - const ConstantArrayType *CArray = - dyn_cast_or_null(Array); + auto *CArray = cast(Array); ElCount *= CArray->getSize().getZExtValue(); FQT = CArray->getElementType(); } @@ -4902,7 +4900,7 @@ LValue CGObjCMac::EmitObjCValueForIvar(CodeGen::CodeGenFunction &CGF, const ObjCIvarDecl *Ivar, unsigned CVRQualifiers) { const ObjCInterfaceDecl *ID = - ObjectTy->getAs()->getInterface(); + ObjectTy->castAs()->getInterface(); return EmitValueForIvarAtOffset(CGF, ID, BaseValue, Ivar, CVRQualifiers, EmitIvarOffset(CGF, ID, Ivar)); } @@ -7053,7 +7051,7 @@ LValue CGObjCNonFragileABIMac::EmitObjCValueForIvar( llvm::Value *BaseValue, const ObjCIvarDecl *Ivar, unsigned CVRQualifiers) { - ObjCInterfaceDecl *ID = ObjectTy->getAs()->getInterface(); + ObjCInterfaceDecl *ID = ObjectTy->castAs()->getInterface(); llvm::Value *Offset = EmitIvarOffset(CGF, ID, Ivar); return EmitValueForIvarAtOffset(CGF, ID, BaseValue, Ivar, CVRQualifiers, Offset); diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp index 99fa0790e908e..4da84f230ec1e 100644 --- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp +++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp @@ -1264,6 +1264,51 @@ CGOpenMPRuntime::CGOpenMPRuntime(CodeGenModule &CGM, StringRef FirstSeparator, loadOffloadInfoMetadata(); } +static bool tryEmitAlias(CodeGenModule &CGM, const GlobalDecl &NewGD, + const GlobalDecl &OldGD, llvm::GlobalValue *OrigAddr, + bool IsForDefinition) { + // Emit at least a definition for the aliasee if the the address of the + // original function is requested. + if (IsForDefinition || OrigAddr) + (void)CGM.GetAddrOfGlobal(NewGD); + StringRef NewMangledName = CGM.getMangledName(NewGD); + llvm::GlobalValue *Addr = CGM.GetGlobalValue(NewMangledName); + if (Addr && !Addr->isDeclaration()) { + const auto *D = cast(OldGD.getDecl()); + const CGFunctionInfo &FI = CGM.getTypes().arrangeGlobalDeclaration(OldGD); + llvm::Type *DeclTy = CGM.getTypes().GetFunctionType(FI); + + // Create a reference to the named value. This ensures that it is emitted + // if a deferred decl. + llvm::GlobalValue::LinkageTypes LT = CGM.getFunctionLinkage(OldGD); + + // Create the new alias itself, but don't set a name yet. + auto *GA = + llvm::GlobalAlias::create(DeclTy, 0, LT, "", Addr, &CGM.getModule()); + + if (OrigAddr) { + assert(OrigAddr->isDeclaration() && "Expected declaration"); + + GA->takeName(OrigAddr); + OrigAddr->replaceAllUsesWith( + llvm::ConstantExpr::getBitCast(GA, OrigAddr->getType())); + OrigAddr->eraseFromParent(); + } else { + GA->setName(CGM.getMangledName(OldGD)); + } + + // Set attributes which are particular to an alias; this is a + // specialization of the attributes which may be set on a global function. + if (D->hasAttr() || D->hasAttr() || + D->isWeakImported()) + GA->setLinkage(llvm::Function::WeakAnyLinkage); + + CGM.SetCommonAttributes(OldGD, GA); + return true; + } + return false; +} + void CGOpenMPRuntime::clear() { InternalVars.clear(); // Clean non-target variable declarations possibly used only in debug info. @@ -1277,6 +1322,14 @@ void CGOpenMPRuntime::clear() { continue; GV->eraseFromParent(); } + // Emit aliases for the deferred aliasees. + for (const auto &Pair : DeferredVariantFunction) { + StringRef MangledName = CGM.getMangledName(Pair.second.second); + llvm::GlobalValue *Addr = CGM.GetGlobalValue(MangledName); + // If not able to emit alias, just emit original declaration. + (void)tryEmitAlias(CGM, Pair.second.first, Pair.second.second, Addr, + /*IsForDefinition=*/false); + } } std::string CGOpenMPRuntime::getName(ArrayRef Parts) const { @@ -2815,6 +2868,8 @@ llvm::Function *CGOpenMPRuntime::emitThreadPrivateVarDefinition( bool CGOpenMPRuntime::emitDeclareTargetVarDefinition(const VarDecl *VD, llvm::GlobalVariable *Addr, bool PerformInit) { + if (CGM.getLangOpts().OMPTargetTriples.empty()) + return false; Optional Res = OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(VD); if (!Res || *Res == OMPDeclareTargetDeclAttr::MT_Link || @@ -3970,16 +4025,16 @@ CGOpenMPRuntime::createOffloadingBinaryDescriptorRegistration() { // host entries section. These will be defined by the linker. llvm::Type *OffloadEntryTy = CGM.getTypes().ConvertTypeForMem(getTgtOffloadEntryQTy()); - std::string EntriesBeginName = getName({"omp_offloading", "entries_begin"}); auto *HostEntriesBegin = new llvm::GlobalVariable( M, OffloadEntryTy, /*isConstant=*/true, llvm::GlobalValue::ExternalLinkage, /*Initializer=*/nullptr, - EntriesBeginName); - std::string EntriesEndName = getName({"omp_offloading", "entries_end"}); - auto *HostEntriesEnd = - new llvm::GlobalVariable(M, OffloadEntryTy, /*isConstant=*/true, - llvm::GlobalValue::ExternalLinkage, - /*Initializer=*/nullptr, EntriesEndName); + "__start_omp_offloading_entries"); + HostEntriesBegin->setVisibility(llvm::GlobalValue::HiddenVisibility); + auto *HostEntriesEnd = new llvm::GlobalVariable( + M, OffloadEntryTy, /*isConstant=*/true, + llvm::GlobalValue::ExternalLinkage, + /*Initializer=*/nullptr, "__stop_omp_offloading_entries"); + HostEntriesEnd->setVisibility(llvm::GlobalValue::HiddenVisibility); // Create all device images auto *DeviceImageTy = cast( @@ -4127,8 +4182,7 @@ void CGOpenMPRuntime::createOffloadEntry( Twine(EntryName).concat(Name), llvm::GlobalValue::WeakAnyLinkage); // The entry has to be created in the section the linker expects it to be. - std::string Section = getName({"omp_offloading", "entries"}); - Entry->setSection(Section); + Entry->setSection("omp_offloading_entries"); } void CGOpenMPRuntime::createOffloadEntriesAndInfoMetadata() { @@ -6770,6 +6824,7 @@ emitNumTeamsForTargetDirective(CodeGenFunction &CGF, case OMPD_teams_distribute_parallel_for_simd: case OMPD_target_update: case OMPD_declare_simd: + case OMPD_declare_variant: case OMPD_declare_target: case OMPD_end_declare_target: case OMPD_declare_reduction: @@ -7075,6 +7130,7 @@ emitNumThreadsForTargetDirective(CodeGenFunction &CGF, case OMPD_teams_distribute_parallel_for_simd: case OMPD_target_update: case OMPD_declare_simd: + case OMPD_declare_variant: case OMPD_declare_target: case OMPD_end_declare_target: case OMPD_declare_reduction: @@ -7246,9 +7302,11 @@ class MappableExprsHandler { OAE->getBase()->IgnoreParenImpCasts()) .getCanonicalType(); - // If there is no length associated with the expression, that means we - // are using the whole length of the base. - if (!OAE->getLength() && OAE->getColonLoc().isValid()) + // If there is no length associated with the expression and lower bound is + // not specified too, that means we are using the whole length of the + // base. + if (!OAE->getLength() && OAE->getColonLoc().isValid() && + !OAE->getLowerBound()) return CGF.getTypeSize(BaseTy); llvm::Value *ElemSize; @@ -7262,13 +7320,30 @@ class MappableExprsHandler { // If we don't have a length at this point, that is because we have an // array section with a single element. - if (!OAE->getLength()) + if (!OAE->getLength() && OAE->getColonLoc().isInvalid()) return ElemSize; - llvm::Value *LengthVal = CGF.EmitScalarExpr(OAE->getLength()); - LengthVal = - CGF.Builder.CreateIntCast(LengthVal, CGF.SizeTy, /*isSigned=*/false); - return CGF.Builder.CreateNUWMul(LengthVal, ElemSize); + if (const Expr *LenExpr = OAE->getLength()) { + llvm::Value *LengthVal = CGF.EmitScalarExpr(LenExpr); + LengthVal = CGF.EmitScalarConversion(LengthVal, LenExpr->getType(), + CGF.getContext().getSizeType(), + LenExpr->getExprLoc()); + return CGF.Builder.CreateNUWMul(LengthVal, ElemSize); + } + assert(!OAE->getLength() && OAE->getColonLoc().isValid() && + OAE->getLowerBound() && "expected array_section[lb:]."); + // Size = sizetype - lb * elemtype; + llvm::Value *LengthVal = CGF.getTypeSize(BaseTy); + llvm::Value *LBVal = CGF.EmitScalarExpr(OAE->getLowerBound()); + LBVal = CGF.EmitScalarConversion(LBVal, OAE->getLowerBound()->getType(), + CGF.getContext().getSizeType(), + OAE->getLowerBound()->getExprLoc()); + LBVal = CGF.Builder.CreateNUWMul(LBVal, ElemSize); + llvm::Value *Cmp = CGF.Builder.CreateICmpUGT(LengthVal, LBVal); + llvm::Value *TrueVal = CGF.Builder.CreateNUWSub(LengthVal, LBVal); + LengthVal = CGF.Builder.CreateSelect( + Cmp, TrueVal, llvm::ConstantInt::get(CGF.SizeTy, 0)); + return LengthVal; } return CGF.getTypeSize(ExprTy); } @@ -8826,6 +8901,7 @@ getNestedDistributeDirective(ASTContext &Ctx, const OMPExecutableDirective &D) { case OMPD_teams_distribute_parallel_for_simd: case OMPD_target_update: case OMPD_declare_simd: + case OMPD_declare_variant: case OMPD_declare_target: case OMPD_end_declare_target: case OMPD_declare_reduction: @@ -9173,9 +9249,11 @@ void CGOpenMPRuntime::emitUDMapperArrayInitOrDel( } void CGOpenMPRuntime::emitTargetNumIterationsCall( - CodeGenFunction &CGF, const OMPExecutableDirective &D, const Expr *Device, - const llvm::function_ref &SizeEmitter) { + CodeGenFunction &CGF, const OMPExecutableDirective &D, + llvm::Value *DeviceID, + llvm::function_ref + SizeEmitter) { OpenMPDirectiveKind Kind = D.getDirectiveKind(); const OMPExecutableDirective *TD = &D; // Get nested teams distribute kind directive, if any. @@ -9184,30 +9262,24 @@ void CGOpenMPRuntime::emitTargetNumIterationsCall( if (!TD) return; const auto *LD = cast(TD); - auto &&CodeGen = [LD, &Device, &SizeEmitter, this](CodeGenFunction &CGF, + auto &&CodeGen = [LD, DeviceID, SizeEmitter, this](CodeGenFunction &CGF, PrePostActionTy &) { - llvm::Value *NumIterations = SizeEmitter(CGF, *LD); - - // Emit device ID if any. - llvm::Value *DeviceID; - if (Device) - DeviceID = CGF.Builder.CreateIntCast(CGF.EmitScalarExpr(Device), - CGF.Int64Ty, /*isSigned=*/true); - else - DeviceID = CGF.Builder.getInt64(OMP_DEVICEID_UNDEF); - - llvm::Value *Args[] = {DeviceID, NumIterations}; - CGF.EmitRuntimeCall( - createRuntimeFunction(OMPRTL__kmpc_push_target_tripcount), Args); + if (llvm::Value *NumIterations = SizeEmitter(CGF, *LD)) { + llvm::Value *Args[] = {DeviceID, NumIterations}; + CGF.EmitRuntimeCall( + createRuntimeFunction(OMPRTL__kmpc_push_target_tripcount), Args); + } }; emitInlinedDirective(CGF, OMPD_unknown, CodeGen); } -void CGOpenMPRuntime::emitTargetCall(CodeGenFunction &CGF, - const OMPExecutableDirective &D, - llvm::Function *OutlinedFn, - llvm::Value *OutlinedFnID, - const Expr *IfCond, const Expr *Device) { +void CGOpenMPRuntime::emitTargetCall( + CodeGenFunction &CGF, const OMPExecutableDirective &D, + llvm::Function *OutlinedFn, llvm::Value *OutlinedFnID, const Expr *IfCond, + const Expr *Device, + llvm::function_ref + SizeEmitter) { if (!CGF.HaveInsertPoint()) return; @@ -9226,8 +9298,8 @@ void CGOpenMPRuntime::emitTargetCall(CodeGenFunction &CGF, llvm::Value *MapTypesArray = nullptr; // Fill up the pointer arrays and transfer execution to the device. auto &&ThenGen = [this, Device, OutlinedFn, OutlinedFnID, &D, &InputInfo, - &MapTypesArray, &CS, RequiresOuterTask, - &CapturedVars](CodeGenFunction &CGF, PrePostActionTy &) { + &MapTypesArray, &CS, RequiresOuterTask, &CapturedVars, + SizeEmitter](CodeGenFunction &CGF, PrePostActionTy &) { // On top of the arrays that were filled up, the target offloading call // takes as arguments the device id as well as the host pointer. The host // pointer is used by the runtime library to identify the current target @@ -9259,6 +9331,9 @@ void CGOpenMPRuntime::emitTargetCall(CodeGenFunction &CGF, llvm::Value *NumTeams = emitNumTeamsForTargetDirective(CGF, D); llvm::Value *NumThreads = emitNumThreadsForTargetDirective(CGF, D); + // Emit tripcount for the target loop-based directive. + emitTargetNumIterationsCall(CGF, D, DeviceID, SizeEmitter); + bool HasNowait = D.hasClausesOfKind(); // The target region is an outlined function launched by the runtime // via calls __tgt_target() or __tgt_target_teams(). @@ -9583,6 +9658,7 @@ void CGOpenMPRuntime::scanForTargetRegionsFunctions(const Stmt *S, case OMPD_teams_distribute_parallel_for_simd: case OMPD_target_update: case OMPD_declare_simd: + case OMPD_declare_variant: case OMPD_declare_target: case OMPD_end_declare_target: case OMPD_declare_reduction: @@ -9715,6 +9791,8 @@ CGOpenMPRuntime::registerTargetFirstprivateCopy(CodeGenFunction &CGF, void CGOpenMPRuntime::registerTargetGlobalVariable(const VarDecl *VD, llvm::Constant *Addr) { + if (CGM.getLangOpts().OMPTargetTriples.empty()) + return; llvm::Optional Res = OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(VD); if (!Res) { @@ -10205,6 +10283,7 @@ void CGOpenMPRuntime::emitTargetDataStandAloneCall( case OMPD_teams_distribute_parallel_for: case OMPD_teams_distribute_parallel_for_simd: case OMPD_declare_simd: + case OMPD_declare_variant: case OMPD_declare_target: case OMPD_end_declare_target: case OMPD_declare_reduction: @@ -11060,6 +11139,80 @@ Address CGOpenMPRuntime::getAddressOfLocalVariable(CodeGenFunction &CGF, return Address(Addr, Align); } +/// Checks current context and returns true if it matches the context selector. +template +static bool checkContext(const OMPDeclareVariantAttr *A) { + assert(CtxSet != OMPDeclareVariantAttr::CtxSetUnknown && + Ctx != OMPDeclareVariantAttr::CtxUnknown && + "Unknown context selector or context selector set."); + return false; +} + +/// Checks for implementation={vendor()} context selector. +/// \returns true iff ="llvm", false otherwise. +template <> +bool checkContext( + const OMPDeclareVariantAttr *A) { + return !A->getImplVendor().compare("llvm"); +} + +/// Finds the variant function that matches current context with its context +/// selector. +static const FunctionDecl *getDeclareVariantFunction(const FunctionDecl *FD) { + if (!FD->hasAttrs() || !FD->hasAttr()) + return FD; + // Iterate through all DeclareVariant attributes and check context selectors. + SmallVector MatchingAttributes; + for (const auto * A : FD->specific_attrs()) { + switch (A->getCtxSelectorSet()) { + case OMPDeclareVariantAttr::CtxSetImplementation: + switch (A->getCtxSelector()) { + case OMPDeclareVariantAttr::CtxVendor: + if (checkContext(A)) + MatchingAttributes.push_back(A); + break; + case OMPDeclareVariantAttr::CtxUnknown: + llvm_unreachable( + "Unknown context selector in implementation selctor set."); + } + break; + case OMPDeclareVariantAttr::CtxSetUnknown: + llvm_unreachable("Unknown context selector set."); + } + } + if (MatchingAttributes.empty()) + return FD; + // TODO: implement score analysis of multiple context selectors. + const OMPDeclareVariantAttr *MainAttr = MatchingAttributes.front(); + return cast( + cast(MainAttr->getVariantFuncRef()->IgnoreParenImpCasts()) + ->getDecl()); +} + +bool CGOpenMPRuntime::emitDeclareVariant(GlobalDecl GD, bool IsForDefinition) { + const auto *D = cast(GD.getDecl()); + // If the original function is defined already, use its definition. + StringRef MangledName = CGM.getMangledName(GD); + llvm::GlobalValue *Orig = CGM.GetGlobalValue(MangledName); + if (Orig && !Orig->isDeclaration()) + return false; + const FunctionDecl *NewFD = getDeclareVariantFunction(D); + // Emit original function if it does not have declare variant attribute or the + // context does not match. + if (NewFD == D) + return false; + GlobalDecl NewGD = GD.getWithDecl(NewFD); + if (tryEmitAlias(CGM, NewGD, GD, Orig, IsForDefinition)) { + DeferredVariantFunction.erase(D); + return true; + } + DeferredVariantFunction.insert(std::make_pair(D, std::make_pair(NewGD, GD))); + return true; +} + llvm::Function *CGOpenMPSIMDRuntime::emitParallelOutlinedFunction( const OMPExecutableDirective &D, const VarDecl *ThreadIDVar, OpenMPDirectiveKind InnermostKind, const RegionCodeGenTy &CodeGen) { @@ -11280,12 +11433,13 @@ void CGOpenMPSIMDRuntime::emitTargetOutlinedFunction( llvm_unreachable("Not supported in SIMD-only mode"); } -void CGOpenMPSIMDRuntime::emitTargetCall(CodeGenFunction &CGF, - const OMPExecutableDirective &D, - llvm::Function *OutlinedFn, - llvm::Value *OutlinedFnID, - const Expr *IfCond, - const Expr *Device) { +void CGOpenMPSIMDRuntime::emitTargetCall( + CodeGenFunction &CGF, const OMPExecutableDirective &D, + llvm::Function *OutlinedFn, llvm::Value *OutlinedFnID, const Expr *IfCond, + const Expr *Device, + llvm::function_ref + SizeEmitter) { llvm_unreachable("Not supported in SIMD-only mode"); } diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.h b/clang/lib/CodeGen/CGOpenMPRuntime.h index 92e946c670c01..b8137a20d0013 100644 --- a/clang/lib/CodeGen/CGOpenMPRuntime.h +++ b/clang/lib/CodeGen/CGOpenMPRuntime.h @@ -15,6 +15,7 @@ #include "CGValue.h" #include "clang/AST/DeclOpenMP.h" +#include "clang/AST/GlobalDecl.h" #include "clang/AST/Type.h" #include "clang/Basic/OpenMPKinds.h" #include "clang/Basic/SourceLocation.h" @@ -36,7 +37,6 @@ class Value; namespace clang { class Expr; -class GlobalDecl; class OMPDependClause; class OMPExecutableDirective; class OMPLoopDirective; @@ -644,6 +644,12 @@ class CGOpenMPRuntime { /// must be emitted. llvm::SmallDenseSet DeferredGlobalVariables; + /// Mapping of the original functions to their variants and original global + /// decl. + llvm::MapVector, + std::pair> + DeferredVariantFunction; + /// Flag for keeping track of weather a requires unified_shared_memory /// directive is present. bool HasRequiresUnifiedSharedMemory = false; @@ -793,6 +799,17 @@ class CGOpenMPRuntime { /// default. virtual unsigned getDefaultFirstprivateAddressSpace() const { return 0; } + /// Emit code that pushes the trip count of loops associated with constructs + /// 'target teams distribute' and 'teams distribute parallel for'. + /// \param SizeEmitter Emits the int64 value for the number of iterations of + /// the associated loop. + void emitTargetNumIterationsCall( + CodeGenFunction &CGF, const OMPExecutableDirective &D, + llvm::Value *DeviceID, + llvm::function_ref + SizeEmitter); + public: explicit CGOpenMPRuntime(CodeGenModule &CGM) : CGOpenMPRuntime(CGM, ".", ".") {} @@ -1414,15 +1431,6 @@ class CGOpenMPRuntime { bool IsOffloadEntry, const RegionCodeGenTy &CodeGen); - /// Emit code that pushes the trip count of loops associated with constructs - /// 'target teams distribute' and 'teams distribute parallel for'. - /// \param SizeEmitter Emits the int64 value for the number of iterations of - /// the associated loop. - virtual void emitTargetNumIterationsCall( - CodeGenFunction &CGF, const OMPExecutableDirective &D, const Expr *Device, - const llvm::function_ref &SizeEmitter); - /// Emit the target offloading code associated with \a D. The emitted /// code attempts offloading the execution to the device, an the event of /// a failure it executes the host version outlined in \a OutlinedFn. @@ -1433,11 +1441,15 @@ class CGOpenMPRuntime { /// directive, or null if no if clause is used. /// \param Device Expression evaluated in device clause associated with the /// target directive, or null if no device clause is used. - virtual void emitTargetCall(CodeGenFunction &CGF, - const OMPExecutableDirective &D, - llvm::Function *OutlinedFn, - llvm::Value *OutlinedFnID, const Expr *IfCond, - const Expr *Device); + /// \param SizeEmitter Callback to emit number of iterations for loop-based + /// directives. + virtual void + emitTargetCall(CodeGenFunction &CGF, const OMPExecutableDirective &D, + llvm::Function *OutlinedFn, llvm::Value *OutlinedFnID, + const Expr *IfCond, const Expr *Device, + llvm::function_ref + SizeEmitter); /// Emit the target regions enclosed in \a GD function definition or /// the function itself in case it is a valid device function. Returns true if @@ -1646,6 +1658,9 @@ class CGOpenMPRuntime { /// Return whether the unified_shared_memory has been specified. bool hasRequiresUnifiedSharedMemory() const; + + /// Emits the definition of the declare variant function. + virtual bool emitDeclareVariant(GlobalDecl GD, bool IsForDefinition); }; /// Class supports emissionof SIMD-only code. @@ -2117,9 +2132,13 @@ class CGOpenMPSIMDRuntime final : public CGOpenMPRuntime { /// directive, or null if no if clause is used. /// \param Device Expression evaluated in device clause associated with the /// target directive, or null if no device clause is used. - void emitTargetCall(CodeGenFunction &CGF, const OMPExecutableDirective &D, - llvm::Function *OutlinedFn, llvm::Value *OutlinedFnID, - const Expr *IfCond, const Expr *Device) override; + void + emitTargetCall(CodeGenFunction &CGF, const OMPExecutableDirective &D, + llvm::Function *OutlinedFn, llvm::Value *OutlinedFnID, + const Expr *IfCond, const Expr *Device, + llvm::function_ref + SizeEmitter) override; /// Emit the target regions enclosed in \a GD function definition or /// the function itself in case it is a valid device function. Returns true if diff --git a/clang/lib/CodeGen/CGOpenMPRuntimeNVPTX.cpp b/clang/lib/CodeGen/CGOpenMPRuntimeNVPTX.cpp index 09042b92d83b2..c6c595dcc2c6e 100644 --- a/clang/lib/CodeGen/CGOpenMPRuntimeNVPTX.cpp +++ b/clang/lib/CodeGen/CGOpenMPRuntimeNVPTX.cpp @@ -795,6 +795,7 @@ static bool hasNestedSPMDDirective(ASTContext &Ctx, case OMPD_teams_distribute_parallel_for_simd: case OMPD_target_update: case OMPD_declare_simd: + case OMPD_declare_variant: case OMPD_declare_target: case OMPD_end_declare_target: case OMPD_declare_reduction: @@ -865,6 +866,7 @@ static bool supportsSPMDExecutionMode(ASTContext &Ctx, case OMPD_teams_distribute_parallel_for_simd: case OMPD_target_update: case OMPD_declare_simd: + case OMPD_declare_variant: case OMPD_declare_target: case OMPD_end_declare_target: case OMPD_declare_reduction: @@ -1028,6 +1030,7 @@ static bool hasNestedLightweightDirective(ASTContext &Ctx, case OMPD_teams_distribute_parallel_for_simd: case OMPD_target_update: case OMPD_declare_simd: + case OMPD_declare_variant: case OMPD_declare_target: case OMPD_end_declare_target: case OMPD_declare_reduction: @@ -1104,6 +1107,7 @@ static bool supportsLightweightRuntime(ASTContext &Ctx, case OMPD_teams_distribute_parallel_for_simd: case OMPD_target_update: case OMPD_declare_simd: + case OMPD_declare_variant: case OMPD_declare_target: case OMPD_end_declare_target: case OMPD_declare_reduction: diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp b/clang/lib/CodeGen/CGStmtOpenMP.cpp index 473c6f7950ae7..bb6323e118cc2 100644 --- a/clang/lib/CodeGen/CGStmtOpenMP.cpp +++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp @@ -120,11 +120,27 @@ class OMPTeamsScope final : public OMPLexicalScope { class OMPLoopScope : public CodeGenFunction::RunCleanupsScope { void emitPreInitStmt(CodeGenFunction &CGF, const OMPLoopDirective &S) { CodeGenFunction::OMPMapVars PreCondVars; + llvm::DenseSet EmittedAsPrivate; for (const auto *E : S.counters()) { const auto *VD = cast(cast(E)->getDecl()); + EmittedAsPrivate.insert(VD->getCanonicalDecl()); (void)PreCondVars.setVarAddr( CGF, VD, CGF.CreateMemTemp(VD->getType().getNonReferenceType())); } + // Mark private vars as undefs. + for (const auto *C : S.getClausesOfKind()) { + for (const Expr *IRef : C->varlists()) { + const auto *OrigVD = cast(cast(IRef)->getDecl()); + if (EmittedAsPrivate.insert(OrigVD->getCanonicalDecl()).second) { + (void)PreCondVars.setVarAddr( + CGF, OrigVD, + Address(llvm::UndefValue::get( + CGF.ConvertTypeForMem(CGF.getContext().getPointerType( + OrigVD->getType().getNonReferenceType()))), + CGF.getContext().getDeclAlign(OrigVD))); + } + } + } (void)PreCondVars.apply(CGF); if (const auto *PreInits = cast_or_null(S.getPreInits())) { for (const auto *I : PreInits->decls()) @@ -4022,6 +4038,7 @@ static void emitOMPAtomicExpr(CodeGenFunction &CGF, OpenMPClauseKind Kind, case OMPC_dynamic_allocators: case OMPC_atomic_default_mem_order: case OMPC_device_type: + case OMPC_match: llvm_unreachable("Clause is not allowed in 'omp atomic'."); } } @@ -4121,18 +4138,21 @@ static void emitCommonOMPTargetDirective(CodeGenFunction &CGF, CGM.getOpenMPRuntime().emitTargetOutlinedFunction(S, ParentName, Fn, FnID, IsOffloadEntry, CodeGen); OMPLexicalScope Scope(CGF, S, OMPD_task); - auto &&SizeEmitter = [](CodeGenFunction &CGF, const OMPLoopDirective &D) { - OMPLoopScope(CGF, D); - // Emit calculation of the iterations count. - llvm::Value *NumIterations = CGF.EmitScalarExpr(D.getNumIterations()); - NumIterations = CGF.Builder.CreateIntCast(NumIterations, CGF.Int64Ty, - /*isSigned=*/false); - return NumIterations; + auto &&SizeEmitter = + [IsOffloadEntry](CodeGenFunction &CGF, + const OMPLoopDirective &D) -> llvm::Value * { + if (IsOffloadEntry) { + OMPLoopScope(CGF, D); + // Emit calculation of the iterations count. + llvm::Value *NumIterations = CGF.EmitScalarExpr(D.getNumIterations()); + NumIterations = CGF.Builder.CreateIntCast(NumIterations, CGF.Int64Ty, + /*isSigned=*/false); + return NumIterations; + } + return nullptr; }; - if (IsOffloadEntry) - CGM.getOpenMPRuntime().emitTargetNumIterationsCall(CGF, S, Device, - SizeEmitter); - CGM.getOpenMPRuntime().emitTargetCall(CGF, S, Fn, FnID, IfCond, Device); + CGM.getOpenMPRuntime().emitTargetCall(CGF, S, Fn, FnID, IfCond, Device, + SizeEmitter); } static void emitTargetRegion(CodeGenFunction &CGF, const OMPTargetDirective &S, diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp index 3055ea3bfd780..a74905fd70fd4 100644 --- a/clang/lib/CodeGen/CGVTables.cpp +++ b/clang/lib/CodeGen/CGVTables.cpp @@ -157,7 +157,7 @@ CodeGenFunction::GenerateVarArgsThunk(llvm::Function *Fn, const CGFunctionInfo &FnInfo, GlobalDecl GD, const ThunkInfo &Thunk) { const CXXMethodDecl *MD = cast(GD.getDecl()); - const FunctionProtoType *FPT = MD->getType()->getAs(); + const FunctionProtoType *FPT = MD->getType()->castAs(); QualType ResultType = FPT->getReturnType(); // Get the original function @@ -242,7 +242,6 @@ void CodeGenFunction::StartThunk(llvm::Function *Fn, GlobalDecl GD, // Build FunctionArgs. const CXXMethodDecl *MD = cast(GD.getDecl()); QualType ThisType = MD->getThisType(); - const FunctionProtoType *FPT = MD->getType()->getAs(); QualType ResultType; if (IsUnprototyped) ResultType = CGM.getContext().VoidTy; @@ -251,7 +250,7 @@ void CodeGenFunction::StartThunk(llvm::Function *Fn, GlobalDecl GD, else if (CGM.getCXXABI().hasMostDerivedReturn(GD)) ResultType = CGM.getContext().VoidPtrTy; else - ResultType = FPT->getReturnType(); + ResultType = MD->getType()->castAs()->getReturnType(); FunctionArgList FunctionArgs; // Create the implicit 'this' parameter declaration. diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 5ae088f6933d9..e2cd56a08d0d6 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -195,7 +195,7 @@ TypeEvaluationKind CodeGenFunction::getEvaluationKind(QualType type) { #define NON_CANONICAL_TYPE(name, parent) case Type::name: #define DEPENDENT_TYPE(name, parent) case Type::name: #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(name, parent) case Type::name: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm_unreachable("non-canonical or dependent type in IR-generation"); case Type::Auto: @@ -1881,7 +1881,7 @@ void CodeGenFunction::EmitVariablyModifiedType(QualType type) { #define NON_CANONICAL_TYPE(Class, Base) #define DEPENDENT_TYPE(Class, Base) case Type::Class: #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm_unreachable("unexpected dependent type!"); // These types are never variably-modified. diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 29369ae06ea66..8d024b5b836a8 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2572,6 +2572,11 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) { return; } + // Check if this must be emitted as declare variant. + if (LangOpts.OpenMP && isa(Global) && OpenMPRuntime && + OpenMPRuntime->emitDeclareVariant(GD, /*IsForDefinition=*/false)) + return; + // If we're deferring emission of a C++ variable with an // initializer, remember the order in which it appeared in the file. if (getLangOpts().CPlusPlus && isa(Global) && @@ -3091,6 +3096,10 @@ llvm::Constant *CodeGenModule::GetOrCreateLLVMFunction( EmitGlobal(GDDef); } } + // Check if this must be emitted as declare variant and emit reference to + // the the declare variant function. + if (LangOpts.OpenMP && OpenMPRuntime) + (void)OpenMPRuntime->emitDeclareVariant(GD, /*IsForDefinition=*/true); if (FD->isMultiVersion()) { const auto *TA = FD->getAttr(); @@ -3957,9 +3966,9 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D, return; llvm::Constant *Init = nullptr; - CXXRecordDecl *RD = ASTTy->getBaseElementTypeUnsafe()->getAsCXXRecordDecl(); bool NeedsGlobalCtor = false; - bool NeedsGlobalDtor = RD && !RD->hasTrivialDestructor(); + bool NeedsGlobalDtor = + D->needsDestruction(getContext()) == QualType::DK_cxx_destructor; const VarDecl *InitDecl; const Expr *InitExpr = D->getAnyInitializer(InitDecl); @@ -4490,6 +4499,11 @@ void CodeGenModule::HandleCXXStaticMemberVarInstantiation(VarDecl *VD) { void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD, llvm::GlobalValue *GV) { + // Check if this must be emitted as declare variant. + if (LangOpts.OpenMP && OpenMPRuntime && + OpenMPRuntime->emitDeclareVariant(GD, /*IsForDefinition=*/true)) + return; + const auto *D = cast(GD.getDecl()); // Compute the function info and LLVM type. @@ -5295,7 +5309,9 @@ void CodeGenModule::EmitObjCIvarInitializations(ObjCImplementationDecl *D) { // EmitLinkageSpec - Emit all declarations in a linkage spec. void CodeGenModule::EmitLinkageSpec(const LinkageSpecDecl *LSD) { if (LSD->getLanguage() != LinkageSpecDecl::lang_c && - LSD->getLanguage() != LinkageSpecDecl::lang_cxx) { + LSD->getLanguage() != LinkageSpecDecl::lang_cxx && + LSD->getLanguage() != LinkageSpecDecl::lang_cxx_11 && + LSD->getLanguage() != LinkageSpecDecl::lang_cxx_14) { ErrorUnsupported(LSD, "linkage spec"); return; } diff --git a/clang/lib/CodeGen/CodeGenPGO.h b/clang/lib/CodeGen/CodeGenPGO.h index 2e740f7892431..a3778b549910b 100644 --- a/clang/lib/CodeGen/CodeGenPGO.h +++ b/clang/lib/CodeGen/CodeGenPGO.h @@ -41,8 +41,8 @@ class CodeGenPGO { public: CodeGenPGO(CodeGenModule &CGM) - : CGM(CGM), NumValueSites({{0}}), NumRegionCounters(0), FunctionHash(0), - CurrentRegionCount(0) {} + : CGM(CGM), FuncNameVar(nullptr), NumValueSites({{0}}), + NumRegionCounters(0), FunctionHash(0), CurrentRegionCount(0) {} /// Whether or not we have PGO region data for the current function. This is /// false both when we have no data at all and when our data has been diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp index dd822d1f3ede3..c87d99e6bdcb0 100644 --- a/clang/lib/CodeGen/CodeGenTypes.cpp +++ b/clang/lib/CodeGen/CodeGenTypes.cpp @@ -416,7 +416,7 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) { #define NON_CANONICAL_TYPE(Class, Base) case Type::Class: #define DEPENDENT_TYPE(Class, Base) case Type::Class: #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) case Type::Class: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm_unreachable("Non-canonical or dependent types aren't possible."); case Type::Builtin: { diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 037185390c983..d6e51c60cc7f8 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -350,7 +350,7 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI { // If we have the only definition, we don't need a thread wrapper if we // will emit the value as a constant. if (isUniqueGVALinkage(getContext().GetGVALinkageForVariable(VD))) - return !VD->getType().isDestructedType() && InitDecl->evaluateValue(); + return !VD->needsDestruction(getContext()) && InitDecl->evaluateValue(); // Otherwise, we need a thread wrapper unless we know that every // translation unit will emit the value as a constant. We rely on @@ -360,7 +360,8 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI { } bool usesThreadWrapperFunction(const VarDecl *VD) const override { - return !isEmittedWithConstantInitializer(VD); + return !isEmittedWithConstantInitializer(VD) || + VD->needsDestruction(getContext()); } LValue EmitThreadLocalVarDeclLValue(CodeGenFunction &CGF, const VarDecl *VD, QualType LValType) override; @@ -579,8 +580,8 @@ CGCallee ItaniumCXXABI::EmitLoadOfMemberFunctionPointer( const FunctionProtoType *FPT = MPT->getPointeeType()->getAs(); - const CXXRecordDecl *RD = - cast(MPT->getClass()->getAs()->getDecl()); + auto *RD = + cast(MPT->getClass()->castAs()->getDecl()); llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType( CGM.getTypes().arrangeCXXMethodType(RD, FPT, /*FD=*/nullptr)); @@ -1142,7 +1143,7 @@ void ItaniumCXXABI::emitVirtualObjectDelete(CodeGenFunction &CGF, // Grab the vtable pointer as an intptr_t*. auto *ClassDecl = - cast(ElementType->getAs()->getDecl()); + cast(ElementType->castAs()->getDecl()); llvm::Value *VTable = CGF.GetVTablePtr(Ptr, CGF.IntPtrTy->getPointerTo(), ClassDecl); @@ -1345,7 +1346,7 @@ llvm::Value *ItaniumCXXABI::EmitTypeid(CodeGenFunction &CGF, Address ThisPtr, llvm::Type *StdTypeInfoPtrTy) { auto *ClassDecl = - cast(SrcRecordTy->getAs()->getDecl()); + cast(SrcRecordTy->castAs()->getDecl()); llvm::Value *Value = CGF.GetVTablePtr(ThisPtr, StdTypeInfoPtrTy->getPointerTo(), ClassDecl); @@ -1411,7 +1412,7 @@ llvm::Value *ItaniumCXXABI::EmitDynamicCastToVoid(CodeGenFunction &CGF, llvm::Type *DestLTy = CGF.ConvertType(DestTy); auto *ClassDecl = - cast(SrcRecordTy->getAs()->getDecl()); + cast(SrcRecordTy->castAs()->getDecl()); // Get the vtable pointer. llvm::Value *VTable = CGF.GetVTablePtr(ThisAddr, PtrDiffLTy->getPointerTo(), ClassDecl); @@ -2606,7 +2607,7 @@ void ItaniumCXXABI::EmitThreadLocalInitFuncs( llvm::GlobalValue *Init = nullptr; bool InitIsInitFunc = false; bool HasConstantInitialization = false; - if (isEmittedWithConstantInitializer(VD)) { + if (!usesThreadWrapperFunction(VD)) { HasConstantInitialization = true; } else if (VD->hasDefinition()) { InitIsInitFunc = true; @@ -3099,8 +3100,8 @@ static bool CanUseSingleInheritance(const CXXRecordDecl *RD) { return false; // Check that the class is dynamic iff the base is. - const CXXRecordDecl *BaseDecl = - cast(Base->getType()->getAs()->getDecl()); + auto *BaseDecl = + cast(Base->getType()->castAs()->getDecl()); if (!BaseDecl->isEmpty() && BaseDecl->isDynamicClass() != RD->isDynamicClass()) return false; @@ -3127,7 +3128,7 @@ void ItaniumRTTIBuilder::BuildVTablePointer(const Type *Ty) { #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) case Type::Class: #define NON_CANONICAL_TYPE(Class, Base) case Type::Class: #define DEPENDENT_TYPE(Class, Base) case Type::Class: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm_unreachable("Non-canonical and dependent types shouldn't get here"); case Type::LValueReference: @@ -3373,7 +3374,7 @@ llvm::Constant *ItaniumRTTIBuilder::BuildTypeInfo( #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) case Type::Class: #define NON_CANONICAL_TYPE(Class, Base) case Type::Class: #define DEPENDENT_TYPE(Class, Base) case Type::Class: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm_unreachable("Non-canonical and dependent types shouldn't get here"); // GCC treats vector types as fundamental types. @@ -3563,8 +3564,8 @@ static unsigned ComputeVMIClassTypeInfoFlags(const CXXBaseSpecifier *Base, unsigned Flags = 0; - const CXXRecordDecl *BaseDecl = - cast(Base->getType()->getAs()->getDecl()); + auto *BaseDecl = + cast(Base->getType()->castAs()->getDecl()); if (Base->isVirtual()) { // Mark the virtual base as seen. @@ -3662,8 +3663,8 @@ void ItaniumRTTIBuilder::BuildVMIClassTypeInfo(const CXXRecordDecl *RD) { // The __base_type member points to the RTTI for the base type. Fields.push_back(ItaniumRTTIBuilder(CXXABI).BuildTypeInfo(Base.getType())); - const CXXRecordDecl *BaseDecl = - cast(Base.getType()->getAs()->getDecl()); + auto *BaseDecl = + cast(Base.getType()->castAs()->getDecl()); int64_t OffsetFlags = 0; diff --git a/clang/lib/CodeGen/ModuleBuilder.cpp b/clang/lib/CodeGen/ModuleBuilder.cpp index 40662b5317632..26a1f41ea7515 100644 --- a/clang/lib/CodeGen/ModuleBuilder.cpp +++ b/clang/lib/CodeGen/ModuleBuilder.cpp @@ -65,6 +65,13 @@ namespace { private: SmallVector DeferredInlineMemberFuncDefs; + static llvm::StringRef ExpandModuleName(llvm::StringRef ModuleName, + const CodeGenOptions &CGO) { + if (ModuleName == "-" && !CGO.MainFileName.empty()) + return CGO.MainFileName; + return ModuleName; + } + public: CodeGeneratorImpl(DiagnosticsEngine &diags, llvm::StringRef ModuleName, const HeaderSearchOptions &HSO, @@ -73,7 +80,8 @@ namespace { CoverageSourceInfo *CoverageInfo = nullptr) : Diags(diags), Ctx(nullptr), HeaderSearchOpts(HSO), PreprocessorOpts(PPO), CodeGenOpts(CGO), HandlingTopLevelDecls(0), - CoverageInfo(CoverageInfo), M(new llvm::Module(ModuleName, C)) { + CoverageInfo(CoverageInfo), + M(new llvm::Module(ExpandModuleName(ModuleName, CGO), C)) { C.setDiscardValueNames(CGO.DiscardValueNames); } @@ -121,7 +129,7 @@ namespace { llvm::Module *StartModule(llvm::StringRef ModuleName, llvm::LLVMContext &C) { assert(!M && "Replacing existing Module?"); - M.reset(new llvm::Module(ModuleName, C)); + M.reset(new llvm::Module(ExpandModuleName(ModuleName, CodeGenOpts), C)); Initialize(*Ctx); return M.get(); } diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp index f2696a33cfbf4..c4d0004f0e288 100644 --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -9954,7 +9954,7 @@ llvm::Function *AMDGPUTargetCodeGenInfo::createEnqueuedBlockKernel( Builder.SetInsertPoint(BB); unsigned BlockAlign = CGF.CGM.getDataLayout().getPrefTypeAlignment(BlockTy); auto *BlockPtr = Builder.CreateAlloca(BlockTy, nullptr); - BlockPtr->setAlignment(BlockAlign); + BlockPtr->setAlignment(llvm::MaybeAlign(BlockAlign)); Builder.CreateAlignedStore(F->arg_begin(), BlockPtr, BlockAlign); auto *Cast = Builder.CreatePointerCast(BlockPtr, InvokeFT->getParamType(0)); llvm::SmallVector Args; diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index a889b7e018d59..d0f12f97353e5 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -288,8 +288,7 @@ phases::ID Driver::getFinalPhase(const DerivedArgList &DAL, (PhaseArg = DAL.getLastArg(options::OPT_rewrite_legacy_objc)) || (PhaseArg = DAL.getLastArg(options::OPT__migrate)) || (PhaseArg = DAL.getLastArg(options::OPT_emit_iterface_stubs)) || - (PhaseArg = DAL.getLastArg(options::OPT__analyze, - options::OPT__analyze_auto)) || + (PhaseArg = DAL.getLastArg(options::OPT__analyze)) || (PhaseArg = DAL.getLastArg(options::OPT_emit_ast))) { FinalPhase = phases::Compile; @@ -2489,15 +2488,13 @@ class OffloadingActionBuilder final { return ABRT_Inactive; } - /// Append top level actions generated by the builder. Return true if errors - /// were found. + /// Append top level actions generated by the builder. virtual void appendTopLevelActions(ActionList &AL) {} /// Append top level actions specific for certain link situations. virtual void appendTopLevelLinkAction(ActionList &AL) {} - /// Append linker actions generated by the builder. Return true if errors - /// were found. + /// Append linker actions generated by the builder. virtual void appendLinkDependences(OffloadAction::DeviceDependences &DA) {} /// Initialize the builder. Return true if any initialization errors are @@ -4301,8 +4298,10 @@ Action *Driver::ConstructPhaseAction( llvm_unreachable("link action invalid here."); case phases::Preprocess: { types::ID OutputTy; - // -{M, MM} alter the output type. - if (Args.hasArg(options::OPT_M, options::OPT_MM)) { + // -M and -MM specify the dependency file name by altering the output type, + // -if -MD and -MMD are not specified. + if (Args.hasArg(options::OPT_M, options::OPT_MM) && + !Args.hasArg(options::OPT_MD, options::OPT_MMD)) { OutputTy = types::TY_Dependencies; } else { OutputTy = Input->getType(); @@ -4350,7 +4349,7 @@ Action *Driver::ConstructPhaseAction( if (Args.hasArg(options::OPT_rewrite_legacy_objc)) return C.MakeAction(Input, types::TY_RewrittenLegacyObjC); - if (Args.hasArg(options::OPT__analyze, options::OPT__analyze_auto)) + if (Args.hasArg(options::OPT__analyze)) return C.MakeAction(Input, types::TY_Plist); if (Args.hasArg(options::OPT__migrate)) return C.MakeAction(Input, types::TY_Remap); diff --git a/clang/lib/Driver/ToolChains/Arch/ARM.cpp b/clang/lib/Driver/ToolChains/Arch/ARM.cpp index 65e1100ee9cd9..947a4c6814eaa 100644 --- a/clang/lib/Driver/ToolChains/Arch/ARM.cpp +++ b/clang/lib/Driver/ToolChains/Arch/ARM.cpp @@ -460,7 +460,7 @@ void arm::getARMTargetFeatures(const ToolChain &TC, // now just be explicit and disable all known dependent features // as well. for (std::string Feature : { - "vfp2", "vfp2sp", "vfp2d16", "vfp2d16sp", + "vfp2", "vfp2sp", "vfp3", "vfp3sp", "vfp3d16", "vfp3d16sp", "vfp4", "vfp4sp", "vfp4d16", "vfp4d16sp", "fp-armv8", "fp-armv8sp", "fp-armv8d16", "fp-armv8d16sp", diff --git a/clang/lib/Driver/ToolChains/Arch/Mips.cpp b/clang/lib/Driver/ToolChains/Arch/Mips.cpp index b512ff64b0c62..384d3acedc2d1 100644 --- a/clang/lib/Driver/ToolChains/Arch/Mips.cpp +++ b/clang/lib/Driver/ToolChains/Arch/Mips.cpp @@ -267,6 +267,13 @@ void mips::getMIPSTargetFeatures(const Driver &D, const llvm::Triple &Triple, D.Diag(diag::warn_drv_unsupported_longcalls) << (ABICallsArg ? 0 : 1); } + if (Arg *A = Args.getLastArg(options::OPT_mxgot, options::OPT_mno_xgot)) { + if (A->getOption().matches(options::OPT_mxgot)) + Features.push_back("+xgot"); + else + Features.push_back("-xgot"); + } + mips::FloatABI FloatABI = mips::getMipsFloatABI(D, Args); if (FloatABI == mips::FloatABI::Soft) { // FIXME: Note, this is a hack. We need to pass the selected float diff --git a/clang/lib/Driver/ToolChains/Arch/PPC.cpp b/clang/lib/Driver/ToolChains/Arch/PPC.cpp index 30f1a0d9022c2..59ff7cbc787c7 100644 --- a/clang/lib/Driver/ToolChains/Arch/PPC.cpp +++ b/clang/lib/Driver/ToolChains/Arch/PPC.cpp @@ -115,7 +115,8 @@ ppc::ReadGOTPtrMode ppc::getPPCReadGOTPtrMode(const Driver &D, const llvm::Tripl const ArgList &Args) { if (Args.getLastArg(options::OPT_msecure_plt)) return ppc::ReadGOTPtrMode::SecurePlt; - if (Triple.isOSNetBSD() || Triple.isOSOpenBSD() || Triple.isMusl()) + if ((Triple.isOSFreeBSD() && Triple.getOSMajorVersion() >= 13) || + Triple.isOSNetBSD() || Triple.isOSOpenBSD() || Triple.isMusl()) return ppc::ReadGOTPtrMode::SecurePlt; else return ppc::ReadGOTPtrMode::Bss; diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 1d40f4e01ba4a..593c32a25f8fa 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -839,12 +839,14 @@ static void addPGOAndCoverageFlags(const ToolChain &TC, Compilation &C, } } - if (Args.hasArg(options::OPT_ftest_coverage) || - Args.hasArg(options::OPT_coverage)) + bool EmitCovNotes = Args.hasArg(options::OPT_ftest_coverage) || + Args.hasArg(options::OPT_coverage); + bool EmitCovData = Args.hasFlag(options::OPT_fprofile_arcs, + options::OPT_fno_profile_arcs, false) || + Args.hasArg(options::OPT_coverage); + if (EmitCovNotes) CmdArgs.push_back("-femit-coverage-notes"); - if (Args.hasFlag(options::OPT_fprofile_arcs, options::OPT_fno_profile_arcs, - false) || - Args.hasArg(options::OPT_coverage)) + if (EmitCovData) CmdArgs.push_back("-femit-coverage-data"); if (Args.hasFlag(options::OPT_fcoverage_mapping, @@ -880,40 +882,48 @@ static void addPGOAndCoverageFlags(const ToolChain &TC, Compilation &C, CmdArgs.push_back(Args.MakeArgString(Twine("-fprofile-filter-files=" + v))); } - if (C.getArgs().hasArg(options::OPT_c) || - C.getArgs().hasArg(options::OPT_S)) { - if (Output.isFilename()) { - CmdArgs.push_back("-coverage-notes-file"); - SmallString<128> OutputFilename; - if (Arg *FinalOutput = C.getArgs().getLastArg(options::OPT_o)) - OutputFilename = FinalOutput->getValue(); - else - OutputFilename = llvm::sys::path::filename(Output.getBaseInput()); - SmallString<128> CoverageFilename = OutputFilename; - if (llvm::sys::path::is_relative(CoverageFilename)) { - SmallString<128> Pwd; - if (!llvm::sys::fs::current_path(Pwd)) { - llvm::sys::path::append(Pwd, CoverageFilename); - CoverageFilename.swap(Pwd); - } + // Leave -fprofile-dir= an unused argument unless .gcda emission is + // enabled. To be polite, with '-fprofile-arcs -fno-profile-arcs' consider + // the flag used. There is no -fno-profile-dir, so the user has no + // targeted way to suppress the warning. + Arg *FProfileDir = nullptr; + if (Args.hasArg(options::OPT_fprofile_arcs) || + Args.hasArg(options::OPT_coverage)) + FProfileDir = Args.getLastArg(options::OPT_fprofile_dir); + + // Put the .gcno and .gcda files (if needed) next to the object file or + // bitcode file in the case of LTO. + // FIXME: There should be a simpler way to find the object file for this + // input, and this code probably does the wrong thing for commands that + // compile and link all at once. + if ((Args.hasArg(options::OPT_c) || Args.hasArg(options::OPT_S)) && + (EmitCovNotes || EmitCovData) && Output.isFilename()) { + SmallString<128> OutputFilename; + if (Arg *FinalOutput = C.getArgs().getLastArg(options::OPT_o)) + OutputFilename = FinalOutput->getValue(); + else + OutputFilename = llvm::sys::path::filename(Output.getBaseInput()); + SmallString<128> CoverageFilename = OutputFilename; + if (llvm::sys::path::is_relative(CoverageFilename)) { + SmallString<128> Pwd; + if (!llvm::sys::fs::current_path(Pwd)) { + llvm::sys::path::append(Pwd, CoverageFilename); + CoverageFilename.swap(Pwd); } - llvm::sys::path::replace_extension(CoverageFilename, "gcno"); - CmdArgs.push_back(Args.MakeArgString(CoverageFilename)); + } + llvm::sys::path::replace_extension(CoverageFilename, "gcno"); - // Leave -fprofile-dir= an unused argument unless .gcda emission is - // enabled. To be polite, with '-fprofile-arcs -fno-profile-arcs' consider - // the flag used. There is no -fno-profile-dir, so the user has no - // targeted way to suppress the warning. - if (Args.hasArg(options::OPT_fprofile_arcs) || - Args.hasArg(options::OPT_coverage)) { - CmdArgs.push_back("-coverage-data-file"); - if (Arg *FProfileDir = Args.getLastArg(options::OPT_fprofile_dir)) { - CoverageFilename = FProfileDir->getValue(); - llvm::sys::path::append(CoverageFilename, OutputFilename); - } - llvm::sys::path::replace_extension(CoverageFilename, "gcda"); - CmdArgs.push_back(Args.MakeArgString(CoverageFilename)); + CmdArgs.push_back("-coverage-notes-file"); + CmdArgs.push_back(Args.MakeArgString(CoverageFilename)); + + if (EmitCovData) { + if (FProfileDir) { + CoverageFilename = FProfileDir->getValue(); + llvm::sys::path::append(CoverageFilename, OutputFilename); } + llvm::sys::path::replace_extension(CoverageFilename, "gcda"); + CmdArgs.push_back("-coverage-data-file"); + CmdArgs.push_back(Args.MakeArgString(CoverageFilename)); } } } @@ -1068,7 +1078,6 @@ void Clang::AddPreprocessingOptions(Compilation &C, const JobAction &JA, ArgStringList &CmdArgs, const InputInfo &Output, const InputInfoList &Inputs) const { - Arg *A; const bool IsIAMCU = getToolChain().getTriple().isOSIAMCU(); CheckPreprocessingOptions(D, Args); @@ -1077,9 +1086,20 @@ void Clang::AddPreprocessingOptions(Compilation &C, const JobAction &JA, Args.AddLastArg(CmdArgs, options::OPT_CC); // Handle dependency file generation. - if ((A = Args.getLastArg(options::OPT_M, options::OPT_MM)) || - (A = Args.getLastArg(options::OPT_MD)) || - (A = Args.getLastArg(options::OPT_MMD))) { + Arg *ArgM = Args.getLastArg(options::OPT_MM); + if (!ArgM) + ArgM = Args.getLastArg(options::OPT_M); + Arg *ArgMD = Args.getLastArg(options::OPT_MMD); + if (!ArgMD) + ArgMD = Args.getLastArg(options::OPT_MD); + + // -M and -MM imply -w. + if (ArgM) + CmdArgs.push_back("-w"); + else + ArgM = ArgMD; + + if (ArgM) { // Determine the output location. const char *DepFile; if (Arg *MF = Args.getLastArg(options::OPT_MF)) { @@ -1087,11 +1107,10 @@ void Clang::AddPreprocessingOptions(Compilation &C, const JobAction &JA, C.addFailureResultFile(DepFile, &JA); } else if (Output.getType() == types::TY_Dependencies) { DepFile = Output.getFilename(); - } else if (A->getOption().matches(options::OPT_M) || - A->getOption().matches(options::OPT_MM)) { + } else if (!ArgMD) { DepFile = "-"; - } else if (A->getOption().matches(options::OPT_MMD) && - Args.hasArg(options::OPT_fintelfpga)) { + } else if (ArgMD->getOption().matches(options::OPT_MMD) && + Args.hasArg(options::OPT_fintelfpga)) { // When generating dependency files for FPGA AOT, the output files will // always be named after the source file. DepFile = Args.MakeArgString(Twine(getBaseInputStem(Args, Inputs)) + ".d"); @@ -1102,8 +1121,22 @@ void Clang::AddPreprocessingOptions(Compilation &C, const JobAction &JA, CmdArgs.push_back("-dependency-file"); CmdArgs.push_back(DepFile); + bool HasTarget = false; + for (const Arg *A : Args.filtered(options::OPT_MT, options::OPT_MQ)) { + HasTarget = true; + A->claim(); + if (A->getOption().matches(options::OPT_MT)) { + A->render(Args, CmdArgs); + } else { + CmdArgs.push_back("-MT"); + SmallString<128> Quoted; + QuoteTarget(A->getValue(), Quoted); + CmdArgs.push_back(Args.MakeArgString(Quoted)); + } + } + // Add a default target if one wasn't specified. - if (!Args.hasArg(options::OPT_MT) && !Args.hasArg(options::OPT_MQ)) { + if (!HasTarget) { const char *DepTarget; // If user provided -o, that is the dependency target, except @@ -1120,17 +1153,14 @@ void Clang::AddPreprocessingOptions(Compilation &C, const JobAction &JA, DepTarget = Args.MakeArgString(llvm::sys::path::filename(P)); } - if (!A->getOption().matches(options::OPT_MD) && !A->getOption().matches(options::OPT_MMD)) { - CmdArgs.push_back("-w"); - } CmdArgs.push_back("-MT"); SmallString<128> Quoted; QuoteTarget(DepTarget, Quoted); CmdArgs.push_back(Args.MakeArgString(Quoted)); } - if (A->getOption().matches(options::OPT_M) || - A->getOption().matches(options::OPT_MD)) + if (ArgM->getOption().matches(options::OPT_M) || + ArgM->getOption().matches(options::OPT_MD)) CmdArgs.push_back("-sys-header-deps"); if ((isa(JA) && !Args.hasArg(options::OPT_fno_module_file_deps)) || @@ -1139,8 +1169,8 @@ void Clang::AddPreprocessingOptions(Compilation &C, const JobAction &JA, } if (Args.hasArg(options::OPT_MG)) { - if (!A || A->getOption().matches(options::OPT_MD) || - A->getOption().matches(options::OPT_MMD)) + if (!ArgM || ArgM->getOption().matches(options::OPT_MD) || + ArgM->getOption().matches(options::OPT_MMD)) D.Diag(diag::err_drv_mg_requires_m_or_mm); CmdArgs.push_back("-MG"); } @@ -1148,22 +1178,6 @@ void Clang::AddPreprocessingOptions(Compilation &C, const JobAction &JA, Args.AddLastArg(CmdArgs, options::OPT_MP); Args.AddLastArg(CmdArgs, options::OPT_MV); - // Convert all -MQ args to -MT - for (const Arg *A : Args.filtered(options::OPT_MT, options::OPT_MQ)) { - A->claim(); - - if (A->getOption().matches(options::OPT_MQ)) { - CmdArgs.push_back("-MT"); - SmallString<128> Quoted; - QuoteTarget(A->getValue(), Quoted); - CmdArgs.push_back(Args.MakeArgString(Quoted)); - - // -MT flag - no change - } else { - A->render(Args, CmdArgs); - } - } - // Add offload include arguments specific for CUDA. This must happen before // we -I or -include anything else, because we must pick up the CUDA headers // from the particular CUDA installation, rather than from e.g. @@ -1683,13 +1697,6 @@ void Clang::AddMIPSTargetArgs(const ArgList &Args, CmdArgs.push_back("hard"); } - if (Arg *A = Args.getLastArg(options::OPT_mxgot, options::OPT_mno_xgot)) { - if (A->getOption().matches(options::OPT_mxgot)) { - CmdArgs.push_back("-mllvm"); - CmdArgs.push_back("-mxgot"); - } - } - if (Arg *A = Args.getLastArg(options::OPT_mldc1_sdc1, options::OPT_mno_ldc1_sdc1)) { if (A->getOption().matches(options::OPT_mno_ldc1_sdc1)) { @@ -2032,7 +2039,7 @@ void Clang::DumpCompilationDatabase(Compilation &C, StringRef Filename, } auto &CDB = *CompilationDatabase; SmallString<128> Buf; - if (!llvm::sys::fs::current_path(Buf)) + if (llvm::sys::fs::current_path(Buf)) Buf = "."; CDB << "{ \"directory\": \"" << escape(Buf) << "\""; CDB << ", \"file\": \"" << escape(Input.getFilename()) << "\""; @@ -4863,6 +4870,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // Forward -sycl-std option to -cc1 Args.AddLastArg(CmdArgs, options::OPT_sycl_std_EQ); + if (Args.hasFlag(options::OPT_fhip_new_launch_api, + options::OPT_fno_hip_new_launch_api, false)) + CmdArgs.push_back("-fhip-new-launch-api"); + if (Arg *A = Args.getLastArg(options::OPT_fcf_protection_EQ)) { CmdArgs.push_back( Args.MakeArgString(Twine("-fcf-protection=") + A->getValue())); @@ -5581,14 +5592,13 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-fwhole-program-vtables"); } - bool RequiresSplitLTOUnit = WholeProgramVTables || Sanitize.needsLTO(); + bool DefaultsSplitLTOUnit = WholeProgramVTables || Sanitize.needsLTO(); bool SplitLTOUnit = Args.hasFlag(options::OPT_fsplit_lto_unit, - options::OPT_fno_split_lto_unit, RequiresSplitLTOUnit); - if (RequiresSplitLTOUnit && !SplitLTOUnit) - D.Diag(diag::err_drv_argument_not_allowed_with) - << "-fno-split-lto-unit" - << (WholeProgramVTables ? "-fwhole-program-vtables" : "-fsanitize=cfi"); + options::OPT_fno_split_lto_unit, DefaultsSplitLTOUnit); + if (Sanitize.needsLTO() && !SplitLTOUnit) + D.Diag(diag::err_drv_argument_not_allowed_with) << "-fno-split-lto-unit" + << "-fsanitize=cfi"; if (SplitLTOUnit) CmdArgs.push_back("-fsplit-lto-unit"); @@ -6209,7 +6219,7 @@ const char *Clang::getDependencyFileName(const ArgList &Args, return Args.MakeArgString(OutputFilename); } - return Args.MakeArgString(std::string(getBaseInputStem(Args, Inputs)) + ".d"); + return Args.MakeArgString(Twine(getBaseInputStem(Args, Inputs)) + ".d"); } // Begin ClangAs diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp index 30b72391f313e..5dae8effa243d 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -1342,17 +1342,6 @@ void tools::AddOpenMPLinkerScript(const ToolChain &TC, Compilation &C, LksStream << " }\n"; } - // Add commands to define host entries begin and end. We use 1-byte subalign - // so that the linker does not add any padding and the elements in this - // section form an array. - LksStream << " .omp_offloading.entries :\n"; - LksStream << " ALIGN(0x10)\n"; - LksStream << " SUBALIGN(0x01)\n"; - LksStream << " {\n"; - LksStream << " PROVIDE_HIDDEN(.omp_offloading.entries_begin = .);\n"; - LksStream << " *(.omp_offloading.entries)\n"; - LksStream << " PROVIDE_HIDDEN(.omp_offloading.entries_end = .);\n"; - LksStream << " }\n"; LksStream << "}\n"; LksStream << "INSERT BEFORE .data\n"; LksStream.flush(); diff --git a/clang/lib/Driver/ToolChains/Fuchsia.cpp b/clang/lib/Driver/ToolChains/Fuchsia.cpp index cb84499320f36..e7d38ff9f227b 100644 --- a/clang/lib/Driver/ToolChains/Fuchsia.cpp +++ b/clang/lib/Driver/ToolChains/Fuchsia.cpp @@ -52,7 +52,7 @@ void fuchsia::Linker::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-z"); CmdArgs.push_back("rodynamic"); CmdArgs.push_back("-z"); - CmdArgs.push_back("separate-code"); + CmdArgs.push_back("separate-loadable-segments"); } if (!D.SysRoot.empty()) diff --git a/clang/lib/Driver/Types.cpp b/clang/lib/Driver/Types.cpp index e304fa1846300..7c6f40aef932b 100644 --- a/clang/lib/Driver/Types.cpp +++ b/clang/lib/Driver/Types.cpp @@ -329,7 +329,7 @@ void types::getCompilationPhases(const clang::driver::Driver &Driver, DAL.getLastArg(options::OPT_rewrite_legacy_objc) || DAL.getLastArg(options::OPT__migrate) || DAL.getLastArg(options::OPT_emit_iterface_stubs) || - DAL.getLastArg(options::OPT__analyze, options::OPT__analyze_auto) || + DAL.getLastArg(options::OPT__analyze) || DAL.getLastArg(options::OPT_emit_ast)) llvm::copy_if(PhaseList, std::back_inserter(P), [](phases::ID Phase) { return Phase <= phases::Compile; }); diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp index ede6a797122b6..52afb6744ad3b 100644 --- a/clang/lib/Format/ContinuationIndenter.cpp +++ b/clang/lib/Format/ContinuationIndenter.cpp @@ -931,6 +931,11 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) { return std::max(State.Stack.back().LastSpace, State.Stack.back().Indent + Style.ContinuationIndentWidth); + if (Style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths && + State.Line->First->is(tok::kw_enum)) + return (Style.IndentWidth * State.Line->First->IndentLevel) + + Style.IndentWidth; + if (NextNonComment->is(tok::l_brace) && NextNonComment->BlockKind == BK_Block) return Current.NestingLevel == 0 ? State.FirstIndent : State.Stack.back().Indent; diff --git a/clang/lib/Format/Encoding.h b/clang/lib/Format/Encoding.h index fe3d5f019858b..a0d664121b2bb 100644 --- a/clang/lib/Format/Encoding.h +++ b/clang/lib/Format/Encoding.h @@ -67,7 +67,8 @@ inline unsigned columnWidthWithTabs(StringRef Text, unsigned StartColumn, if (TabPos == StringRef::npos) return TotalWidth + columnWidth(Tail, Encoding); TotalWidth += columnWidth(Tail.substr(0, TabPos), Encoding); - TotalWidth += TabWidth - (TotalWidth + StartColumn) % TabWidth; + if (TabWidth) + TotalWidth += TabWidth - (TotalWidth + StartColumn) % TabWidth; Tail = Tail.substr(TabPos + 1); } } diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 81fdab4ed70aa..dd561cc0d8925 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -67,10 +67,19 @@ template <> struct ScalarEnumerationTraits { template <> struct ScalarEnumerationTraits { static void enumeration(IO &IO, FormatStyle::LanguageStandard &Value) { - IO.enumCase(Value, "Cpp03", FormatStyle::LS_Cpp03); - IO.enumCase(Value, "C++03", FormatStyle::LS_Cpp03); - IO.enumCase(Value, "Cpp11", FormatStyle::LS_Cpp11); - IO.enumCase(Value, "C++11", FormatStyle::LS_Cpp11); + IO.enumCase(Value, "c++03", FormatStyle::LS_Cpp03); + IO.enumCase(Value, "C++03", FormatStyle::LS_Cpp03); // Legacy alias + IO.enumCase(Value, "Cpp03", FormatStyle::LS_Cpp03); // Legacy alias + + IO.enumCase(Value, "c++11", FormatStyle::LS_Cpp11); + IO.enumCase(Value, "C++11", FormatStyle::LS_Cpp11); // Legacy alias + + IO.enumCase(Value, "c++14", FormatStyle::LS_Cpp14); + IO.enumCase(Value, "c++17", FormatStyle::LS_Cpp17); + IO.enumCase(Value, "c++20", FormatStyle::LS_Cpp20); + + IO.enumCase(Value, "Latest", FormatStyle::LS_Latest); + IO.enumCase(Value, "Cpp11", FormatStyle::LS_Latest); // Legacy alias IO.enumCase(Value, "Auto", FormatStyle::LS_Auto); } }; @@ -165,6 +174,7 @@ template <> struct ScalarEnumerationTraits { IO.enumCase(Value, "Mozilla", FormatStyle::BS_Mozilla); IO.enumCase(Value, "Stroustrup", FormatStyle::BS_Stroustrup); IO.enumCase(Value, "Allman", FormatStyle::BS_Allman); + IO.enumCase(Value, "Whitesmiths", FormatStyle::BS_Whitesmiths); IO.enumCase(Value, "GNU", FormatStyle::BS_GNU); IO.enumCase(Value, "WebKit", FormatStyle::BS_WebKit); IO.enumCase(Value, "Custom", FormatStyle::BS_Custom); @@ -657,6 +667,19 @@ static FormatStyle expandPresets(const FormatStyle &Style) { Expanded.BraceWrapping.BeforeCatch = true; Expanded.BraceWrapping.BeforeElse = true; break; + case FormatStyle::BS_Whitesmiths: + Expanded.BraceWrapping.AfterCaseLabel = true; + Expanded.BraceWrapping.AfterClass = true; + Expanded.BraceWrapping.AfterControlStatement = true; + Expanded.BraceWrapping.AfterEnum = true; + Expanded.BraceWrapping.AfterFunction = true; + Expanded.BraceWrapping.AfterNamespace = true; + Expanded.BraceWrapping.AfterObjCDeclaration = true; + Expanded.BraceWrapping.AfterStruct = true; + Expanded.BraceWrapping.AfterExternBlock = true; + Expanded.BraceWrapping.BeforeCatch = true; + Expanded.BraceWrapping.BeforeElse = true; + break; case FormatStyle::BS_GNU: Expanded.BraceWrapping = {true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true}; @@ -720,9 +743,9 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.ForEachMacros.push_back("Q_FOREACH"); LLVMStyle.ForEachMacros.push_back("BOOST_FOREACH"); LLVMStyle.IncludeStyle.IncludeCategories = { - {"^\"(llvm|llvm-c|clang|clang-c)/", 2}, - {"^(<|\"(gtest|gmock|isl|json)/)", 3}, - {".*", 1}}; + {"^\"(llvm|llvm-c|clang|clang-c)/", 2, 0}, + {"^(<|\"(gtest|gmock|isl|json)/)", 3, 0}, + {".*", 1, 0}}; LLVMStyle.IncludeStyle.IncludeIsMainRegex = "(Test)?$"; LLVMStyle.IncludeStyle.IncludeBlocks = tooling::IncludeStyle::IBS_Preserve; LLVMStyle.IndentCaseLabels = false; @@ -742,7 +765,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.ObjCSpaceBeforeProtocolList = true; LLVMStyle.PointerAlignment = FormatStyle::PAS_Right; LLVMStyle.SpacesBeforeTrailingComments = 1; - LLVMStyle.Standard = FormatStyle::LS_Cpp11; + LLVMStyle.Standard = FormatStyle::LS_Latest; LLVMStyle.UseTab = FormatStyle::UT_Never; LLVMStyle.ReflowComments = true; LLVMStyle.SpacesInParentheses = false; @@ -804,8 +827,10 @@ FormatStyle getGoogleStyle(FormatStyle::LanguageKind Language) { GoogleStyle.AlwaysBreakTemplateDeclarations = FormatStyle::BTDS_Yes; GoogleStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = true; GoogleStyle.DerivePointerAlignment = true; - GoogleStyle.IncludeStyle.IncludeCategories = { - {"^", 2}, {"^<.*\\.h>", 1}, {"^<.*", 2}, {".*", 3}}; + GoogleStyle.IncludeStyle.IncludeCategories = {{"^", 2, 0}, + {"^<.*\\.h>", 1, 0}, + {"^<.*", 2, 0}, + {".*", 3, 0}}; GoogleStyle.IncludeStyle.IncludeIsMainRegex = "([-_](test|unittest))?$"; GoogleStyle.IncludeStyle.IncludeBlocks = tooling::IncludeStyle::IBS_Regroup; GoogleStyle.IndentCaseLabels = true; @@ -1383,7 +1408,7 @@ class Formatter : public TokenAnalyzer { : FormatStyle::PAS_Right; if (Style.Standard == FormatStyle::LS_Auto) Style.Standard = hasCpp03IncompatibleFormat(AnnotatedLines) - ? FormatStyle::LS_Cpp11 + ? FormatStyle::LS_Latest : FormatStyle::LS_Cpp03; BinPackInconclusiveFunctions = HasBinPackedFunction || !HasOnePerLineFunction; @@ -1417,22 +1442,29 @@ class Cleaner : public TokenAnalyzer { checkEmptyNamespace(AnnotatedLines); - for (auto &Line : AnnotatedLines) { - if (Line->Affected) { - cleanupRight(Line->First, tok::comma, tok::comma); - cleanupRight(Line->First, TT_CtorInitializerColon, tok::comma); - cleanupRight(Line->First, tok::l_paren, tok::comma); - cleanupLeft(Line->First, tok::comma, tok::r_paren); - cleanupLeft(Line->First, TT_CtorInitializerComma, tok::l_brace); - cleanupLeft(Line->First, TT_CtorInitializerColon, tok::l_brace); - cleanupLeft(Line->First, TT_CtorInitializerColon, tok::equal); - } - } + for (auto *Line : AnnotatedLines) + cleanupLine(Line); return {generateFixes(), 0}; } private: + void cleanupLine(AnnotatedLine *Line) { + for (auto *Child : Line->Children) { + cleanupLine(Child); + } + + if (Line->Affected) { + cleanupRight(Line->First, tok::comma, tok::comma); + cleanupRight(Line->First, TT_CtorInitializerColon, tok::comma); + cleanupRight(Line->First, tok::l_paren, tok::comma); + cleanupLeft(Line->First, tok::comma, tok::r_paren); + cleanupLeft(Line->First, TT_CtorInitializerComma, tok::l_brace); + cleanupLeft(Line->First, TT_CtorInitializerColon, tok::l_brace); + cleanupLeft(Line->First, TT_CtorInitializerColon, tok::equal); + } + } + bool containsOnlyComments(const AnnotatedLine &Line) { for (FormatToken *Tok = Line.First; Tok != nullptr; Tok = Tok->Next) { if (Tok->isNot(tok::comment)) @@ -1750,6 +1782,7 @@ struct IncludeDirective { StringRef Text; unsigned Offset; int Category; + int Priority; }; struct JavaImportDirective { @@ -1801,6 +1834,28 @@ FindCursorIndex(const SmallVectorImpl &Includes, return std::make_pair(CursorIndex, OffsetToEOL); } +// Replace all "\r\n" with "\n". +std::string replaceCRLF(const std::string &Code) { + std::string NewCode; + size_t Pos = 0, LastPos = 0; + + do { + Pos = Code.find("\r\n", LastPos); + if (Pos == LastPos) { + LastPos++; + continue; + } + if (Pos == std::string::npos) { + NewCode += Code.substr(LastPos); + break; + } + NewCode += Code.substr(LastPos, Pos - LastPos) + "\n"; + LastPos = Pos + 2; + } while (Pos != std::string::npos); + + return NewCode; +} + // Sorts and deduplicate a block of includes given by 'Includes' alphabetically // adding the necessary replacement to 'Replaces'. 'Includes' must be in strict // source order. @@ -1813,6 +1868,7 @@ static void sortCppIncludes(const FormatStyle &Style, ArrayRef Ranges, StringRef FileName, StringRef Code, tooling::Replacements &Replaces, unsigned *Cursor) { + tooling::IncludeCategoryManager Categories(Style.IncludeStyle, FileName); unsigned IncludesBeginOffset = Includes.front().Offset; unsigned IncludesEndOffset = Includes.back().Offset + Includes.back().Text.size(); @@ -1820,11 +1876,12 @@ static void sortCppIncludes(const FormatStyle &Style, if (!affectsRange(Ranges, IncludesBeginOffset, IncludesEndOffset)) return; SmallVector Indices; - for (unsigned i = 0, e = Includes.size(); i != e; ++i) + for (unsigned i = 0, e = Includes.size(); i != e; ++i) { Indices.push_back(i); + } llvm::stable_sort(Indices, [&](unsigned LHSI, unsigned RHSI) { - return std::tie(Includes[LHSI].Category, Includes[LHSI].Filename) < - std::tie(Includes[RHSI].Category, Includes[RHSI].Filename); + return std::tie(Includes[LHSI].Priority, Includes[LHSI].Filename) < + std::tie(Includes[RHSI].Priority, Includes[RHSI].Filename); }); // The index of the include on which the cursor will be put after // sorting/deduplicating. @@ -1872,7 +1929,8 @@ static void sortCppIncludes(const FormatStyle &Style, // If the #includes are out of order, we generate a single replacement fixing // the entire range of blocks. Otherwise, no replacement is generated. - if (result == Code.substr(IncludesBeginOffset, IncludesBlockSize)) + if (replaceCRLF(result) == + replaceCRLF(Code.substr(IncludesBeginOffset, IncludesBlockSize))) return; auto Err = Replaces.add(tooling::Replacement( @@ -1939,9 +1997,12 @@ tooling::Replacements sortCppIncludes(const FormatStyle &Style, StringRef Code, int Category = Categories.getIncludePriority( IncludeName, /*CheckMainHeader=*/!MainIncludeFound && FirstIncludeBlock); + int Priority = Categories.getSortIncludePriority( + IncludeName, !MainIncludeFound && FirstIncludeBlock); if (Category == 0) MainIncludeFound = true; - IncludesInBlock.push_back({IncludeName, Line, Prev, Category}); + IncludesInBlock.push_back( + {IncludeName, Line, Prev, Category, Priority}); } else if (!IncludesInBlock.empty() && !EmptyLineSkipped) { sortCppIncludes(Style, IncludesInBlock, Ranges, FileName, Code, Replaces, Cursor); @@ -2037,7 +2098,8 @@ static void sortJavaImports(const FormatStyle &Style, // If the imports are out of order, we generate a single replacement fixing // the entire block. Otherwise, no replacement is generated. - if (result == Code.substr(Imports.front().Offset, ImportsBlockSize)) + if (replaceCRLF(result) == + replaceCRLF(Code.substr(Imports.front().Offset, ImportsBlockSize))) return; auto Err = Replaces.add(tooling::Replacement(FileName, Imports.front().Offset, @@ -2327,7 +2389,7 @@ reformat(const FormatStyle &Style, StringRef Code, auto Env = std::make_unique(Code, FileName, Ranges, FirstStartColumn, - NextStartColumn, LastStartColumn); + NextStartColumn, LastStartColumn); llvm::Optional CurrentCode = None; tooling::Replacements Fixes; unsigned Penalty = 0; @@ -2402,14 +2464,18 @@ tooling::Replacements sortUsingDeclarations(const FormatStyle &Style, LangOptions getFormattingLangOpts(const FormatStyle &Style) { LangOptions LangOpts; - FormatStyle::LanguageStandard LexingStd = - Style.Standard == FormatStyle::LS_Auto ? FormatStyle::LS_Cpp11 - : Style.Standard; + + FormatStyle::LanguageStandard LexingStd = Style.Standard; + if (LexingStd == FormatStyle::LS_Auto) + LexingStd = FormatStyle::LS_Latest; + if (LexingStd == FormatStyle::LS_Latest) + LexingStd = FormatStyle::LS_Cpp20; LangOpts.CPlusPlus = 1; LangOpts.CPlusPlus11 = LexingStd >= FormatStyle::LS_Cpp11; - LangOpts.CPlusPlus14 = LexingStd >= FormatStyle::LS_Cpp11; - LangOpts.CPlusPlus17 = LexingStd >= FormatStyle::LS_Cpp11; - LangOpts.CPlusPlus2a = LexingStd >= FormatStyle::LS_Cpp11; + LangOpts.CPlusPlus14 = LexingStd >= FormatStyle::LS_Cpp14; + LangOpts.CPlusPlus17 = LexingStd >= FormatStyle::LS_Cpp17; + LangOpts.CPlusPlus2a = LexingStd >= FormatStyle::LS_Cpp20; + LangOpts.LineComment = 1; bool AlternativeOperators = Style.isCpp(); LangOpts.CXXOperatorNames = AlternativeOperators ? 1 : 0; diff --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp index 009b8849753cf..e59a059fd6b23 100644 --- a/clang/lib/Format/FormatTokenLexer.cpp +++ b/clang/lib/Format/FormatTokenLexer.cpp @@ -657,7 +657,8 @@ FormatToken *FormatTokenLexer::getNextToken() { ++Column; break; case '\t': - Column += Style.TabWidth - Column % Style.TabWidth; + Column += + Style.TabWidth - (Style.TabWidth ? Column % Style.TabWidth : 0); break; case '\\': if (i + 1 == e || (Text[i + 1] != '\r' && Text[i + 1] != '\n')) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 9802711834e69..91ac9822c8ace 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -65,7 +65,7 @@ class AnnotatingParser { AnnotatingParser(const FormatStyle &Style, AnnotatedLine &Line, const AdditionalKeywords &Keywords) : Style(Style), Line(Line), CurrentToken(Line.First), AutoFound(false), - Keywords(Keywords) { + TrailingReturnFound(false), Keywords(Keywords) { Contexts.push_back(Context(tok::unknown, 1, /*IsExpression=*/false)); resetTokenMetadata(CurrentToken); } @@ -1389,7 +1389,11 @@ class AnnotatingParser { } else if (Current.is(tok::arrow) && AutoFound && Line.MustBeDeclaration && Current.NestingLevel == 0) { Current.Type = TT_TrailingReturnArrow; - } else if (Current.isOneOf(tok::star, tok::amp, tok::ampamp)) { + TrailingReturnFound = true; + } else if (Current.is(tok::star) || + (Current.isOneOf(tok::amp, tok::ampamp) && + (Current.NestingLevel != 0 || !Line.MightBeFunctionDecl || + TrailingReturnFound))) { Current.Type = determineStarAmpUsage(Current, Contexts.back().CanBeExpression && Contexts.back().IsExpression, @@ -1412,6 +1416,8 @@ class AnnotatingParser { Current.Type = TT_ConditionalExpr; } } else if (Current.isBinaryOperator() && + !(Line.MightBeFunctionDecl && Current.NestingLevel == 0 && + Current.isOneOf(tok::amp, tok::ampamp)) && (!Current.Previous || Current.Previous->isNot(tok::l_square)) && (!Current.is(tok::greater) && Style.Language != FormatStyle::LK_TextProto)) { @@ -1486,10 +1492,12 @@ class AnnotatingParser { // colon after this, this is the only place which annotates the identifier // as a selector.) Current.Type = TT_SelectorName; - } else if (Current.isOneOf(tok::identifier, tok::kw_const) && + } else if (Current.isOneOf(tok::identifier, tok::kw_const, tok::amp, + tok::ampamp) && Current.Previous && !Current.Previous->isOneOf(tok::equal, tok::at) && - Line.MightBeFunctionDecl && Contexts.size() == 1) { + Line.MightBeFunctionDecl && !TrailingReturnFound && + Contexts.size() == 1) { // Line.MightBeFunctionDecl can only be true after the parentheses of a // function declaration have been found. Current.Type = TT_TrailingAnnotation; @@ -1767,6 +1775,7 @@ class AnnotatingParser { AnnotatedLine &Line; FormatToken *CurrentToken; bool AutoFound; + bool TrailingReturnFound; const AdditionalKeywords &Keywords; // Set of "<" tokens that do not open a template parameter list. If parseAngle @@ -2580,7 +2589,8 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, (Style.PointerAlignment != FormatStyle::PAS_Right && !Line.IsMultiVariableDeclStmt) && Left.Previous && - !Left.Previous->isOneOf(tok::l_paren, tok::coloncolon)); + !Left.Previous->isOneOf(tok::l_paren, tok::coloncolon, + tok::l_square)); if (Right.is(tok::star) && Left.is(tok::l_paren)) return false; const auto SpaceRequiredForArrayInitializerLSquare = @@ -2679,6 +2689,14 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, Right.MatchingParen->endsSequence(TT_DictLiteral, tok::at)) // Objective-C dictionary literal -> no space before closing brace. return false; + if (Right.Type == TT_TrailingAnnotation && + Right.isOneOf(tok::amp, tok::ampamp) && + Left.isOneOf(tok::kw_const, tok::kw_volatile) && + (!Right.Next || Right.Next->is(tok::semi))) + // Match const and volatile ref-qualifiers without any additional + // qualifiers such as + // void Fn() const &; + return Style.PointerAlignment != FormatStyle::PAS_Left; return true; } diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 9878d45f58635..df30f7db80e65 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -1193,6 +1193,12 @@ void UnwrappedLineFormatter::formatFirstToken( if (Newlines) Indent = NewlineIndent; + // If in Whitemsmiths mode, indent start and end of blocks + if (Style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths) { + if (RootToken.isOneOf(tok::l_brace, tok::r_brace, tok::kw_case)) + Indent += Style.IndentWidth; + } + // Preprocessor directives get indented before the hash only if specified if (Style.IndentPPDirectives != FormatStyle::PPDIS_BeforeHash && (Line.Type == LT_PreprocessorDirective || diff --git a/clang/lib/Format/WhitespaceManager.cpp b/clang/lib/Format/WhitespaceManager.cpp index 33c0d53402e6f..5a44500d355fc 100644 --- a/clang/lib/Format/WhitespaceManager.cpp +++ b/clang/lib/Format/WhitespaceManager.cpp @@ -815,19 +815,24 @@ void WhitespaceManager::appendIndentText(std::string &Text, Text.append(Spaces, ' '); break; case FormatStyle::UT_Always: { - unsigned FirstTabWidth = - Style.TabWidth - WhitespaceStartColumn % Style.TabWidth; - // Insert only spaces when we want to end up before the next tab. - if (Spaces < FirstTabWidth || Spaces == 1) { + if (Style.TabWidth) { + unsigned FirstTabWidth = + Style.TabWidth - WhitespaceStartColumn % Style.TabWidth; + + // Insert only spaces when we want to end up before the next tab. + if (Spaces < FirstTabWidth || Spaces == 1) { + Text.append(Spaces, ' '); + break; + } + // Align to the next tab. + Spaces -= FirstTabWidth; + Text.append("\t"); + + Text.append(Spaces / Style.TabWidth, '\t'); + Text.append(Spaces % Style.TabWidth, ' '); + } else if (Spaces == 1) { Text.append(Spaces, ' '); - break; } - // Align to the next tab. - Spaces -= FirstTabWidth; - Text.append("\t"); - - Text.append(Spaces / Style.TabWidth, '\t'); - Text.append(Spaces % Style.TabWidth, ' '); break; } case FormatStyle::UT_ForIndentation: @@ -837,14 +842,16 @@ void WhitespaceManager::appendIndentText(std::string &Text, // the first one. if (Indentation > Spaces) Indentation = Spaces; - unsigned Tabs = Indentation / Style.TabWidth; - Text.append(Tabs, '\t'); - Spaces -= Tabs * Style.TabWidth; + if (Style.TabWidth) { + unsigned Tabs = Indentation / Style.TabWidth; + Text.append(Tabs, '\t'); + Spaces -= Tabs * Style.TabWidth; + } } Text.append(Spaces, ' '); break; case FormatStyle::UT_ForContinuationAndIndentation: - if (WhitespaceStartColumn == 0) { + if (WhitespaceStartColumn == 0 && Style.TabWidth) { unsigned Tabs = Spaces / Style.TabWidth; Text.append(Tabs, '\t'); Spaces -= Tabs * Style.TabWidth; diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp index 35348ffe13b19..f5e291b7fe17e 100644 --- a/clang/lib/Frontend/ASTUnit.cpp +++ b/clang/lib/Frontend/ASTUnit.cpp @@ -84,6 +84,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileUtilities.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Timer.h" #include "llvm/Support/VirtualFileSystem.h" @@ -2301,26 +2302,19 @@ bool ASTUnit::Save(StringRef File) { SmallString<128> TempPath; TempPath = File; TempPath += "-%%%%%%%%"; - int fd; - if (llvm::sys::fs::createUniqueFile(TempPath, fd, TempPath)) - return true; - // FIXME: Can we somehow regenerate the stat cache here, or do we need to // unconditionally create a stat cache when we parse the file? - llvm::raw_fd_ostream Out(fd, /*shouldClose=*/true); - - serialize(Out); - Out.close(); - if (Out.has_error()) { - Out.clear_error(); - return true; - } - if (llvm::sys::fs::rename(TempPath, File)) { - llvm::sys::fs::remove(TempPath); + if (llvm::Error Err = llvm::writeFileAtomically( + TempPath, File, [this](llvm::raw_ostream &Out) { + return serialize(Out) ? llvm::make_error( + "ASTUnit serialization failed", + llvm::inconvertibleErrorCode()) + : llvm::Error::success(); + })) { + consumeError(std::move(Err)); return true; } - return false; } diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index fd6038d784e8b..2a263124ac583 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2537,6 +2537,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Opts.CUDADeviceApproxTranscendentals = 1; Opts.GPURelocatableDeviceCode = Args.hasArg(OPT_fgpu_rdc); + Opts.HIPUseNewLaunchAPI = Args.hasArg(OPT_fhip_new_launch_api); Opts.SYCLIntHeader = Args.getLastArgValue(OPT_fsycl_int_header); diff --git a/clang/lib/Frontend/InitHeaderSearch.cpp b/clang/lib/Frontend/InitHeaderSearch.cpp index ad0d23271487a..5d877ee9c0d73 100644 --- a/clang/lib/Frontend/InitHeaderSearch.cpp +++ b/clang/lib/Frontend/InitHeaderSearch.cpp @@ -137,6 +137,13 @@ bool InitHeaderSearch::AddUnmappedPath(const Twine &Path, IncludeDirGroup Group, SmallString<256> MappedPathStorage; StringRef MappedPathStr = Path.toStringRef(MappedPathStorage); + // If use system headers while cross-compiling, emit the warning. + if (HasSysroot && (MappedPathStr.startswith("/usr/include") || + MappedPathStr.startswith("/usr/local/include"))) { + Headers.getDiags().Report(diag::warn_poison_system_directories) + << MappedPathStr; + } + // Compute the DirectoryLookup type. SrcMgr::CharacteristicKind Type; if (Group == Quoted || Group == Angled || Group == IndexHeaderMap) { diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index f632139efa557..3fa2cd4841421 100644 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -555,6 +555,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts, // C++20 features. if (LangOpts.CPlusPlus2a) { Builder.defineMacro("__cpp_conditional_explicit", "201806L"); + Builder.defineMacro("__cpp_constexpr_dynamic_alloc", "201907L"); Builder.defineMacro("__cpp_constinit", "201907L"); } if (LangOpts.Char8) diff --git a/clang/lib/Frontend/Rewrite/InclusionRewriter.cpp b/clang/lib/Frontend/Rewrite/InclusionRewriter.cpp index 984b63866422e..dcf645f67f2f3 100644 --- a/clang/lib/Frontend/Rewrite/InclusionRewriter.cpp +++ b/clang/lib/Frontend/Rewrite/InclusionRewriter.cpp @@ -49,6 +49,8 @@ class InclusionRewriter : public PPCallbacks { std::map ModuleIncludes; /// Tracks where inclusions that enter modules (in a module build) are found. std::map ModuleEntryIncludes; + /// Tracks where #if and #elif directives get evaluated and whether to true. + std::map IfConditions; /// Used transitively for building up the FileIncludes mapping over the /// various \c PPCallbacks callbacks. SourceLocation LastInclusionLocation; @@ -78,6 +80,10 @@ class InclusionRewriter : public PPCallbacks { StringRef SearchPath, StringRef RelativePath, const Module *Imported, SrcMgr::CharacteristicKind FileType) override; + void If(SourceLocation Loc, SourceRange ConditionRange, + ConditionValueKind ConditionValue) override; + void Elif(SourceLocation Loc, SourceRange ConditionRange, + ConditionValueKind ConditionValue, SourceLocation IfLoc) override; void WriteLineInfo(StringRef Filename, int Line, SrcMgr::CharacteristicKind FileType, StringRef Extra = StringRef()); @@ -89,12 +95,10 @@ class InclusionRewriter : public PPCallbacks { void CommentOutDirective(Lexer &DirectivesLex, const Token &StartToken, const MemoryBuffer &FromFile, StringRef EOL, unsigned &NextToWrite, int &Lines); - bool HandleHasInclude(FileID FileId, Lexer &RawLex, - const DirectoryLookup *Lookup, Token &Tok, - bool &FileExists); const IncludedFile *FindIncludeAtLocation(SourceLocation Loc) const; const Module *FindModuleAtLocation(SourceLocation Loc) const; const Module *FindEnteredModule(SourceLocation Loc) const; + bool IsIfAtLocationTrue(SourceLocation Loc) const; StringRef NextIdentifierName(Lexer &RawLex, Token &RawToken); }; @@ -203,6 +207,23 @@ void InclusionRewriter::InclusionDirective(SourceLocation HashLoc, LastInclusionLocation = HashLoc; } +void InclusionRewriter::If(SourceLocation Loc, SourceRange ConditionRange, + ConditionValueKind ConditionValue) { + auto P = IfConditions.insert( + std::make_pair(Loc.getRawEncoding(), ConditionValue == CVK_True)); + (void)P; + assert(P.second && "Unexpected revisitation of the same if directive"); +} + +void InclusionRewriter::Elif(SourceLocation Loc, SourceRange ConditionRange, + ConditionValueKind ConditionValue, + SourceLocation IfLoc) { + auto P = IfConditions.insert( + std::make_pair(Loc.getRawEncoding(), ConditionValue == CVK_True)); + (void)P; + assert(P.second && "Unexpected revisitation of the same elif directive"); +} + /// Simple lookup for a SourceLocation (specifically one denoting the hash in /// an inclusion directive) in the map of inclusion information, FileChanges. const InclusionRewriter::IncludedFile * @@ -233,6 +254,13 @@ InclusionRewriter::FindEnteredModule(SourceLocation Loc) const { return nullptr; } +bool InclusionRewriter::IsIfAtLocationTrue(SourceLocation Loc) const { + const auto I = IfConditions.find(Loc.getRawEncoding()); + if (I != IfConditions.end()) + return I->second; + return false; +} + /// Detect the likely line ending style of \p FromFile by examining the first /// newline found within it. static StringRef DetectEOL(const MemoryBuffer &FromFile) { @@ -346,80 +374,6 @@ StringRef InclusionRewriter::NextIdentifierName(Lexer &RawLex, return StringRef(); } -// Expand __has_include and __has_include_next if possible. If there's no -// definitive answer return false. -bool InclusionRewriter::HandleHasInclude( - FileID FileId, Lexer &RawLex, const DirectoryLookup *Lookup, Token &Tok, - bool &FileExists) { - // Lex the opening paren. - RawLex.LexFromRawLexer(Tok); - if (Tok.isNot(tok::l_paren)) - return false; - - RawLex.LexFromRawLexer(Tok); - - SmallString<128> FilenameBuffer; - StringRef Filename; - // Since the raw lexer doesn't give us angle_literals we have to parse them - // ourselves. - // FIXME: What to do if the file name is a macro? - if (Tok.is(tok::less)) { - RawLex.LexFromRawLexer(Tok); - - FilenameBuffer += '<'; - do { - if (Tok.is(tok::eod)) // Sanity check. - return false; - - if (Tok.is(tok::raw_identifier)) - PP.LookUpIdentifierInfo(Tok); - - // Get the string piece. - SmallVector TmpBuffer; - bool Invalid = false; - StringRef TmpName = PP.getSpelling(Tok, TmpBuffer, &Invalid); - if (Invalid) - return false; - - FilenameBuffer += TmpName; - - RawLex.LexFromRawLexer(Tok); - } while (Tok.isNot(tok::greater)); - - FilenameBuffer += '>'; - Filename = FilenameBuffer; - } else { - if (Tok.isNot(tok::string_literal)) - return false; - - bool Invalid = false; - Filename = PP.getSpelling(Tok, FilenameBuffer, &Invalid); - if (Invalid) - return false; - } - - // Lex the closing paren. - RawLex.LexFromRawLexer(Tok); - if (Tok.isNot(tok::r_paren)) - return false; - - // Now ask HeaderInfo if it knows about the header. - // FIXME: Subframeworks aren't handled here. Do we care? - bool isAngled = PP.GetIncludeFilenameSpelling(Tok.getLocation(), Filename); - const DirectoryLookup *CurDir; - const FileEntry *FileEnt = PP.getSourceManager().getFileEntryForID(FileId); - SmallVector, 1> - Includers; - Includers.push_back(std::make_pair(FileEnt, FileEnt->getDir())); - // FIXME: Why don't we call PP.LookupFile here? - Optional File = PP.getHeaderSearchInfo().LookupFile( - Filename, SourceLocation(), isAngled, Lookup, CurDir, Includers, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr); - - FileExists = File.hasValue(); - return true; -} - /// Use a raw lexer to analyze \p FileId, incrementally copying parts of it /// and including content of included files recursively. void InclusionRewriter::Process(FileID FileId, @@ -519,53 +473,33 @@ void InclusionRewriter::Process(FileID FileId, case tok::pp_elif: { bool elif = (RawToken.getIdentifierInfo()->getPPKeywordID() == tok::pp_elif); - // Rewrite special builtin macros to avoid pulling in host details. + bool isTrue = IsIfAtLocationTrue(RawToken.getLocation()); + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(HashToken.getLocation()), + LocalEOL, Line, /*EnsureNewline=*/true); do { - // Walk over the directive. RawLex.LexFromRawLexer(RawToken); - if (RawToken.is(tok::raw_identifier)) - PP.LookUpIdentifierInfo(RawToken); - - if (RawToken.is(tok::identifier)) { - bool HasFile; - SourceLocation Loc = RawToken.getLocation(); - - // Rewrite __has_include(x) - if (RawToken.getIdentifierInfo()->isStr("__has_include")) { - if (!HandleHasInclude(FileId, RawLex, nullptr, RawToken, - HasFile)) - continue; - // Rewrite __has_include_next(x) - } else if (RawToken.getIdentifierInfo()->isStr( - "__has_include_next")) { - if (DirLookup) - ++DirLookup; - - if (!HandleHasInclude(FileId, RawLex, DirLookup, RawToken, - HasFile)) - continue; - } else { - continue; - } - // Replace the macro with (0) or (1), followed by the commented - // out macro for reference. - OutputContentUpTo(FromFile, NextToWrite, SM.getFileOffset(Loc), - LocalEOL, Line, false); - OS << '(' << (int) HasFile << ")/*"; - OutputContentUpTo(FromFile, NextToWrite, - SM.getFileOffset(RawToken.getLocation()) + - RawToken.getLength(), - LocalEOL, Line, false); - OS << "*/"; - } - } while (RawToken.isNot(tok::eod)); + } while (!RawToken.is(tok::eod) && RawToken.isNot(tok::eof)); + // We need to disable the old condition, but that is tricky. + // Trying to comment it out can easily lead to comment nesting. + // So instead make the condition harmless by making it enclose + // and empty block. Moreover, put it itself inside an #if 0 block + // to disable it from getting evaluated (e.g. __has_include_next + // warns if used from the primary source file). + OS << "#if 0 /* disabled by -frewrite-includes */" << MainEOL; if (elif) { - OutputContentUpTo(FromFile, NextToWrite, - SM.getFileOffset(RawToken.getLocation()) + - RawToken.getLength(), - LocalEOL, Line, /*EnsureNewline=*/ true); - WriteLineInfo(FileName, Line, FileType); + OS << "#if 0" << MainEOL; } + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(RawToken.getLocation()) + + RawToken.getLength(), + LocalEOL, Line, /*EnsureNewline=*/true); + // Close the empty block and the disabling block. + OS << "#endif" << MainEOL; + OS << "#endif /* disabled by -frewrite-includes */" << MainEOL; + OS << (elif ? "#elif " : "#if ") << (isTrue ? "1" : "0") + << " /* evaluated by -frewrite-includes */" << MainEOL; + WriteLineInfo(FileName, Line, FileType); break; } case tok::pp_endif: diff --git a/clang/lib/Frontend/TextDiagnostic.cpp b/clang/lib/Frontend/TextDiagnostic.cpp index d6e75a9180cf5..7bb6c5b74d5f6 100644 --- a/clang/lib/Frontend/TextDiagnostic.cpp +++ b/clang/lib/Frontend/TextDiagnostic.cpp @@ -683,8 +683,9 @@ void TextDiagnostic::emitDiagnosticMessage( if (DiagOpts->ShowColors) OS.resetColor(); - printDiagnosticLevel(OS, Level, DiagOpts->ShowColors, - DiagOpts->CLFallbackMode); + if (DiagOpts->ShowLevel) + printDiagnosticLevel(OS, Level, DiagOpts->ShowColors, + DiagOpts->CLFallbackMode); printDiagnosticMessage(OS, /*IsSupplemental*/ Level == DiagnosticsEngine::Note, Message, OS.tell() - StartOfLocationInfo, diff --git a/clang/lib/Headers/altivec.h b/clang/lib/Headers/altivec.h index bc26f1089d04d..8352f8f740c24 100644 --- a/clang/lib/Headers/altivec.h +++ b/clang/lib/Headers/altivec.h @@ -2761,8 +2761,8 @@ static __inline__ vector double __ATTRS_o_ai vec_xl_len(double *__a, return (vector double)__builtin_vsx_lxvl(__a, (__b << 56)); } -static __inline__ vector double __ATTRS_o_ai vec_xl_len_r(unsigned char *__a, - size_t __b) { +static __inline__ vector unsigned char __ATTRS_o_ai +vec_xl_len_r(unsigned char *__a, size_t __b) { vector unsigned char __res = (vector unsigned char)__builtin_vsx_lxvll(__a, (__b << 56)); #ifdef __LITTLE_ENDIAN__ @@ -2912,10 +2912,11 @@ static __inline__ vector double __ATTRS_o_ai vec_cpsgn(vector double __a, #ifdef __VSX__ #define vec_cts(__a, __b) \ _Generic((__a), vector float \ - : __builtin_altivec_vctsxs((__a), (__b)), vector double \ + : __builtin_altivec_vctsxs((vector float)(__a), (__b)), \ + vector double \ : __extension__({ \ vector double __ret = \ - (__a) * \ + (vector double)(__a) * \ (vector double)(vector unsigned long long)((0x3ffULL + (__b)) \ << 52); \ __builtin_convertvector(__ret, vector signed long long); \ @@ -2933,10 +2934,11 @@ static __inline__ vector double __ATTRS_o_ai vec_cpsgn(vector double __a, #ifdef __VSX__ #define vec_ctu(__a, __b) \ _Generic((__a), vector float \ - : __builtin_altivec_vctuxs((__a), (__b)), vector double \ + : __builtin_altivec_vctuxs((vector float)(__a), (__b)), \ + vector double \ : __extension__({ \ vector double __ret = \ - (__a) * \ + (vector double)(__a) * \ (vector double)(vector unsigned long long)((0x3ffULL + __b) \ << 52); \ __builtin_convertvector(__ret, vector unsigned long long); \ @@ -6301,19 +6303,20 @@ static __inline__ vector float __ATTRS_o_ai vec_or(vector float __a, #ifdef __VSX__ static __inline__ vector double __ATTRS_o_ai vec_or(vector bool long long __a, vector double __b) { - return (vector unsigned long long)__a | (vector unsigned long long)__b; + return (vector double)((vector unsigned long long)__a | + (vector unsigned long long)__b); } static __inline__ vector double __ATTRS_o_ai vec_or(vector double __a, vector bool long long __b) { - return (vector unsigned long long)__a | (vector unsigned long long)__b; + return (vector double)((vector unsigned long long)__a | + (vector unsigned long long)__b); } static __inline__ vector double __ATTRS_o_ai vec_or(vector double __a, vector double __b) { - vector unsigned long long __res = - (vector unsigned long long)__a | (vector unsigned long long)__b; - return (vector double)__res; + return (vector double)((vector unsigned long long)__a | + (vector unsigned long long)__b); } static __inline__ vector signed long long __ATTRS_o_ai @@ -14781,7 +14784,7 @@ static __inline__ int __ATTRS_o_ai vec_all_ne(vector bool long long __a, static __inline__ int __ATTRS_o_ai vec_all_ne(vector float __a, vector float __b) { #ifdef __VSX__ - return __builtin_vsx_xvcmpeqdp_p(__CR6_EQ, __a, __b); + return __builtin_vsx_xvcmpeqdp_p(__CR6_EQ, (vector double)__a, (vector double)__b); #else return __builtin_altivec_vcmpeqfp_p(__CR6_EQ, __a, __b); #endif @@ -16671,13 +16674,13 @@ static __inline__ void __ATTRS_o_ai vec_xst_be(vector unsigned __int128 __vec, #endif #ifdef __POWER9_VECTOR__ -#define vec_test_data_class(__a, __b) \ - _Generic((__a), \ - vector float: \ - (vector bool int)__builtin_vsx_xvtstdcsp((__a), (__b)), \ - vector double: \ - (vector bool long long)__builtin_vsx_xvtstdcdp((__a), (__b)) \ - ) +#define vec_test_data_class(__a, __b) \ + _Generic( \ + (__a), vector float \ + : (vector bool int)__builtin_vsx_xvtstdcsp((vector float)(__a), (__b)), \ + vector double \ + : (vector bool long long)__builtin_vsx_xvtstdcdp((vector double)(__a), \ + (__b))) #endif /* #ifdef __POWER9_VECTOR__ */ diff --git a/clang/lib/Headers/avx512fintrin.h b/clang/lib/Headers/avx512fintrin.h index 4e341a10fa948..698e477fe5f36 100644 --- a/clang/lib/Headers/avx512fintrin.h +++ b/clang/lib/Headers/avx512fintrin.h @@ -7658,13 +7658,13 @@ _mm512_maskz_getexp_ps (__mmask16 __U, __m512 __A) #define _mm512_i32gather_ps(index, addr, scale) \ (__m512)__builtin_ia32_gathersiv16sf((__v16sf)_mm512_undefined_ps(), \ (void const *)(addr), \ - (__v16sf)(__m512)(index), \ + (__v16si)(__m512)(index), \ (__mmask16)-1, (int)(scale)) #define _mm512_mask_i32gather_ps(v1_old, mask, index, addr, scale) \ (__m512)__builtin_ia32_gathersiv16sf((__v16sf)(__m512)(v1_old), \ (void const *)(addr), \ - (__v16sf)(__m512)(index), \ + (__v16si)(__m512)(index), \ (__mmask16)(mask), (int)(scale)) #define _mm512_i32gather_epi32(index, addr, scale) \ diff --git a/clang/lib/Headers/ia32intrin.h b/clang/lib/Headers/ia32intrin.h index 8e38df73187d1..79b7f0655cf01 100644 --- a/clang/lib/Headers/ia32intrin.h +++ b/clang/lib/Headers/ia32intrin.h @@ -195,6 +195,74 @@ __writeeflags(unsigned int __f) } #endif /* !__x86_64__ */ +/** Cast a 32-bit float value to a 32-bit unsigned integer value + * + * \headerfile + * This intrinsic corresponds to the VMOVD / MOVD instruction in x86_64, + * and corresponds to the VMOVL / MOVL instruction in ia32. + * + * \param __A + * A 32-bit float value. + * \returns a 32-bit unsigned integer containing the converted value. + */ +static __inline__ unsigned int __attribute__((__always_inline__)) +_castf32_u32(float __A) { + unsigned int D; + __builtin_memcpy(&D, &__A, sizeof(__A)); + return D; +} + +/** Cast a 64-bit float value to a 64-bit unsigned integer value + * + * \headerfile + * This intrinsic corresponds to the VMOVQ / MOVQ instruction in x86_64, + * and corresponds to the VMOVL / MOVL instruction in ia32. + * + * \param __A + * A 64-bit float value. + * \returns a 64-bit unsigned integer containing the converted value. + */ +static __inline__ unsigned long long __attribute__((__always_inline__)) +_castf64_u64(double __A) { + unsigned long long D; + __builtin_memcpy(&D, &__A, sizeof(__A)); + return D; +} + +/** Cast a 32-bit unsigned integer value to a 32-bit float value + * + * \headerfile + * This intrinsic corresponds to the VMOVQ / MOVQ instruction in x86_64, + * and corresponds to the FLDS instruction in ia32. + * + * \param __A + * A 32-bit unsigned integer value. + * \returns a 32-bit float value containing the converted value. + */ +static __inline__ float __attribute__((__always_inline__)) +_castu32_f32(unsigned int __A) { + float D; + __builtin_memcpy(&D, &__A, sizeof(__A)); + return D; +} + +/** Cast a 64-bit unsigned integer value to a 64-bit float value + * + * \headerfile + * This intrinsic corresponds to the VMOVQ / MOVQ instruction in x86_64, + * and corresponds to the FLDL instruction in ia32. + * + * \param __A + * A 64-bit unsigned integer value. + * \returns a 64-bit float value containing the converted value. + */ +static __inline__ double __attribute__((__always_inline__)) +_castu64_f64(unsigned long long __A) { + double D; + __builtin_memcpy(&D, &__A, sizeof(__A)); + return D; +} + /** Adds the unsigned integer operand to the CRC-32C checksum of the * unsigned char operand. * diff --git a/clang/lib/Lex/DependencyDirectivesSourceMinimizer.cpp b/clang/lib/Lex/DependencyDirectivesSourceMinimizer.cpp index 0a0656699ab54..2ffbcfb7e6768 100644 --- a/clang/lib/Lex/DependencyDirectivesSourceMinimizer.cpp +++ b/clang/lib/Lex/DependencyDirectivesSourceMinimizer.cpp @@ -244,13 +244,22 @@ static void skipToNewlineRaw(const char *&First, const char *const End) { } } -static const char *reverseOverSpaces(const char *First, const char *Last) { +static const char *findLastNonSpace(const char *First, const char *Last) { assert(First <= Last); while (First != Last && isHorizontalWhitespace(Last[-1])) --Last; return Last; } +static const char *findFirstTrailingSpace(const char *First, + const char *Last) { + const char *LastNonSpace = findLastNonSpace(First, Last); + if (Last == LastNonSpace) + return Last; + assert(isHorizontalWhitespace(LastNonSpace[0])); + return LastNonSpace + 1; +} + static void skipLineComment(const char *&First, const char *const End) { assert(First[0] == '/' && First[1] == '/'); First += 2; @@ -382,7 +391,7 @@ void Minimizer::printToNewline(const char *&First, const char *const End) { } // Deal with "//..." and "/*...*/". - append(First, reverseOverSpaces(First, Last)); + append(First, findFirstTrailingSpace(First, Last)); First = Last; if (Last[1] == '/') { @@ -397,15 +406,20 @@ void Minimizer::printToNewline(const char *&First, const char *const End) { } while (Last != End && !isVerticalWhitespace(*Last)); // Print out the string. - if (Last == End || Last == First || Last[-1] != '\\') { - append(First, reverseOverSpaces(First, Last)); + const char *LastBeforeTrailingSpace = findLastNonSpace(First, Last); + if (Last == End || LastBeforeTrailingSpace == First || + LastBeforeTrailingSpace[-1] != '\\') { + append(First, LastBeforeTrailingSpace); First = Last; skipNewline(First, End); return; } - // Print up to the backslash, backing up over spaces. - append(First, reverseOverSpaces(First, Last - 1)); + // Print up to the backslash, backing up over spaces. Preserve at least one + // space, as the space matters when tokens are separated by a line + // continuation. + append(First, findFirstTrailingSpace( + First, LastBeforeTrailingSpace - 1)); First = Last; skipNewline(First, End); diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index c4f660f88c73f..3b7eaee3c914f 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -1716,11 +1716,11 @@ Optional Preprocessor::LookupHeaderIncludeOrImport( SourceLocation FilenameLoc, CharSourceRange FilenameRange, const Token &FilenameTok, bool &IsFrameworkFound, bool IsImportDecl, bool &IsMapped, const DirectoryLookup *LookupFrom, - const FileEntry *LookupFromFile, SmallString<128> &NormalizedPath, + const FileEntry *LookupFromFile, StringRef LookupFilename, SmallVectorImpl &RelativePath, SmallVectorImpl &SearchPath, ModuleMap::KnownHeader &SuggestedModule, bool isAngled) { Optional File = LookupFile( - FilenameLoc, LangOpts.MSVCCompat ? NormalizedPath.c_str() : Filename, + FilenameLoc, LookupFilename, isAngled, LookupFrom, LookupFromFile, CurDir, Callbacks ? &SearchPath : nullptr, Callbacks ? &RelativePath : nullptr, &SuggestedModule, &IsMapped, &IsFrameworkFound); @@ -1739,7 +1739,7 @@ Optional Preprocessor::LookupHeaderIncludeOrImport( // Try the lookup again, skipping the cache. Optional File = LookupFile( FilenameLoc, - LangOpts.MSVCCompat ? NormalizedPath.c_str() : Filename, isAngled, + LookupFilename, isAngled, LookupFrom, LookupFromFile, CurDir, nullptr, nullptr, &SuggestedModule, &IsMapped, /*IsFrameworkFound=*/nullptr, /*SkipCache*/ true); @@ -1757,7 +1757,7 @@ Optional Preprocessor::LookupHeaderIncludeOrImport( // provide the user with a possible fixit. if (isAngled) { Optional File = LookupFile( - FilenameLoc, LangOpts.MSVCCompat ? NormalizedPath.c_str() : Filename, + FilenameLoc, LookupFilename, false, LookupFrom, LookupFromFile, CurDir, Callbacks ? &SearchPath : nullptr, Callbacks ? &RelativePath : nullptr, &SuggestedModule, &IsMapped, @@ -1785,20 +1785,23 @@ Optional Preprocessor::LookupHeaderIncludeOrImport( return Filename; }; StringRef TypoCorrectionName = CorrectTypoFilename(Filename); - SmallString<128> NormalizedTypoCorrectionPath; - if (LangOpts.MSVCCompat) { - NormalizedTypoCorrectionPath = TypoCorrectionName.str(); + #ifndef _WIN32 + // Normalize slashes when compiling with -fms-extensions on non-Windows. + // This is unnecessary on Windows since the filesystem there handles + // backslashes. + SmallString<128> NormalizedTypoCorrectionPath; + if (LangOpts.MicrosoftExt) { + NormalizedTypoCorrectionPath = TypoCorrectionName; llvm::sys::path::native(NormalizedTypoCorrectionPath); -#endif + TypoCorrectionName = NormalizedTypoCorrectionPath; } +#endif + Optional File = LookupFile( - FilenameLoc, - LangOpts.MSVCCompat ? NormalizedTypoCorrectionPath.c_str() - : TypoCorrectionName, - isAngled, LookupFrom, LookupFromFile, CurDir, - Callbacks ? &SearchPath : nullptr, Callbacks ? &RelativePath : nullptr, - &SuggestedModule, &IsMapped, + FilenameLoc, TypoCorrectionName, isAngled, LookupFrom, LookupFromFile, + CurDir, Callbacks ? &SearchPath : nullptr, + Callbacks ? &RelativePath : nullptr, &SuggestedModule, &IsMapped, /*IsFrameworkFound=*/nullptr); if (File) { auto Hint = @@ -1906,18 +1909,23 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport( // the path. ModuleMap::KnownHeader SuggestedModule; SourceLocation FilenameLoc = FilenameTok.getLocation(); + StringRef LookupFilename = Filename; + +#ifndef _WIN32 + // Normalize slashes when compiling with -fms-extensions on non-Windows. This + // is unnecessary on Windows since the filesystem there handles backslashes. SmallString<128> NormalizedPath; - if (LangOpts.MSVCCompat) { + if (LangOpts.MicrosoftExt) { NormalizedPath = Filename.str(); -#ifndef _WIN32 llvm::sys::path::native(NormalizedPath); -#endif + LookupFilename = NormalizedPath; } +#endif Optional File = LookupHeaderIncludeOrImport( CurDir, Filename, FilenameLoc, FilenameRange, FilenameTok, IsFrameworkFound, IsImportDecl, IsMapped, LookupFrom, LookupFromFile, - NormalizedPath, RelativePath, SearchPath, SuggestedModule, isAngled); + LookupFilename, RelativePath, SearchPath, SuggestedModule, isAngled); if (usingPCHWithThroughHeader() && SkippingUntilPCHThroughHeader) { if (File && isPCHThroughHeader(&File->getFileEntry())) @@ -2059,10 +2067,9 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport( // Notify the callback object that we've seen an inclusion directive. // FIXME: Use a different callback for a pp-import? Callbacks->InclusionDirective( - HashLoc, IncludeTok, - LangOpts.MSVCCompat ? NormalizedPath.c_str() : Filename, isAngled, - FilenameRange, File ? &File->getFileEntry() : nullptr, SearchPath, - RelativePath, Action == Import ? SuggestedModule.getModule() : nullptr, + HashLoc, IncludeTok, LookupFilename, isAngled, FilenameRange, + File ? &File->getFileEntry() : nullptr, SearchPath, RelativePath, + Action == Import ? SuggestedModule.getModule() : nullptr, FileCharacter); if (Action == Skip && File) Callbacks->FileSkipped(*File, FilenameTok, FileCharacter); @@ -2085,7 +2092,7 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport( !IsMapped && !File->getFileEntry().tryGetRealPathName().empty(); if (CheckIncludePathPortability) { - StringRef Name = LangOpts.MSVCCompat ? NormalizedPath.str() : Filename; + StringRef Name = LookupFilename; StringRef RealPathName = File->getFileEntry().tryGetRealPathName(); SmallVector Components(llvm::sys::path::begin(Name), llvm::sys::path::end(Name)); @@ -2782,7 +2789,8 @@ void Preprocessor::HandleDefineDirective( // If we need warning for not using the macro, add its location in the // warn-because-unused-macro set. If it gets used it will be removed from set. if (getSourceManager().isInMainFile(MI->getDefinitionLoc()) && - !Diags->isIgnored(diag::pp_macro_not_used, MI->getDefinitionLoc())) { + !Diags->isIgnored(diag::pp_macro_not_used, MI->getDefinitionLoc()) && + !MacroExpansionInDirectivesOverride) { MI->setIsWarnIfUnused(true); WarnUnusedMacroLocs.insert(MI->getDefinitionLoc()); } diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 35a05b258bc35..2cbe0788070d1 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -4684,8 +4684,10 @@ void Parser::ParseEnumBody(SourceLocation StartLoc, Decl *EnumDecl) { ExprResult AssignedVal; EnumAvailabilityDiags.emplace_back(*this); + EnterExpressionEvaluationContext ConstantEvaluated( + Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); if (TryConsumeToken(tok::equal, EqualLoc)) { - AssignedVal = ParseConstantExpression(); + AssignedVal = ParseConstantExpressionInExprEvalContext(); if (AssignedVal.isInvalid()) SkipUntil(tok::comma, tok::r_brace, StopBeforeMatch); } diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index 6bd96051f9e17..c7143fd410410 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -42,6 +42,7 @@ enum OpenMPDirectiveKindEx { OMPD_teams_distribute_parallel, OMPD_target_teams_distribute_parallel, OMPD_mapper, + OMPD_variant, }; class DeclDirectiveListParserHelper final { @@ -80,6 +81,7 @@ static unsigned getOpenMPDirectiveKindEx(StringRef S) { .Case("reduction", OMPD_reduction) .Case("update", OMPD_update) .Case("mapper", OMPD_mapper) + .Case("variant", OMPD_variant) .Default(OMPD_unknown); } @@ -93,6 +95,7 @@ static OpenMPDirectiveKind parseOpenMPDirectiveKind(Parser &P) { {OMPD_declare, OMPD_mapper, OMPD_declare_mapper}, {OMPD_declare, OMPD_simd, OMPD_declare_simd}, {OMPD_declare, OMPD_target, OMPD_declare_target}, + {OMPD_declare, OMPD_variant, OMPD_declare_variant}, {OMPD_distribute, OMPD_parallel, OMPD_distribute_parallel}, {OMPD_distribute_parallel, OMPD_for, OMPD_distribute_parallel_for}, {OMPD_distribute_parallel_for, OMPD_simd, @@ -752,6 +755,7 @@ Parser::ParseOMPDeclareSimdClauses(Parser::DeclGroupPtrTy Ptr, /*IsReinject*/ true); // Consume the previously pushed token. ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true); + ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true); FNContextRAII FnContext(*this, Ptr); OMPDeclareSimdDeclAttr::BranchStateTy BS = @@ -782,6 +786,232 @@ Parser::ParseOMPDeclareSimdClauses(Parser::DeclGroupPtrTy Ptr, LinModifiers, Steps, SourceRange(Loc, EndLoc)); } +/// Parse optional 'score' '(' ')' ':'. +static ExprResult parseContextScore(Parser &P) { + ExprResult ScoreExpr; + SmallString<16> Buffer; + StringRef SelectorName = + P.getPreprocessor().getSpelling(P.getCurToken(), Buffer); + OMPDeclareVariantAttr::ScoreType ScoreKind = + OMPDeclareVariantAttr::ScoreUnknown; + (void)OMPDeclareVariantAttr::ConvertStrToScoreType(SelectorName, ScoreKind); + if (ScoreKind == OMPDeclareVariantAttr::ScoreUnknown) + return ScoreExpr; + assert(ScoreKind == OMPDeclareVariantAttr::ScoreSpecified && + "Expected \"score\" clause."); + (void)P.ConsumeToken(); + SourceLocation RLoc; + ScoreExpr = P.ParseOpenMPParensExpr(SelectorName, RLoc); + // Parse ':' + if (P.getCurToken().is(tok::colon)) + (void)P.ConsumeAnyToken(); + else + P.Diag(P.getCurToken(), diag::warn_pragma_expected_colon) + << "context selector score clause"; + return ScoreExpr; +} + +/// Parse context selector for 'implementation' selector set: +/// 'vendor' '(' ')' +static void +parseImplementationSelector(Parser &P, + Sema::OpenMPDeclareVariantCtsSelectorData &Data) { + const Token &Tok = P.getCurToken(); + // Parse inner context selector set name, if any. + if (!Tok.is(tok::identifier)) { + P.Diag(Tok.getLocation(), diag::warn_omp_declare_variant_cs_name_expected) + << "implementation"; + // Skip until either '}', ')', or end of directive. + while (!P.SkipUntil(tok::r_brace, tok::r_paren, + tok::annot_pragma_openmp_end, Parser::StopBeforeMatch)) + ; + return; + } + SmallString<16> Buffer; + StringRef CtxSelectorName = P.getPreprocessor().getSpelling(Tok, Buffer); + OMPDeclareVariantAttr::CtxSelectorType CSKind = + OMPDeclareVariantAttr::CtxUnknown; + (void)OMPDeclareVariantAttr::ConvertStrToCtxSelectorType(CtxSelectorName, + CSKind); + (void)P.ConsumeToken(); + switch (CSKind) { + case OMPDeclareVariantAttr::CtxVendor: { + // Parse '('. + BalancedDelimiterTracker T(P, tok::l_paren, tok::annot_pragma_openmp_end); + (void)T.expectAndConsume(diag::err_expected_lparen_after, + CtxSelectorName.data()); + Data.CtxScore = parseContextScore(P); + // Parse . + StringRef VendorName; + if (Tok.is(tok::identifier)) { + VendorName = P.getPreprocessor().getSpelling(P.getCurToken(), Buffer); + (void)P.ConsumeToken(); + } else { + P.Diag(Tok.getLocation(), diag::err_omp_declare_variant_item_expected) + << "vendor identifier" << "vendor" << "implementation"; + } + // Parse ')'. + (void)T.consumeClose(); + if (!VendorName.empty()) + Data.ImplVendor = VendorName; + break; + } + case OMPDeclareVariantAttr::CtxUnknown: + P.Diag(Tok.getLocation(), diag::warn_omp_declare_variant_cs_name_expected) + << "implementation"; + // Skip until either '}', ')', or end of directive. + while (!P.SkipUntil(tok::r_brace, tok::r_paren, + tok::annot_pragma_openmp_end, Parser::StopBeforeMatch)) + ; + return; + } + Data.CtxSet = OMPDeclareVariantAttr::CtxSetImplementation; + Data.Ctx = CSKind; +} + +/// Parses clauses for 'declare variant' directive. +/// clause: +/// '=' '{' '}' +/// [ ',' '=' '{' '}' ] +bool Parser::parseOpenMPContextSelectors( + SourceLocation Loc, + llvm::function_ref + Callback) { + do { + // Parse inner context selector set name. + if (!Tok.is(tok::identifier)) { + Diag(Tok.getLocation(), diag::err_omp_declare_variant_no_ctx_selector) + << getOpenMPClauseName(OMPC_match); + return true; + } + SmallString<16> Buffer; + StringRef CtxSelectorSetName = PP.getSpelling(Tok, Buffer); + // Parse '='. + (void)ConsumeToken(); + if (Tok.isNot(tok::equal)) { + Diag(Tok.getLocation(), diag::err_omp_declare_variant_equal_expected) + << CtxSelectorSetName; + return true; + } + (void)ConsumeToken(); + // TBD: add parsing of known context selectors. + // Unknown selector - just ignore it completely. + Sema::OpenMPDeclareVariantCtsSelectorData Data; + { + // Parse '{'. + BalancedDelimiterTracker TBr(*this, tok::l_brace, + tok::annot_pragma_openmp_end); + if (TBr.expectAndConsume(diag::err_expected_lbrace_after, "=")) + return true; + OMPDeclareVariantAttr::CtxSelectorSetType CSSKind = + OMPDeclareVariantAttr::CtxSetUnknown; + (void)OMPDeclareVariantAttr::ConvertStrToCtxSelectorSetType( + CtxSelectorSetName, CSSKind); + switch (CSSKind) { + case OMPDeclareVariantAttr::CtxSetImplementation: + parseImplementationSelector(*this, Data); + break; + case OMPDeclareVariantAttr::CtxSetUnknown: + // Skip until either '}', ')', or end of directive. + while (!SkipUntil(tok::r_brace, tok::r_paren, + tok::annot_pragma_openmp_end, StopBeforeMatch)) + ; + break; + } + // Parse '}'. + (void)TBr.consumeClose(); + } + Callback(SourceRange(Loc, Tok.getLocation()), Data); + // Consume ',' + if (Tok.isNot(tok::r_paren) && Tok.isNot(tok::annot_pragma_openmp_end)) + (void)ExpectAndConsume(tok::comma); + } while (Tok.isAnyIdentifier()); + return false; +} + +/// Parse clauses for '#pragma omp declare variant ( variant-func-id ) clause'. +void Parser::ParseOMPDeclareVariantClauses(Parser::DeclGroupPtrTy Ptr, + CachedTokens &Toks, + SourceLocation Loc) { + PP.EnterToken(Tok, /*IsReinject*/ true); + PP.EnterTokenStream(Toks, /*DisableMacroExpansion=*/true, + /*IsReinject*/ true); + // Consume the previously pushed token. + ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true); + ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true); + + FNContextRAII FnContext(*this, Ptr); + // Parse function declaration id. + SourceLocation RLoc; + // Parse with IsAddressOfOperand set to true to parse methods as DeclRefExprs + // instead of MemberExprs. + ExprResult AssociatedFunction = + ParseOpenMPParensExpr(getOpenMPDirectiveName(OMPD_declare_variant), RLoc, + /*IsAddressOfOperand=*/true); + if (!AssociatedFunction.isUsable()) { + if (!Tok.is(tok::annot_pragma_openmp_end)) + while (!SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch)) + ; + // Skip the last annot_pragma_openmp_end. + (void)ConsumeAnnotationToken(); + return; + } + Optional> DeclVarData = + Actions.checkOpenMPDeclareVariantFunction( + Ptr, AssociatedFunction.get(), SourceRange(Loc, Tok.getLocation())); + + // Parse 'match'. + OpenMPClauseKind CKind = Tok.isAnnotation() + ? OMPC_unknown + : getOpenMPClauseKind(PP.getSpelling(Tok)); + if (CKind != OMPC_match) { + Diag(Tok.getLocation(), diag::err_omp_declare_variant_wrong_clause) + << getOpenMPClauseName(OMPC_match); + while (!SkipUntil(tok::annot_pragma_openmp_end, Parser::StopBeforeMatch)) + ; + // Skip the last annot_pragma_openmp_end. + (void)ConsumeAnnotationToken(); + return; + } + (void)ConsumeToken(); + // Parse '('. + BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end); + if (T.expectAndConsume(diag::err_expected_lparen_after, + getOpenMPClauseName(OMPC_match))) { + while (!SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch)) + ; + // Skip the last annot_pragma_openmp_end. + (void)ConsumeAnnotationToken(); + return; + } + + // Parse inner context selectors. + if (!parseOpenMPContextSelectors( + Loc, [this, &DeclVarData]( + SourceRange SR, + const Sema::OpenMPDeclareVariantCtsSelectorData &Data) { + if (DeclVarData.hasValue()) + Actions.ActOnOpenMPDeclareVariantDirective( + DeclVarData.getValue().first, DeclVarData.getValue().second, + SR, Data); + })) { + // Parse ')'. + (void)T.consumeClose(); + // Need to check for extra tokens. + if (Tok.isNot(tok::annot_pragma_openmp_end)) { + Diag(Tok, diag::warn_omp_extra_tokens_at_eol) + << getOpenMPDirectiveName(OMPD_declare_variant); + } + } + + // Skip last tokens. + while (Tok.isNot(tok::annot_pragma_openmp_end)) + ConsumeAnyToken(); + // Skip the last annot_pragma_openmp_end. + (void)ConsumeAnnotationToken(); +} + /// Parsing of simple OpenMP clauses like 'default' or 'proc_bind'. /// /// default-clause: @@ -1103,13 +1333,15 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl( } break; } + case OMPD_declare_variant: case OMPD_declare_simd: { // The syntax is: - // { #pragma omp declare simd } + // { #pragma omp declare {simd|variant} } // // - ConsumeToken(); CachedTokens Toks; + Toks.push_back(Tok); + ConsumeToken(); while(Tok.isNot(tok::annot_pragma_openmp_end)) { Toks.push_back(Tok); ConsumeAnyToken(); @@ -1133,10 +1365,16 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl( } } if (!Ptr) { - Diag(Loc, diag::err_omp_decl_in_declare_simd); + Diag(Loc, diag::err_omp_decl_in_declare_simd_variant) + << (DKind == OMPD_declare_simd ? 0 : 1); return DeclGroupPtrTy(); } - return ParseOMPDeclareSimdClauses(Ptr, Toks, Loc); + if (DKind == OMPD_declare_simd) + return ParseOMPDeclareSimdClauses(Ptr, Toks, Loc); + assert(DKind == OMPD_declare_variant && + "Expected declare variant directive only"); + ParseOMPDeclareVariantClauses(Ptr, Toks, Loc); + return Ptr; } case OMPD_declare_target: { SourceLocation DTLoc = ConsumeAnyToken(); @@ -1572,6 +1810,7 @@ Parser::ParseOpenMPDeclarativeOrExecutableDirective(ParsedStmtContext StmtCtx) { case OMPD_declare_target: case OMPD_end_declare_target: case OMPD_requires: + case OMPD_declare_variant: Diag(Tok, diag::err_omp_unexpected_directive) << 1 << getOpenMPDirectiveName(DKind); SkipUntil(tok::annot_pragma_openmp_end); @@ -1818,6 +2057,7 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, break; case OMPC_threadprivate: case OMPC_uniform: + case OMPC_match: if (!WrongDirective) Diag(Tok, diag::err_omp_unexpected_clause) << getOpenMPClauseName(CKind) << getOpenMPDirectiveName(DKind); @@ -1831,14 +2071,15 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, /// constructs. /// \param RLoc Returned location of right paren. ExprResult Parser::ParseOpenMPParensExpr(StringRef ClauseName, - SourceLocation &RLoc) { + SourceLocation &RLoc, + bool IsAddressOfOperand) { BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end); if (T.expectAndConsume(diag::err_expected_lparen_after, ClauseName.data())) return ExprError(); SourceLocation ELoc = Tok.getLocation(); ExprResult LHS(ParseCastExpression( - /*isUnaryExpression=*/false, /*isAddressOfOperand=*/false, NotTypeCast)); + /*isUnaryExpression=*/false, IsAddressOfOperand, NotTypeCast)); ExprResult Val(ParseRHSOfBinaryExpression(LHS, prec::Conditional)); Val = Actions.ActOnFinishFullExpr(Val.get(), ELoc, /*DiscardedValue*/ false); diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index 9bb5b6eac37e2..928bc5aa25b35 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -630,11 +630,11 @@ NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) { } // Grab the template parameter name (if given) - SourceLocation NameLoc; + SourceLocation NameLoc = Tok.getLocation(); IdentifierInfo *ParamName = nullptr; if (Tok.is(tok::identifier)) { ParamName = Tok.getIdentifierInfo(); - NameLoc = ConsumeToken(); + ConsumeToken(); } else if (Tok.isOneOf(tok::equal, tok::comma, tok::greater, tok::greatergreater)) { // Unnamed template parameter. Don't have to do anything here, just @@ -727,11 +727,11 @@ Parser::ParseTemplateTemplateParameter(unsigned Depth, unsigned Position) { : diag::ext_variadic_templates); // Get the identifier, if given. - SourceLocation NameLoc; + SourceLocation NameLoc = Tok.getLocation(); IdentifierInfo *ParamName = nullptr; if (Tok.is(tok::identifier)) { ParamName = Tok.getIdentifierInfo(); - NameLoc = ConsumeToken(); + ConsumeToken(); } else if (Tok.isOneOf(tok::equal, tok::comma, tok::greater, tok::greatergreater)) { // Unnamed template parameter. Don't have to do anything here, just diff --git a/clang/lib/Sema/OpenCLBuiltins.td b/clang/lib/Sema/OpenCLBuiltins.td index 5da67e339da46..2986140594675 100644 --- a/clang/lib/Sema/OpenCLBuiltins.td +++ b/clang/lib/Sema/OpenCLBuiltins.td @@ -20,12 +20,13 @@ //===----------------------------------------------------------------------===// // Versions of OpenCL class Version { - int Version = _Version; + int ID = _Version; } -def CL10: Version<100>; -def CL11: Version<110>; -def CL12: Version<120>; -def CL20: Version<200>; +def CLAll : Version< 0>; +def CL10 : Version<100>; +def CL11 : Version<110>; +def CL12 : Version<120>; +def CL20 : Version<200>; // Address spaces // Pointer types need to be assigned an address space. @@ -61,10 +62,8 @@ class IntList _List> { // OpenCL C classes for types //===----------------------------------------------------------------------===// // OpenCL C basic data types (int, float, image2d_t, ...). -// Its Child classes can represent concrete types (e.g.: VectorType) or -// custom types (e.g.: GenType). -// Instances of these child classes should be used in Builtin function -// arguments. See the definition of the "read_imagef" function as example. +// Its child classes can represent concrete types (e.g. VectorType) or +// abstract types (e.g. GenType). class Type { // Name of the Type. string Name = _Name; @@ -150,12 +149,8 @@ class TypeList _Type> { // A GenericType is an abstract type that defines a set of types as a // combination of Types and vector sizes. // -// E.g.: If TypeList = and VectorList = <1, 2, 4>, then it -// represents . -// _Ty : Name of the GenType. -// _TypeList : List of basic data Types. -// _VectorList : Sizes of the vector for each type of the _TypeList, 1 being a -// scalar. +// For example, if TypeList = and VectorList = <1, 2, 4>, then it +// represents . // // Some rules apply when using multiple GenericType arguments in a declaration: // 1. The number of vector sizes must be equal or 1 for all gentypes in a @@ -197,8 +192,13 @@ class Builtin _Signature> { list Signature = _Signature; // OpenCL Extension to which the function belongs (cl_khr_subgroups, ...) string Extension = ""; - // OpenCL Version to which the function belongs (CL10, ...) - Version Version = CL10; + // Version of OpenCL from which the function is available (e.g.: CL10). + // MinVersion is inclusive. + Version MinVersion = CL10; + // Version of OpenCL from which the function is not supported anymore. + // MaxVersion is exclusive. + // CLAll makes the function available for all versions. + Version MaxVersion = CLAll; } //===----------------------------------------------------------------------===// @@ -318,6 +318,137 @@ foreach RType = [Float, Double, Half, Char, UChar, Short, } } +//-------------------------------------------------------------------- +// OpenCL v1.1 s6.11.1, v1.2 s6.12.1, v2.0 s6.13.1 - Work-item Functions +// --- Table 7 --- +def : Builtin<"get_work_dim", [UInt]>; +foreach name = ["get_global_size", "get_global_id", "get_local_size", + "get_local_id", "get_num_groups", "get_group_id", + "get_global_offset"] in { + def : Builtin; +} + +let MinVersion = CL20 in { + def : Builtin<"get_enqueued_local_size", [Size, UInt]>; + foreach name = ["get_global_linear_id", "get_local_linear_id"] in { + def : Builtin; + } +} + +//-------------------------------------------------------------------- +// OpenCL v1.1 s6.11.7, v1.2 s6.12.7, v2.0 s6.13.7 - Vector Data Load and Store Functions +// OpenCL Extension v1.1 s9.3.6 and s9.6.6, v1.2 s9.5.6, v2.0 s9.4.6, v2.0 s5.1.6 and 6.1.6 - Vector Data Load and Store Functions +// --- Table 15 --- +// Variants for OpenCL versions below 2.0, using pointers to the global, local +// and private address spaces. +let MaxVersion = CL20 in { + foreach AS = [GlobalAS, LocalAS, PrivateAS] in { + foreach VSize = [2, 3, 4, 8, 16] in { + foreach name = ["vload" # VSize] in { + def : Builtin, Size, PointerType, AS>]>; + def : Builtin, Size, PointerType, AS>]>; + def : Builtin, Size, PointerType, AS>]>; + def : Builtin, Size, PointerType, AS>]>; + def : Builtin, Size, PointerType, AS>]>; + def : Builtin, Size, PointerType, AS>]>; + def : Builtin, Size, PointerType, AS>]>; + def : Builtin, Size, PointerType, AS>]>; + def : Builtin, Size, PointerType, AS>]>; + def : Builtin, Size, PointerType, AS>]>; + def : Builtin, Size, PointerType, AS>]>; + } + foreach name = ["vstore" # VSize] in { + def : Builtin, Size, PointerType, AS>]>; + def : Builtin, Size, PointerType, AS>]>; + def : Builtin, Size, PointerType, AS>]>; + def : Builtin, Size, PointerType, AS>]>; + def : Builtin, Size, PointerType, AS>]>; + def : Builtin, Size, PointerType, AS>]>; + def : Builtin, Size, PointerType, AS>]>; + def : Builtin, Size, PointerType, AS>]>; + def : Builtin, Size, PointerType, AS>]>; + def : Builtin, Size, PointerType, AS>]>; + def : Builtin, Size, PointerType, AS>]>; + } + foreach name = ["vloada_half" # VSize] in { + def : Builtin, Size, PointerType, AS>]>; + } + foreach rnd = ["", "_rte", "_rtz", "_rtp", "_rtn"] in { + foreach name = ["vstorea_half" # VSize # rnd] in { + def : Builtin, Size, PointerType]>; + def : Builtin, Size, PointerType]>; + } + } + } + } +} +// Variants for OpenCL versions above 2.0, using pointers to the generic +// address space. +let MinVersion = CL20 in { + foreach VSize = [2, 3, 4, 8, 16] in { + foreach name = ["vload" # VSize] in { + def : Builtin, Size, PointerType, GenericAS>]>; + def : Builtin, Size, PointerType, GenericAS>]>; + def : Builtin, Size, PointerType, GenericAS>]>; + def : Builtin, Size, PointerType, GenericAS>]>; + def : Builtin, Size, PointerType, GenericAS>]>; + def : Builtin, Size, PointerType, GenericAS>]>; + def : Builtin, Size, PointerType, GenericAS>]>; + def : Builtin, Size, PointerType, GenericAS>]>; + def : Builtin, Size, PointerType, GenericAS>]>; + def : Builtin, Size, PointerType, GenericAS>]>; + def : Builtin, Size, PointerType, GenericAS>]>; + } + foreach name = ["vstore" # VSize] in { + def : Builtin, Size, PointerType, GenericAS>]>; + def : Builtin, Size, PointerType, GenericAS>]>; + def : Builtin, Size, PointerType, GenericAS>]>; + def : Builtin, Size, PointerType, GenericAS>]>; + def : Builtin, Size, PointerType, GenericAS>]>; + def : Builtin, Size, PointerType, GenericAS>]>; + def : Builtin, Size, PointerType, GenericAS>]>; + def : Builtin, Size, PointerType, GenericAS>]>; + def : Builtin, Size, PointerType, GenericAS>]>; + def : Builtin, Size, PointerType, GenericAS>]>; + def : Builtin, Size, PointerType, GenericAS>]>; + } + foreach name = ["vloada_half" # VSize] in { + def : Builtin, Size, PointerType, GenericAS>]>; + } + foreach rnd = ["", "_rte", "_rtz", "_rtp", "_rtn"] in { + foreach name = ["vstorea_half" # VSize # rnd] in { + def : Builtin, Size, PointerType]>; + def : Builtin, Size, PointerType]>; + } + } + } +} +// Variants using pointers to the constant address space. +foreach VSize = [2, 3, 4, 8, 16] in { + foreach name = ["vload" # VSize] in { + def : Builtin, Size, PointerType, ConstantAS>]>; + def : Builtin, Size, PointerType, ConstantAS>]>; + def : Builtin, Size, PointerType, ConstantAS>]>; + def : Builtin, Size, PointerType, ConstantAS>]>; + def : Builtin, Size, PointerType, ConstantAS>]>; + def : Builtin, Size, PointerType, ConstantAS>]>; + def : Builtin, Size, PointerType, ConstantAS>]>; + def : Builtin, Size, PointerType, ConstantAS>]>; + def : Builtin, Size, PointerType, ConstantAS>]>; + def : Builtin, Size, PointerType, ConstantAS>]>; + def : Builtin, Size, PointerType, ConstantAS>]>; + } + foreach name = ["vloada_half" # VSize] in { + def : Builtin, Size, PointerType, ConstantAS>]>; + } + foreach rnd = ["", "_rte", "_rtz", "_rtp", "_rtn"] in { + foreach name = ["vstorea_half" # VSize # rnd] in { + def : Builtin, Size, PointerType]>; + def : Builtin, Size, PointerType]>; + } + } +} + //-------------------------------------------------------------------- // OpenCL v1.1 s6.11.10, v1.2 s6.12.10, v2.0 s6.13.10: Async Copies from Global to Local Memory, Local to Global Memory, and Prefetch // OpenCL Extension v2.0 s5.1.7 and s6.1.7: Async Copies from Global to Local Memory, Local to Global Memory, and Prefetch @@ -356,14 +487,6 @@ foreach Type = [Int, UInt] in { } } -// OpenCL v1.2 s6.12.1: Work-Item Functions -def get_work_dim : Builtin<"get_work_dim", [UInt]>; -foreach name = ["get_global_size", "get_global_id", "get_local_size", - "get_local_id", "get_num_groups", "get_group_id", - "get_global_offset"] in { - def : Builtin; -} - // OpenCL v1.2 s6.12.2: Math Functions foreach name = ["acos", "acosh", "acospi", "asin", "asinh", "asinpi", @@ -470,6 +593,31 @@ foreach aQual = ["WO", "RW"] in { def : Builtin<"write_imagef", [Void, ImageType, VectorType, Float]>; } +// --- Table 25: Image Query Functions --- +foreach aQual = ["RO", "WO", "RW"] in { + foreach imgTy = [Image1d, Image1dBuffer, Image2d, Image3d, + Image1dArray, Image2dArray, Image2dDepth, + Image2dArrayDepth] in { + foreach name = ["get_image_width", "get_image_channel_data_type", + "get_image_channel_order"] in { + def : Builtin]>; + } + } + foreach imgTy = [Image2d, Image3d, Image2dArray, Image2dDepth, + Image2dArrayDepth] in { + def : Builtin<"get_image_height", [Int, ImageType]>; + } + def : Builtin<"get_image_depth", [Int, ImageType]>; + foreach imgTy = [Image2d, Image2dArray, Image2dDepth, + Image2dArrayDepth] in { + def : Builtin<"get_image_dim", [VectorType, ImageType]>; + } + def : Builtin<"get_image_dim", [VectorType, ImageType]>; + foreach imgTy = [Image1dArray, Image2dArray, Image2dArrayDepth] in { + def : Builtin<"get_image_array_size", [Size, ImageType]>; + } +} + // OpenCL extension v2.0 s5.1.9: Built-in Image Read Functions // --- Table 8 --- foreach aQual = ["RO"] in { @@ -517,7 +665,7 @@ foreach aQual = ["WO", "RW"] in { // OpenCL v2.0 s9.17.3: Additions to section 6.13.1: Work-Item Functions -let Version = CL20 in { +let MinVersion = CL20 in { let Extension = "cl_khr_subgroups" in { def get_sub_group_size : Builtin<"get_sub_group_size", [UInt]>; def get_max_sub_group_size : Builtin<"get_max_sub_group_size", [UInt]>; diff --git a/clang/lib/Sema/SemaCUDA.cpp b/clang/lib/Sema/SemaCUDA.cpp index 203c09c57112b..3c9c991c77feb 100644 --- a/clang/lib/Sema/SemaCUDA.cpp +++ b/clang/lib/Sema/SemaCUDA.cpp @@ -267,6 +267,18 @@ bool Sema::inferCUDATargetForImplicitSpecialMember(CXXRecordDecl *ClassDecl, CXXMethodDecl *MemberDecl, bool ConstRHS, bool Diagnose) { + // If the defaulted special member is defined lexically outside of its + // owning class, or the special member already has explicit device or host + // attributes, do not infer. + bool InClass = MemberDecl->getLexicalParent() == MemberDecl->getParent(); + bool HasH = MemberDecl->hasAttr(); + bool HasD = MemberDecl->hasAttr(); + bool HasExplicitAttr = + (HasD && !MemberDecl->getAttr()->isImplicit()) || + (HasH && !MemberDecl->getAttr()->isImplicit()); + if (!InClass || HasExplicitAttr) + return false; + llvm::Optional InferredTarget; // We're going to invoke special member lookup; mark that these special @@ -371,21 +383,23 @@ bool Sema::inferCUDATargetForImplicitSpecialMember(CXXRecordDecl *ClassDecl, } } + + // If no target was inferred, mark this member as __host__ __device__; + // it's the least restrictive option that can be invoked from any target. + bool NeedsH = true, NeedsD = true; if (InferredTarget.hasValue()) { - if (InferredTarget.getValue() == CFT_Device) { - MemberDecl->addAttr(CUDADeviceAttr::CreateImplicit(Context)); - } else if (InferredTarget.getValue() == CFT_Host) { - MemberDecl->addAttr(CUDAHostAttr::CreateImplicit(Context)); - } else { - MemberDecl->addAttr(CUDADeviceAttr::CreateImplicit(Context)); - MemberDecl->addAttr(CUDAHostAttr::CreateImplicit(Context)); - } - } else { - // If no target was inferred, mark this member as __host__ __device__; - // it's the least restrictive option that can be invoked from any target. + if (InferredTarget.getValue() == CFT_Device) + NeedsH = false; + else if (InferredTarget.getValue() == CFT_Host) + NeedsD = false; + } + + // We either setting attributes first time, or the inferred ones must match + // previously set ones. + if (NeedsD && !HasD) MemberDecl->addAttr(CUDADeviceAttr::CreateImplicit(Context)); + if (NeedsH && !HasH) MemberDecl->addAttr(CUDAHostAttr::CreateImplicit(Context)); - } return false; } @@ -806,7 +820,8 @@ void Sema::inheritCUDATargetAttrs(FunctionDecl *FD, std::string Sema::getCudaConfigureFuncName() const { if (getLangOpts().HIP) - return "hipConfigureCall"; + return getLangOpts().HIPUseNewLaunchAPI ? "__hipPushCallConfiguration" + : "hipConfigureCall"; // New CUDA kernel launch sequence. if (CudaFeatureEnabled(Context.getTargetInfo().getSDKVersion(), diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index f3b617d3520ba..cbf04f7313c27 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -191,7 +191,7 @@ static bool SemaBuiltinAddressof(Sema &S, CallExpr *TheCall) { return false; } -/// Check the number of arguments, and set the result type to +/// Check the number of arguments and set the result type to /// the argument type. static bool SemaBuiltinPreserveAI(Sema &S, CallExpr *TheCall) { if (checkArgCount(S, TheCall, 1)) @@ -4535,7 +4535,16 @@ ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, AtomicExpr::AtomicOp Op) { CallExpr *TheCall = cast(TheCallResult.get()); DeclRefExpr *DRE =cast(TheCall->getCallee()->IgnoreParenCasts()); + MultiExprArg Args{TheCall->getArgs(), TheCall->getNumArgs()}; + return BuildAtomicExpr({TheCall->getBeginLoc(), TheCall->getEndLoc()}, + DRE->getSourceRange(), TheCall->getRParenLoc(), Args, + Op); +} +ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange, + SourceLocation RParenLoc, MultiExprArg Args, + AtomicExpr::AtomicOp Op, + AtomicArgumentOrder ArgOrder) { // All the non-OpenCL operations take one of the following forms. // The OpenCL operations take the __c11 forms with one extra argument for // synchronization scope. @@ -4682,21 +4691,21 @@ ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, if (IsOpenCL && Op != AtomicExpr::AO__opencl_atomic_init) ++AdjustedNumArgs; // Check we have the right number of arguments. - if (TheCall->getNumArgs() < AdjustedNumArgs) { - Diag(TheCall->getEndLoc(), diag::err_typecheck_call_too_few_args) - << 0 << AdjustedNumArgs << TheCall->getNumArgs() - << TheCall->getCallee()->getSourceRange(); + if (Args.size() < AdjustedNumArgs) { + Diag(CallRange.getEnd(), diag::err_typecheck_call_too_few_args) + << 0 << AdjustedNumArgs << static_cast(Args.size()) + << ExprRange; return ExprError(); - } else if (TheCall->getNumArgs() > AdjustedNumArgs) { - Diag(TheCall->getArg(AdjustedNumArgs)->getBeginLoc(), + } else if (Args.size() > AdjustedNumArgs) { + Diag(Args[AdjustedNumArgs]->getBeginLoc(), diag::err_typecheck_call_too_many_args) - << 0 << AdjustedNumArgs << TheCall->getNumArgs() - << TheCall->getCallee()->getSourceRange(); + << 0 << AdjustedNumArgs << static_cast(Args.size()) + << ExprRange; return ExprError(); } // Inspect the first argument of the atomic operation. - Expr *Ptr = TheCall->getArg(0); + Expr *Ptr = Args[0]; ExprResult ConvertedPtr = DefaultFunctionArrayLvalueConversion(Ptr); if (ConvertedPtr.isInvalid()) return ExprError(); @@ -4704,7 +4713,7 @@ ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, Ptr = ConvertedPtr.get(); const PointerType *pointerType = Ptr->getType()->getAs(); if (!pointerType) { - Diag(DRE->getBeginLoc(), diag::err_atomic_builtin_must_be_pointer) + Diag(ExprRange.getBegin(), diag::err_atomic_builtin_must_be_pointer) << Ptr->getType() << Ptr->getSourceRange(); return ExprError(); } @@ -4714,13 +4723,13 @@ ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, QualType ValType = AtomTy; // 'C' if (IsC11) { if (!AtomTy->isAtomicType()) { - Diag(DRE->getBeginLoc(), diag::err_atomic_op_needs_atomic) + Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_atomic) << Ptr->getType() << Ptr->getSourceRange(); return ExprError(); } if ((Form != Load && Form != LoadCopy && AtomTy.isConstQualified()) || AtomTy.getAddressSpace() == LangAS::opencl_constant) { - Diag(DRE->getBeginLoc(), diag::err_atomic_op_needs_non_const_atomic) + Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_non_const_atomic) << (AtomTy.isConstQualified() ? 0 : 1) << Ptr->getType() << Ptr->getSourceRange(); return ExprError(); @@ -4728,7 +4737,7 @@ ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, ValType = AtomTy->getAs()->getValueType(); } else if (Form != Load && Form != LoadCopy) { if (ValType.isConstQualified()) { - Diag(DRE->getBeginLoc(), diag::err_atomic_op_needs_non_const_pointer) + Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_non_const_pointer) << Ptr->getType() << Ptr->getSourceRange(); return ExprError(); } @@ -4739,7 +4748,7 @@ ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, // gcc does not enforce these rules for GNU atomics, but we do so for sanity. if (IsAddSub && !ValType->isIntegerType() && !ValType->isPointerType()) { - Diag(DRE->getBeginLoc(), diag::err_atomic_op_needs_atomic_int_or_ptr) + Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_atomic_int_or_ptr) << IsC11 << Ptr->getType() << Ptr->getSourceRange(); return ExprError(); } @@ -4747,12 +4756,12 @@ ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, const BuiltinType *BT = ValType->getAs(); if (!BT || (BT->getKind() != BuiltinType::Int && BT->getKind() != BuiltinType::UInt)) { - Diag(DRE->getBeginLoc(), diag::err_atomic_op_needs_int32_or_ptr); + Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_int32_or_ptr); return ExprError(); } } if (!IsAddSub && !IsMinMax && !ValType->isIntegerType()) { - Diag(DRE->getBeginLoc(), diag::err_atomic_op_bitwise_needs_atomic_int) + Diag(ExprRange.getBegin(), diag::err_atomic_op_bitwise_needs_atomic_int) << IsC11 << Ptr->getType() << Ptr->getSourceRange(); return ExprError(); } @@ -4764,7 +4773,7 @@ ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, } else if (IsN && !ValType->isIntegerType() && !ValType->isPointerType()) { // For __atomic_*_n operations, the value type must be a scalar integral or // pointer type which is 1, 2, 4, 8 or 16 bytes in length. - Diag(DRE->getBeginLoc(), diag::err_atomic_op_needs_atomic_int_or_ptr) + Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_atomic_int_or_ptr) << IsC11 << Ptr->getType() << Ptr->getSourceRange(); return ExprError(); } @@ -4773,7 +4782,7 @@ ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, !AtomTy->isScalarType()) { // For GNU atomics, require a trivially-copyable type. This is not part of // the GNU atomics specification, but we enforce it for sanity. - Diag(DRE->getBeginLoc(), diag::err_atomic_op_needs_trivial_copy) + Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_trivial_copy) << Ptr->getType() << Ptr->getSourceRange(); return ExprError(); } @@ -4789,7 +4798,7 @@ ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, case Qualifiers::OCL_Autoreleasing: // FIXME: Can this happen? By this point, ValType should be known // to be trivially copyable. - Diag(DRE->getBeginLoc(), diag::err_arc_atomic_ownership) + Diag(ExprRange.getBegin(), diag::err_arc_atomic_ownership) << ValType << Ptr->getSourceRange(); return ExprError(); } @@ -4816,19 +4825,56 @@ ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, IsPassedByAddress = true; } + SmallVector APIOrderedArgs; + if (ArgOrder == Sema::AtomicArgumentOrder::AST) { + APIOrderedArgs.push_back(Args[0]); + switch (Form) { + case Init: + case Load: + APIOrderedArgs.push_back(Args[1]); // Val1/Order + break; + case LoadCopy: + case Copy: + case Arithmetic: + case Xchg: + APIOrderedArgs.push_back(Args[2]); // Val1 + APIOrderedArgs.push_back(Args[1]); // Order + break; + case GNUXchg: + APIOrderedArgs.push_back(Args[2]); // Val1 + APIOrderedArgs.push_back(Args[3]); // Val2 + APIOrderedArgs.push_back(Args[1]); // Order + break; + case C11CmpXchg: + APIOrderedArgs.push_back(Args[2]); // Val1 + APIOrderedArgs.push_back(Args[4]); // Val2 + APIOrderedArgs.push_back(Args[1]); // Order + APIOrderedArgs.push_back(Args[3]); // OrderFail + break; + case GNUCmpXchg: + APIOrderedArgs.push_back(Args[2]); // Val1 + APIOrderedArgs.push_back(Args[4]); // Val2 + APIOrderedArgs.push_back(Args[5]); // Weak + APIOrderedArgs.push_back(Args[1]); // Order + APIOrderedArgs.push_back(Args[3]); // OrderFail + break; + } + } else + APIOrderedArgs.append(Args.begin(), Args.end()); + // The first argument's non-CV pointer type is used to deduce the type of // subsequent arguments, except for: // - weak flag (always converted to bool) // - memory order (always converted to int) // - scope (always converted to int) - for (unsigned i = 0; i != TheCall->getNumArgs(); ++i) { + for (unsigned i = 0; i != APIOrderedArgs.size(); ++i) { QualType Ty; if (i < NumVals[Form] + 1) { switch (i) { case 0: // The first argument is always a pointer. It has a fixed type. // It is always dereferenced, a nullptr is undefined. - CheckNonNullArgument(*this, TheCall->getArg(i), DRE->getBeginLoc()); + CheckNonNullArgument(*this, APIOrderedArgs[i], ExprRange.getBegin()); // Nothing else to do: we already know all we want about this pointer. continue; case 1: @@ -4840,16 +4886,18 @@ ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, if (Form == Init || (Form == Arithmetic && ValType->isIntegerType())) Ty = ValType; else if (Form == Copy || Form == Xchg) { - if (IsPassedByAddress) + if (IsPassedByAddress) { // The value pointer is always dereferenced, a nullptr is undefined. - CheckNonNullArgument(*this, TheCall->getArg(i), DRE->getBeginLoc()); + CheckNonNullArgument(*this, APIOrderedArgs[i], + ExprRange.getBegin()); + } Ty = ByValType; } else if (Form == Arithmetic) Ty = Context.getPointerDiffType(); else { - Expr *ValArg = TheCall->getArg(i); + Expr *ValArg = APIOrderedArgs[i]; // The value pointer is always dereferenced, a nullptr is undefined. - CheckNonNullArgument(*this, ValArg, DRE->getBeginLoc()); + CheckNonNullArgument(*this, ValArg, ExprRange.getBegin()); LangAS AS = LangAS::Default; // Keep address space of non-atomic pointer type. if (const PointerType *PtrTy = @@ -4864,7 +4912,7 @@ ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, // The third argument to compare_exchange / GNU exchange is the desired // value, either by-value (for the C11 and *_n variant) or as a pointer. if (IsPassedByAddress) - CheckNonNullArgument(*this, TheCall->getArg(i), DRE->getBeginLoc()); + CheckNonNullArgument(*this, APIOrderedArgs[i], ExprRange.getBegin()); Ty = ByValType; break; case 3: @@ -4879,11 +4927,11 @@ ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, InitializedEntity Entity = InitializedEntity::InitializeParameter(Context, Ty, false); - ExprResult Arg = TheCall->getArg(i); + ExprResult Arg = APIOrderedArgs[i]; Arg = PerformCopyInitialization(Entity, SourceLocation(), Arg); if (Arg.isInvalid()) return true; - TheCall->setArg(i, Arg.get()); + APIOrderedArgs[i] = Arg.get(); } // Permute the arguments into a 'consistent' order. @@ -4892,36 +4940,36 @@ ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, switch (Form) { case Init: // Note, AtomicExpr::getVal1() has a special case for this atomic. - SubExprs.push_back(TheCall->getArg(1)); // Val1 + SubExprs.push_back(APIOrderedArgs[1]); // Val1 break; case Load: - SubExprs.push_back(TheCall->getArg(1)); // Order + SubExprs.push_back(APIOrderedArgs[1]); // Order break; case LoadCopy: case Copy: case Arithmetic: case Xchg: - SubExprs.push_back(TheCall->getArg(2)); // Order - SubExprs.push_back(TheCall->getArg(1)); // Val1 + SubExprs.push_back(APIOrderedArgs[2]); // Order + SubExprs.push_back(APIOrderedArgs[1]); // Val1 break; case GNUXchg: // Note, AtomicExpr::getVal2() has a special case for this atomic. - SubExprs.push_back(TheCall->getArg(3)); // Order - SubExprs.push_back(TheCall->getArg(1)); // Val1 - SubExprs.push_back(TheCall->getArg(2)); // Val2 + SubExprs.push_back(APIOrderedArgs[3]); // Order + SubExprs.push_back(APIOrderedArgs[1]); // Val1 + SubExprs.push_back(APIOrderedArgs[2]); // Val2 break; case C11CmpXchg: - SubExprs.push_back(TheCall->getArg(3)); // Order - SubExprs.push_back(TheCall->getArg(1)); // Val1 - SubExprs.push_back(TheCall->getArg(4)); // OrderFail - SubExprs.push_back(TheCall->getArg(2)); // Val2 + SubExprs.push_back(APIOrderedArgs[3]); // Order + SubExprs.push_back(APIOrderedArgs[1]); // Val1 + SubExprs.push_back(APIOrderedArgs[4]); // OrderFail + SubExprs.push_back(APIOrderedArgs[2]); // Val2 break; case GNUCmpXchg: - SubExprs.push_back(TheCall->getArg(4)); // Order - SubExprs.push_back(TheCall->getArg(1)); // Val1 - SubExprs.push_back(TheCall->getArg(5)); // OrderFail - SubExprs.push_back(TheCall->getArg(2)); // Val2 - SubExprs.push_back(TheCall->getArg(3)); // Weak + SubExprs.push_back(APIOrderedArgs[4]); // Order + SubExprs.push_back(APIOrderedArgs[1]); // Val1 + SubExprs.push_back(APIOrderedArgs[5]); // OrderFail + SubExprs.push_back(APIOrderedArgs[2]); // Val2 + SubExprs.push_back(APIOrderedArgs[3]); // Weak break; } @@ -4935,7 +4983,7 @@ ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, } if (auto ScopeModel = AtomicExpr::getScopeModel(Op)) { - auto *Scope = TheCall->getArg(TheCall->getNumArgs() - 1); + auto *Scope = Args[Args.size() - 1]; llvm::APSInt Result(32); if (Scope->isIntegerConstantExpr(Result, Context) && !ScopeModel->isValid(Result.getZExtValue())) { @@ -4945,9 +4993,8 @@ ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, SubExprs.push_back(Scope); } - AtomicExpr *AE = - new (Context) AtomicExpr(TheCall->getCallee()->getBeginLoc(), SubExprs, - ResultType, Op, TheCall->getRParenLoc()); + AtomicExpr *AE = new (Context) + AtomicExpr(ExprRange.getBegin(), SubExprs, ResultType, Op, RParenLoc); if ((Op == AtomicExpr::AO__c11_atomic_load || Op == AtomicExpr::AO__c11_atomic_store || @@ -5866,7 +5913,7 @@ ExprResult Sema::SemaBuiltinShuffleVector(CallExpr *TheCall) { << SourceRange(TheCall->getArg(0)->getBeginLoc(), TheCall->getArg(1)->getEndLoc())); - numElements = LHSType->getAs()->getNumElements(); + numElements = LHSType->castAs()->getNumElements(); unsigned numResElements = TheCall->getNumArgs() - 2; // Check to see if we have a call with 2 vector arguments, the unary shuffle @@ -5874,7 +5921,7 @@ ExprResult Sema::SemaBuiltinShuffleVector(CallExpr *TheCall) { // same number of elts as lhs. if (TheCall->getNumArgs() == 2) { if (!RHSType->hasIntegerRepresentation() || - RHSType->getAs()->getNumElements() != numElements) + RHSType->castAs()->getNumElements() != numElements) return ExprError(Diag(TheCall->getBeginLoc(), diag::err_vec_builtin_incompatible_vector) << TheCall->getDirectCallee() @@ -5887,7 +5934,7 @@ ExprResult Sema::SemaBuiltinShuffleVector(CallExpr *TheCall) { << SourceRange(TheCall->getArg(0)->getBeginLoc(), TheCall->getArg(1)->getEndLoc())); } else if (numElements != numResElements) { - QualType eltType = LHSType->getAs()->getElementType(); + QualType eltType = LHSType->castAs()->getElementType(); resType = Context.getVectorType(eltType, numResElements, VectorType::GenericVector); } @@ -5944,8 +5991,8 @@ ExprResult Sema::SemaConvertVectorExpr(Expr *E, TypeSourceInfo *TInfo, diag::err_convertvector_non_vector_type)); if (!SrcTy->isDependentType() && !DstTy->isDependentType()) { - unsigned SrcElts = SrcTy->getAs()->getNumElements(); - unsigned DstElts = DstTy->getAs()->getNumElements(); + unsigned SrcElts = SrcTy->castAs()->getNumElements(); + unsigned DstElts = DstTy->castAs()->getNumElements(); if (SrcElts != DstElts) return ExprError(Diag(BuiltinLoc, diag::err_convertvector_incompatible_vector) @@ -8179,6 +8226,22 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, ExprTy = TET->getUnderlyingExpr()->getType(); } + // Diagnose attempts to print a boolean value as a character. Unlike other + // -Wformat diagnostics, this is fine from a type perspective, but it still + // doesn't make sense. + if (FS.getConversionSpecifier().getKind() == ConversionSpecifier::cArg && + E->isKnownToHaveBooleanValue()) { + const CharSourceRange &CSR = + getSpecifierRange(StartSpecifier, SpecifierLen); + SmallString<4> FSString; + llvm::raw_svector_ostream os(FSString); + FS.toString(os); + EmitFormatDiagnostic(S.PDiag(diag::warn_format_bool_as_character) + << FSString, + E->getExprLoc(), false, CSR); + return true; + } + const analyze_printf::ArgType::MatchKind Match = AT.matchesType(S.Context, ExprTy); bool Pedantic = Match == analyze_printf::ArgType::NoMatchPedantic; @@ -10910,6 +10973,26 @@ static void DiagnoseImpCast(Sema &S, Expr *E, QualType T, DiagnoseImpCast(S, E, E->getType(), T, CContext, diag, pruneControlFlow); } +static bool isObjCSignedCharBool(Sema &S, QualType Ty) { + return Ty->isSpecificBuiltinType(BuiltinType::SChar) && + S.getLangOpts().ObjC && S.NSAPIObj->isObjCBOOLType(Ty); +} + +static void adornObjCBoolConversionDiagWithTernaryFixit( + Sema &S, Expr *SourceExpr, const Sema::SemaDiagnosticBuilder &Builder) { + Expr *Ignored = SourceExpr->IgnoreImplicit(); + if (const auto *OVE = dyn_cast(Ignored)) + Ignored = OVE->getSourceExpr(); + bool NeedsParens = isa(Ignored) || + isa(Ignored) || + isa(Ignored); + SourceLocation EndLoc = S.getLocForEndOfToken(SourceExpr->getEndLoc()); + if (NeedsParens) + Builder << FixItHint::CreateInsertion(SourceExpr->getBeginLoc(), "(") + << FixItHint::CreateInsertion(EndLoc, ")"); + Builder << FixItHint::CreateInsertion(EndLoc, " ? YES : NO"); +} + /// Diagnose an implicit cast from a floating point value to an integer value. static void DiagnoseFloatingImpCast(Sema &S, Expr *E, QualType T, SourceLocation CContext) { @@ -10929,6 +11012,13 @@ static void DiagnoseFloatingImpCast(Sema &S, Expr *E, QualType T, bool IsConstant = E->EvaluateAsFloat(Value, S.Context, Expr::SE_AllowSideEffects); if (!IsConstant) { + if (isObjCSignedCharBool(S, T)) { + return adornObjCBoolConversionDiagWithTernaryFixit( + S, E, + S.Diag(CContext, diag::warn_impcast_float_to_objc_signed_char_bool) + << E->getType()); + } + return DiagnoseImpCast(S, E, T, CContext, diag::warn_impcast_float_integer, PruneWarnings); } @@ -10940,6 +11030,23 @@ static void DiagnoseFloatingImpCast(Sema &S, Expr *E, QualType T, llvm::APFloat::opStatus Result = Value.convertToInteger( IntegerValue, llvm::APFloat::rmTowardZero, &isExact); + // FIXME: Force the precision of the source value down so we don't print + // digits which are usually useless (we don't really care here if we + // truncate a digit by accident in edge cases). Ideally, APFloat::toString + // would automatically print the shortest representation, but it's a bit + // tricky to implement. + SmallString<16> PrettySourceValue; + unsigned precision = llvm::APFloat::semanticsPrecision(Value.getSemantics()); + precision = (precision * 59 + 195) / 196; + Value.toString(PrettySourceValue, precision); + + if (isObjCSignedCharBool(S, T) && IntegerValue != 0 && IntegerValue != 1) { + return adornObjCBoolConversionDiagWithTernaryFixit( + S, E, + S.Diag(CContext, diag::warn_impcast_constant_value_to_objc_bool) + << PrettySourceValue); + } + if (Result == llvm::APFloat::opOK && isExact) { if (IsLiteral) return; return DiagnoseImpCast(S, E, T, CContext, diag::warn_impcast_float_integer, @@ -10983,16 +11090,6 @@ static void DiagnoseFloatingImpCast(Sema &S, Expr *E, QualType T, DiagID = diag::warn_impcast_float_to_integer; } - // FIXME: Force the precision of the source value down so we don't print - // digits which are usually useless (we don't really care here if we - // truncate a digit by accident in edge cases). Ideally, APFloat::toString - // would automatically print the shortest representation, but it's a bit - // tricky to implement. - SmallString<16> PrettySourceValue; - unsigned precision = llvm::APFloat::semanticsPrecision(Value.getSemantics()); - precision = (precision * 59 + 195) / 196; - Value.toString(PrettySourceValue, precision); - SmallString<16> PrettyTargetValue; if (IsBool) PrettyTargetValue = Value.isZero() ? "false" : "true"; @@ -11269,9 +11366,79 @@ static bool isSameWidthConstantConversion(Sema &S, Expr *E, QualType T, return true; } -static bool isObjCSignedCharBool(Sema &S, QualType Ty) { - return Ty->isSpecificBuiltinType(BuiltinType::SChar) && - S.getLangOpts().ObjC && S.NSAPIObj->isObjCBOOLType(Ty); +static const IntegerLiteral *getIntegerLiteral(Expr *E) { + const auto *IL = dyn_cast(E); + if (!IL) { + if (auto *UO = dyn_cast(E)) { + if (UO->getOpcode() == UO_Minus) + return dyn_cast(UO->getSubExpr()); + } + } + + return IL; +} + +static void CheckConditionalWithEnumTypes(Sema &S, SourceLocation Loc, + Expr *LHS, Expr *RHS) { + QualType LHSStrippedType = LHS->IgnoreParenImpCasts()->getType(); + QualType RHSStrippedType = RHS->IgnoreParenImpCasts()->getType(); + + const auto *LHSEnumType = LHSStrippedType->getAs(); + if (!LHSEnumType) + return; + const auto *RHSEnumType = RHSStrippedType->getAs(); + if (!RHSEnumType) + return; + + // Ignore anonymous enums. + if (!LHSEnumType->getDecl()->hasNameForLinkage()) + return; + if (!RHSEnumType->getDecl()->hasNameForLinkage()) + return; + + if (S.Context.hasSameUnqualifiedType(LHSStrippedType, RHSStrippedType)) + return; + + S.Diag(Loc, diag::warn_conditional_mixed_enum_types) + << LHSStrippedType << RHSStrippedType << LHS->getSourceRange() + << RHS->getSourceRange(); +} + +static void DiagnoseIntInBoolContext(Sema &S, Expr *E) { + E = E->IgnoreParenImpCasts(); + SourceLocation ExprLoc = E->getExprLoc(); + + if (const auto *BO = dyn_cast(E)) { + BinaryOperator::Opcode Opc = BO->getOpcode(); + Expr::EvalResult Result; + // Do not diagnose unsigned shifts. + if (Opc == BO_Shl) { + const auto *LHS = getIntegerLiteral(BO->getLHS()); + const auto *RHS = getIntegerLiteral(BO->getRHS()); + if (LHS && LHS->getValue() == 0) + S.Diag(ExprLoc, diag::warn_left_shift_always) << 0; + else if (!E->isValueDependent() && LHS && RHS && + RHS->getValue().isNonNegative() && + E->EvaluateAsInt(Result, S.Context, Expr::SE_AllowSideEffects)) + S.Diag(ExprLoc, diag::warn_left_shift_always) + << (Result.Val.getInt() != 0); + else if (E->getType()->isSignedIntegerType()) + S.Diag(ExprLoc, diag::warn_left_shift_in_bool_context) << E; + } + } + + if (const auto *CO = dyn_cast(E)) { + const auto *LHS = getIntegerLiteral(CO->getTrueExpr()); + const auto *RHS = getIntegerLiteral(CO->getFalseExpr()); + if (!LHS || !RHS) + return; + if ((LHS->getValue() == 0 || LHS->getValue() == 1) && + (RHS->getValue() == 0 || RHS->getValue() == 1)) + // Do not diagnose common idioms. + return; + if (LHS->getValue() != 0 && LHS->getValue() != 0) + S.Diag(ExprLoc, diag::warn_integer_constants_in_conditional_always_true); + } } static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, @@ -11324,19 +11491,13 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, if (isObjCSignedCharBool(S, T) && Source->isIntegralType(S.Context)) { Expr::EvalResult Result; if (E->EvaluateAsInt(Result, S.getASTContext(), - Expr::SE_AllowSideEffects) && - Result.Val.getInt() != 1 && Result.Val.getInt() != 0) { - auto Builder = S.Diag(CC, diag::warn_impcast_constant_int_to_objc_bool) - << Result.Val.getInt().toString(10); - Expr *Ignored = E->IgnoreImplicit(); - bool NeedsParens = isa(Ignored) || - isa(Ignored) || - isa(Ignored); - SourceLocation EndLoc = S.getLocForEndOfToken(E->getEndLoc()); - if (NeedsParens) - Builder << FixItHint::CreateInsertion(E->getBeginLoc(), "(") - << FixItHint::CreateInsertion(EndLoc, ")"); - Builder << FixItHint::CreateInsertion(EndLoc, " ? YES : NO"); + Expr::SE_AllowSideEffects)) { + if (Result.Val.getInt() != 1 && Result.Val.getInt() != 0) { + adornObjCBoolConversionDiagWithTernaryFixit( + S, E, + S.Diag(CC, diag::warn_impcast_constant_value_to_objc_bool) + << Result.Val.getInt().toString(10)); + } return; } } @@ -11571,6 +11732,9 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, S.DiscardMisalignedMemberAddress(Target, E); + if (Target->isBooleanType()) + DiagnoseIntInBoolContext(S, E); + if (!Source->isIntegerType() || !Target->isIntegerType()) return; @@ -11579,6 +11743,14 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, if (Target->isSpecificBuiltinType(BuiltinType::Bool)) return; + if (isObjCSignedCharBool(S, T) && !Source->isCharType() && + !E->isKnownToHaveBooleanValue()) { + return adornObjCBoolConversionDiagWithTernaryFixit( + S, E, + S.Diag(CC, diag::warn_impcast_int_to_objc_signed_char_bool) + << E->getType()); + } + IntRange SourceRange = GetExprRange(S.Context, E, S.isConstantEvaluated()); IntRange TargetRange = IntRange::forTargetOfCanonicalType(S.Context, Target); @@ -11723,6 +11895,11 @@ static void CheckConditionalOperator(Sema &S, ConditionalOperator *E, bool Suspicious = false; CheckConditionalOperand(S, E->getTrueExpr(), T, CC, Suspicious); CheckConditionalOperand(S, E->getFalseExpr(), T, CC, Suspicious); + CheckConditionalWithEnumTypes(S, E->getBeginLoc(), E->getTrueExpr(), + E->getFalseExpr()); + + if (T->isBooleanType()) + DiagnoseIntInBoolContext(S, E); // If -Wconversion would have warned about either of the candidates // for a signedness conversion to the context type... diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index b868fb3645f87..4d4966afb0166 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2766,7 +2766,7 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old, if (AsmLabelAttr *NewA = New->getAttr()) { if (AsmLabelAttr *OldA = Old->getAttr()) { - if (OldA->getLabel() != NewA->getLabel()) { + if (!OldA->isEquivalent(NewA)) { // This redeclaration changes __asm__ label. Diag(New->getLocation(), diag::err_different_asm_label); Diag(OldA->getLocation(), diag::note_previous_declaration); @@ -3562,7 +3562,12 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, } } - if (OldQTypeForComparison == NewQType) + // If the function types are compatible, merge the declarations. Ignore the + // exception specifier because it was already checked above in + // CheckEquivalentExceptionSpec, and we don't want follow-on diagnostics + // about incompatible types under -fms-compatibility. + if (Context.hasSameFunctionTypeIgnoringExceptionSpec(OldQTypeForComparison, + NewQType)) return MergeCompatibleFunctionDecls(New, Old, S, MergeTypeWithOld); // If the types are imprecise (due to dependent constructs in friends or @@ -6986,8 +6991,8 @@ NamedDecl *Sema::ActOnVariableDeclarator( } } - NewVD->addAttr(::new (Context) - AsmLabelAttr(Context, SE->getStrTokenLoc(0), Label)); + NewVD->addAttr(::new (Context) AsmLabelAttr( + Context, SE->getStrTokenLoc(0), Label, /*IsLiteralLabel=*/true)); } else if (!ExtnameUndeclaredIdentifiers.empty()) { llvm::DenseMap::iterator I = ExtnameUndeclaredIdentifiers.find(NewVD->getIdentifier()); @@ -8175,10 +8180,10 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D, if (DC->isRecord()) { R = SemaRef.CheckDestructorDeclarator(D, R, SC); CXXRecordDecl *Record = cast(DC); - CXXDestructorDecl *NewDD = - CXXDestructorDecl::Create(SemaRef.Context, Record, D.getBeginLoc(), - NameInfo, R, TInfo, isInline, - /*isImplicitlyDeclared=*/false); + CXXDestructorDecl *NewDD = CXXDestructorDecl::Create( + SemaRef.Context, Record, D.getBeginLoc(), NameInfo, R, TInfo, + isInline, + /*isImplicitlyDeclared=*/false, ConstexprKind); // If the destructor needs an implicit exception specification, set it // now. FIXME: It'd be nice to be able to create the right type to start @@ -8787,7 +8792,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, // C++11 [dcl.constexpr]p3: functions declared constexpr are required to // be either constructors or to return a literal type. Therefore, // destructors cannot be declared constexpr. - if (isa(NewFD)) { + if (isa(NewFD) && !getLangOpts().CPlusPlus2a) { Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_constexpr_dtor) << ConstexprKind; } @@ -8885,8 +8890,9 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, if (Expr *E = (Expr*) D.getAsmLabel()) { // The parser guarantees this is a string. StringLiteral *SE = cast(E); - NewFD->addAttr(::new (Context) AsmLabelAttr(Context, SE->getStrTokenLoc(0), - SE->getString())); + NewFD->addAttr(::new (Context) + AsmLabelAttr(Context, SE->getStrTokenLoc(0), + SE->getString(), /*IsLiteralLabel=*/true)); } else if (!ExtnameUndeclaredIdentifiers.empty()) { llvm::DenseMap::iterator I = ExtnameUndeclaredIdentifiers.find(NewFD->getIdentifier()); @@ -9674,10 +9680,13 @@ static bool HasNonMultiVersionAttributes(const FunctionDecl *FD, return false; } -static bool CheckMultiVersionAdditionalRules(Sema &S, const FunctionDecl *OldFD, - const FunctionDecl *NewFD, - bool CausesMV, - MultiVersionKind MVType) { +bool Sema::areMultiversionVariantFunctionsCompatible( + const FunctionDecl *OldFD, const FunctionDecl *NewFD, + const PartialDiagnostic &NoProtoDiagID, + const PartialDiagnosticAt &NoteCausedDiagIDAt, + const PartialDiagnosticAt &NoSupportDiagIDAt, + const PartialDiagnosticAt &DiffDiagIDAt, bool TemplatesSupported, + bool ConstexprSupported) { enum DoesntSupport { FuncTemplates = 0, VirtFuncs = 1, @@ -9695,123 +9704,85 @@ static bool CheckMultiVersionAdditionalRules(Sema &S, const FunctionDecl *OldFD, ConstexprSpec = 2, InlineSpec = 3, StorageClass = 4, - Linkage = 5 + Linkage = 5, }; - bool IsCPUSpecificCPUDispatchMVType = - MVType == MultiVersionKind::CPUDispatch || - MVType == MultiVersionKind::CPUSpecific; - if (OldFD && !OldFD->getType()->getAs()) { - S.Diag(OldFD->getLocation(), diag::err_multiversion_noproto); - S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); + Diag(OldFD->getLocation(), NoProtoDiagID); + Diag(NoteCausedDiagIDAt.first, NoteCausedDiagIDAt.second); return true; } if (!NewFD->getType()->getAs()) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_noproto); - - if (!S.getASTContext().getTargetInfo().supportsMultiVersioning()) { - S.Diag(NewFD->getLocation(), diag::err_multiversion_not_supported); - if (OldFD) - S.Diag(OldFD->getLocation(), diag::note_previous_declaration); - return true; - } - - // For now, disallow all other attributes. These should be opt-in, but - // an analysis of all of them is a future FIXME. - if (CausesMV && OldFD && HasNonMultiVersionAttributes(OldFD, MVType)) { - S.Diag(OldFD->getLocation(), diag::err_multiversion_no_other_attrs) - << IsCPUSpecificCPUDispatchMVType; - S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); - return true; - } + return Diag(NewFD->getLocation(), NoProtoDiagID); - if (HasNonMultiVersionAttributes(NewFD, MVType)) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_no_other_attrs) - << IsCPUSpecificCPUDispatchMVType; - - if (NewFD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) - << IsCPUSpecificCPUDispatchMVType << FuncTemplates; + if (!TemplatesSupported && + NewFD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate) + return Diag(NoSupportDiagIDAt.first, NoSupportDiagIDAt.second) + << FuncTemplates; if (const auto *NewCXXFD = dyn_cast(NewFD)) { if (NewCXXFD->isVirtual()) - return S.Diag(NewCXXFD->getLocation(), - diag::err_multiversion_doesnt_support) - << IsCPUSpecificCPUDispatchMVType << VirtFuncs; + return Diag(NoSupportDiagIDAt.first, NoSupportDiagIDAt.second) + << VirtFuncs; - if (const auto *NewCXXCtor = dyn_cast(NewFD)) - return S.Diag(NewCXXCtor->getLocation(), - diag::err_multiversion_doesnt_support) - << IsCPUSpecificCPUDispatchMVType << Constructors; + if (isa(NewCXXFD)) + return Diag(NoSupportDiagIDAt.first, NoSupportDiagIDAt.second) + << Constructors; - if (const auto *NewCXXDtor = dyn_cast(NewFD)) - return S.Diag(NewCXXDtor->getLocation(), - diag::err_multiversion_doesnt_support) - << IsCPUSpecificCPUDispatchMVType << Destructors; + if (isa(NewCXXFD)) + return Diag(NoSupportDiagIDAt.first, NoSupportDiagIDAt.second) + << Destructors; } if (NewFD->isDeleted()) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) - << IsCPUSpecificCPUDispatchMVType << DeletedFuncs; + return Diag(NoSupportDiagIDAt.first, NoSupportDiagIDAt.second) + << DeletedFuncs; if (NewFD->isDefaulted()) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) - << IsCPUSpecificCPUDispatchMVType << DefaultedFuncs; + return Diag(NoSupportDiagIDAt.first, NoSupportDiagIDAt.second) + << DefaultedFuncs; - if (NewFD->isConstexpr() && (MVType == MultiVersionKind::CPUDispatch || - MVType == MultiVersionKind::CPUSpecific)) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) - << IsCPUSpecificCPUDispatchMVType + if (!ConstexprSupported && NewFD->isConstexpr()) + return Diag(NoSupportDiagIDAt.first, NoSupportDiagIDAt.second) << (NewFD->isConsteval() ? ConstevalFuncs : ConstexprFuncs); - QualType NewQType = S.getASTContext().getCanonicalType(NewFD->getType()); + QualType NewQType = Context.getCanonicalType(NewFD->getType()); const auto *NewType = cast(NewQType); QualType NewReturnType = NewType->getReturnType(); if (NewReturnType->isUndeducedType()) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) - << IsCPUSpecificCPUDispatchMVType << DeducedReturn; - - // Only allow transition to MultiVersion if it hasn't been used. - if (OldFD && CausesMV && OldFD->isUsed(false)) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_after_used); + return Diag(NoSupportDiagIDAt.first, NoSupportDiagIDAt.second) + << DeducedReturn; // Ensure the return type is identical. if (OldFD) { - QualType OldQType = S.getASTContext().getCanonicalType(OldFD->getType()); + QualType OldQType = Context.getCanonicalType(OldFD->getType()); const auto *OldType = cast(OldQType); FunctionType::ExtInfo OldTypeInfo = OldType->getExtInfo(); FunctionType::ExtInfo NewTypeInfo = NewType->getExtInfo(); if (OldTypeInfo.getCC() != NewTypeInfo.getCC()) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) - << CallingConv; + return Diag(DiffDiagIDAt.first, DiffDiagIDAt.second) << CallingConv; QualType OldReturnType = OldType->getReturnType(); if (OldReturnType != NewReturnType) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) - << ReturnType; + return Diag(DiffDiagIDAt.first, DiffDiagIDAt.second) << ReturnType; if (OldFD->getConstexprKind() != NewFD->getConstexprKind()) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) - << ConstexprSpec; + return Diag(DiffDiagIDAt.first, DiffDiagIDAt.second) << ConstexprSpec; if (OldFD->isInlineSpecified() != NewFD->isInlineSpecified()) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) - << InlineSpec; + return Diag(DiffDiagIDAt.first, DiffDiagIDAt.second) << InlineSpec; if (OldFD->getStorageClass() != NewFD->getStorageClass()) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) - << StorageClass; + return Diag(DiffDiagIDAt.first, DiffDiagIDAt.second) << StorageClass; if (OldFD->isExternC() != NewFD->isExternC()) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) - << Linkage; + return Diag(DiffDiagIDAt.first, DiffDiagIDAt.second) << Linkage; - if (S.CheckEquivalentExceptionSpec( + if (CheckEquivalentExceptionSpec( OldFD->getType()->getAs(), OldFD->getLocation(), NewFD->getType()->getAs(), NewFD->getLocation())) return true; @@ -9819,6 +9790,51 @@ static bool CheckMultiVersionAdditionalRules(Sema &S, const FunctionDecl *OldFD, return false; } +static bool CheckMultiVersionAdditionalRules(Sema &S, const FunctionDecl *OldFD, + const FunctionDecl *NewFD, + bool CausesMV, + MultiVersionKind MVType) { + if (!S.getASTContext().getTargetInfo().supportsMultiVersioning()) { + S.Diag(NewFD->getLocation(), diag::err_multiversion_not_supported); + if (OldFD) + S.Diag(OldFD->getLocation(), diag::note_previous_declaration); + return true; + } + + bool IsCPUSpecificCPUDispatchMVType = + MVType == MultiVersionKind::CPUDispatch || + MVType == MultiVersionKind::CPUSpecific; + + // For now, disallow all other attributes. These should be opt-in, but + // an analysis of all of them is a future FIXME. + if (CausesMV && OldFD && HasNonMultiVersionAttributes(OldFD, MVType)) { + S.Diag(OldFD->getLocation(), diag::err_multiversion_no_other_attrs) + << IsCPUSpecificCPUDispatchMVType; + S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); + return true; + } + + if (HasNonMultiVersionAttributes(NewFD, MVType)) + return S.Diag(NewFD->getLocation(), diag::err_multiversion_no_other_attrs) + << IsCPUSpecificCPUDispatchMVType; + + // Only allow transition to MultiVersion if it hasn't been used. + if (OldFD && CausesMV && OldFD->isUsed(false)) + return S.Diag(NewFD->getLocation(), diag::err_multiversion_after_used); + + return S.areMultiversionVariantFunctionsCompatible( + OldFD, NewFD, S.PDiag(diag::err_multiversion_noproto), + PartialDiagnosticAt(NewFD->getLocation(), + S.PDiag(diag::note_multiversioning_caused_here)), + PartialDiagnosticAt(NewFD->getLocation(), + S.PDiag(diag::err_multiversion_doesnt_support) + << IsCPUSpecificCPUDispatchMVType), + PartialDiagnosticAt(NewFD->getLocation(), + S.PDiag(diag::err_multiversion_diff)), + /*TemplatesSupported=*/false, + /*ConstexprSupported=*/!IsCPUSpecificCPUDispatchMVType); +} + /// Check the validity of a multiversion function declaration that is the /// first of its kind. Also sets the multiversion'ness' of the function itself. /// @@ -10266,7 +10282,7 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, CXXMethodDecl *MD = dyn_cast(NewFD); if (!getLangOpts().CPlusPlus14 && MD && MD->isConstexpr() && !MD->isStatic() && !isa(MD) && - !MD->getMethodQualifiers().hasConst()) { + !isa(MD) && !MD->getMethodQualifiers().hasConst()) { CXXMethodDecl *OldMD = nullptr; if (OldDecl) OldMD = dyn_cast_or_null(OldDecl->getAsFunction()); @@ -17544,8 +17560,8 @@ void Sema::ActOnPragmaRedefineExtname(IdentifierInfo* Name, LookupOrdinaryName); AttributeCommonInfo Info(AliasName, SourceRange(AliasNameLoc), AttributeCommonInfo::AS_Pragma); - AsmLabelAttr *Attr = - AsmLabelAttr::CreateImplicit(Context, AliasName->getName(), Info); + AsmLabelAttr *Attr = AsmLabelAttr::CreateImplicit( + Context, AliasName->getName(), /*LiteralLabel=*/true, Info); // If a declaration that: // 1) declares a function or a variable diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 2d2ce148b3c7a..5c9a618d87d5c 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4347,7 +4347,9 @@ static void handleGlobalAttr(Sema &S, Decl *D, const ParsedAttr &AL) { return; } const auto *FD = cast(D); - if (!FD->getReturnType()->isVoidType()) { + if (!FD->getReturnType()->isVoidType() && + !FD->getReturnType()->getAs() && + !FD->getReturnType()->isInstantiationDependentType()) { SourceRange RTRange = FD->getReturnTypeSourceRange(); S.Diag(FD->getTypeSpecStartLoc(), diag::err_kern_type_not_void_return) << FD->getType() @@ -4377,6 +4379,9 @@ static void handleGNUInlineAttr(Sema &S, Decl *D, const ParsedAttr &AL) { return; } + if (S.LangOpts.CPlusPlus && Fn->getStorageClass() != SC_Extern) + S.Diag(AL.getLoc(), diag::warn_gnu_inline_cplusplus_without_extern); + D->addAttr(::new (S.Context) GNUInlineAttr(S.Context, AL)); } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index a221ab0239253..cbc46a7492264 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -1590,6 +1590,36 @@ static bool CheckLiteralType(Sema &SemaRef, Sema::CheckConstexprKind Kind, llvm_unreachable("unknown CheckConstexprKind"); } +/// Determine whether a destructor cannot be constexpr due to +static bool CheckConstexprDestructorSubobjects(Sema &SemaRef, + const CXXDestructorDecl *DD, + Sema::CheckConstexprKind Kind) { + auto Check = [&](SourceLocation Loc, QualType T, const FieldDecl *FD) { + const CXXRecordDecl *RD = + T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl(); + if (!RD || RD->hasConstexprDestructor()) + return true; + + if (Kind == Sema::CheckConstexprKind::Diagnose) { + SemaRef.Diag(DD->getLocation(), diag::err_constexpr_dtor_subobject) + << DD->getConstexprKind() << !FD + << (FD ? FD->getDeclName() : DeclarationName()) << T; + SemaRef.Diag(Loc, diag::note_constexpr_dtor_subobject) + << !FD << (FD ? FD->getDeclName() : DeclarationName()) << T; + } + return false; + }; + + const CXXRecordDecl *RD = DD->getParent(); + for (const CXXBaseSpecifier &B : RD->bases()) + if (!Check(B.getBaseTypeLoc(), B.getType(), nullptr)) + return false; + for (const FieldDecl *FD : RD->fields()) + if (!Check(FD->getLocation(), FD->getType(), FD)) + return false; + return true; +} + // CheckConstexprParameterTypes - Check whether a function's parameter types // are all literal types. If so, return true. If not, produce a suitable // diagnostic and return false. @@ -1645,8 +1675,8 @@ bool Sema::CheckConstexprFunctionDefinition(const FunctionDecl *NewFD, // constraints: // - the class shall not have any virtual base classes; // - // FIXME: This only applies to constructors, not arbitrary member - // functions. + // FIXME: This only applies to constructors and destructors, not arbitrary + // member functions. const CXXRecordDecl *RD = MD->getParent(); if (RD->getNumVBases()) { if (Kind == CheckConstexprKind::CheckValid) @@ -1699,6 +1729,18 @@ bool Sema::CheckConstexprFunctionDefinition(const FunctionDecl *NewFD, return false; } + if (auto *Dtor = dyn_cast(NewFD)) { + // A destructor can be constexpr only if the defaulted destructor could be; + // we don't need to check the members and bases if we already know they all + // have constexpr destructors. + if (!Dtor->getParent()->defaultedDestructorIsConstexpr()) { + if (Kind == CheckConstexprKind::CheckValid) + return false; + if (!CheckConstexprDestructorSubobjects(*this, Dtor, Kind)) + return false; + } + } + // - each of its parameter types shall be a literal type; if (!CheckConstexprParameterTypes(*this, NewFD, Kind)) return false; @@ -1778,7 +1820,8 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl, case Decl::Decomposition: { // C++1y [dcl.constexpr]p3 allows anything except: // a definition of a variable of non-literal type or of static or - // thread storage duration or for which no initialization is performed. + // thread storage duration or [before C++2a] for which no + // initialization is performed. const auto *VD = cast(DclIt); if (VD->isThisDeclarationADefinition()) { if (VD->isStaticLocal()) { @@ -1797,11 +1840,16 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl, if (!VD->getType()->isDependentType() && !VD->hasInit() && !VD->isCXXForRangeDecl()) { if (Kind == Sema::CheckConstexprKind::Diagnose) { - SemaRef.Diag(VD->getLocation(), - diag::err_constexpr_local_var_no_init) - << isa(Dcl); + SemaRef.Diag( + VD->getLocation(), + SemaRef.getLangOpts().CPlusPlus2a + ? diag::warn_cxx17_compat_constexpr_local_var_no_init + : diag::ext_constexpr_local_var_no_init) + << isa(Dcl); + } else if (!SemaRef.getLangOpts().CPlusPlus2a) { + return false; } - return false; + continue; } } if (Kind == Sema::CheckConstexprKind::Diagnose) { @@ -1855,6 +1903,11 @@ static bool CheckConstexprCtorInitializer(Sema &SemaRef, llvm::SmallSet &Inits, bool &Diagnosed, Sema::CheckConstexprKind Kind) { + // In C++20 onwards, there's nothing to check for validity. + if (Kind == Sema::CheckConstexprKind::CheckValid && + SemaRef.getLangOpts().CPlusPlus2a) + return true; + if (Field->isInvalidDecl()) return true; @@ -1873,12 +1926,15 @@ static bool CheckConstexprCtorInitializer(Sema &SemaRef, if (!Inits.count(Field)) { if (Kind == Sema::CheckConstexprKind::Diagnose) { if (!Diagnosed) { - SemaRef.Diag(Dcl->getLocation(), diag::err_constexpr_ctor_missing_init); + SemaRef.Diag(Dcl->getLocation(), + SemaRef.getLangOpts().CPlusPlus2a + ? diag::warn_cxx17_compat_constexpr_ctor_missing_init + : diag::ext_constexpr_ctor_missing_init); Diagnosed = true; } SemaRef.Diag(Field->getLocation(), diag::note_constexpr_ctor_missing_init); - } else { + } else if (!SemaRef.getLangOpts().CPlusPlus2a) { return false; } } else if (Field->isAnonymousStructOrUnion()) { @@ -2121,10 +2177,15 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl, if (RD->isUnion()) { if (Constructor->getNumCtorInitializers() == 0 && RD->hasVariantMembers()) { - if (Kind == Sema::CheckConstexprKind::Diagnose) - SemaRef.Diag(Dcl->getLocation(), - diag::err_constexpr_union_ctor_no_init); - return false; + if (Kind == Sema::CheckConstexprKind::Diagnose) { + SemaRef.Diag( + Dcl->getLocation(), + SemaRef.getLangOpts().CPlusPlus2a + ? diag::warn_cxx17_compat_constexpr_union_ctor_no_init + : diag::ext_constexpr_union_ctor_no_init); + } else if (!SemaRef.getLangOpts().CPlusPlus2a) { + return false; + } } } else if (!Constructor->isDependentContext() && !Constructor->isDelegatingConstructor()) { @@ -6303,6 +6364,12 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) { DelayedDllExportMemberFunctions.push_back(M); } } + + // Define defaulted constexpr virtual functions that override a base class + // function right away. + // FIXME: We can defer doing this until the vtable is marked as used. + if (M->isDefaulted() && M->isConstexpr() && M->size_overridden_methods()) + DefineImplicitSpecialMember(*this, M, M->getLocation()); }; bool HasMethodWithOverrideControl = false, @@ -6516,6 +6583,8 @@ specialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl, if (CSM == Sema::CXXDefaultConstructor) return ClassDecl->hasConstexprDefaultConstructor(); + if (CSM == Sema::CXXDestructor) + return ClassDecl->hasConstexprDestructor(); Sema::SpecialMemberOverloadResult SMOR = lookupCallFromSpecialMember(S, ClassDecl, CSM, Quals, ConstRHS); @@ -6564,6 +6633,8 @@ static bool defaultedSpecialMemberIsConstexpr( break; case Sema::CXXDestructor: + return ClassDecl->defaultedDestructorIsConstexpr(); + case Sema::CXXInvalid: return false; } @@ -6816,13 +6887,14 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) { // Do not apply this rule to members of class templates, since core issue 1358 // makes such functions always instantiate to constexpr functions. For // functions which cannot be constexpr (for non-constructors in C++11 and for - // destructors in C++1y), this is checked elsewhere. + // destructors in C++14 and C++17), this is checked elsewhere. // // FIXME: This should not apply if the member is deleted. bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, RD, CSM, HasConstParam); - if ((getLangOpts().CPlusPlus14 ? !isa(MD) - : isa(MD)) && + if ((getLangOpts().CPlusPlus2a || + (getLangOpts().CPlusPlus14 ? !isa(MD) + : isa(MD))) && MD->isConstexpr() && !Constexpr && MD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) { Diag(MD->getBeginLoc(), MD->isConsteval() @@ -11445,6 +11517,10 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { if (DSM.isAlreadyBeingDeclared()) return nullptr; + bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, ClassDecl, + CXXDestructor, + false); + // Create the actual destructor declaration. CanQualType ClassType = Context.getCanonicalType(Context.getTypeDeclType(ClassDecl)); @@ -11452,10 +11528,11 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { DeclarationName Name = Context.DeclarationNames.getCXXDestructorName(ClassType); DeclarationNameInfo NameInfo(Name, ClassLoc); - CXXDestructorDecl *Destructor - = CXXDestructorDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, - QualType(), nullptr, /*isInline=*/true, - /*isImplicitlyDeclared=*/true); + CXXDestructorDecl *Destructor = + CXXDestructorDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, + QualType(), nullptr, /*isInline=*/true, + /*isImplicitlyDeclared=*/true, + Constexpr ? CSK_constexpr : CSK_unspecified); Destructor->setAccess(AS_public); Destructor->setDefaulted(); @@ -13323,6 +13400,20 @@ void Sema::FinalizeVarWithDestructor(VarDecl *VD, const RecordType *Record) { } if (Destructor->isTrivial()) return; + + // If the destructor is constexpr, check whether the variable has constant + // destruction now. + if (Destructor->isConstexpr() && VD->getInit() && + !VD->getInit()->isValueDependent() && VD->evaluateValue()) { + SmallVector Notes; + if (!VD->evaluateDestruction(Notes) && VD->isConstexpr()) { + Diag(VD->getLocation(), + diag::err_constexpr_var_requires_const_destruction) << VD; + for (unsigned I = 0, N = Notes.size(); I != N; ++I) + Diag(Notes[I].first, Notes[I].second); + } + } + if (!VD->hasGlobalStorage()) return; // Emit warning for non-trivial dtor in global scope (a real global, @@ -13913,6 +14004,10 @@ Decl *Sema::ActOnStartLinkageSpecification(Scope *S, SourceLocation ExternLoc, Language = LinkageSpecDecl::lang_c; else if (Lang == "C++") Language = LinkageSpecDecl::lang_cxx; + else if (Lang == "C++11") + Language = LinkageSpecDecl::lang_cxx_11; + else if (Lang == "C++14") + Language = LinkageSpecDecl::lang_cxx_14; else { Diag(LangStr->getExprLoc(), diag::err_language_linkage_spec_unknown) << LangStr->getSourceRange(); @@ -14165,8 +14260,17 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, if (Converted.isInvalid()) Failed = true; + ExprResult FullAssertExpr = + ActOnFinishFullExpr(Converted.get(), StaticAssertLoc, + /*DiscardedValue*/ false, + /*IsConstexpr*/ true); + if (FullAssertExpr.isInvalid()) + Failed = true; + else + AssertExpr = FullAssertExpr.get(); + llvm::APSInt Cond; - if (!Failed && VerifyIntegerConstantExpression(Converted.get(), &Cond, + if (!Failed && VerifyIntegerConstantExpression(AssertExpr, &Cond, diag::err_static_assert_expression_is_not_constant, /*AllowFold=*/false).isInvalid()) Failed = true; @@ -14192,16 +14296,16 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, } Failed = true; } + } else { + ExprResult FullAssertExpr = ActOnFinishFullExpr(AssertExpr, StaticAssertLoc, + /*DiscardedValue*/false, + /*IsConstexpr*/true); + if (FullAssertExpr.isInvalid()) + Failed = true; + else + AssertExpr = FullAssertExpr.get(); } - ExprResult FullAssertExpr = ActOnFinishFullExpr(AssertExpr, StaticAssertLoc, - /*DiscardedValue*/false, - /*IsConstexpr*/true); - if (FullAssertExpr.isInvalid()) - Failed = true; - else - AssertExpr = FullAssertExpr.get(); - Decl *Decl = StaticAssertDecl::Create(Context, CurContext, StaticAssertLoc, AssertExpr, AssertMessage, RParenLoc, Failed); diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index 5fab2a147879a..a0fef8346c2a5 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -2275,9 +2275,7 @@ static bool isObjCTypeSubstitutable(ASTContext &Context, // stricter definition so it is not substitutable for id. if (B->isObjCQualifiedIdType()) { return A->isObjCQualifiedIdType() && - Context.ObjCQualifiedIdTypesAreCompatible(QualType(A, 0), - QualType(B,0), - false); + Context.ObjCQualifiedIdTypesAreCompatible(A, B, false); } /* diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 61fda54b32199..a9f7d98a8027b 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -4081,7 +4081,7 @@ static void captureVariablyModifiedType(ASTContext &Context, QualType T, #define NON_CANONICAL_TYPE(Class, Base) #define DEPENDENT_TYPE(Class, Base) case Type::Class: #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" T = QualType(); break; // These types are never variably-modified. @@ -5955,7 +5955,9 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, << FDecl << Fn->getSourceRange()); // CUDA: Kernel function must have 'void' return type - if (!FuncT->getReturnType()->isVoidType()) + if (!FuncT->getReturnType()->isVoidType() && + !FuncT->getReturnType()->getAs() && + !FuncT->getReturnType()->isInstantiationDependentType()) return ExprError(Diag(LParenLoc, diag::err_kern_type_not_void_return) << Fn->getType() << Fn->getSourceRange()); } else { @@ -6776,8 +6778,8 @@ ExprResult Sema::BuildVectorLiteral(SourceLocation LParenLoc, assert(Ty->isVectorType() && "Expected vector type"); SmallVector initExprs; - const VectorType *VTy = Ty->getAs(); - unsigned numElems = Ty->getAs()->getNumElements(); + const VectorType *VTy = Ty->castAs(); + unsigned numElems = VTy->getNumElements(); // '(...)' form of vector initialization in AltiVec: the number of // initializers must be one or must match the size of the vector. @@ -6788,7 +6790,7 @@ ExprResult Sema::BuildVectorLiteral(SourceLocation LParenLoc, // vector. If a single value is specified in the initializer then it will // be replicated to all the components of the vector if (numExprs == 1) { - QualType ElemTy = Ty->getAs()->getElementType(); + QualType ElemTy = VTy->getElementType(); ExprResult Literal = DefaultLvalueConversion(exprs[0]); if (Literal.isInvalid()) return ExprError(); @@ -6810,7 +6812,7 @@ ExprResult Sema::BuildVectorLiteral(SourceLocation LParenLoc, if (getLangOpts().OpenCL && VTy->getVectorKind() == VectorType::GenericVector && numExprs == 1) { - QualType ElemTy = Ty->getAs()->getElementType(); + QualType ElemTy = VTy->getElementType(); ExprResult Literal = DefaultLvalueConversion(exprs[0]); if (Literal.isInvalid()) return ExprError(); @@ -7109,8 +7111,8 @@ checkConditionalObjectPointersCompatibility(Sema &S, ExprResult &LHS, QualType RHSTy = RHS.get()->getType(); // get the "pointed to" types - QualType lhptee = LHSTy->getAs()->getPointeeType(); - QualType rhptee = RHSTy->getAs()->getPointeeType(); + QualType lhptee = LHSTy->castAs()->getPointeeType(); + QualType rhptee = RHSTy->castAs()->getPointeeType(); // ignore qualifiers on void (C99 6.5.15p3, clause 6) if (lhptee->isVoidType() && rhptee->isIncompleteOrObjectType()) { @@ -7560,9 +7562,10 @@ QualType Sema::FindCompositeObjCPointerType(ExprResult &LHS, ExprResult &RHS, compositeType = RHSOPT->isObjCBuiltinType() ? RHSTy : LHSTy; } else if (Context.canAssignObjCInterfaces(RHSOPT, LHSOPT)) { compositeType = LHSOPT->isObjCBuiltinType() ? LHSTy : RHSTy; - } else if ((LHSTy->isObjCQualifiedIdType() || - RHSTy->isObjCQualifiedIdType()) && - Context.ObjCQualifiedIdTypesAreCompatible(LHSTy, RHSTy, true)) { + } else if ((LHSOPT->isObjCQualifiedIdType() || + RHSOPT->isObjCQualifiedIdType()) && + Context.ObjCQualifiedIdTypesAreCompatible(LHSOPT, RHSOPT, + true)) { // Need to handle "id" explicitly. // GCC allows qualified id and any Objective-C type to devolve to // id. Currently localizing to here until clear this should be @@ -7594,8 +7597,8 @@ QualType Sema::FindCompositeObjCPointerType(ExprResult &LHS, ExprResult &RHS, LHS = RHS = true; return QualType(); } - QualType lhptee = LHSTy->getAs()->getPointeeType(); - QualType rhptee = RHSTy->getAs()->getPointeeType(); + QualType lhptee = LHSTy->castAs()->getPointeeType(); + QualType rhptee = RHSTy->castAs()->getPointeeType(); QualType destPointee = Context.getQualifiedType(lhptee, rhptee.getQualifiers()); QualType destType = Context.getPointerType(destPointee); @@ -7614,8 +7617,8 @@ QualType Sema::FindCompositeObjCPointerType(ExprResult &LHS, ExprResult &RHS, LHS = RHS = true; return QualType(); } - QualType lhptee = LHSTy->getAs()->getPointeeType(); - QualType rhptee = RHSTy->getAs()->getPointeeType(); + QualType lhptee = LHSTy->castAs()->getPointeeType(); + QualType rhptee = RHSTy->castAs()->getPointeeType(); QualType destPointee = Context.getQualifiedType(rhptee, lhptee.getQualifiers()); QualType destType = Context.getPointerType(destPointee); @@ -8120,8 +8123,8 @@ checkObjCPointerTypesForAssignment(Sema &S, QualType LHSType, return Sema::IncompatiblePointer; return Sema::Compatible; } - QualType lhptee = LHSType->getAs()->getPointeeType(); - QualType rhptee = RHSType->getAs()->getPointeeType(); + QualType lhptee = LHSType->castAs()->getPointeeType(); + QualType rhptee = RHSType->castAs()->getPointeeType(); if (!lhptee.isAtLeastAsQualifiedAs(rhptee) && // make an exception for id

@@ -9254,7 +9257,8 @@ static void DiagnoseDivisionSizeofPointerOrArray(Sema &S, Expr *LHS, Expr *RHS, } } else if (const auto *ArrayTy = S.Context.getAsArrayType(LHSTy)) { QualType ArrayElemTy = ArrayTy->getElementType(); - if (ArrayElemTy->isDependentType() || RHSTy->isDependentType() || + if (ArrayElemTy != S.Context.getBaseElementType(ArrayTy) || + ArrayElemTy->isDependentType() || RHSTy->isDependentType() || S.Context.getTypeSize(ArrayElemTy) == S.Context.getTypeSize(RHSTy)) return; S.Diag(Loc, diag::warn_division_sizeof_array) @@ -9264,6 +9268,8 @@ static void DiagnoseDivisionSizeofPointerOrArray(Sema &S, Expr *LHS, Expr *RHS, S.Diag(LHSArgDecl->getLocation(), diag::note_array_declared_here) << LHSArgDecl; } + + S.Diag(Loc, diag::note_precedence_silence) << RHS; } } @@ -9459,8 +9465,8 @@ static bool checkArithmeticBinOpPointerOperands(Sema &S, SourceLocation Loc, // if both are pointers check if operation is valid wrt address spaces if (S.getLangOpts().OpenCL && isLHSPointer && isRHSPointer) { - const PointerType *lhsPtr = LHSExpr->getType()->getAs(); - const PointerType *rhsPtr = RHSExpr->getType()->getAs(); + const PointerType *lhsPtr = LHSExpr->getType()->castAs(); + const PointerType *rhsPtr = RHSExpr->getType()->castAs(); if (!lhsPtr->isAddressSpaceOverlapping(*rhsPtr)) { S.Diag(Loc, diag::err_typecheck_op_on_nonoverlapping_address_space_pointers) @@ -10307,20 +10313,18 @@ static void diagnoseLogicalNotOnLHSofCheck(Sema &S, ExprResult &LHS, << FixItHint::CreateInsertion(SecondClose, ")"); } -// Get the decl for a simple expression: a reference to a variable, -// an implicit C++ field reference, or an implicit ObjC ivar reference. -static ValueDecl *getCompareDecl(Expr *E) { - if (DeclRefExpr *DR = dyn_cast(E)) - return DR->getDecl(); - if (ObjCIvarRefExpr *Ivar = dyn_cast(E)) { - if (Ivar->isFreeIvar()) - return Ivar->getDecl(); - } - if (MemberExpr *Mem = dyn_cast(E)) { +// Returns true if E refers to a non-weak array. +static bool checkForArray(const Expr *E) { + const ValueDecl *D = nullptr; + if (const DeclRefExpr *DR = dyn_cast(E)) { + D = DR->getDecl(); + } else if (const MemberExpr *Mem = dyn_cast(E)) { if (Mem->isImplicitAccess()) - return Mem->getMemberDecl(); + D = Mem->getMemberDecl(); } - return nullptr; + if (!D) + return false; + return D->getType()->isArrayType() && !D->isWeak(); } /// Diagnose some forms of syntactically-obvious tautological comparison. @@ -10353,8 +10357,6 @@ static void diagnoseTautologicalComparison(Sema &S, SourceLocation Loc, // obvious cases in the definition of the template anyways. The idea is to // warn when the typed comparison operator will always evaluate to the same // result. - ValueDecl *DL = getCompareDecl(LHSStripped); - ValueDecl *DR = getCompareDecl(RHSStripped); // Used for indexing into %select in warn_comparison_always enum { @@ -10363,7 +10365,8 @@ static void diagnoseTautologicalComparison(Sema &S, SourceLocation Loc, AlwaysFalse, AlwaysEqual, // std::strong_ordering::equal from operator<=> }; - if (DL && DR && declaresSameEntity(DL, DR)) { + + if (Expr::isSameComparisonOperand(LHS, RHS)) { unsigned Result; switch (Opc) { case BO_EQ: case BO_LE: case BO_GE: @@ -10383,9 +10386,7 @@ static void diagnoseTautologicalComparison(Sema &S, SourceLocation Loc, S.PDiag(diag::warn_comparison_always) << 0 /*self-comparison*/ << Result); - } else if (DL && DR && - DL->getType()->isArrayType() && DR->getType()->isArrayType() && - !DL->isWeak() && !DR->isWeak()) { + } else if (checkForArray(LHSStripped) && checkForArray(RHSStripped)) { // What is it always going to evaluate to? unsigned Result; switch(Opc) { @@ -10558,7 +10559,7 @@ static QualType checkArithmeticOrEnumeralThreeWayCompare(Sema &S, return QualType(); } QualType IntType = - LHSStrippedType->getAs()->getDecl()->getIntegerType(); + LHSStrippedType->castAs()->getDecl()->getIntegerType(); assert(IntType->isArithmeticType()); // We can't use `CK_IntegralCast` when the underlying type is 'bool', so we @@ -10841,8 +10842,8 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, if (LCanPointeeTy != RCanPointeeTy) { // Treat NULL constant as a special case in OpenCL. if (getLangOpts().OpenCL && !LHSIsNull && !RHSIsNull) { - const PointerType *LHSPtr = LHSType->getAs(); - if (!LHSPtr->isAddressSpaceOverlapping(*RHSType->getAs())) { + const PointerType *LHSPtr = LHSType->castAs(); + if (!LHSPtr->isAddressSpaceOverlapping(*RHSType->castAs())) { Diag(Loc, diag::err_typecheck_op_on_nonoverlapping_address_space_pointers) << LHSType << RHSType << 0 /* comparison */ @@ -11107,7 +11108,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, // the largest type to the smallest type to avoid cases where long long == long, // where long gets picked over long long. QualType Sema::GetSignedVectorType(QualType V) { - const VectorType *VTy = V->getAs(); + const VectorType *VTy = V->castAs(); unsigned TypeSize = Context.getTypeSize(VTy->getElementType()); if (isa(VTy)) { @@ -11162,7 +11163,7 @@ QualType Sema::CheckVectorCompareOperands(ExprResult &LHS, ExprResult &RHS, // If AltiVec, the comparison results in a numeric type, i.e. // bool for C++, int for C if (getLangOpts().AltiVec && - vType->getAs()->getVectorKind() == VectorType::AltiVecVector) + vType->castAs()->getVectorKind() == VectorType::AltiVecVector) return Context.getLogicalOperationType(); // For non-floating point types, check for self-comparisons of the form @@ -11362,10 +11363,22 @@ inline QualType Sema::CheckLogicalOperands(ExprResult &LHS, ExprResult &RHS, if (LHS.get()->getType()->isVectorType() || RHS.get()->getType()->isVectorType()) return CheckVectorLogicalOperands(LHS, RHS, Loc); + bool EnumConstantInBoolContext = false; + for (const ExprResult &HS : {LHS, RHS}) { + if (const auto *DREHS = dyn_cast(HS.get())) { + const auto *ECDHS = dyn_cast(DREHS->getDecl()); + if (ECDHS && ECDHS->getInitVal() != 0 && ECDHS->getInitVal() != 1) + EnumConstantInBoolContext = true; + } + } + + if (EnumConstantInBoolContext) + Diag(Loc, diag::warn_enum_constant_in_bool_context); + // Diagnose cases where the user write a logical and/or but probably meant a // bitwise one. We do this when the LHS is a non-bool integer and the RHS // is a constant. - if (LHS.get()->getType()->isIntegerType() && + if (!EnumConstantInBoolContext && LHS.get()->getType()->isIntegerType() && !LHS.get()->getType()->isBooleanType() && RHS.get()->getType()->isIntegerType() && !RHS.get()->isValueDependent() && // Don't warn in macros or template instantiations. @@ -12159,11 +12172,11 @@ static QualType CheckIncrementDecrementOperand(Sema &S, Expr *Op, } else if (S.getLangOpts().AltiVec && ResType->isVectorType()) { // OK! ( C/C++ Language Extensions for CBEA(Version 2.6) 10.3 ) } else if (S.getLangOpts().ZVector && ResType->isVectorType() && - (ResType->getAs()->getVectorKind() != + (ResType->castAs()->getVectorKind() != VectorType::AltiVecBool)) { // The z vector extensions allow ++ and -- for non-bool vectors. } else if(S.getLangOpts().OpenCL && ResType->isVectorType() && - ResType->getAs()->getElementType()->isIntegerType()) { + ResType->castAs()->getElementType()->isIntegerType()) { // OpenCL V1.2 6.3 says dec/inc ops operate on integer vector types. } else { S.Diag(OpLoc, diag::err_typecheck_illegal_increment_decrement) @@ -12749,7 +12762,7 @@ static ExprResult convertHalfVecBinOp(Sema &S, ExprResult LHS, ExprResult RHS, LHS = convertVector(LHS.get(), Context.FloatTy, S); auto *BO = new (Context) BinaryOperator(LHS.get(), RHS.get(), Opc, BinOpResTy, VK, OK, OpLoc, FPFeatures); - return convertVector(BO, ResultTy->getAs()->getElementType(), S); + return convertVector(BO, ResultTy->castAs()->getElementType(), S); } static std::pair @@ -13505,7 +13518,7 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, else if (resultType->isVectorType() && // The z vector extensions don't allow + or - with bool vectors. (!Context.getLangOpts().ZVector || - resultType->getAs()->getVectorKind() != + resultType->castAs()->getVectorKind() != VectorType::AltiVecBool)) break; else if (getLangOpts().CPlusPlus && // C++ [expr.unary.op]p6 @@ -13534,7 +13547,7 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, else if (resultType->isExtVectorType() && Context.getLangOpts().OpenCL) { // OpenCL v1.1 s6.3.f: The bitwise operator not (~) does not operate // on vector float types. - QualType T = resultType->getAs()->getElementType(); + QualType T = resultType->castAs()->getElementType(); if (!T->isIntegerType()) return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) << resultType << Input.get()->getSourceRange()); @@ -13579,7 +13592,7 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, !Context.getLangOpts().OpenCLCPlusPlus) { // OpenCL v1.1 6.3.h: The logical operator not (!) does not // operate on vector float types. - QualType T = resultType->getAs()->getElementType(); + QualType T = resultType->castAs()->getElementType(); if (!T->isIntegerType()) return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) << resultType << Input.get()->getSourceRange()); @@ -14244,7 +14257,7 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, // If the user wrote a function type in some form, try to use that. if (!BSI->FunctionType.isNull()) { - const FunctionType *FTy = BSI->FunctionType->getAs(); + const FunctionType *FTy = BSI->FunctionType->castAs(); FunctionType::ExtInfo Ext = FTy->getExtInfo(); if (NoReturn && !Ext.getNoReturn()) Ext = Ext.withNoReturn(true); @@ -14716,24 +14729,24 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, case IncompatibleObjCQualifiedId: { if (SrcType->isObjCQualifiedIdType()) { const ObjCObjectPointerType *srcOPT = - SrcType->getAs(); + SrcType->castAs(); for (auto *srcProto : srcOPT->quals()) { PDecl = srcProto; break; } if (const ObjCInterfaceType *IFaceT = - DstType->getAs()->getInterfaceType()) + DstType->castAs()->getInterfaceType()) IFace = IFaceT->getDecl(); } else if (DstType->isObjCQualifiedIdType()) { const ObjCObjectPointerType *dstOPT = - DstType->getAs(); + DstType->castAs(); for (auto *dstProto : dstOPT->quals()) { PDecl = dstProto; break; } if (const ObjCInterfaceType *IFaceT = - SrcType->getAs()->getInterfaceType()) + SrcType->castAs()->getInterfaceType()) IFace = IFaceT->getDecl(); } DiagKind = diag::warn_incompatible_qualified_id; @@ -15561,6 +15574,7 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func, } if (LangOpts.OpenMP) { + markOpenMPDeclareVariantFuncsReferenced(Loc, Func, MightBeOdrUse); if (LangOpts.OpenMPIsDevice) checkOpenMPDeviceFunction(Loc, Func); else diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 21b50b7c685ad..a0a32d1dd2c61 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -4615,7 +4615,9 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT, return RD->hasAttr(); return false; case UTT_IsSigned: - return T->isSignedIntegerType(); + // Enum types should always return false. + // Floating points should always return true. + return !T->isEnumeralType() && (T->isFloatingType() || T->isSignedIntegerType()); case UTT_IsUnsigned: return T->isUnsignedIntegerType(); diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index a114164186d0b..21a32dbeea1c3 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -974,7 +974,7 @@ int InitListChecker::numArrayElements(QualType DeclType) { } int InitListChecker::numStructUnionElements(QualType DeclType) { - RecordDecl *structDecl = DeclType->getAs()->getDecl(); + RecordDecl *structDecl = DeclType->castAs()->getDecl(); int InitializableMembers = 0; if (auto *CXXRD = dyn_cast(structDecl)) InitializableMembers += CXXRD->getNumBases(); @@ -1033,7 +1033,7 @@ void InitListChecker::CheckImplicitInitList(const InitializedEntity &Entity, else if (T->isRecordType()) maxElements = numStructUnionElements(T); else if (T->isVectorType()) - maxElements = T->getAs()->getNumElements(); + maxElements = T->castAs()->getNumElements(); else llvm_unreachable("CheckImplicitInitList(): Illegal type"); @@ -1264,7 +1264,7 @@ void InitListChecker::CheckListElementTypes(const InitializedEntity &Entity, } else if (DeclType->isRecordType()) { assert(DeclType->isAggregateType() && "non-aggregate records should be handed in CheckSubElementType"); - RecordDecl *RD = DeclType->getAs()->getDecl(); + RecordDecl *RD = DeclType->castAs()->getDecl(); auto Bases = CXXRecordDecl::base_class_range(CXXRecordDecl::base_class_iterator(), CXXRecordDecl::base_class_iterator()); @@ -1490,7 +1490,7 @@ void InitListChecker::CheckComplexType(const InitializedEntity &Entity, << IList->getSourceRange(); // Initialize the complex number. - QualType elementType = DeclType->getAs()->getElementType(); + QualType elementType = DeclType->castAs()->getElementType(); InitializedEntity ElementEntity = InitializedEntity::InitializeElement(SemaRef.Context, 0, Entity); @@ -1636,7 +1636,7 @@ void InitListChecker::CheckVectorType(const InitializedEntity &Entity, unsigned &Index, InitListExpr *StructuredList, unsigned &StructuredIndex) { - const VectorType *VT = DeclType->getAs(); + const VectorType *VT = DeclType->castAs(); unsigned maxElements = VT->getNumElements(); unsigned numEltsInit = 0; QualType elementType = VT->getElementType(); @@ -1706,7 +1706,7 @@ void InitListChecker::CheckVectorType(const InitializedEntity &Entity, return; bool isBigEndian = SemaRef.Context.getTargetInfo().isBigEndian(); - const VectorType *T = Entity.getType()->getAs(); + const VectorType *T = Entity.getType()->castAs(); if (isBigEndian && (T->getVectorKind() == VectorType::NeonVector || T->getVectorKind() == VectorType::NeonPolyVector)) { // The ability to use vector initializer lists is a GNU vector extension @@ -1762,7 +1762,7 @@ void InitListChecker::CheckVectorType(const InitializedEntity &Entity, ++numEltsInit; } else { QualType VecType; - const VectorType *IVT = IType->getAs(); + const VectorType *IVT = IType->castAs(); unsigned numIElts = IVT->getNumElements(); if (IType->isExtVectorType()) @@ -1995,7 +1995,7 @@ void InitListChecker::CheckStructUnionTypes( bool SubobjectIsDesignatorContext, unsigned &Index, InitListExpr *StructuredList, unsigned &StructuredIndex, bool TopLevelObject) { - RecordDecl *structDecl = DeclType->getAs()->getDecl(); + RecordDecl *structDecl = DeclType->castAs()->getDecl(); // If the record is invalid, some of it's members are invalid. To avoid // confusion, we forgo checking the intializer for the entire record. @@ -2007,7 +2007,7 @@ void InitListChecker::CheckStructUnionTypes( } if (DeclType->isUnionType() && IList->getNumInits() == 0) { - RecordDecl *RD = DeclType->getAs()->getDecl(); + RecordDecl *RD = DeclType->castAs()->getDecl(); if (!VerifyOnly) for (FieldDecl *FD : RD->fields()) { @@ -2082,7 +2082,7 @@ void InitListChecker::CheckStructUnionTypes( // anything except look at designated initializers; That's okay, // because an error should get printed out elsewhere. It might be // worthwhile to skip over the rest of the initializer, though. - RecordDecl *RD = DeclType->getAs()->getDecl(); + RecordDecl *RD = DeclType->castAs()->getDecl(); RecordDecl::field_iterator FieldEnd = RD->field_end(); bool CheckForMissingFields = !IList->isIdiomaticZeroInitializer(SemaRef.getLangOpts()); @@ -4206,7 +4206,7 @@ static void TryReferenceListInitialization(Sema &S, } QualType DestType = Entity.getType(); - QualType cv1T1 = DestType->getAs()->getPointeeType(); + QualType cv1T1 = DestType->castAs()->getPointeeType(); Qualifiers T1Quals; QualType T1 = S.Context.getUnqualifiedArrayType(cv1T1, T1Quals); @@ -4463,7 +4463,7 @@ static OverloadingResult TryRefInitWithConversionFunction( Expr *Initializer, bool AllowRValues, bool IsLValueRef, InitializationSequence &Sequence) { QualType DestType = Entity.getType(); - QualType cv1T1 = DestType->getAs()->getPointeeType(); + QualType cv1T1 = DestType->castAs()->getPointeeType(); QualType T1 = cv1T1.getUnqualifiedType(); QualType cv2T2 = Initializer->getType(); QualType T2 = cv2T2.getUnqualifiedType(); @@ -4656,7 +4656,7 @@ static void TryReferenceInitialization(Sema &S, Expr *Initializer, InitializationSequence &Sequence) { QualType DestType = Entity.getType(); - QualType cv1T1 = DestType->getAs()->getPointeeType(); + QualType cv1T1 = DestType->castAs()->getPointeeType(); Qualifiers T1Quals; QualType T1 = S.Context.getUnqualifiedArrayType(cv1T1, T1Quals); QualType cv2T2 = Initializer->getType(); @@ -7580,34 +7580,27 @@ static void CheckMoveOnConstruction(Sema &S, const Expr *InitExpr, if (!DestType->isRecordType()) return; - const CXXConstructExpr *CCE = - dyn_cast(InitExpr->IgnoreParens()); - if (!CCE || CCE->getNumArgs() != 1) - return; + unsigned DiagID = 0; + if (IsReturnStmt) { + const CXXConstructExpr *CCE = + dyn_cast(InitExpr->IgnoreParens()); + if (!CCE || CCE->getNumArgs() != 1) + return; - if (!CCE->getConstructor()->isCopyOrMoveConstructor()) - return; + if (!CCE->getConstructor()->isCopyOrMoveConstructor()) + return; - InitExpr = CCE->getArg(0)->IgnoreImpCasts(); + InitExpr = CCE->getArg(0)->IgnoreImpCasts(); + } // Find the std::move call and get the argument. const CallExpr *CE = dyn_cast(InitExpr->IgnoreParens()); if (!CE || !CE->isCallToStdMove()) return; - const Expr *Arg = CE->getArg(0); + const Expr *Arg = CE->getArg(0)->IgnoreImplicit(); - unsigned DiagID = 0; - - if (!IsReturnStmt && !isa(Arg)) - return; - - if (isa(Arg)) { - DiagID = diag::warn_pessimizing_move_on_initialization; - const Expr *ArgStripped = Arg->IgnoreImplicit()->IgnoreParens(); - if (!ArgStripped->isRValue() || !ArgStripped->getType()->isRecordType()) - return; - } else { // IsReturnStmt + if (IsReturnStmt) { const DeclRefExpr *DRE = dyn_cast(Arg->IgnoreParenImpCasts()); if (!DRE || DRE->refersToEnclosingVariableOrCapture()) return; @@ -7634,18 +7627,24 @@ static void CheckMoveOnConstruction(Sema &S, const Expr *InitExpr, DiagID = diag::warn_redundant_move_on_return; else DiagID = diag::warn_pessimizing_move_on_return; + } else { + DiagID = diag::warn_pessimizing_move_on_initialization; + const Expr *ArgStripped = Arg->IgnoreImplicit()->IgnoreParens(); + if (!ArgStripped->isRValue() || !ArgStripped->getType()->isRecordType()) + return; } S.Diag(CE->getBeginLoc(), DiagID); // Get all the locations for a fix-it. Don't emit the fix-it if any location // is within a macro. - SourceLocation BeginLoc = CCE->getBeginLoc(); - if (BeginLoc.isMacroID()) + SourceLocation CallBegin = CE->getCallee()->getBeginLoc(); + if (CallBegin.isMacroID()) return; SourceLocation RParen = CE->getRParenLoc(); if (RParen.isMacroID()) return; + SourceLocation LParen; SourceLocation ArgLoc = Arg->getBeginLoc(); // Special testing for the argument location. Since the fix-it needs the @@ -7656,16 +7655,14 @@ static void CheckMoveOnConstruction(Sema &S, const Expr *InitExpr, ArgLoc = S.getSourceManager().getImmediateExpansionRange(ArgLoc).getBegin(); } - SourceLocation LParen = ArgLoc.getLocWithOffset(-1); if (LParen.isMacroID()) return; - SourceLocation EndLoc = CCE->getEndLoc(); - if (EndLoc.isMacroID()) - return; + + LParen = ArgLoc.getLocWithOffset(-1); S.Diag(CE->getBeginLoc(), diag::note_remove_move) - << FixItHint::CreateRemoval(SourceRange(BeginLoc, LParen)) - << FixItHint::CreateRemoval(SourceRange(RParen, EndLoc)); + << FixItHint::CreateRemoval(SourceRange(CallBegin, LParen)) + << FixItHint::CreateRemoval(SourceRange(RParen, RParen)); } static void CheckForNullPointerDereference(Sema &S, const Expr *E) { @@ -8204,7 +8201,7 @@ ExprResult InitializationSequence::Perform(Sema &S, Ty = S.Context.getRValueReferenceType(Ty); else if ((*ResultType)->isLValueReferenceType()) Ty = S.Context.getLValueReferenceType(Ty, - (*ResultType)->getAs()->isSpelledAsLValue()); + (*ResultType)->castAs()->isSpelledAsLValue()); *ResultType = Ty; } @@ -8672,7 +8669,7 @@ static void diagnoseListInit(Sema &S, const InitializedEntity &Entity, // A list-initialization failure for a reference means that we tried to // create a temporary of the inner type (per [dcl.init.list]p3.6) and the // inner initialization failed. - QualType T = DestType->getAs()->getPointeeType(); + QualType T = DestType->castAs()->getPointeeType(); diagnoseListInit(S, InitializedEntity::InitializeTemporary(T), InitList); SourceLocation Loc = InitList->getBeginLoc(); if (auto *D = Entity.getDecl()) @@ -9029,7 +9026,7 @@ bool InitializationSequence::Diagnose(Sema &S, << InheritedFrom; RecordDecl *BaseDecl - = Entity.getBaseSpecifier()->getType()->getAs() + = Entity.getBaseSpecifier()->getType()->castAs() ->getDecl(); S.Diag(BaseDecl->getLocation(), diag::note_previous_decl) << S.Context.getTagDeclType(BaseDecl); diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 83825275782b1..fdb8f488471f6 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -764,6 +764,13 @@ static void InsertOCLBuiltinDeclarationsFromTable(Sema &S, LookupResult &LR, BuiltinTable[FctIndex + SignatureIndex]; ASTContext &Context = S.Context; + // Ignore this BIF if its version does not match the language options. + if (Context.getLangOpts().OpenCLVersion < OpenCLBuiltin.MinVersion) + continue; + if ((OpenCLBuiltin.MaxVersion != 0) && + (Context.getLangOpts().OpenCLVersion >= OpenCLBuiltin.MaxVersion)) + continue; + SmallVector RetTypes; SmallVector, 5> ArgTypes; @@ -2822,7 +2829,7 @@ addAssociatedClassesAndNamespaces(AssociatedLookup &Result, QualType Ty) { #define NON_CANONICAL_TYPE(Class, Base) case Type::Class: #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) case Type::Class: #define ABSTRACT_TYPE(Class, Base) -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" // T is canonical. We can also ignore dependent types because // we don't need to do ADL at the definition point, but if we // wanted to implement template export (or if we find some other @@ -3701,6 +3708,7 @@ NamedDecl *VisibleDeclsRecord::checkHidden(NamedDecl *ND) { return nullptr; } +namespace { class LookupVisibleHelper { public: LookupVisibleHelper(VisibleDeclConsumer &Consumer, bool IncludeDependentBases, @@ -4025,6 +4033,7 @@ class LookupVisibleHelper { bool IncludeDependentBases; bool LoadExternal; }; +} // namespace void Sema::LookupVisibleDecls(Scope *S, LookupNameKind Kind, VisibleDeclConsumer &Consumer, @@ -5273,8 +5282,11 @@ static NamedDecl *getDefinitionToImport(NamedDecl *D) { return FD->getDefinition(); if (TagDecl *TD = dyn_cast(D)) return TD->getDefinition(); + // The first definition for this ObjCInterfaceDecl might be in the TU + // and not associated with any module. Use the one we know to be complete + // and have just seen in a module. if (ObjCInterfaceDecl *ID = dyn_cast(D)) - return ID->getDefinition(); + return ID; if (ObjCProtocolDecl *PD = dyn_cast(D)) return PD->getDefinition(); if (TemplateDecl *TD = dyn_cast(D)) diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp index 10de0ca91221c..1fca351bfb096 100644 --- a/clang/lib/Sema/SemaModule.cpp +++ b/clang/lib/Sema/SemaModule.cpp @@ -31,6 +31,8 @@ static void checkModuleImportContext(Sema &S, Module *M, ExternCLoc = LSD->getBeginLoc(); break; case LinkageSpecDecl::lang_cxx: + case LinkageSpecDecl::lang_cxx_11: + case LinkageSpecDecl::lang_cxx_14: break; } DC = LSD->getParent(); diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index dbf8155d029f8..2b659f7f11f98 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -170,7 +170,7 @@ class DSAStackTy { OpenMPClauseKind ClauseKindMode = OMPC_unknown; Sema &SemaRef; bool ForceCapturing = false; - /// true if all the vaiables in the target executable directives must be + /// true if all the variables in the target executable directives must be /// captured by reference. bool ForceCaptureByReferenceInTargetExecutable = false; CriticalsWithHintsTy Criticals; @@ -3447,6 +3447,7 @@ void Sema::ActOnOpenMPRegionStart(OpenMPDirectiveKind DKind, Scope *CurScope) { case OMPD_declare_target: case OMPD_end_declare_target: case OMPD_requires: + case OMPD_declare_variant: llvm_unreachable("OpenMP Directive is not allowed"); case OMPD_unknown: llvm_unreachable("Unknown OpenMP directive"); @@ -4516,6 +4517,7 @@ StmtResult Sema::ActOnOpenMPExecutableDirective( case OMPD_declare_mapper: case OMPD_declare_simd: case OMPD_requires: + case OMPD_declare_variant: llvm_unreachable("OpenMP Directive is not allowed"); case OMPD_unknown: llvm_unreachable("Unknown OpenMP directive"); @@ -4597,6 +4599,7 @@ StmtResult Sema::ActOnOpenMPExecutableDirective( case OMPC_dynamic_allocators: case OMPC_atomic_default_mem_order: case OMPC_device_type: + case OMPC_match: llvm_unreachable("Unexpected clause"); } for (Stmt *CC : C->children()) { @@ -4653,8 +4656,10 @@ Sema::DeclGroupPtrTy Sema::ActOnOpenMPDeclareSimdDirective( if (!DG || DG.get().isNull()) return DeclGroupPtrTy(); + const int SimdId = 0; if (!DG.get().isSingleDecl()) { - Diag(SR.getBegin(), diag::err_omp_single_decl_in_declare_simd); + Diag(SR.getBegin(), diag::err_omp_single_decl_in_declare_simd_variant) + << SimdId; return DG; } Decl *ADecl = DG.get().getSingleDecl(); @@ -4663,7 +4668,7 @@ Sema::DeclGroupPtrTy Sema::ActOnOpenMPDeclareSimdDirective( auto *FD = dyn_cast(ADecl); if (!FD) { - Diag(ADecl->getLocation(), diag::err_omp_function_expected); + Diag(ADecl->getLocation(), diag::err_omp_function_expected) << SimdId; return DeclGroupPtrTy(); } @@ -4885,7 +4890,265 @@ Sema::DeclGroupPtrTy Sema::ActOnOpenMPDeclareSimdDirective( const_cast(LinModifiers.data()), LinModifiers.size(), NewSteps.data(), NewSteps.size(), SR); ADecl->addAttr(NewAttr); - return ConvertDeclToDeclGroup(ADecl); + return DG; +} + +Optional> +Sema::checkOpenMPDeclareVariantFunction(Sema::DeclGroupPtrTy DG, + Expr *VariantRef, SourceRange SR) { + if (!DG || DG.get().isNull()) + return None; + + const int VariantId = 1; + // Must be applied only to single decl. + if (!DG.get().isSingleDecl()) { + Diag(SR.getBegin(), diag::err_omp_single_decl_in_declare_simd_variant) + << VariantId << SR; + return None; + } + Decl *ADecl = DG.get().getSingleDecl(); + if (auto *FTD = dyn_cast(ADecl)) + ADecl = FTD->getTemplatedDecl(); + + // Decl must be a function. + auto *FD = dyn_cast(ADecl); + if (!FD) { + Diag(ADecl->getLocation(), diag::err_omp_function_expected) + << VariantId << SR; + return None; + } + + auto &&HasMultiVersionAttributes = [](const FunctionDecl *FD) { + return FD->hasAttrs() && + (FD->hasAttr() || FD->hasAttr() || + FD->hasAttr()); + }; + // OpenMP is not compatible with CPU-specific attributes. + if (HasMultiVersionAttributes(FD)) { + Diag(FD->getLocation(), diag::err_omp_declare_variant_incompat_attributes) + << SR; + return None; + } + + // Allow #pragma omp declare variant only if the function is not used. + if (FD->isUsed(false)) + Diag(SR.getBegin(), diag::warn_omp_declare_variant_after_used) + << FD->getLocation(); + + // Check if the function was emitted already. + const FunctionDecl *Definition; + if (!FD->isThisDeclarationADefinition() && FD->isDefined(Definition) && + (LangOpts.EmitAllDecls || Context.DeclMustBeEmitted(Definition))) + Diag(SR.getBegin(), diag::warn_omp_declare_variant_after_emitted) + << FD->getLocation(); + + // The VariantRef must point to function. + if (!VariantRef) { + Diag(SR.getBegin(), diag::err_omp_function_expected) << VariantId; + return None; + } + + // Do not check templates, wait until instantiation. + if (VariantRef->isTypeDependent() || VariantRef->isValueDependent() || + VariantRef->containsUnexpandedParameterPack() || + VariantRef->isInstantiationDependent() || FD->isDependentContext()) + return std::make_pair(FD, VariantRef); + + // Convert VariantRef expression to the type of the original function to + // resolve possible conflicts. + ExprResult VariantRefCast; + if (LangOpts.CPlusPlus) { + QualType FnPtrType; + auto *Method = dyn_cast(FD); + if (Method && !Method->isStatic()) { + const Type *ClassType = + Context.getTypeDeclType(Method->getParent()).getTypePtr(); + FnPtrType = Context.getMemberPointerType(FD->getType(), ClassType); + ExprResult ER; + { + // Build adrr_of unary op to correctly handle type checks for member + // functions. + Sema::TentativeAnalysisScope Trap(*this); + ER = CreateBuiltinUnaryOp(VariantRef->getBeginLoc(), UO_AddrOf, + VariantRef); + } + if (!ER.isUsable()) { + Diag(VariantRef->getExprLoc(), diag::err_omp_function_expected) + << VariantId << VariantRef->getSourceRange(); + return None; + } + VariantRef = ER.get(); + } else { + FnPtrType = Context.getPointerType(FD->getType()); + } + ImplicitConversionSequence ICS = + TryImplicitConversion(VariantRef, FnPtrType.getUnqualifiedType(), + /*SuppressUserConversions=*/false, + /*AllowExplicit=*/false, + /*InOverloadResolution=*/false, + /*CStyle=*/false, + /*AllowObjCWritebackConversion=*/false); + if (ICS.isFailure()) { + Diag(VariantRef->getExprLoc(), + diag::err_omp_declare_variant_incompat_types) + << VariantRef->getType() << FnPtrType << VariantRef->getSourceRange(); + return None; + } + VariantRefCast = PerformImplicitConversion( + VariantRef, FnPtrType.getUnqualifiedType(), AA_Converting); + if (!VariantRefCast.isUsable()) + return None; + // Drop previously built artificial addr_of unary op for member functions. + if (Method && !Method->isStatic()) { + Expr *PossibleAddrOfVariantRef = VariantRefCast.get(); + if (auto *UO = dyn_cast( + PossibleAddrOfVariantRef->IgnoreImplicit())) + VariantRefCast = UO->getSubExpr(); + } + } else { + VariantRefCast = VariantRef; + } + + ExprResult ER = CheckPlaceholderExpr(VariantRefCast.get()); + if (!ER.isUsable() || + !ER.get()->IgnoreParenImpCasts()->getType()->isFunctionType()) { + Diag(VariantRef->getExprLoc(), diag::err_omp_function_expected) + << VariantId << VariantRef->getSourceRange(); + return None; + } + + // The VariantRef must point to function. + auto *DRE = dyn_cast(ER.get()->IgnoreParenImpCasts()); + if (!DRE) { + Diag(VariantRef->getExprLoc(), diag::err_omp_function_expected) + << VariantId << VariantRef->getSourceRange(); + return None; + } + auto *NewFD = dyn_cast_or_null(DRE->getDecl()); + if (!NewFD) { + Diag(VariantRef->getExprLoc(), diag::err_omp_function_expected) + << VariantId << VariantRef->getSourceRange(); + return None; + } + + // Check if variant function is not marked with declare variant directive. + if (NewFD->hasAttrs() && NewFD->hasAttr()) { + Diag(VariantRef->getExprLoc(), + diag::warn_omp_declare_variant_marked_as_declare_variant) + << VariantRef->getSourceRange(); + SourceRange SR = + NewFD->specific_attr_begin()->getRange(); + Diag(SR.getBegin(), diag::note_omp_marked_declare_variant_here) << SR; + return None; + } + + enum DoesntSupport { + VirtFuncs = 1, + Constructors = 3, + Destructors = 4, + DeletedFuncs = 5, + DefaultedFuncs = 6, + ConstexprFuncs = 7, + ConstevalFuncs = 8, + }; + if (const auto *CXXFD = dyn_cast(FD)) { + if (CXXFD->isVirtual()) { + Diag(FD->getLocation(), diag::err_omp_declare_variant_doesnt_support) + << VirtFuncs; + return None; + } + + if (isa(FD)) { + Diag(FD->getLocation(), diag::err_omp_declare_variant_doesnt_support) + << Constructors; + return None; + } + + if (isa(FD)) { + Diag(FD->getLocation(), diag::err_omp_declare_variant_doesnt_support) + << Destructors; + return None; + } + } + + if (FD->isDeleted()) { + Diag(FD->getLocation(), diag::err_omp_declare_variant_doesnt_support) + << DeletedFuncs; + return None; + } + + if (FD->isDefaulted()) { + Diag(FD->getLocation(), diag::err_omp_declare_variant_doesnt_support) + << DefaultedFuncs; + return None; + } + + if (FD->isConstexpr()) { + Diag(FD->getLocation(), diag::err_omp_declare_variant_doesnt_support) + << (NewFD->isConsteval() ? ConstevalFuncs : ConstexprFuncs); + return None; + } + + // Check general compatibility. + if (areMultiversionVariantFunctionsCompatible( + FD, NewFD, PDiag(diag::err_omp_declare_variant_noproto), + PartialDiagnosticAt( + SR.getBegin(), + PDiag(diag::note_omp_declare_variant_specified_here) << SR), + PartialDiagnosticAt( + VariantRef->getExprLoc(), + PDiag(diag::err_omp_declare_variant_doesnt_support)), + PartialDiagnosticAt(VariantRef->getExprLoc(), + PDiag(diag::err_omp_declare_variant_diff) + << FD->getLocation()), + /*TemplatesSupported=*/true, /*ConstexprSupported=*/false)) + return None; + return std::make_pair(FD, cast(DRE)); +} + +void Sema::ActOnOpenMPDeclareVariantDirective( + FunctionDecl *FD, Expr *VariantRef, SourceRange SR, + const Sema::OpenMPDeclareVariantCtsSelectorData &Data) { + if (Data.CtxSet == OMPDeclareVariantAttr::CtxSetUnknown || + Data.Ctx == OMPDeclareVariantAttr::CtxUnknown) + return; + Expr *Score = nullptr; + OMPDeclareVariantAttr::ScoreType ST = OMPDeclareVariantAttr::ScoreUnknown; + if (Data.CtxScore.isUsable()) { + ST = OMPDeclareVariantAttr::ScoreSpecified; + Score = Data.CtxScore.get(); + if (!Score->isTypeDependent() && !Score->isValueDependent() && + !Score->isInstantiationDependent() && + !Score->containsUnexpandedParameterPack()) { + llvm::APSInt Result; + ExprResult ICE = VerifyIntegerConstantExpression(Score, &Result); + if (ICE.isInvalid()) + return; + } + } + auto *NewAttr = OMPDeclareVariantAttr::CreateImplicit( + Context, VariantRef, Score, Data.CtxSet, ST, Data.Ctx, Data.ImplVendor, + SR); + FD->addAttr(NewAttr); +} + +void Sema::markOpenMPDeclareVariantFuncsReferenced(SourceLocation Loc, + FunctionDecl *Func, + bool MightBeOdrUse) { + assert(LangOpts.OpenMP && "Expected OpenMP mode."); + + if (!Func->isDependentContext() && Func->hasAttrs()) { + for (OMPDeclareVariantAttr *A : + Func->specific_attrs()) { + // TODO: add checks for active OpenMP context where possible. + Expr *VariantRef = A->getVariantFuncRef(); + auto *DRE = dyn_cast(VariantRef->IgnoreParenImpCasts()); + auto *F = cast(DRE->getDecl()); + if (!F->isDefined() && F->isTemplateInstantiation()) + InstantiateFunctionDefinition(Loc, F->getFirstDecl()); + MarkFunctionReferenced(Loc, F, MightBeOdrUse); + } + } } StmtResult Sema::ActOnOpenMPParallelDirective(ArrayRef Clauses, @@ -5999,13 +6262,21 @@ Expr *OpenMPIterationSpaceChecker::buildFinalCondition(Scope *S) const { Expr *OpenMPIterationSpaceChecker::buildPreCond( Scope *S, Expr *Cond, llvm::MapVector &Captures) const { + // Do not build a precondition when the condition/initialization is dependent + // to prevent pessimistic early loop exit. + // TODO: this can be improved by calculating min/max values but not sure that + // it will be very effective. + if (CondDependOnLC || InitDependOnLC) + return SemaRef.PerformImplicitConversion( + SemaRef.ActOnIntegerConstant(SourceLocation(), 1).get(), + SemaRef.Context.BoolTy, /*Action=*/Sema::AA_Casting, + /*AllowExplicit=*/true).get(); + // Try to build LB UB, where is <, >, <=, or >=. Sema::TentativeAnalysisScope Trap(SemaRef); - ExprResult NewLB = - InitDependOnLC ? LB : tryBuildCapture(SemaRef, LB, Captures); - ExprResult NewUB = - CondDependOnLC ? UB : tryBuildCapture(SemaRef, UB, Captures); + ExprResult NewLB = tryBuildCapture(SemaRef, LB, Captures); + ExprResult NewUB = tryBuildCapture(SemaRef, UB, Captures); if (!NewLB.isUsable() || !NewUB.isUsable()) return nullptr; @@ -7177,7 +7448,7 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr, Built.DependentCounters[Cnt] = nullptr; Built.DependentInits[Cnt] = nullptr; Built.FinalsConditions[Cnt] = nullptr; - if (IS.IsNonRectangularLB) { + if (IS.IsNonRectangularLB || IS.IsNonRectangularUB) { Built.DependentCounters[Cnt] = Built.Counters[NestedLoopCount - 1 - IS.LoopDependentIdx]; Built.DependentInits[Cnt] = @@ -9826,6 +10097,7 @@ OMPClause *Sema::ActOnOpenMPSingleExprClause(OpenMPClauseKind Kind, Expr *Expr, case OMPC_dynamic_allocators: case OMPC_atomic_default_mem_order: case OMPC_device_type: + case OMPC_match: llvm_unreachable("Clause is not allowed."); } return Res; @@ -9895,6 +10167,7 @@ static OpenMPDirectiveKind getOpenMPCaptureRegionForClause( case OMPD_declare_reduction: case OMPD_declare_mapper: case OMPD_declare_simd: + case OMPD_declare_variant: case OMPD_declare_target: case OMPD_end_declare_target: case OMPD_teams: @@ -9963,6 +10236,7 @@ static OpenMPDirectiveKind getOpenMPCaptureRegionForClause( case OMPD_declare_reduction: case OMPD_declare_mapper: case OMPD_declare_simd: + case OMPD_declare_variant: case OMPD_declare_target: case OMPD_end_declare_target: case OMPD_teams: @@ -10032,6 +10306,7 @@ static OpenMPDirectiveKind getOpenMPCaptureRegionForClause( case OMPD_declare_reduction: case OMPD_declare_mapper: case OMPD_declare_simd: + case OMPD_declare_variant: case OMPD_declare_target: case OMPD_end_declare_target: case OMPD_simd: @@ -10098,6 +10373,7 @@ static OpenMPDirectiveKind getOpenMPCaptureRegionForClause( case OMPD_declare_reduction: case OMPD_declare_mapper: case OMPD_declare_simd: + case OMPD_declare_variant: case OMPD_declare_target: case OMPD_end_declare_target: case OMPD_simd: @@ -10165,6 +10441,7 @@ static OpenMPDirectiveKind getOpenMPCaptureRegionForClause( case OMPD_declare_reduction: case OMPD_declare_mapper: case OMPD_declare_simd: + case OMPD_declare_variant: case OMPD_declare_target: case OMPD_end_declare_target: case OMPD_simd: @@ -10231,6 +10508,7 @@ static OpenMPDirectiveKind getOpenMPCaptureRegionForClause( case OMPD_declare_reduction: case OMPD_declare_mapper: case OMPD_declare_simd: + case OMPD_declare_variant: case OMPD_declare_target: case OMPD_end_declare_target: case OMPD_simd: @@ -10296,6 +10574,7 @@ static OpenMPDirectiveKind getOpenMPCaptureRegionForClause( case OMPD_declare_reduction: case OMPD_declare_mapper: case OMPD_declare_simd: + case OMPD_declare_variant: case OMPD_declare_target: case OMPD_end_declare_target: case OMPD_simd: @@ -10369,6 +10648,7 @@ static OpenMPDirectiveKind getOpenMPCaptureRegionForClause( case OMPC_dynamic_allocators: case OMPC_atomic_default_mem_order: case OMPC_device_type: + case OMPC_match: llvm_unreachable("Unexpected OpenMP clause."); } return CaptureRegion; @@ -10763,6 +11043,7 @@ OMPClause *Sema::ActOnOpenMPSimpleClause( case OMPC_reverse_offload: case OMPC_dynamic_allocators: case OMPC_device_type: + case OMPC_match: llvm_unreachable("Clause is not allowed."); } return Res; @@ -10942,6 +11223,7 @@ OMPClause *Sema::ActOnOpenMPSingleExprWithArgClause( case OMPC_dynamic_allocators: case OMPC_atomic_default_mem_order: case OMPC_device_type: + case OMPC_match: llvm_unreachable("Clause is not allowed."); } return Res; @@ -11152,6 +11434,7 @@ OMPClause *Sema::ActOnOpenMPClause(OpenMPClauseKind Kind, case OMPC_is_device_ptr: case OMPC_atomic_default_mem_order: case OMPC_device_type: + case OMPC_match: llvm_unreachable("Clause is not allowed."); } return Res; @@ -11359,6 +11642,7 @@ OMPClause *Sema::ActOnOpenMPVarListClause( case OMPC_dynamic_allocators: case OMPC_atomic_default_mem_order: case OMPC_device_type: + case OMPC_match: llvm_unreachable("Clause is not allowed."); } return Res; @@ -14494,6 +14778,11 @@ static ExprResult buildUserDefinedMapperRef(Sema &SemaRef, Scope *S, Expr *UnresolvedMapper) { if (MapperIdScopeSpec.isInvalid()) return ExprError(); + // Get the actual type for the array type. + if (Type->isArrayType()) { + assert(Type->getAsArrayTypeUnsafe() && "Expect to get a valid array type"); + Type = Type->getAsArrayTypeUnsafe()->getElementType().getCanonicalType(); + } // Find all user-defined mappers with the given MapperId. SmallVector, 4> Lookups; LookupResult Lookup(SemaRef, MapperId, Sema::LookupOMPMapperName); @@ -14540,11 +14829,14 @@ static ExprResult buildUserDefinedMapperRef(Sema &SemaRef, Scope *S, MapperIdScopeSpec.getWithLocInContext(SemaRef.Context), MapperId, /*ADL=*/false, /*Overloaded=*/true, URS.begin(), URS.end()); } + SourceLocation Loc = MapperId.getLoc(); // [OpenMP 5.0], 2.19.7.3 declare mapper Directive, Restrictions // The type must be of struct, union or class type in C and C++ - if (!Type->isStructureOrClassType() && !Type->isUnionType()) - return ExprEmpty(); - SourceLocation Loc = MapperId.getLoc(); + if (!Type->isStructureOrClassType() && !Type->isUnionType() && + (MapperIdScopeSpec.isSet() || MapperId.getAsString() != "default")) { + SemaRef.Diag(Loc, diag::err_omp_mapper_wrong_type); + return ExprError(); + } // Perform argument dependent lookup. if (SemaRef.getLangOpts().CPlusPlus && !MapperIdScopeSpec.isSet()) argumentDependentLookup(SemaRef, MapperId, Loc, Type, Lookups); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index bd6788c1d0d31..cc93ad9195f1b 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -257,8 +257,18 @@ isPointerConversionToVoidPointer(ASTContext& Context) const { /// Skip any implicit casts which could be either part of a narrowing conversion /// or after one in an implicit conversion. -static const Expr *IgnoreNarrowingConversion(const Expr *Converted) { - while (const ImplicitCastExpr *ICE = dyn_cast(Converted)) { +static const Expr *IgnoreNarrowingConversion(ASTContext &Ctx, + const Expr *Converted) { + // We can have cleanups wrapping the converted expression; these need to be + // preserved so that destructors run if necessary. + if (auto *EWC = dyn_cast(Converted)) { + Expr *Inner = + const_cast(IgnoreNarrowingConversion(Ctx, EWC->getSubExpr())); + return ExprWithCleanups::Create(Ctx, Inner, EWC->cleanupsHaveSideEffects(), + EWC->getObjects()); + } + + while (auto *ICE = dyn_cast(Converted)) { switch (ICE->getCastKind()) { case CK_NoOp: case CK_IntegralCast: @@ -332,7 +342,7 @@ NarrowingKind StandardConversionSequence::getNarrowingKind( if (IgnoreFloatToIntegralConversion) return NK_Not_Narrowing; llvm::APSInt IntConstantValue; - const Expr *Initializer = IgnoreNarrowingConversion(Converted); + const Expr *Initializer = IgnoreNarrowingConversion(Ctx, Converted); assert(Initializer && "Unknown conversion expression"); // If it's value-dependent, we can't tell whether it's narrowing. @@ -370,7 +380,7 @@ NarrowingKind StandardConversionSequence::getNarrowingKind( if (FromType->isRealFloatingType() && ToType->isRealFloatingType() && Ctx.getFloatingTypeOrder(FromType, ToType) == 1) { // FromType is larger than ToType. - const Expr *Initializer = IgnoreNarrowingConversion(Converted); + const Expr *Initializer = IgnoreNarrowingConversion(Ctx, Converted); // If it's value-dependent, we can't tell whether it's narrowing. if (Initializer->isValueDependent()) @@ -416,7 +426,7 @@ NarrowingKind StandardConversionSequence::getNarrowingKind( (FromSigned && !ToSigned)) { // Not all values of FromType can be represented in ToType. llvm::APSInt InitializerValue; - const Expr *Initializer = IgnoreNarrowingConversion(Converted); + const Expr *Initializer = IgnoreNarrowingConversion(Ctx, Converted); // If it's value-dependent, we can't tell whether it's narrowing. if (Initializer->isValueDependent()) @@ -1463,14 +1473,14 @@ bool Sema::IsFunctionConversion(QualType FromType, QualType ToType, if (TyClass != CanFrom->getTypeClass()) return false; if (TyClass != Type::FunctionProto && TyClass != Type::FunctionNoProto) { if (TyClass == Type::Pointer) { - CanTo = CanTo.getAs()->getPointeeType(); - CanFrom = CanFrom.getAs()->getPointeeType(); + CanTo = CanTo.castAs()->getPointeeType(); + CanFrom = CanFrom.castAs()->getPointeeType(); } else if (TyClass == Type::BlockPointer) { - CanTo = CanTo.getAs()->getPointeeType(); - CanFrom = CanFrom.getAs()->getPointeeType(); + CanTo = CanTo.castAs()->getPointeeType(); + CanFrom = CanFrom.castAs()->getPointeeType(); } else if (TyClass == Type::MemberPointer) { - auto ToMPT = CanTo.getAs(); - auto FromMPT = CanFrom.getAs(); + auto ToMPT = CanTo.castAs(); + auto FromMPT = CanFrom.castAs(); // A function pointer conversion cannot change the class of the function. if (ToMPT->getClass() != FromMPT->getClass()) return false; @@ -2273,7 +2283,7 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType, // Blocks: Block pointers can be converted to void*. if (FromType->isBlockPointerType() && ToType->isPointerType() && - ToType->getAs()->getPointeeType()->isVoidType()) { + ToType->castAs()->getPointeeType()->isVoidType()) { ConvertedType = ToType; return true; } @@ -3272,7 +3282,7 @@ IsInitializerListConstructorConversion(Sema &S, Expr *From, QualType ToType, User.ConversionFunction = Constructor; User.FoundConversionFunction = Best->FoundDecl; User.After.setAsIdentityConversion(); - User.After.setFromType(ThisType->getAs()->getPointeeType()); + User.After.setFromType(ThisType->castAs()->getPointeeType()); User.After.setAllToTypes(ToType); return Result; } @@ -3463,7 +3473,7 @@ IsUserDefinedConversion(Sema &S, Expr *From, QualType ToType, User.ConversionFunction = Constructor; User.FoundConversionFunction = Best->FoundDecl; User.After.setAsIdentityConversion(); - User.After.setFromType(ThisType->getAs()->getPointeeType()); + User.After.setFromType(ThisType->castAs()->getPointeeType()); User.After.setAllToTypes(ToType); return Result; } @@ -4105,14 +4115,14 @@ CompareDerivedToBaseConversions(Sema &S, SourceLocation Loc, /*FIXME: Remove if Objective-C id conversions get their own rank*/ FromType1->isPointerType() && FromType2->isPointerType() && ToType1->isPointerType() && ToType2->isPointerType()) { - QualType FromPointee1 - = FromType1->getAs()->getPointeeType().getUnqualifiedType(); - QualType ToPointee1 - = ToType1->getAs()->getPointeeType().getUnqualifiedType(); - QualType FromPointee2 - = FromType2->getAs()->getPointeeType().getUnqualifiedType(); - QualType ToPointee2 - = ToType2->getAs()->getPointeeType().getUnqualifiedType(); + QualType FromPointee1 = + FromType1->castAs()->getPointeeType().getUnqualifiedType(); + QualType ToPointee1 = + ToType1->castAs()->getPointeeType().getUnqualifiedType(); + QualType FromPointee2 = + FromType2->castAs()->getPointeeType().getUnqualifiedType(); + QualType ToPointee2 = + ToType2->castAs()->getPointeeType().getUnqualifiedType(); // -- conversion of C* to B* is better than conversion of C* to A*, if (FromPointee1 == FromPointee2 && ToPointee1 != ToPointee2) { @@ -4392,7 +4402,7 @@ FindConversionForRefInit(Sema &S, ImplicitConversionSequence &ICS, bool AllowExplicit) { assert(T2->isRecordType() && "Can only find conversions of record types."); CXXRecordDecl *T2RecordDecl - = dyn_cast(T2->getAs()->getDecl()); + = dyn_cast(T2->castAs()->getDecl()); OverloadCandidateSet CandidateSet( DeclLoc, OverloadCandidateSet::CSK_InitByUserDefinedConversion); @@ -4523,7 +4533,7 @@ TryReferenceInit(Sema &S, Expr *Init, QualType DeclType, ImplicitConversionSequence ICS; ICS.setBad(BadConversionSequence::no_conversion, Init, DeclType); - QualType T1 = DeclType->getAs()->getPointeeType(); + QualType T1 = DeclType->castAs()->getPointeeType(); QualType T2 = Init->getType(); // If the initializer is the address of an overloaded function, try @@ -4947,7 +4957,7 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType, // mention initializer lists in any way. So we go by what list- // initialization would do and try to extrapolate from that. - QualType T1 = ToType->getAs()->getPointeeType(); + QualType T1 = ToType->castAs()->getPointeeType(); // If the initializer list has a single element that is reference-related // to the parameter type, we initialize the reference from that. @@ -5217,7 +5227,7 @@ Sema::PerformObjectArgumentInitialization(Expr *From, CXXMethodDecl *Method) { QualType FromRecordType, DestType; QualType ImplicitParamRecordType = - Method->getThisType()->getAs()->getPointeeType(); + Method->getThisType()->castAs()->getPointeeType(); Expr::Classification FromClassification; if (const PointerType *PT = From->getType()->getAs()) { @@ -5465,6 +5475,14 @@ static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From, if (Result.isInvalid()) return Result; + // C++2a [intro.execution]p5: + // A full-expression is [...] a constant-expression [...] + Result = + S.ActOnFinishFullExpr(Result.get(), From->getExprLoc(), + /*DiscardedValue=*/false, /*IsConstexpr=*/true); + if (Result.isInvalid()) + return Result; + // Check for a narrowing implicit conversion. APValue PreNarrowingValue; QualType PreNarrowingType; @@ -6996,7 +7014,7 @@ void Sema::AddConversionCandidate( if (const PointerType *FromPtrType = ImplicitParamType->getAs()) ImplicitParamType = FromPtrType->getPointeeType(); CXXRecordDecl *ConversionContext - = cast(ImplicitParamType->getAs()->getDecl()); + = cast(ImplicitParamType->castAs()->getDecl()); Candidate.Conversions[0] = TryObjectArgumentInitialization( *this, CandidateSet.getLocation(), From->getType(), @@ -8756,7 +8774,7 @@ class BuiltinOperatorOverloadBuilder { Enum = CandidateTypes[ArgIdx].enumeration_begin(), EnumEnd = CandidateTypes[ArgIdx].enumeration_end(); Enum != EnumEnd; ++Enum) { - if (!(*Enum)->getAs()->getDecl()->isScoped()) + if (!(*Enum)->castAs()->getDecl()->isScoped()) continue; if (!AddedTypes.insert(S.Context.getCanonicalType(*Enum)).second) @@ -9422,13 +9440,15 @@ OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc, const FunctionDecl *Caller = dyn_cast(S.CurContext); bool ContainsSameSideCandidate = llvm::any_of(Candidates, [&](OverloadCandidate *Cand) { - return Cand->Function && + // Check viable function only. + return Cand->Viable && Cand->Function && S.IdentifyCUDAPreference(Caller, Cand->Function) == Sema::CFP_SameSide; }); if (ContainsSameSideCandidate) { auto IsWrongSideCandidate = [&](OverloadCandidate *Cand) { - return Cand->Function && + // Check viable function only to avoid unnecessary data copying/moving. + return Cand->Viable && Cand->Function && S.IdentifyCUDAPreference(Caller, Cand->Function) == Sema::CFP_WrongSide; }; @@ -10779,12 +10799,12 @@ static void CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand, = Cand->Surrogate->getConversionType().getNonReferenceType(); if (const PointerType *ConvPtrType = ConvType->getAs()) ConvType = ConvPtrType->getPointeeType(); - ParamTypes = ConvType->getAs()->getParamTypes(); + ParamTypes = ConvType->castAs()->getParamTypes(); // Conversion 0 is 'this', which doesn't have a corresponding argument. ConvIdx = 1; } else if (Cand->Function) { ParamTypes = - Cand->Function->getType()->getAs()->getParamTypes(); + Cand->Function->getType()->castAs()->getParamTypes(); if (isa(Cand->Function) && !isa(Cand->Function)) { // Conversion 0 is 'this', which doesn't have a corresponding argument. diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index a14580d785c90..34c366cc91759 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -430,45 +430,44 @@ Sema::ActOnCaseExpr(SourceLocation CaseLoc, ExprResult Val) { // If we're not inside a switch, let the 'case' statement handling diagnose // this. Just clean up after the expression as best we can. - if (!getCurFunction()->SwitchStack.empty()) { - Expr *CondExpr = - getCurFunction()->SwitchStack.back().getPointer()->getCond(); - if (!CondExpr) - return ExprError(); - QualType CondType = CondExpr->getType(); - - auto CheckAndFinish = [&](Expr *E) { - if (CondType->isDependentType() || E->isTypeDependent()) - return ExprResult(E); - - if (getLangOpts().CPlusPlus11) { - // C++11 [stmt.switch]p2: the constant-expression shall be a converted - // constant expression of the promoted type of the switch condition. - llvm::APSInt TempVal; - return CheckConvertedConstantExpression(E, CondType, TempVal, - CCEK_CaseValue); - } + if (getCurFunction()->SwitchStack.empty()) + return ActOnFinishFullExpr(Val.get(), Val.get()->getExprLoc(), false, + getLangOpts().CPlusPlus11); - ExprResult ER = E; - if (!E->isValueDependent()) - ER = VerifyIntegerConstantExpression(E); - if (!ER.isInvalid()) - ER = DefaultLvalueConversion(ER.get()); - if (!ER.isInvalid()) - ER = ImpCastExprToType(ER.get(), CondType, CK_IntegralCast); - return ER; - }; + Expr *CondExpr = + getCurFunction()->SwitchStack.back().getPointer()->getCond(); + if (!CondExpr) + return ExprError(); + QualType CondType = CondExpr->getType(); - ExprResult Converted = CorrectDelayedTyposInExpr(Val, CheckAndFinish); - if (Converted.get() == Val.get()) - Converted = CheckAndFinish(Val.get()); - if (Converted.isInvalid()) - return ExprError(); - Val = Converted; - } + auto CheckAndFinish = [&](Expr *E) { + if (CondType->isDependentType() || E->isTypeDependent()) + return ExprResult(E); + + if (getLangOpts().CPlusPlus11) { + // C++11 [stmt.switch]p2: the constant-expression shall be a converted + // constant expression of the promoted type of the switch condition. + llvm::APSInt TempVal; + return CheckConvertedConstantExpression(E, CondType, TempVal, + CCEK_CaseValue); + } - return ActOnFinishFullExpr(Val.get(), Val.get()->getExprLoc(), false, - getLangOpts().CPlusPlus11); + ExprResult ER = E; + if (!E->isValueDependent()) + ER = VerifyIntegerConstantExpression(E); + if (!ER.isInvalid()) + ER = DefaultLvalueConversion(ER.get()); + if (!ER.isInvalid()) + ER = ImpCastExprToType(ER.get(), CondType, CK_IntegralCast); + if (!ER.isInvalid()) + ER = ActOnFinishFullExpr(ER.get(), ER.get()->getExprLoc(), false); + return ER; + }; + + ExprResult Converted = CorrectDelayedTyposInExpr(Val, CheckAndFinish); + if (Converted.get() == Val.get()) + Converted = CheckAndFinish(Val.get()); + return Converted; } StmtResult @@ -3501,6 +3500,14 @@ bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, return true; } + // CUDA: Kernel function must have 'void' return type. + if (getLangOpts().CUDA) + if (FD->hasAttr() && !Deduced->isVoidType()) { + Diag(FD->getLocation(), diag::err_kern_type_not_void_return) + << FD->getType() << FD->getSourceRange(); + return true; + } + // If a function with a declared return type that contains a placeholder type // has multiple return statements, the return type is deduced for each return // statement. [...] if the type deduced is not the same in each deduction, diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 91466fa904be4..e189b5235a970 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1005,15 +1005,10 @@ NamedDecl *Sema::ActOnTypeParameter(Scope *S, bool Typename, assert(S->isTemplateParamScope() && "Template type parameter not in template parameter scope!"); - SourceLocation Loc = ParamNameLoc; - if (!ParamName) - Loc = KeyLoc; - bool IsParameterPack = EllipsisLoc.isValid(); - TemplateTypeParmDecl *Param - = TemplateTypeParmDecl::Create(Context, Context.getTranslationUnitDecl(), - KeyLoc, Loc, Depth, Position, ParamName, - Typename, IsParameterPack); + TemplateTypeParmDecl *Param = TemplateTypeParmDecl::Create( + Context, Context.getTranslationUnitDecl(), KeyLoc, ParamNameLoc, Depth, + Position, ParamName, Typename, IsParameterPack); Param->setAccess(AS_public); if (Param->isParameterPack()) @@ -1044,7 +1039,7 @@ NamedDecl *Sema::ActOnTypeParameter(Scope *S, bool Typename, assert(DefaultTInfo && "expected source information for type"); // Check for unexpanded parameter packs. - if (DiagnoseUnexpandedParameterPack(Loc, DefaultTInfo, + if (DiagnoseUnexpandedParameterPack(ParamNameLoc, DefaultTInfo, UPPC_DefaultArgument)) return Param; @@ -5503,7 +5498,7 @@ namespace { bool Visit##Class##Type(const Class##Type *) { return false; } #define NON_CANONICAL_TYPE(Class, Parent) \ bool Visit##Class##Type(const Class##Type *) { return false; } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" bool VisitTagDecl(const TagDecl *Tag); bool VisitNestedNameSpecifier(NestedNameSpecifier *NNS); @@ -6431,8 +6426,11 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, } // If either the parameter has a dependent type or the argument is - // type-dependent, there's nothing we can check now. - if (ParamType->isDependentType() || Arg->isTypeDependent()) { + // type-dependent, there's nothing we can check now. The argument only + // contains an unexpanded pack during partial ordering, and there's + // nothing more we can check in that case. + if (ParamType->isDependentType() || Arg->isTypeDependent() || + Arg->containsUnexpandedParameterPack()) { // Force the argument to the type of the parameter to maintain invariants. auto *PE = dyn_cast(Arg); if (PE) diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index db50830a15da0..16d5afbd87e11 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -1486,7 +1486,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, #define NON_CANONICAL_TYPE(Class, Base) \ case Type::Class: llvm_unreachable("deducing non-canonical type: " #Class); #define TYPE(Class, Base) -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" case Type::TemplateTypeParm: case Type::SubstTemplateTypeParmPack: @@ -3093,6 +3093,13 @@ Sema::SubstituteExplicitTemplateArguments( Function->getTypeSpecStartLoc(), Function->getDeclName()); if (ResultType.isNull() || Trap.hasErrorOccurred()) return TDK_SubstitutionFailure; + // CUDA: Kernel function must have 'void' return type. + if (getLangOpts().CUDA) + if (Function->hasAttr() && !ResultType->isVoidType()) { + Diag(Function->getLocation(), diag::err_kern_type_not_void_return) + << Function->getType() << Function->getSourceRange(); + return TDK_SubstitutionFailure; + } } // Instantiate the types of each of the function parameters given the @@ -5608,7 +5615,7 @@ MarkUsedTemplateParameters(ASTContext &Ctx, QualType T, #define ABSTRACT_TYPE(Class, Base) #define DEPENDENT_TYPE(Class, Base) #define NON_CANONICAL_TYPE(Class, Base) case Type::Class: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" break; } } diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 632f76dc37cb5..cfce84a9c0333 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -349,6 +349,65 @@ static void instantiateOMPDeclareSimdDeclAttr( Attr.getRange()); } +/// Instantiation of 'declare variant' attribute and its arguments. +static void instantiateOMPDeclareVariantAttr( + Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, + const OMPDeclareVariantAttr &Attr, Decl *New) { + // Allow 'this' in clauses with varlists. + if (auto *FTD = dyn_cast(New)) + New = FTD->getTemplatedDecl(); + auto *FD = cast(New); + auto *ThisContext = dyn_cast_or_null(FD->getDeclContext()); + + auto &&SubstExpr = [FD, ThisContext, &S, &TemplateArgs](Expr *E) { + if (auto *DRE = dyn_cast(E->IgnoreParenImpCasts())) + if (auto *PVD = dyn_cast(DRE->getDecl())) { + Sema::ContextRAII SavedContext(S, FD); + LocalInstantiationScope Local(S); + if (FD->getNumParams() > PVD->getFunctionScopeIndex()) + Local.InstantiatedLocal( + PVD, FD->getParamDecl(PVD->getFunctionScopeIndex())); + return S.SubstExpr(E, TemplateArgs); + } + Sema::CXXThisScopeRAII ThisScope(S, ThisContext, Qualifiers(), + FD->isCXXInstanceMember()); + return S.SubstExpr(E, TemplateArgs); + }; + + // Substitute a single OpenMP clause, which is a potentially-evaluated + // full-expression. + auto &&Subst = [&SubstExpr, &S](Expr *E) { + EnterExpressionEvaluationContext Evaluated( + S, Sema::ExpressionEvaluationContext::PotentiallyEvaluated); + ExprResult Res = SubstExpr(E); + if (Res.isInvalid()) + return Res; + return S.ActOnFinishFullExpr(Res.get(), false); + }; + + ExprResult VariantFuncRef; + if (Expr *E = Attr.getVariantFuncRef()) + VariantFuncRef = Subst(E); + + ExprResult Score; + if (Expr *E = Attr.getScore()) + Score = Subst(E); + + // Check function/variant ref. + Optional> DeclVarData = + S.checkOpenMPDeclareVariantFunction( + S.ConvertDeclToDeclGroup(New), VariantFuncRef.get(), Attr.getRange()); + if (!DeclVarData) + return; + // Instantiate the attribute. + Sema::OpenMPDeclareVariantCtsSelectorData Data(Attr.getCtxSelectorSet(), + Attr.getCtxSelector(), + Attr.getImplVendor(), Score); + S.ActOnOpenMPDeclareVariantDirective(DeclVarData.getValue().first, + DeclVarData.getValue().second, + Attr.getRange(), Data); +} + static void instantiateDependentAMDGPUFlatWorkGroupSizeAttr( Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, const AMDGPUFlatWorkGroupSizeAttr &Attr, Decl *New) { @@ -506,6 +565,11 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, continue; } + if (const auto *OMPAttr = dyn_cast(TmplAttr)) { + instantiateOMPDeclareVariantAttr(*this, TemplateArgs, *OMPAttr, New); + continue; + } + if (const auto *AMDGPUFlatWorkGroupSize = dyn_cast(TmplAttr)) { instantiateDependentAMDGPUFlatWorkGroupSizeAttr( @@ -2091,10 +2155,9 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl( Constructor->getConstexprKind()); Method->setRangeEnd(Constructor->getEndLoc()); } else if (CXXDestructorDecl *Destructor = dyn_cast(D)) { - Method = CXXDestructorDecl::Create(SemaRef.Context, Record, - StartLoc, NameInfo, T, TInfo, - Destructor->isInlineSpecified(), - false); + Method = CXXDestructorDecl::Create( + SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo, + Destructor->isInlineSpecified(), false, Destructor->getConstexprKind()); Method->setRangeEnd(Destructor->getEndLoc()); } else if (CXXConversionDecl *Conversion = dyn_cast(D)) { Method = CXXConversionDecl::Create( diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index f3b90c3567934..c8fe686a84a60 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -1635,6 +1635,8 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { case OpenCLAccessAttr::Keyword_read_only: \ Result = Context.Id##ROTy; \ break; \ + case OpenCLAccessAttr::SpellingNotCalculated: \ + llvm_unreachable("Spelling not yet calculated"); \ } \ break; #include "clang/Basic/OpenCLImageTypes.def" @@ -8254,20 +8256,28 @@ bool Sema::RequireLiteralType(SourceLocation Loc, QualType T, return true; } } - } else if (!RD->hasTrivialDestructor()) { - // All fields and bases are of literal types, so have trivial destructors. - // If this class's destructor is non-trivial it must be user-declared. + } else if (getLangOpts().CPlusPlus2a ? !RD->hasConstexprDestructor() + : !RD->hasTrivialDestructor()) { + // All fields and bases are of literal types, so have trivial or constexpr + // destructors. If this class's destructor is non-trivial / non-constexpr, + // it must be user-declared. CXXDestructorDecl *Dtor = RD->getDestructor(); assert(Dtor && "class has literal fields and bases but no dtor?"); if (!Dtor) return true; - Diag(Dtor->getLocation(), Dtor->isUserProvided() ? - diag::note_non_literal_user_provided_dtor : - diag::note_non_literal_nontrivial_dtor) << RD; - if (!Dtor->isUserProvided()) - SpecialMemberIsTrivial(Dtor, CXXDestructor, TAH_IgnoreTrivialABI, - /*Diagnose*/true); + if (getLangOpts().CPlusPlus2a) { + Diag(Dtor->getLocation(), diag::note_non_literal_non_constexpr_dtor) + << RD; + } else { + Diag(Dtor->getLocation(), Dtor->isUserProvided() + ? diag::note_non_literal_user_provided_dtor + : diag::note_non_literal_nontrivial_dtor) + << RD; + if (!Dtor->isUserProvided()) + SpecialMemberIsTrivial(Dtor, CXXDestructor, TAH_IgnoreTrivialABI, + /*Diagnose*/ true); + } } return true; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index c2a144a401f6f..84e38c6d78fbe 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -3308,16 +3308,14 @@ class TreeTransform { /// /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. - ExprResult RebuildAtomicExpr(SourceLocation BuiltinLoc, - MultiExprArg SubExprs, - QualType RetTy, + ExprResult RebuildAtomicExpr(SourceLocation BuiltinLoc, MultiExprArg SubExprs, AtomicExpr::AtomicOp Op, SourceLocation RParenLoc) { - // Just create the expression; there is not any interesting semantic - // analysis here because we can't actually build an AtomicExpr until - // we are sure it is semantically sound. - return new (SemaRef.Context) AtomicExpr(BuiltinLoc, SubExprs, RetTy, Op, - RParenLoc); + // Use this for all of the locations, since we don't know the difference + // between the call and the expr at this point. + SourceRange Range{BuiltinLoc, RParenLoc}; + return getSema().BuildAtomicExpr(Range, Range, RParenLoc, SubExprs, Op, + Sema::AtomicArgumentOrder::AST); } private: @@ -9221,6 +9219,7 @@ TreeTransform::TransformDeclRefExpr(DeclRefExpr *E) { if (!getDerived().AlwaysRebuild() && QualifierLoc == E->getQualifierLoc() && ND == E->getDecl() && + Found == E->getFoundDecl() && NameInfo.getName() == E->getDecl()->getDeclName() && !E->hasExplicitTemplateArgs()) { @@ -11325,10 +11324,14 @@ TreeTransform::TransformLambdaExpr(LambdaExpr *E) { } } + LambdaScopeInfo *LSI = getSema().PushLambdaScope(); + Sema::FunctionScopeRAII FuncScopeCleanup(getSema()); + // Transform the template parameters, and add them to the current // instantiation scope. The null case is handled correctly. auto TPL = getDerived().TransformTemplateParameterList( E->getTemplateParameterList()); + LSI->GLTemplateParameterList = TPL; // Transform the type of the original lambda's call operator. // The transformation MUST be done in the CurrentInstantiationScope since @@ -11355,10 +11358,6 @@ TreeTransform::TransformLambdaExpr(LambdaExpr *E) { NewCallOpType); } - LambdaScopeInfo *LSI = getSema().PushLambdaScope(); - Sema::FunctionScopeRAII FuncScopeCleanup(getSema()); - LSI->GLTemplateParameterList = TPL; - // Create the local class that will describe the lambda. CXXRecordDecl *OldClass = E->getLambdaClass(); CXXRecordDecl *Class @@ -12673,7 +12672,6 @@ TreeTransform::TransformAsTypeExpr(AsTypeExpr *E) { template ExprResult TreeTransform::TransformAtomicExpr(AtomicExpr *E) { - QualType RetTy = getDerived().TransformType(E->getType()); bool ArgumentChanged = false; SmallVector SubExprs; SubExprs.reserve(E->getNumSubExprs()); @@ -12686,7 +12684,7 @@ TreeTransform::TransformAtomicExpr(AtomicExpr *E) { return E; return getDerived().RebuildAtomicExpr(E->getBuiltinLoc(), SubExprs, - RetTy, E->getOp(), E->getRParenLoc()); + E->getOp(), E->getRParenLoc()); } //===----------------------------------------------------------------------===// diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 10b2a5c4f9dfb..fcb437e0cb1e7 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -12202,7 +12202,7 @@ Expected ASTRecordReader::readRecord(llvm::BitstreamCursor &Cursor, ////===----------------------------------------------------------------------===// OMPClause *OMPClauseReader::readClause() { - OMPClause *C; + OMPClause *C = nullptr; switch (Record.readInt()) { case OMPC_if: C = new (Context) OMPIfClause(); @@ -12403,6 +12403,8 @@ OMPClause *OMPClauseReader::readClause() { C = OMPAllocateClause::CreateEmpty(Context, Record.readInt()); break; } + assert(C && "Unknown OMPClause type"); + Visit(C); C->setLocStart(Record.readSourceLocation()); C->setLocEnd(Record.readSourceLocation()); diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index e9cf366983fb3..76928182a1e8e 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1390,10 +1390,11 @@ ASTDeclReader::RedeclarableResult ASTDeclReader::VisitVarDeclImpl(VarDecl *VD) { if (uint64_t Val = Record.readInt()) { VD->setInit(Record.readExpr()); - if (Val > 1) { // IsInitKnownICE = 1, IsInitNotICE = 2, IsInitICE = 3 + if (Val > 1) { EvaluatedStmt *Eval = VD->ensureEvaluatedStmt(); Eval->CheckedICE = true; - Eval->IsICE = Val == 3; + Eval->IsICE = (Val & 1) != 0; + Eval->HasConstantDestruction = (Val & 4) != 0; } } diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 8018ee8b78e98..e1f3d50456b11 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -166,7 +166,7 @@ namespace clang { #define TYPE(Class, Base) \ case Type::Class: Visit##Class##Type(cast(T)); break; #define ABSTRACT_TYPE(Class, Base) -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } } } @@ -177,7 +177,7 @@ namespace clang { #define TYPE(Class, Base) void Visit##Class##Type(const Class##Type *T); #define ABSTRACT_TYPE(Class, Base) -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" }; } // namespace clang diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index b71315505de90..2c22587957e89 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -968,7 +968,14 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) { Record.push_back(D->getLinkageInternal()); if (D->getInit()) { - Record.push_back(!D->isInitKnownICE() ? 1 : (D->isInitICE() ? 3 : 2)); + if (!D->isInitKnownICE()) + Record.push_back(1); + else { + Record.push_back( + 2 | + (D->isInitICE() ? 1 : 0) | + (D->ensureEvaluatedStmt()->HasConstantDestruction ? 4 : 0)); + } Record.AddStmt(D->getInit()); } else { Record.push_back(0); @@ -2140,7 +2147,7 @@ void ASTWriter::WriteDeclAbbrevs() { Abv->Add(BitCodeAbbrevOp(0)); // ImplicitParamKind Abv->Add(BitCodeAbbrevOp(0)); // EscapingByref Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Linkage - Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // IsInitICE (local) + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // IsInitICE (local) Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // VarKind (local enum) // Type Source Info Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array)); diff --git a/clang/lib/Serialization/GlobalModuleIndex.cpp b/clang/lib/Serialization/GlobalModuleIndex.cpp index 1833ebbdc84cc..54ab17681ee9e 100644 --- a/clang/lib/Serialization/GlobalModuleIndex.cpp +++ b/clang/lib/Serialization/GlobalModuleIndex.cpp @@ -10,7 +10,6 @@ // //===----------------------------------------------------------------------===// - #include "ASTReaderInternals.h" #include "clang/Basic/FileManager.h" #include "clang/Lex/HeaderSearch.h" @@ -21,10 +20,12 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Bitstream/BitstreamReader.h" #include "llvm/Bitstream/BitstreamWriter.h" #include "llvm/Support/DJB.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileUtilities.h" #include "llvm/Support/LockFileManager.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/OnDiskHashTable.h" @@ -912,37 +913,9 @@ GlobalModuleIndex::writeIndex(FileManager &FileMgr, "failed writing index"); } - // Write the global index file to a temporary file. - llvm::SmallString<128> IndexTmpPath; - int TmpFD; - if (llvm::sys::fs::createUniqueFile(IndexPath + "-%%%%%%%%", TmpFD, - IndexTmpPath)) - return llvm::createStringError(std::errc::io_error, - "failed creating unique file"); - - // Open the temporary global index file for output. - llvm::raw_fd_ostream Out(TmpFD, true); - if (Out.has_error()) - return llvm::createStringError(Out.error(), "failed outputting to stream"); - - // Write the index. - Out.write(OutputBuffer.data(), OutputBuffer.size()); - Out.close(); - if (Out.has_error()) - return llvm::createStringError(Out.error(), "failed writing to stream"); - - // Remove the old index file. It isn't relevant any more. - llvm::sys::fs::remove(IndexPath); - - // Rename the newly-written index file to the proper name. - if (std::error_code Err = llvm::sys::fs::rename(IndexTmpPath, IndexPath)) { - // Remove the file on failure, don't check whether removal succeeded. - llvm::sys::fs::remove(IndexTmpPath); - return llvm::createStringError(Err, "failed renaming file \"%s\" to \"%s\"", - IndexTmpPath.c_str(), IndexPath.c_str()); - } - - return llvm::Error::success(); + return llvm::writeFileAtomically( + (IndexPath + "-%%%%%%%%").str(), IndexPath, + llvm::StringRef(OutputBuffer.data(), OutputBuffer.size())); } namespace { diff --git a/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp index 6be6762468724..e064ca6bd88f6 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp @@ -113,8 +113,10 @@ class MacOSKeychainAPIChecker : public Checker, const ExplodedNode *getAllocationNode(const ExplodedNode *N, SymbolRef Sym, CheckerContext &C) const; - std::unique_ptr generateAllocatedDataNotReleasedReport( - const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const; + std::unique_ptr + generateAllocatedDataNotReleasedReport(const AllocationPair &AP, + ExplodedNode *N, + CheckerContext &C) const; /// Mark an AllocationPair interesting for diagnostic reporting. void markInteresting(PathSensitiveBugReport *R, @@ -467,7 +469,7 @@ MacOSKeychainAPIChecker::getAllocationNode(const ExplodedNode *N, return AllocNode; } -std::unique_ptr +std::unique_ptr MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport( const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const { const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx]; diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 699b5c6b9fa8e..a824499518730 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -6,8 +6,41 @@ // //===----------------------------------------------------------------------===// // -// This file defines malloc/free checker, which checks for potential memory -// leaks, double free, and use-after-free problems. +// This file defines a variety of memory management related checkers, such as +// leak, double free, and use-after-free. +// +// The following checkers are defined here: +// +// * MallocChecker +// Despite its name, it models all sorts of memory allocations and +// de- or reallocation, including but not limited to malloc, free, +// relloc, new, delete. It also reports on a variety of memory misuse +// errors. +// Many other checkers interact very closely with this checker, in fact, +// most are merely options to this one. Other checkers may register +// MallocChecker, but do not enable MallocChecker's reports (more details +// to follow around its field, ChecksEnabled). +// It also has a boolean "Optimistic" checker option, which if set to true +// will cause the checker to model user defined memory management related +// functions annotated via the attribute ownership_takes, ownership_holds +// and ownership_returns. +// +// * NewDeleteChecker +// Enables the modeling of new, new[], delete, delete[] in MallocChecker, +// and checks for related double-free and use-after-free errors. +// +// * NewDeleteLeaksChecker +// Checks for leaks related to new, new[], delete, delete[]. +// Depends on NewDeleteChecker. +// +// * MismatchedDeallocatorChecker +// Enables checking whether memory is deallocated with the correspending +// allocation function in MallocChecker, such as malloc() allocated +// regions are only freed by free(), new by delete, new[] by delete[]. +// +// InnerPointerChecker interacts very closely with MallocChecker, but unlike +// the above checkers, it has it's own file, hence the many InnerPointerChecker +// related headers and non-static functions. // //===----------------------------------------------------------------------===// @@ -37,6 +70,10 @@ using namespace clang; using namespace ento; +//===----------------------------------------------------------------------===// +// The types of allocation we're modeling. +//===----------------------------------------------------------------------===// + namespace { // Used to check correspondence between allocators and deallocators. @@ -50,57 +87,88 @@ enum AllocationFamily { AF_InnerBuffer }; +struct MemFunctionInfoTy; + +} // end of anonymous namespace + +/// Determine family of a deallocation expression. +static AllocationFamily +getAllocationFamily(const MemFunctionInfoTy &MemFunctionInfo, CheckerContext &C, + const Stmt *S); + +/// Print names of allocators and deallocators. +/// +/// \returns true on success. +static bool printAllocDeallocName(raw_ostream &os, CheckerContext &C, + const Expr *E); + +/// Print expected name of an allocator based on the deallocator's +/// family derived from the DeallocExpr. +static void printExpectedAllocName(raw_ostream &os, + const MemFunctionInfoTy &MemFunctionInfo, + CheckerContext &C, const Expr *E); + +/// Print expected name of a deallocator based on the allocator's +/// family. +static void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family); + +//===----------------------------------------------------------------------===// +// The state of a symbol, in terms of memory management. +//===----------------------------------------------------------------------===// + +namespace { + class RefState { - enum Kind { // Reference to allocated memory. - Allocated, - // Reference to zero-allocated memory. - AllocatedOfSizeZero, - // Reference to released/freed memory. - Released, - // The responsibility for freeing resources has transferred from - // this reference. A relinquished symbol should not be freed. - Relinquished, - // We are no longer guaranteed to have observed all manipulations - // of this pointer/memory. For example, it could have been - // passed as a parameter to an opaque function. - Escaped + enum Kind { + // Reference to allocated memory. + Allocated, + // Reference to zero-allocated memory. + AllocatedOfSizeZero, + // Reference to released/freed memory. + Released, + // The responsibility for freeing resources has transferred from + // this reference. A relinquished symbol should not be freed. + Relinquished, + // We are no longer guaranteed to have observed all manipulations + // of this pointer/memory. For example, it could have been + // passed as a parameter to an opaque function. + Escaped }; const Stmt *S; - unsigned K : 3; // Kind enum, but stored as a bitfield. - unsigned Family : 29; // Rest of 32-bit word, currently just an allocation - // family. - RefState(Kind k, const Stmt *s, unsigned family) - : S(s), K(k), Family(family) { + Kind K; + AllocationFamily Family; + + RefState(Kind k, const Stmt *s, AllocationFamily family) + : S(s), K(k), Family(family) { assert(family != AF_None); } + public: bool isAllocated() const { return K == Allocated; } bool isAllocatedOfSizeZero() const { return K == AllocatedOfSizeZero; } bool isReleased() const { return K == Released; } bool isRelinquished() const { return K == Relinquished; } bool isEscaped() const { return K == Escaped; } - AllocationFamily getAllocationFamily() const { - return (AllocationFamily)Family; - } + AllocationFamily getAllocationFamily() const { return Family; } const Stmt *getStmt() const { return S; } bool operator==(const RefState &X) const { return K == X.K && S == X.S && Family == X.Family; } - static RefState getAllocated(unsigned family, const Stmt *s) { + static RefState getAllocated(AllocationFamily family, const Stmt *s) { return RefState(Allocated, s, family); } static RefState getAllocatedOfSizeZero(const RefState *RS) { return RefState(AllocatedOfSizeZero, RS->getStmt(), RS->getAllocationFamily()); } - static RefState getReleased(unsigned family, const Stmt *s) { + static RefState getReleased(AllocationFamily family, const Stmt *s) { return RefState(Released, s, family); } - static RefState getRelinquished(unsigned family, const Stmt *s) { + static RefState getRelinquished(AllocationFamily family, const Stmt *s) { return RefState(Relinquished, s, family); } static RefState getEscaped(const RefState *RS) { @@ -113,8 +181,8 @@ class RefState { ID.AddInteger(Family); } - void dump(raw_ostream &OS) const { - switch (static_cast(K)) { + LLVM_DUMP_METHOD void dump(raw_ostream &OS) const { + switch (K) { #define CASE(ID) case ID: OS << #ID; break; CASE(Allocated) CASE(AllocatedOfSizeZero) @@ -127,24 +195,62 @@ class RefState { LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); } }; -enum ReallocPairKind { - RPToBeFreedAfterFailure, - // The symbol has been freed when reallocation failed. - RPIsFreeOnFailure, - // The symbol does not need to be freed after reallocation fails. - RPDoNotTrackAfterFailure +} // end of anonymous namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, SymbolRef, RefState) + +/// Check if the memory associated with this symbol was released. +static bool isReleased(SymbolRef Sym, CheckerContext &C); + +/// Update the RefState to reflect the new memory allocation. +/// The optional \p RetVal parameter specifies the newly allocated pointer +/// value; if unspecified, the value of expression \p E is used. +static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E, + ProgramStateRef State, + AllocationFamily Family = AF_Malloc, + Optional RetVal = None); + +//===----------------------------------------------------------------------===// +// The modeling of memory reallocation. +// +// The terminology 'toPtr' and 'fromPtr' will be used: +// toPtr = realloc(fromPtr, 20); +//===----------------------------------------------------------------------===// + +REGISTER_SET_WITH_PROGRAMSTATE(ReallocSizeZeroSymbols, SymbolRef) + +namespace { + +/// The state of 'fromPtr' after reallocation is known to have failed. +enum OwnershipAfterReallocKind { + // The symbol needs to be freed (e.g.: realloc) + OAR_ToBeFreedAfterFailure, + // The symbol has been freed (e.g.: reallocf) + OAR_FreeOnFailure, + // The symbol doesn't have to freed (e.g.: we aren't sure if, how and where + // 'fromPtr' was allocated: + // void Haha(int *ptr) { + // ptr = realloc(ptr, 67); + // // ... + // } + // ). + OAR_DoNotTrackAfterFailure }; -/// \class ReallocPair -/// Stores information about the symbol being reallocated by a call to -/// 'realloc' to allow modeling failed reallocation later in the path. +/// Stores information about the 'fromPtr' symbol after reallocation. +/// +/// This is important because realloc may fail, and that needs special modeling. +/// Whether reallocation failed or not will not be known until later, so we'll +/// store whether upon failure 'fromPtr' will be freed, or needs to be freed +/// later, etc. struct ReallocPair { - // The symbol which realloc reallocated. + + // The 'fromPtr'. SymbolRef ReallocatedSym; - ReallocPairKind Kind; + OwnershipAfterReallocKind Kind; - ReallocPair(SymbolRef S, ReallocPairKind K) : - ReallocatedSym(S), Kind(K) {} + ReallocPair(SymbolRef S, OwnershipAfterReallocKind K) + : ReallocatedSym(S), Kind(K) {} void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Kind); ID.AddPointer(ReallocatedSym); @@ -155,42 +261,88 @@ struct ReallocPair { } }; -typedef std::pair LeakInfo; - -class MallocChecker : public Checker, - check::EndFunction, - check::PreCall, - check::PostStmt, - check::PostStmt, - check::NewAllocator, - check::PreStmt, - check::PostStmt, - check::PostObjCMessage, - check::Location, - eval::Assume> -{ -public: - MallocChecker() - : II_alloca(nullptr), II_win_alloca(nullptr), II_malloc(nullptr), - II_free(nullptr), II_realloc(nullptr), II_calloc(nullptr), - II_valloc(nullptr), II_reallocf(nullptr), II_strndup(nullptr), - II_strdup(nullptr), II_win_strdup(nullptr), II_kmalloc(nullptr), - II_kfree(nullptr), II_if_nameindex(nullptr), - II_if_freenameindex(nullptr), II_wcsdup(nullptr), - II_win_wcsdup(nullptr), II_g_malloc(nullptr), II_g_malloc0(nullptr), - II_g_realloc(nullptr), II_g_try_malloc(nullptr), - II_g_try_malloc0(nullptr), II_g_try_realloc(nullptr), - II_g_free(nullptr), II_g_memdup(nullptr), II_g_malloc_n(nullptr), - II_g_malloc0_n(nullptr), II_g_realloc_n(nullptr), - II_g_try_malloc_n(nullptr), II_g_try_malloc0_n(nullptr), - II_g_try_realloc_n(nullptr) {} +} // end of anonymous namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(ReallocPairs, SymbolRef, ReallocPair) + +//===----------------------------------------------------------------------===// +// Kinds of memory operations, information about resource managing functions. +//===----------------------------------------------------------------------===// + +namespace { + +enum class MemoryOperationKind { MOK_Allocate, MOK_Free, MOK_Any }; +struct MemFunctionInfoTy { + /// The value of the MallocChecker:Optimistic is stored in this variable. + /// /// In pessimistic mode, the checker assumes that it does not know which /// functions might free the memory. + /// In optimistic mode, the checker assumes that all user-defined functions + /// which might free a pointer are annotated. + DefaultBool ShouldIncludeOwnershipAnnotatedFunctions; + + // TODO: Change these to CallDescription, and get rid of lazy initialization. + mutable IdentifierInfo *II_alloca = nullptr, *II_win_alloca = nullptr, + *II_malloc = nullptr, *II_free = nullptr, + *II_realloc = nullptr, *II_calloc = nullptr, + *II_valloc = nullptr, *II_reallocf = nullptr, + *II_strndup = nullptr, *II_strdup = nullptr, + *II_win_strdup = nullptr, *II_kmalloc = nullptr, + *II_if_nameindex = nullptr, + *II_if_freenameindex = nullptr, *II_wcsdup = nullptr, + *II_win_wcsdup = nullptr, *II_g_malloc = nullptr, + *II_g_malloc0 = nullptr, *II_g_realloc = nullptr, + *II_g_try_malloc = nullptr, + *II_g_try_malloc0 = nullptr, + *II_g_try_realloc = nullptr, *II_g_free = nullptr, + *II_g_memdup = nullptr, *II_g_malloc_n = nullptr, + *II_g_malloc0_n = nullptr, *II_g_realloc_n = nullptr, + *II_g_try_malloc_n = nullptr, + *II_g_try_malloc0_n = nullptr, *II_kfree = nullptr, + *II_g_try_realloc_n = nullptr; + + void initIdentifierInfo(ASTContext &C) const; + + ///@{ + /// Check if this is one of the functions which can allocate/reallocate + /// memory pointed to by one of its arguments. + bool isMemFunction(const FunctionDecl *FD, ASTContext &C) const; + bool isCMemFunction(const FunctionDecl *FD, ASTContext &C, + AllocationFamily Family, + MemoryOperationKind MemKind) const; + + /// Tells if the callee is one of the builtin new/delete operators, including + /// placement operators and other standard overloads. + bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const; + ///@} +}; + +} // end of anonymous namespace + +//===----------------------------------------------------------------------===// +// Definition of the MallocChecker class. +//===----------------------------------------------------------------------===// + +namespace { + +class MallocChecker + : public Checker, + check::EndFunction, check::PreCall, + check::PostStmt, check::PostStmt, + check::NewAllocator, check::PreStmt, + check::PostStmt, check::PostObjCMessage, + check::Location, eval::Assume> { +public: + MemFunctionInfoTy MemFunctionInfo; + + /// Many checkers are essentially built into this one, so enabling them will + /// make MallocChecker perform additional modeling and reporting. enum CheckKind { + /// When a subchecker is enabled but MallocChecker isn't, model memory + /// management but do not emit warnings emitted with MallocChecker only + /// enabled. CK_MallocChecker, CK_NewDeleteChecker, CK_NewDeleteLeaksChecker, @@ -199,13 +351,7 @@ class MallocChecker : public Checker; DefaultBool ChecksEnabled[CK_NumCheckKinds]; CheckerNameRef CheckNames[CK_NumCheckKinds]; @@ -248,47 +394,9 @@ class MallocChecker : public Checker BT_MismatchedDealloc; mutable std::unique_ptr BT_OffsetFree[CK_NumCheckKinds]; mutable std::unique_ptr BT_UseZerroAllocated[CK_NumCheckKinds]; - mutable IdentifierInfo *II_alloca, *II_win_alloca, *II_malloc, *II_free, - *II_realloc, *II_calloc, *II_valloc, *II_reallocf, - *II_strndup, *II_strdup, *II_win_strdup, *II_kmalloc, - *II_kfree, *II_if_nameindex, *II_if_freenameindex, - *II_wcsdup, *II_win_wcsdup, *II_g_malloc, - *II_g_malloc0, *II_g_realloc, *II_g_try_malloc, - *II_g_try_malloc0, *II_g_try_realloc, *II_g_free, - *II_g_memdup, *II_g_malloc_n, *II_g_malloc0_n, - *II_g_realloc_n, *II_g_try_malloc_n, - *II_g_try_malloc0_n, *II_g_try_realloc_n; - mutable Optional KernelZeroFlagVal; - void initIdentifierInfo(ASTContext &C) const; - - /// Determine family of a deallocation expression. - AllocationFamily getAllocationFamily(CheckerContext &C, const Stmt *S) const; - - /// Print names of allocators and deallocators. - /// - /// \returns true on success. - bool printAllocDeallocName(raw_ostream &os, CheckerContext &C, - const Expr *E) const; - - /// Print expected name of an allocator based on the deallocator's - /// family derived from the DeallocExpr. - void printExpectedAllocName(raw_ostream &os, CheckerContext &C, - const Expr *DeallocExpr) const; - /// Print expected name of a deallocator based on the allocator's - /// family. - void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) const; - - ///@{ - /// Check if this is one of the functions which can allocate/reallocate memory - /// pointed to by one of its arguments. - bool isMemFunction(const FunctionDecl *FD, ASTContext &C) const; - bool isCMemFunction(const FunctionDecl *FD, - ASTContext &C, - AllocationFamily Family, - MemoryOperationKind MemKind) const; - bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const; - ///@} + // TODO: Remove mutable by moving the initializtaion to the registry function. + mutable Optional KernelZeroFlagVal; /// Process C++ operator new()'s allocation, which is the part of C++ /// new-expression that goes before the constructor. @@ -296,23 +404,64 @@ class MallocChecker : public Checker RetVal = None) const; - + /// + /// \param [in] E The expression that allocates memory. + /// \param [in] IndexOfSizeArg Index of the argument that specifies the size + /// of the memory that needs to be allocated. E.g. for malloc, this would be + /// 0. + /// \param [in] RetVal Specifies the newly allocated pointer value; + /// if unspecified, the value of expression \p E is used. + static ProgramStateRef ProcessZeroAllocCheck(CheckerContext &C, const Expr *E, + const unsigned IndexOfSizeArg, + ProgramStateRef State, + Optional RetVal = None); + + /// Model functions with the ownership_returns attribute. + /// + /// User-defined function may have the ownership_returns attribute, which + /// annotates that the function returns with an object that was allocated on + /// the heap, and passes the ownertship to the callee. + /// + /// void __attribute((ownership_returns(malloc, 1))) *my_malloc(size_t); + /// + /// It has two parameters: + /// - first: name of the resource (e.g. 'malloc') + /// - (OPTIONAL) second: size of the allocated region + /// + /// \param [in] CE The expression that allocates memory. + /// \param [in] Att The ownership_returns attribute. + /// \param [in] State The \c ProgramState right before allocation. + /// \returns The ProgramState right after allocation. ProgramStateRef MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, const OwnershipAttr* Att, ProgramStateRef State) const; + + /// Models memory allocation. + /// + /// \param [in] CE The expression that allocates memory. + /// \param [in] SizeEx Size of the memory that needs to be allocated. + /// \param [in] Init The value the allocated memory needs to be initialized. + /// with. For example, \c calloc initializes the allocated memory to 0, + /// malloc leaves it undefined. + /// \param [in] State The \c ProgramState right before allocation. + /// \returns The ProgramState right after allocation. static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE, const Expr *SizeEx, SVal Init, ProgramStateRef State, AllocationFamily Family = AF_Malloc); + + /// Models memory allocation. + /// + /// \param [in] CE The expression that allocates memory. + /// \param [in] Size Size of the memory that needs to be allocated. + /// \param [in] Init The value the allocated memory needs to be initialized. + /// with. For example, \c calloc initializes the allocated memory to 0, + /// malloc leaves it undefined. + /// \param [in] State The \c ProgramState right before allocation. + /// \returns The ProgramState right after allocation. static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE, - SVal SizeEx, SVal Init, + SVal Size, SVal Init, ProgramStateRef State, AllocationFamily Family = AF_Malloc); @@ -325,54 +474,125 @@ class MallocChecker : public Checker RetVal = None); - + /// Model functions with the ownership_takes and ownership_holds attributes. + /// + /// User-defined function may have the ownership_takes and/or ownership_holds + /// attributes, which annotates that the function frees the memory passed as a + /// parameter. + /// + /// void __attribute((ownership_takes(malloc, 1))) my_free(void *); + /// void __attribute((ownership_holds(malloc, 1))) my_hold(void *); + /// + /// They have two parameters: + /// - first: name of the resource (e.g. 'malloc') + /// - second: index of the parameter the attribute applies to + /// + /// \param [in] CE The expression that frees memory. + /// \param [in] Att The ownership_takes or ownership_holds attribute. + /// \param [in] State The \c ProgramState right before allocation. + /// \returns The ProgramState right after deallocation. ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE, const OwnershipAttr* Att, ProgramStateRef State) const; + + /// Models memory deallocation. + /// + /// \param [in] CE The expression that frees memory. + /// \param [in] State The \c ProgramState right before allocation. + /// \param [in] Num Index of the argument that needs to be freed. This is + /// normally 0, but for custom free functions it may be different. + /// \param [in] Hold Whether the parameter at \p Index has the ownership_holds + /// attribute. + /// \param [out] IsKnownToBeAllocated Whether the memory to be freed is known + /// to have been allocated, or in other words, the symbol to be freed was + /// registered as allocated by this checker. In the following case, \c ptr + /// isn't known to be allocated. + /// void Haha(int *ptr) { + /// ptr = realloc(ptr, 67); + /// // ... + /// } + /// \param [in] ReturnsNullOnFailure Whether the memory deallocation function + /// we're modeling returns with Null on failure. + /// \returns The ProgramState right after deallocation. ProgramStateRef FreeMemAux(CheckerContext &C, const CallExpr *CE, - ProgramStateRef state, unsigned Num, - bool Hold, - bool &ReleasedAllocated, + ProgramStateRef State, unsigned Num, bool Hold, + bool &IsKnownToBeAllocated, bool ReturnsNullOnFailure = false) const; - ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *Arg, - const Expr *ParentExpr, - ProgramStateRef State, - bool Hold, - bool &ReleasedAllocated, + + /// Models memory deallocation. + /// + /// \param [in] ArgExpr The variable who's pointee needs to be freed. + /// \param [in] ParentExpr The expression that frees the memory. + /// \param [in] State The \c ProgramState right before allocation. + /// normally 0, but for custom free functions it may be different. + /// \param [in] Hold Whether the parameter at \p Index has the ownership_holds + /// attribute. + /// \param [out] IsKnownToBeAllocated Whether the memory to be freed is known + /// to have been allocated, or in other words, the symbol to be freed was + /// registered as allocated by this checker. In the following case, \c ptr + /// isn't known to be allocated. + /// void Haha(int *ptr) { + /// ptr = realloc(ptr, 67); + /// // ... + /// } + /// \param [in] ReturnsNullOnFailure Whether the memory deallocation function + /// we're modeling returns with Null on failure. + /// \returns The ProgramState right after deallocation. + ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *ArgExpr, + const Expr *ParentExpr, ProgramStateRef State, + bool Hold, bool &IsKnownToBeAllocated, bool ReturnsNullOnFailure = false) const; + // TODO: Needs some refactoring, as all other deallocation modeling + // functions are suffering from out parameters and messy code due to how + // realloc is handled. + // + /// Models memory reallocation. + /// + /// \param [in] CE The expression that reallocated memory + /// \param [in] ShouldFreeOnFail Whether if reallocation fails, the supplied + /// memory should be freed. + /// \param [in] State The \c ProgramState right before reallocation. + /// \param [in] SuffixWithN Whether the reallocation function we're modeling + /// has an '_n' suffix, such as g_realloc_n. + /// \returns The ProgramState right after reallocation. ProgramStateRef ReallocMemAux(CheckerContext &C, const CallExpr *CE, - bool FreesMemOnFailure, - ProgramStateRef State, + bool ShouldFreeOnFail, ProgramStateRef State, bool SuffixWithN = false) const; + + /// Evaluates the buffer size that needs to be allocated. + /// + /// \param [in] Blocks The amount of blocks that needs to be allocated. + /// \param [in] BlockBytes The size of a block. + /// \returns The symbolic value of \p Blocks * \p BlockBytes. static SVal evalMulForBufferSize(CheckerContext &C, const Expr *Blocks, const Expr *BlockBytes); + + /// Models zero initialized array allocation. + /// + /// \param [in] CE The expression that reallocated memory + /// \param [in] State The \c ProgramState right before reallocation. + /// \returns The ProgramState right after allocation. static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE, ProgramStateRef State); - /// Check if the memory associated with this symbol was released. - bool isReleased(SymbolRef Sym, CheckerContext &C) const; - /// See if deallocation happens in a suspicious context. If so, escape the /// pointers that otherwise would have been deallocated and return true. bool suppressDeallocationsInSuspiciousContexts(const CallExpr *CE, CheckerContext &C) const; + /// If in \p S \p Sym is used, check whether \p Sym was already freed. bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const; + /// If in \p S \p Sym is used, check whether \p Sym was allocated as a zero + /// sized memory region. void checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C, const Stmt *S) const; + /// If in \p S \p Sym is being freed, check whether \p Sym was already freed. bool checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const; - /// Check if the function is known free memory, or if it is + /// Check if the function is known to free memory, or if it is /// "interesting" and should be modeled explicitly. /// /// \param [out] EscapingSymbol A function might not free memory in general, @@ -386,12 +606,12 @@ class MallocChecker : public Checker allocated. Other state (released) -> allocated. - return (Stmt && (isa(Stmt) || isa(Stmt)) && - (S && (S->isAllocated() || S->isAllocatedOfSizeZero())) && - (!SPrev || !(SPrev->isAllocated() || - SPrev->isAllocatedOfSizeZero()))); - } + // The allocated region symbol tracked by the main analysis. + SymbolRef Sym; - inline bool isReleased(const RefState *S, const RefState *SPrev, - const Stmt *Stmt) { - // Did not track -> released. Other state (allocated) -> released. - // The statement associated with the release might be missing. - bool IsReleased = (S && S->isReleased()) && - (!SPrev || !SPrev->isReleased()); - assert(!IsReleased || - (Stmt && (isa(Stmt) || isa(Stmt))) || - (!Stmt && S->getAllocationFamily() == AF_InnerBuffer)); - return IsReleased; - } + // The mode we are in, i.e. what kind of diagnostics will be emitted. + NotificationMode Mode; - inline bool isRelinquished(const RefState *S, const RefState *SPrev, - const Stmt *Stmt) { - // Did not track -> relinquished. Other state (allocated) -> relinquished. - return (Stmt && (isa(Stmt) || isa(Stmt) || - isa(Stmt)) && - (S && S->isRelinquished()) && - (!SPrev || !SPrev->isRelinquished())); - } + // A symbol from when the primary region should have been reallocated. + SymbolRef FailedReallocSymbol; - inline bool isReallocFailedCheck(const RefState *S, const RefState *SPrev, - const Stmt *Stmt) { - // If the expression is not a call, and the state change is - // released -> allocated, it must be the realloc return value - // check. If we have to handle more cases here, it might be cleaner just - // to track this extra bit in the state itself. - return ( - (!Stmt || !isa(Stmt)) && - (S && (S->isAllocated() || S->isAllocatedOfSizeZero())) && - (SPrev && !(SPrev->isAllocated() || SPrev->isAllocatedOfSizeZero()))); - } + // A C++ destructor stack frame in which memory was released. Used for + // miscellaneous false positive suppression. + const StackFrameContext *ReleaseDestructorLC; - PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - PathSensitiveBugReport &BR) override; + bool IsLeak; - PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC, - const ExplodedNode *EndPathNode, - PathSensitiveBugReport &BR) override { - if (!IsLeak) - return nullptr; +public: + MallocBugVisitor(SymbolRef S, bool isLeak = false) + : Sym(S), Mode(Normal), FailedReallocSymbol(nullptr), + ReleaseDestructorLC(nullptr), IsLeak(isLeak) {} + + static void *getTag() { + static int Tag = 0; + return &Tag; + } + + void Profile(llvm::FoldingSetNodeID &ID) const override { + ID.AddPointer(getTag()); + ID.AddPointer(Sym); + } + + /// Did not track -> allocated. Other state (released) -> allocated. + static inline bool isAllocated(const RefState *RSCurr, const RefState *RSPrev, + const Stmt *Stmt) { + return (Stmt && (isa(Stmt) || isa(Stmt)) && + (RSCurr && + (RSCurr->isAllocated() || RSCurr->isAllocatedOfSizeZero())) && + (!RSPrev || + !(RSPrev->isAllocated() || RSPrev->isAllocatedOfSizeZero()))); + } + + /// Did not track -> released. Other state (allocated) -> released. + /// The statement associated with the release might be missing. + static inline bool isReleased(const RefState *RSCurr, const RefState *RSPrev, + const Stmt *Stmt) { + bool IsReleased = + (RSCurr && RSCurr->isReleased()) && (!RSPrev || !RSPrev->isReleased()); + assert(!IsReleased || + (Stmt && (isa(Stmt) || isa(Stmt))) || + (!Stmt && RSCurr->getAllocationFamily() == AF_InnerBuffer)); + return IsReleased; + } + + /// Did not track -> relinquished. Other state (allocated) -> relinquished. + static inline bool isRelinquished(const RefState *RSCurr, + const RefState *RSPrev, const Stmt *Stmt) { + return (Stmt && + (isa(Stmt) || isa(Stmt) || + isa(Stmt)) && + (RSCurr && RSCurr->isRelinquished()) && + (!RSPrev || !RSPrev->isRelinquished())); + } + + /// If the expression is not a call, and the state change is + /// released -> allocated, it must be the realloc return value + /// check. If we have to handle more cases here, it might be cleaner just + /// to track this extra bit in the state itself. + static inline bool hasReallocFailed(const RefState *RSCurr, + const RefState *RSPrev, + const Stmt *Stmt) { + return ((!Stmt || !isa(Stmt)) && + (RSCurr && + (RSCurr->isAllocated() || RSCurr->isAllocatedOfSizeZero())) && + (RSPrev && + !(RSPrev->isAllocated() || RSPrev->isAllocatedOfSizeZero()))); + } + + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; + + PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC, + const ExplodedNode *EndPathNode, + PathSensitiveBugReport &BR) override { + if (!IsLeak) + return nullptr; - PathDiagnosticLocation L = BR.getLocation(); - // Do not add the statement itself as a range in case of leak. - return std::make_shared(L, BR.getDescription(), - false); - } + PathDiagnosticLocation L = BR.getLocation(); + // Do not add the statement itself as a range in case of leak. + return std::make_shared(L, BR.getDescription(), + false); + } - private: - class StackHintGeneratorForReallocationFailed - : public StackHintGeneratorForSymbol { - public: - StackHintGeneratorForReallocationFailed(SymbolRef S, StringRef M) +private: + class StackHintGeneratorForReallocationFailed + : public StackHintGeneratorForSymbol { + public: + StackHintGeneratorForReallocationFailed(SymbolRef S, StringRef M) : StackHintGeneratorForSymbol(S, M) {} - std::string getMessageForArg(const Expr *ArgE, - unsigned ArgIndex) override { - // Printed parameters start at 1, not 0. - ++ArgIndex; + std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex) override { + // Printed parameters start at 1, not 0. + ++ArgIndex; - SmallString<200> buf; - llvm::raw_svector_ostream os(buf); + SmallString<200> buf; + llvm::raw_svector_ostream os(buf); - os << "Reallocation of " << ArgIndex << llvm::getOrdinalSuffix(ArgIndex) - << " parameter failed"; + os << "Reallocation of " << ArgIndex << llvm::getOrdinalSuffix(ArgIndex) + << " parameter failed"; - return os.str(); - } + return os.str(); + } - std::string getMessageForReturn(const CallExpr *CallExpr) override { - return "Reallocation of returned value failed"; - } - }; + std::string getMessageForReturn(const CallExpr *CallExpr) override { + return "Reallocation of returned value failed"; + } }; }; -} // end anonymous namespace -REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, SymbolRef, RefState) -REGISTER_MAP_WITH_PROGRAMSTATE(ReallocPairs, SymbolRef, ReallocPair) -REGISTER_SET_WITH_PROGRAMSTATE(ReallocSizeZeroSymbols, SymbolRef) +} // end anonymous namespace // A map from the freed symbol to the symbol representing the return value of // the free function. @@ -589,7 +811,11 @@ class StopTrackingCallback final : public SymbolVisitor { }; } // end anonymous namespace -void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const { +//===----------------------------------------------------------------------===// +// Methods of MemFunctionInfoTy. +//===----------------------------------------------------------------------===// + +void MemFunctionInfoTy::initIdentifierInfo(ASTContext &Ctx) const { if (II_malloc) return; II_alloca = &Ctx.Idents.get("alloca"); @@ -629,7 +855,8 @@ void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const { II_g_try_realloc_n = &Ctx.Idents.get("g_try_realloc_n"); } -bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const { +bool MemFunctionInfoTy::isMemFunction(const FunctionDecl *FD, + ASTContext &C) const { if (isCMemFunction(FD, C, AF_Malloc, MemoryOperationKind::MOK_Any)) return true; @@ -645,10 +872,9 @@ bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const { return false; } -bool MallocChecker::isCMemFunction(const FunctionDecl *FD, - ASTContext &C, - AllocationFamily Family, - MemoryOperationKind MemKind) const { +bool MemFunctionInfoTy::isCMemFunction(const FunctionDecl *FD, ASTContext &C, + AllocationFamily Family, + MemoryOperationKind MemKind) const { if (!FD) return false; @@ -701,7 +927,7 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD, if (Family != AF_Malloc) return false; - if (IsOptimistic && FD->hasAttrs()) { + if (ShouldIncludeOwnershipAnnotatedFunctions && FD->hasAttrs()) { for (const auto *I : FD->specific_attrs()) { OwnershipAttr::OwnershipKind OwnKind = I->getOwnKind(); if(OwnKind == OwnershipAttr::Takes || OwnKind == OwnershipAttr::Holds) { @@ -716,11 +942,8 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD, return false; } - -// Tells if the callee is one of the builtin new/delete operators, including -// placement operators and other standard overloads. -bool MallocChecker::isStandardNewDelete(const FunctionDecl *FD, - ASTContext &C) const { +bool MemFunctionInfoTy::isStandardNewDelete(const FunctionDecl *FD, + ASTContext &C) const { if (!FD) return false; @@ -736,6 +959,10 @@ bool MallocChecker::isStandardNewDelete(const FunctionDecl *FD, return !L.isValid() || C.getSourceManager().isInSystemHeader(L); } +//===----------------------------------------------------------------------===// +// Methods of MallocChecker and MallocBugVisitor. +//===----------------------------------------------------------------------===// + llvm::Optional MallocChecker::performKernelMalloc( const CallExpr *CE, CheckerContext &C, const ProgramStateRef &State) const { // 3-argument malloc(), as commonly used in {Free,Net,Open}BSD Kernels: @@ -834,28 +1061,35 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { return; ProgramStateRef State = C.getState(); - bool ReleasedAllocatedMemory = false; + bool IsKnownToBeAllocatedMemory = false; if (FD->getKind() == Decl::Function) { - initIdentifierInfo(C.getASTContext()); + MemFunctionInfo.initIdentifierInfo(C.getASTContext()); IdentifierInfo *FunI = FD->getIdentifier(); - if (FunI == II_malloc || FunI == II_g_malloc || FunI == II_g_try_malloc) { - if (CE->getNumArgs() < 1) + if (FunI == MemFunctionInfo.II_malloc || + FunI == MemFunctionInfo.II_g_malloc || + FunI == MemFunctionInfo.II_g_try_malloc) { + switch (CE->getNumArgs()) { + default: return; - if (CE->getNumArgs() < 3) { + case 1: + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); + State = ProcessZeroAllocCheck(C, CE, 0, State); + break; + case 2: State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); - if (CE->getNumArgs() == 1) - State = ProcessZeroAllocation(C, CE, 0, State); - } else if (CE->getNumArgs() == 3) { + break; + case 3: llvm::Optional MaybeState = performKernelMalloc(CE, C, State); if (MaybeState.hasValue()) State = MaybeState.getValue(); else State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); + break; } - } else if (FunI == II_kmalloc) { + } else if (FunI == MemFunctionInfo.II_kmalloc) { if (CE->getNumArgs() < 1) return; llvm::Optional MaybeState = @@ -864,100 +1098,116 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { State = MaybeState.getValue(); else State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); - } else if (FunI == II_valloc) { + } else if (FunI == MemFunctionInfo.II_valloc) { if (CE->getNumArgs() < 1) return; State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); - State = ProcessZeroAllocation(C, CE, 0, State); - } else if (FunI == II_realloc || FunI == II_g_realloc || - FunI == II_g_try_realloc) { - State = ReallocMemAux(C, CE, false, State); - State = ProcessZeroAllocation(C, CE, 1, State); - } else if (FunI == II_reallocf) { - State = ReallocMemAux(C, CE, true, State); - State = ProcessZeroAllocation(C, CE, 1, State); - } else if (FunI == II_calloc) { + State = ProcessZeroAllocCheck(C, CE, 0, State); + } else if (FunI == MemFunctionInfo.II_realloc || + FunI == MemFunctionInfo.II_g_realloc || + FunI == MemFunctionInfo.II_g_try_realloc) { + State = ReallocMemAux(C, CE, /*ShouldFreeOnFail*/ false, State); + State = ProcessZeroAllocCheck(C, CE, 1, State); + } else if (FunI == MemFunctionInfo.II_reallocf) { + State = ReallocMemAux(C, CE, /*ShouldFreeOnFail*/ true, State); + State = ProcessZeroAllocCheck(C, CE, 1, State); + } else if (FunI == MemFunctionInfo.II_calloc) { State = CallocMem(C, CE, State); - State = ProcessZeroAllocation(C, CE, 0, State); - State = ProcessZeroAllocation(C, CE, 1, State); - } else if (FunI == II_free || FunI == II_g_free || FunI == II_kfree) { + State = ProcessZeroAllocCheck(C, CE, 0, State); + State = ProcessZeroAllocCheck(C, CE, 1, State); + } else if (FunI == MemFunctionInfo.II_free || + FunI == MemFunctionInfo.II_g_free || + FunI == MemFunctionInfo.II_kfree) { if (suppressDeallocationsInSuspiciousContexts(CE, C)) return; - State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); - } else if (FunI == II_strdup || FunI == II_win_strdup || - FunI == II_wcsdup || FunI == II_win_wcsdup) { + State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory); + } else if (FunI == MemFunctionInfo.II_strdup || + FunI == MemFunctionInfo.II_win_strdup || + FunI == MemFunctionInfo.II_wcsdup || + FunI == MemFunctionInfo.II_win_wcsdup) { State = MallocUpdateRefState(C, CE, State); - } else if (FunI == II_strndup) { + } else if (FunI == MemFunctionInfo.II_strndup) { State = MallocUpdateRefState(C, CE, State); - } else if (FunI == II_alloca || FunI == II_win_alloca) { + } else if (FunI == MemFunctionInfo.II_alloca || + FunI == MemFunctionInfo.II_win_alloca) { if (CE->getNumArgs() < 1) return; State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, AF_Alloca); - State = ProcessZeroAllocation(C, CE, 0, State); - } else if (isStandardNewDelete(FD, C.getASTContext())) { + State = ProcessZeroAllocCheck(C, CE, 0, State); + } else if (MemFunctionInfo.isStandardNewDelete(FD, C.getASTContext())) { // Process direct calls to operator new/new[]/delete/delete[] functions // as distinct from new/new[]/delete/delete[] expressions that are // processed by the checkPostStmt callbacks for CXXNewExpr and // CXXDeleteExpr. - OverloadedOperatorKind K = FD->getOverloadedOperator(); - if (K == OO_New) { + switch (FD->getOverloadedOperator()) { + case OO_New: State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, AF_CXXNew); - State = ProcessZeroAllocation(C, CE, 0, State); - } - else if (K == OO_Array_New) { + State = ProcessZeroAllocCheck(C, CE, 0, State); + break; + case OO_Array_New: State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, AF_CXXNewArray); - State = ProcessZeroAllocation(C, CE, 0, State); - } - else if (K == OO_Delete || K == OO_Array_Delete) - State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); - else + State = ProcessZeroAllocCheck(C, CE, 0, State); + break; + case OO_Delete: + case OO_Array_Delete: + State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory); + break; + default: llvm_unreachable("not a new/delete operator"); - } else if (FunI == II_if_nameindex) { + } + } else if (FunI == MemFunctionInfo.II_if_nameindex) { // Should we model this differently? We can allocate a fixed number of // elements with zeros in the last one. State = MallocMemAux(C, CE, UnknownVal(), UnknownVal(), State, AF_IfNameIndex); - } else if (FunI == II_if_freenameindex) { - State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); - } else if (FunI == II_g_malloc0 || FunI == II_g_try_malloc0) { + } else if (FunI == MemFunctionInfo.II_if_freenameindex) { + State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory); + } else if (FunI == MemFunctionInfo.II_g_malloc0 || + FunI == MemFunctionInfo.II_g_try_malloc0) { if (CE->getNumArgs() < 1) return; SValBuilder &svalBuilder = C.getSValBuilder(); SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); State = MallocMemAux(C, CE, CE->getArg(0), zeroVal, State); - State = ProcessZeroAllocation(C, CE, 0, State); - } else if (FunI == II_g_memdup) { + State = ProcessZeroAllocCheck(C, CE, 0, State); + } else if (FunI == MemFunctionInfo.II_g_memdup) { if (CE->getNumArgs() < 2) return; State = MallocMemAux(C, CE, CE->getArg(1), UndefinedVal(), State); - State = ProcessZeroAllocation(C, CE, 1, State); - } else if (FunI == II_g_malloc_n || FunI == II_g_try_malloc_n || - FunI == II_g_malloc0_n || FunI == II_g_try_malloc0_n) { + State = ProcessZeroAllocCheck(C, CE, 1, State); + } else if (FunI == MemFunctionInfo.II_g_malloc_n || + FunI == MemFunctionInfo.II_g_try_malloc_n || + FunI == MemFunctionInfo.II_g_malloc0_n || + FunI == MemFunctionInfo.II_g_try_malloc0_n) { if (CE->getNumArgs() < 2) return; SVal Init = UndefinedVal(); - if (FunI == II_g_malloc0_n || FunI == II_g_try_malloc0_n) { + if (FunI == MemFunctionInfo.II_g_malloc0_n || + FunI == MemFunctionInfo.II_g_try_malloc0_n) { SValBuilder &SB = C.getSValBuilder(); Init = SB.makeZeroVal(SB.getContext().CharTy); } SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1)); State = MallocMemAux(C, CE, TotalSize, Init, State); - State = ProcessZeroAllocation(C, CE, 0, State); - State = ProcessZeroAllocation(C, CE, 1, State); - } else if (FunI == II_g_realloc_n || FunI == II_g_try_realloc_n) { + State = ProcessZeroAllocCheck(C, CE, 0, State); + State = ProcessZeroAllocCheck(C, CE, 1, State); + } else if (FunI == MemFunctionInfo.II_g_realloc_n || + FunI == MemFunctionInfo.II_g_try_realloc_n) { if (CE->getNumArgs() < 3) return; - State = ReallocMemAux(C, CE, false, State, true); - State = ProcessZeroAllocation(C, CE, 1, State); - State = ProcessZeroAllocation(C, CE, 2, State); + State = ReallocMemAux(C, CE, /*ShouldFreeOnFail*/ false, State, + /*SuffixWithN*/ true); + State = ProcessZeroAllocCheck(C, CE, 1, State); + State = ProcessZeroAllocCheck(C, CE, 2, State); } } - if (IsOptimistic || ChecksEnabled[CK_MismatchedDeallocatorChecker]) { + if (MemFunctionInfo.ShouldIncludeOwnershipAnnotatedFunctions || + ChecksEnabled[CK_MismatchedDeallocatorChecker]) { // Check all the attributes, if there are any. // There can be multiple of these attributes. if (FD->hasAttrs()) @@ -977,9 +1227,9 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { } // Performs a 0-sized allocations check. -ProgramStateRef MallocChecker::ProcessZeroAllocation( - CheckerContext &C, const Expr *E, const unsigned AllocationSizeArg, - ProgramStateRef State, Optional RetVal) const { +ProgramStateRef MallocChecker::ProcessZeroAllocCheck( + CheckerContext &C, const Expr *E, const unsigned IndexOfSizeArg, + ProgramStateRef State, Optional RetVal) { if (!State) return nullptr; @@ -989,7 +1239,7 @@ ProgramStateRef MallocChecker::ProcessZeroAllocation( const Expr *Arg = nullptr; if (const CallExpr *CE = dyn_cast(E)) { - Arg = CE->getArg(AllocationSizeArg); + Arg = CE->getArg(IndexOfSizeArg); } else if (const CXXNewExpr *NE = dyn_cast(E)) { if (NE->isArray()) @@ -1051,7 +1301,9 @@ static QualType getDeepPointeeType(QualType T) { return Result; } -static bool treatUnusedNewEscaped(const CXXNewExpr *NE) { +/// \returns true if the constructor invoked by \p NE has an argument of a +/// pointer/reference to a record type. +static bool hasNonTrivialConstructorCall(const CXXNewExpr *NE) { const CXXConstructExpr *ConstructE = NE->getConstructExpr(); if (!ConstructE) @@ -1081,11 +1333,17 @@ static bool treatUnusedNewEscaped(const CXXNewExpr *NE) { void MallocChecker::processNewAllocation(const CXXNewExpr *NE, CheckerContext &C, SVal Target) const { - if (!isStandardNewDelete(NE->getOperatorNew(), C.getASTContext())) + if (!MemFunctionInfo.isStandardNewDelete(NE->getOperatorNew(), + C.getASTContext())) return; const ParentMap &PM = C.getLocationContext()->getParentMap(); - if (!PM.isConsumedExpr(NE) && treatUnusedNewEscaped(NE)) + + // Non-trivial constructors have a chance to escape 'this', but marking all + // invocations of trivial constructors as escaped would cause too great of + // reduction of true positives, so let's just do that for constructors that + // have an argument of a pointer-to-record type. + if (!PM.isConsumedExpr(NE) && hasNonTrivialConstructorCall(NE)) return; ProgramStateRef State = C.getState(); @@ -1096,7 +1354,7 @@ void MallocChecker::processNewAllocation(const CXXNewExpr *NE, State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray : AF_CXXNew, Target); State = addExtentSize(C, NE, State, Target); - State = ProcessZeroAllocation(C, NE, 0, State, Target); + State = ProcessZeroAllocCheck(C, NE, 0, State, Target); C.addTransition(State); } @@ -1164,13 +1422,14 @@ void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE, if (SymbolRef Sym = C.getSVal(DE->getArgument()).getAsSymbol()) checkUseAfterFree(Sym, C, DE->getArgument()); - if (!isStandardNewDelete(DE->getOperatorDelete(), C.getASTContext())) + if (!MemFunctionInfo.isStandardNewDelete(DE->getOperatorDelete(), + C.getASTContext())) return; ProgramStateRef State = C.getState(); - bool ReleasedAllocated; + bool IsKnownToBeAllocated; State = FreeMemAux(C, DE->getArgument(), DE, State, - /*Hold*/false, ReleasedAllocated); + /*Hold*/ false, IsKnownToBeAllocated); C.addTransition(State); } @@ -1210,11 +1469,11 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call, if (!*FreeWhenDone) return; - bool ReleasedAllocatedMemory; - ProgramStateRef State = FreeMemAux(C, Call.getArgExpr(0), - Call.getOriginExpr(), C.getState(), - /*Hold=*/true, ReleasedAllocatedMemory, - /*ReturnsNullOnFailure=*/true); + bool IsKnownToBeAllocatedMemory; + ProgramStateRef State = + FreeMemAux(C, Call.getArgExpr(0), Call.getOriginExpr(), C.getState(), + /*Hold=*/true, IsKnownToBeAllocatedMemory, + /*RetNullOnFailure=*/true); C.addTransition(State); } @@ -1226,7 +1485,7 @@ MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, if (!State) return nullptr; - if (Att->getModule() != II_malloc) + if (Att->getModule() != MemFunctionInfo.II_malloc) return nullptr; OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end(); @@ -1292,11 +1551,10 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, return MallocUpdateRefState(C, CE, State, Family); } -ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C, - const Expr *E, - ProgramStateRef State, - AllocationFamily Family, - Optional RetVal) { +static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E, + ProgramStateRef State, + AllocationFamily Family, + Optional RetVal) { if (!State) return nullptr; @@ -1324,27 +1582,24 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C, if (!State) return nullptr; - if (Att->getModule() != II_malloc) + if (Att->getModule() != MemFunctionInfo.II_malloc) return nullptr; - bool ReleasedAllocated = false; + bool IsKnownToBeAllocated = false; for (const auto &Arg : Att->args()) { ProgramStateRef StateI = FreeMemAux( C, CE, State, Arg.getASTIndex(), - Att->getOwnKind() == OwnershipAttr::Holds, ReleasedAllocated); + Att->getOwnKind() == OwnershipAttr::Holds, IsKnownToBeAllocated); if (StateI) State = StateI; } return State; } -ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, - const CallExpr *CE, - ProgramStateRef State, - unsigned Num, - bool Hold, - bool &ReleasedAllocated, +ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE, + ProgramStateRef State, unsigned Num, + bool Hold, bool &IsKnownToBeAllocated, bool ReturnsNullOnFailure) const { if (!State) return nullptr; @@ -1352,8 +1607,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, if (CE->getNumArgs() < (Num + 1)) return nullptr; - return FreeMemAux(C, CE->getArg(Num), CE, State, Hold, - ReleasedAllocated, ReturnsNullOnFailure); + return FreeMemAux(C, CE->getArg(Num), CE, State, Hold, IsKnownToBeAllocated, + ReturnsNullOnFailure); } /// Checks if the previous call to free on the given symbol failed - if free @@ -1371,8 +1626,10 @@ static bool didPreviousFreeFail(ProgramStateRef State, return false; } -AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C, - const Stmt *S) const { +static AllocationFamily +getAllocationFamily(const MemFunctionInfoTy &MemFunctionInfo, CheckerContext &C, + const Stmt *S) { + if (!S) return AF_None; @@ -1384,10 +1641,11 @@ AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C, ASTContext &Ctx = C.getASTContext(); - if (isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Any)) + if (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_Malloc, + MemoryOperationKind::MOK_Any)) return AF_Malloc; - if (isStandardNewDelete(FD, Ctx)) { + if (MemFunctionInfo.isStandardNewDelete(FD, Ctx)) { OverloadedOperatorKind Kind = FD->getOverloadedOperator(); if (Kind == OO_New || Kind == OO_Delete) return AF_CXXNew; @@ -1395,10 +1653,12 @@ AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C, return AF_CXXNewArray; } - if (isCMemFunction(FD, Ctx, AF_IfNameIndex, MemoryOperationKind::MOK_Any)) + if (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_IfNameIndex, + MemoryOperationKind::MOK_Any)) return AF_IfNameIndex; - if (isCMemFunction(FD, Ctx, AF_Alloca, MemoryOperationKind::MOK_Any)) + if (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_Alloca, + MemoryOperationKind::MOK_Any)) return AF_Alloca; return AF_None; @@ -1416,8 +1676,8 @@ AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C, return AF_None; } -bool MallocChecker::printAllocDeallocName(raw_ostream &os, CheckerContext &C, - const Expr *E) const { +static bool printAllocDeallocName(raw_ostream &os, CheckerContext &C, + const Expr *E) { if (const CallExpr *CE = dyn_cast(E)) { // FIXME: This doesn't handle indirect calls. const FunctionDecl *FD = CE->getDirectCallee(); @@ -1456,9 +1716,10 @@ bool MallocChecker::printAllocDeallocName(raw_ostream &os, CheckerContext &C, return false; } -void MallocChecker::printExpectedAllocName(raw_ostream &os, CheckerContext &C, - const Expr *E) const { - AllocationFamily Family = getAllocationFamily(C, E); +static void printExpectedAllocName(raw_ostream &os, + const MemFunctionInfoTy &MemFunctionInfo, + CheckerContext &C, const Expr *E) { + AllocationFamily Family = getAllocationFamily(MemFunctionInfo, C, E); switch(Family) { case AF_Malloc: os << "malloc()"; return; @@ -1471,8 +1732,7 @@ void MallocChecker::printExpectedAllocName(raw_ostream &os, CheckerContext &C, } } -void MallocChecker::printExpectedDeallocName(raw_ostream &os, - AllocationFamily Family) const { +static void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) { switch(Family) { case AF_Malloc: os << "free()"; return; case AF_CXXNew: os << "'delete'"; return; @@ -1487,9 +1747,8 @@ void MallocChecker::printExpectedDeallocName(raw_ostream &os, ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr, const Expr *ParentExpr, - ProgramStateRef State, - bool Hold, - bool &ReleasedAllocated, + ProgramStateRef State, bool Hold, + bool &IsKnownToBeAllocated, bool ReturnsNullOnFailure) const { if (!State) @@ -1563,6 +1822,9 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, const RefState *RsBase = State->get(SymBase); SymbolRef PreviousRetStatusSymbol = nullptr; + IsKnownToBeAllocated = + RsBase && (RsBase->isAllocated() || RsBase->isAllocatedOfSizeZero()); + if (RsBase) { // Memory returned by alloca() shouldn't be freed. @@ -1585,7 +1847,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, // Check if an expected deallocation function matches the real one. bool DeallocMatchesAlloc = - RsBase->getAllocationFamily() == getAllocationFamily(C, ParentExpr); + RsBase->getAllocationFamily() == + getAllocationFamily(MemFunctionInfo, C, ParentExpr); if (!DeallocMatchesAlloc) { ReportMismatchedDealloc(C, ArgExpr->getSourceRange(), ParentExpr, RsBase, SymBase, Hold); @@ -1611,9 +1874,6 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, return nullptr; } - ReleasedAllocated = (RsBase != nullptr) && (RsBase->isAllocated() || - RsBase->isAllocatedOfSizeZero()); - // Clean out the info on previous call to free return info. State = State->remove(SymBase); @@ -1628,8 +1888,9 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, } } - AllocationFamily Family = RsBase ? RsBase->getAllocationFamily() - : getAllocationFamily(C, ParentExpr); + AllocationFamily Family = + RsBase ? RsBase->getAllocationFamily() + : getAllocationFamily(MemFunctionInfo, C, ParentExpr); // Normal free. if (Hold) return State->set(SymBase, @@ -1679,8 +1940,8 @@ Optional MallocChecker::getCheckIfTracked(CheckerContext &C, const Stmt *AllocDeallocStmt, bool IsALeakCheck) const { - return getCheckIfTracked(getAllocationFamily(C, AllocDeallocStmt), - IsALeakCheck); + return getCheckIfTracked( + getAllocationFamily(MemFunctionInfo, C, AllocDeallocStmt), IsALeakCheck); } Optional @@ -1818,7 +2079,7 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, else os << "not memory allocated by "; - printExpectedAllocName(os, C, DeallocExpr); + printExpectedAllocName(os, MemFunctionInfo, C, DeallocExpr); auto R = std::make_unique(*BT_BadFree[*CheckKind], os.str(), N); @@ -2130,7 +2391,7 @@ void MallocChecker::ReportFunctionPointerFree(CheckerContext &C, SVal ArgVal, ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C, const CallExpr *CE, - bool FreesOnFail, + bool ShouldFreeOnFail, ProgramStateRef State, bool SuffixWithN) const { if (!State) @@ -2195,33 +2456,32 @@ ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C, if (!FromPtr || !ToPtr) return nullptr; - bool ReleasedAllocated = false; + bool IsKnownToBeAllocated = false; // If the size is 0, free the memory. if (SizeIsZero) - if (ProgramStateRef stateFree = FreeMemAux(C, CE, StateSizeIsZero, 0, - false, ReleasedAllocated)){ - // The semantics of the return value are: - // If size was equal to 0, either NULL or a pointer suitable to be passed - // to free() is returned. We just free the input pointer and do not add - // any constrains on the output pointer. + // The semantics of the return value are: + // If size was equal to 0, either NULL or a pointer suitable to be passed + // to free() is returned. We just free the input pointer and do not add + // any constrains on the output pointer. + if (ProgramStateRef stateFree = + FreeMemAux(C, CE, StateSizeIsZero, 0, false, IsKnownToBeAllocated)) return stateFree; - } // Default behavior. if (ProgramStateRef stateFree = - FreeMemAux(C, CE, State, 0, false, ReleasedAllocated)) { + FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocated)) { ProgramStateRef stateRealloc = MallocMemAux(C, CE, TotalSize, UnknownVal(), stateFree); if (!stateRealloc) return nullptr; - ReallocPairKind Kind = RPToBeFreedAfterFailure; - if (FreesOnFail) - Kind = RPIsFreeOnFailure; - else if (!ReleasedAllocated) - Kind = RPDoNotTrackAfterFailure; + OwnershipAfterReallocKind Kind = OAR_ToBeFreedAfterFailure; + if (ShouldFreeOnFail) + Kind = OAR_FreeOnFailure; + else if (!IsKnownToBeAllocated) + Kind = OAR_DoNotTrackAfterFailure; // Record the info about the reallocated symbol so that we could properly // process failed reallocation. @@ -2249,9 +2509,9 @@ ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE, return MallocMemAux(C, CE, TotalSize, zeroVal, State); } -LeakInfo -MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym, - CheckerContext &C) const { +MallocChecker::LeakInfo MallocChecker::getAllocationSite(const ExplodedNode *N, + SymbolRef Sym, + CheckerContext &C) { const LocationContext *LeakContext = N->getLocationContext(); // Walk the ExplodedGraph backwards and find the first node that referred to // the tracked symbol. @@ -2432,9 +2692,10 @@ void MallocChecker::checkPreCall(const CallEvent &Call, ASTContext &Ctx = C.getASTContext(); if (ChecksEnabled[CK_MallocChecker] && - (isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Free) || - isCMemFunction(FD, Ctx, AF_IfNameIndex, - MemoryOperationKind::MOK_Free))) + (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_Malloc, + MemoryOperationKind::MOK_Free) || + MemFunctionInfo.isCMemFunction(FD, Ctx, AF_IfNameIndex, + MemoryOperationKind::MOK_Free))) return; } @@ -2537,7 +2798,7 @@ void MallocChecker::checkPostStmt(const BlockExpr *BE, C.addTransition(state); } -bool MallocChecker::isReleased(SymbolRef Sym, CheckerContext &C) const { +static bool isReleased(SymbolRef Sym, CheckerContext &C) { assert(Sym); const RefState *RS = C.getState()->get(Sym); return (RS && RS->isReleased()); @@ -2642,13 +2903,17 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state, SymbolRef ReallocSym = I.getData().ReallocatedSym; if (const RefState *RS = state->get(ReallocSym)) { if (RS->isReleased()) { - if (I.getData().Kind == RPToBeFreedAfterFailure) + switch (I.getData().Kind) { + case OAR_ToBeFreedAfterFailure: state = state->set(ReallocSym, RefState::getAllocated(RS->getAllocationFamily(), RS->getStmt())); - else if (I.getData().Kind == RPDoNotTrackAfterFailure) + break; + case OAR_DoNotTrackAfterFailure: state = state->remove(ReallocSym); - else - assert(I.getData().Kind == RPIsFreeOnFailure); + break; + default: + assert(I.getData().Kind == OAR_FreeOnFailure); + } } } state = state->remove(I.getKey()); @@ -2731,7 +2996,7 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly( // If it's one of the allocation functions we can reason about, we model // its behavior explicitly. - if (isMemFunction(FD, ASTC)) + if (MemFunctionInfo.isMemFunction(FD, ASTC)) return false; // If it's not a system call, assume it frees memory. @@ -2823,35 +3088,32 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly( return false; } -static bool retTrue(const RefState *RS) { - return true; -} - -static bool checkIfNewOrNewArrayFamily(const RefState *RS) { - return (RS->getAllocationFamily() == AF_CXXNewArray || - RS->getAllocationFamily() == AF_CXXNew); -} - ProgramStateRef MallocChecker::checkPointerEscape(ProgramStateRef State, const InvalidatedSymbols &Escaped, const CallEvent *Call, PointerEscapeKind Kind) const { - return checkPointerEscapeAux(State, Escaped, Call, Kind, &retTrue); + return checkPointerEscapeAux(State, Escaped, Call, Kind, + /*IsConstPointerEscape*/ false); } ProgramStateRef MallocChecker::checkConstPointerEscape(ProgramStateRef State, const InvalidatedSymbols &Escaped, const CallEvent *Call, PointerEscapeKind Kind) const { + // If a const pointer escapes, it may not be freed(), but it could be deleted. return checkPointerEscapeAux(State, Escaped, Call, Kind, - &checkIfNewOrNewArrayFamily); + /*IsConstPointerEscape*/ true); } -ProgramStateRef MallocChecker::checkPointerEscapeAux(ProgramStateRef State, - const InvalidatedSymbols &Escaped, - const CallEvent *Call, - PointerEscapeKind Kind, - bool(*CheckRefState)(const RefState*)) const { +static bool checkIfNewOrNewArrayFamily(const RefState *RS) { + return (RS->getAllocationFamily() == AF_CXXNewArray || + RS->getAllocationFamily() == AF_CXXNew); +} + +ProgramStateRef MallocChecker::checkPointerEscapeAux( + ProgramStateRef State, const InvalidatedSymbols &Escaped, + const CallEvent *Call, PointerEscapeKind Kind, + bool IsConstPointerEscape) const { // If we know that the call does not free memory, or we want to process the // call later, keep tracking the top level arguments. SymbolRef EscapingSymbol = nullptr; @@ -2870,12 +3132,10 @@ ProgramStateRef MallocChecker::checkPointerEscapeAux(ProgramStateRef State, if (EscapingSymbol && EscapingSymbol != sym) continue; - if (const RefState *RS = State->get(sym)) { - if ((RS->isAllocated() || RS->isAllocatedOfSizeZero()) && - CheckRefState(RS)) { - State = State->set(sym, RefState::getEscaped(RS)); - } - } + if (const RefState *RS = State->get(sym)) + if (RS->isAllocated() || RS->isAllocatedOfSizeZero()) + if (!IsConstPointerEscape || checkIfNewOrNewArrayFamily(RS)) + State = State->set(sym, RefState::getEscaped(RS)); } return State; } @@ -2885,9 +3145,8 @@ static SymbolRef findFailedReallocSymbol(ProgramStateRef currState, ReallocPairsTy currMap = currState->get(); ReallocPairsTy prevMap = prevState->get(); - for (ReallocPairsTy::iterator I = prevMap.begin(), E = prevMap.end(); - I != E; ++I) { - SymbolRef sym = I.getKey(); + for (const ReallocPairsTy::value_type &Pair : prevMap) { + SymbolRef sym = Pair.first; if (!currMap.lookup(sym)) return sym; } @@ -2908,21 +3167,19 @@ static bool isReferenceCountingPointerDestructor(const CXXDestructorDecl *DD) { return false; } -PathDiagnosticPieceRef -MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - PathSensitiveBugReport &BR) { - +PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) { ProgramStateRef state = N->getState(); ProgramStateRef statePrev = N->getFirstPred()->getState(); - const RefState *RS = state->get(Sym); + const RefState *RSCurr = state->get(Sym); const RefState *RSPrev = statePrev->get(Sym); const Stmt *S = N->getStmtForDiagnostics(); // When dealing with containers, we sometimes want to give a note // even if the statement is missing. - if (!S && (!RS || RS->getAllocationFamily() != AF_InnerBuffer)) + if (!S && (!RSCurr || RSCurr->getAllocationFamily() != AF_InnerBuffer)) return nullptr; const LocationContext *CurrentLC = N->getLocationContext(); @@ -2957,12 +3214,12 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N, llvm::raw_svector_ostream OS(Buf); if (Mode == Normal) { - if (isAllocated(RS, RSPrev, S)) { + if (isAllocated(RSCurr, RSPrev, S)) { Msg = "Memory is allocated"; StackHint = std::make_unique( Sym, "Returned allocated memory"); - } else if (isReleased(RS, RSPrev, S)) { - const auto Family = RS->getAllocationFamily(); + } else if (isReleased(RSCurr, RSPrev, S)) { + const auto Family = RSCurr->getAllocationFamily(); switch (Family) { case AF_Alloca: case AF_Malloc: @@ -2986,7 +3243,7 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N, Sym, "Returning; inner buffer was deallocated"); } else { OS << "reallocated by call to '"; - const Stmt *S = RS->getStmt(); + const Stmt *S = RSCurr->getStmt(); if (const auto *MemCallE = dyn_cast(S)) { OS << MemCallE->getMethodDecl()->getNameAsString(); } else if (const auto *OpCallE = dyn_cast(S)) { @@ -3037,10 +3294,10 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N, } } } - } else if (isRelinquished(RS, RSPrev, S)) { + } else if (isRelinquished(RSCurr, RSPrev, S)) { Msg = "Memory ownership is transferred"; StackHint = std::make_unique(Sym, ""); - } else if (isReallocFailedCheck(RS, RSPrev, S)) { + } else if (hasReallocFailed(RSCurr, RSPrev, S)) { Mode = ReallocationFailed; Msg = "Reallocation failed"; StackHint = std::make_unique( @@ -3080,7 +3337,7 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N, // Generate the extra diagnostic. PathDiagnosticLocation Pos; if (!S) { - assert(RS->getAllocationFamily() == AF_InnerBuffer); + assert(RSCurr->getAllocationFamily() == AF_InnerBuffer); auto PostImplCall = N->getLocation().getAs(); if (!PostImplCall) return nullptr; @@ -3145,8 +3402,8 @@ void ento::registerInnerPointerCheckerAux(CheckerManager &mgr) { void ento::registerDynamicMemoryModeling(CheckerManager &mgr) { auto *checker = mgr.registerChecker(); - checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption( - checker, "Optimistic"); + checker->MemFunctionInfo.ShouldIncludeOwnershipAnnotatedFunctions = + mgr.getAnalyzerOptions().getCheckerBooleanOption(checker, "Optimistic"); } bool ento::shouldRegisterDynamicMemoryModeling(const LangOptions &LO) { diff --git a/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp index 0734272381b8a..6ffc89745365c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp @@ -35,11 +35,11 @@ class NonNullParamChecker void checkPreCall(const CallEvent &Call, CheckerContext &C) const; - std::unique_ptr + std::unique_ptr genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE, unsigned IdxOfArg) const; - std::unique_ptr + std::unique_ptr genReportReferenceToNullPointer(const ExplodedNode *ErrorN, const Expr *ArgE) const; }; @@ -179,7 +179,7 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, C.addTransition(state); } -std::unique_ptr +std::unique_ptr NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode, const Expr *ArgE, unsigned IdxOfArg) const { @@ -204,7 +204,8 @@ NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode, return R; } -std::unique_ptr NonNullParamChecker::genReportReferenceToNullPointer( +std::unique_ptr +NonNullParamChecker::genReportReferenceToNullPointer( const ExplodedNode *ErrorNode, const Expr *ArgE) const { if (!BTNullRefArg) BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer")); diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index c6b7ef041ac4e..f1592ebff669d 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -1438,6 +1438,7 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, if (!StoreSite) return nullptr; + Satisfied = true; // If we have an expression that provided the value, try to track where it @@ -1802,7 +1803,7 @@ TrackControlDependencyCondBRVisitor::VisitNode(const ExplodedNode *N, if (ControlDeps.isControlDependent(OriginB, NB)) { // We don't really want to explain for range loops. Evidence suggests that // the only thing that leads to is the addition of calls to operator!=. - if (isa(NB->getTerminator())) + if (llvm::isa_and_nonnull(NB->getTerminatorStmt())) return nullptr; if (const Expr *Condition = NB->getLastCondition()) { diff --git a/clang/lib/StaticAnalyzer/Core/Store.cpp b/clang/lib/StaticAnalyzer/Core/Store.cpp index 3cf616161c669..b4ab6877726ce 100644 --- a/clang/lib/StaticAnalyzer/Core/Store.cpp +++ b/clang/lib/StaticAnalyzer/Core/Store.cpp @@ -52,7 +52,7 @@ StoreRef StoreManager::enterStackFrame(Store OldStore, Call.getInitialStackFrameContents(LCtx, InitialBindings); for (const auto &I : InitialBindings) - Store = Bind(Store.getStore(), I.first, I.second); + Store = Bind(Store.getStore(), I.first.castAs(), I.second); return Store; } diff --git a/clang/lib/Tooling/ArgumentsAdjusters.cpp b/clang/lib/Tooling/ArgumentsAdjusters.cpp index 942b35df453e9..f56d08c47b9a4 100644 --- a/clang/lib/Tooling/ArgumentsAdjusters.cpp +++ b/clang/lib/Tooling/ArgumentsAdjusters.cpp @@ -57,6 +57,22 @@ ArgumentsAdjuster getClangStripOutputAdjuster() { }; } +ArgumentsAdjuster getClangStripSerializeDiagnosticAdjuster() { + return [](const CommandLineArguments &Args, StringRef /*unused*/) { + CommandLineArguments AdjustedArgs; + for (size_t i = 0, e = Args.size(); i < e; ++i) { + StringRef Arg = Args[i]; + if (Arg == "--serialize-diagnostics") { + // Skip the diagnostic output argument. + ++i; + continue; + } + AdjustedArgs.push_back(Args[i]); + } + return AdjustedArgs; + }; +} + ArgumentsAdjuster getClangStripDependencyFileAdjuster() { return [](const CommandLineArguments &Args, StringRef /*unused*/) { CommandLineArguments AdjustedArgs; diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp index 7a3d189adac28..35ecbd4a7fb4c 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp @@ -193,6 +193,9 @@ class MinimizedVFSFile final : public llvm::vfs::File { llvm::ErrorOr> createFile(const CachedFileSystemEntry *Entry, ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings) { + if (Entry->isDirectory()) + return llvm::ErrorOr>( + std::make_error_code(std::errc::is_a_directory)); llvm::ErrorOr Contents = Entry->getContents(); if (!Contents) return Contents.getError(); diff --git a/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp b/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp index a7f79c33bc556..e746bbb7f87b2 100644 --- a/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp +++ b/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp @@ -199,6 +199,20 @@ int IncludeCategoryManager::getIncludePriority(StringRef IncludeName, return Ret; } +int IncludeCategoryManager::getSortIncludePriority(StringRef IncludeName, + bool CheckMainHeader) const { + int Ret = INT_MAX; + for (unsigned i = 0, e = CategoryRegexs.size(); i != e; ++i) + if (CategoryRegexs[i].match(IncludeName)) { + Ret = Style.IncludeCategories[i].SortPriority; + if (Ret == 0) + Ret = Style.IncludeCategories[i].Priority; + break; + } + if (CheckMainHeader && IsMainFile && Ret > 0 && isMainHeader(IncludeName)) + Ret = 0; + return Ret; +} bool IncludeCategoryManager::isMainHeader(StringRef IncludeName) const { if (!IncludeName.startswith("\"")) return false; diff --git a/clang/lib/Tooling/Inclusions/IncludeStyle.cpp b/clang/lib/Tooling/Inclusions/IncludeStyle.cpp index c53c82c836307..26dc0b87cf9d7 100644 --- a/clang/lib/Tooling/Inclusions/IncludeStyle.cpp +++ b/clang/lib/Tooling/Inclusions/IncludeStyle.cpp @@ -17,6 +17,7 @@ void MappingTraits::mapping( IO &IO, IncludeStyle::IncludeCategory &Category) { IO.mapOptional("Regex", Category.Regex); IO.mapOptional("Priority", Category.Priority); + IO.mapOptional("SortPriority", Category.SortPriority); } void ScalarEnumerationTraits::enumeration( diff --git a/clang/lib/Tooling/Refactoring/CMakeLists.txt b/clang/lib/Tooling/Refactoring/CMakeLists.txt index d1f092f261c9f..e3961db2841ec 100644 --- a/clang/lib/Tooling/Refactoring/CMakeLists.txt +++ b/clang/lib/Tooling/Refactoring/CMakeLists.txt @@ -14,6 +14,7 @@ add_clang_library(clangToolingRefactoring Rename/USRFindingAction.cpp Rename/USRLocFinder.cpp SourceCode.cpp + SourceCodeBuilders.cpp Stencil.cpp Transformer.cpp diff --git a/clang/lib/Tooling/Refactoring/RangeSelector.cpp b/clang/lib/Tooling/Refactoring/RangeSelector.cpp index 768c02e2277b3..972c7e65540a9 100644 --- a/clang/lib/Tooling/Refactoring/RangeSelector.cpp +++ b/clang/lib/Tooling/Refactoring/RangeSelector.cpp @@ -219,6 +219,9 @@ RangeSelector tooling::name(std::string ID) { } namespace { +// FIXME: make this available in the public API for users to easily create their +// own selectors. + // Creates a selector from a range-selection function \p Func, which selects a // range that is relative to a bound node id. \c T is the node type expected by // \p Func. @@ -286,6 +289,19 @@ RangeSelector tooling::initListElements(std::string ID) { return RelativeSelector(std::move(ID)); } +namespace { +// Returns the range of the else branch, including the `else` keyword. +CharSourceRange getElseRange(const MatchResult &Result, const IfStmt &S) { + return maybeExtendRange( + CharSourceRange::getTokenRange(S.getElseLoc(), S.getEndLoc()), + tok::TokenKind::semi, *Result.Context); +} +} // namespace + +RangeSelector tooling::elseBranch(std::string ID) { + return RelativeSelector(std::move(ID)); +} + RangeSelector tooling::expansion(RangeSelector S) { return [S](const MatchResult &Result) -> Expected { Expected SRange = S(Result); diff --git a/clang/lib/Tooling/Refactoring/SourceCodeBuilders.cpp b/clang/lib/Tooling/Refactoring/SourceCodeBuilders.cpp new file mode 100644 index 0000000000000..2499c0f1eb398 --- /dev/null +++ b/clang/lib/Tooling/Refactoring/SourceCodeBuilders.cpp @@ -0,0 +1,160 @@ +//===--- SourceCodeBuilder.cpp ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactoring/SourceCodeBuilders.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/Tooling/Refactoring/SourceCode.h" +#include "llvm/ADT/Twine.h" +#include + +using namespace clang; +using namespace tooling; + +const Expr *tooling::reallyIgnoreImplicit(const Expr &E) { + const Expr *Expr = E.IgnoreImplicit(); + if (const auto *CE = dyn_cast(Expr)) { + if (CE->getNumArgs() > 0 && + CE->getArg(0)->getSourceRange() == Expr->getSourceRange()) + return CE->getArg(0)->IgnoreImplicit(); + } + return Expr; +} + +bool tooling::mayEverNeedParens(const Expr &E) { + const Expr *Expr = reallyIgnoreImplicit(E); + // We always want parens around unary, binary, and ternary operators, because + // they are lower precedence. + if (isa(Expr) || isa(Expr) || + isa(Expr)) + return true; + + // We need parens around calls to all overloaded operators except: function + // calls, subscripts, and expressions that are already part of an (implicit) + // call to operator->. These latter are all in the same precedence level as + // dot/arrow and that level is left associative, so they don't need parens + // when appearing on the left. + if (const auto *Op = dyn_cast(Expr)) + return Op->getOperator() != OO_Call && Op->getOperator() != OO_Subscript && + Op->getOperator() != OO_Arrow; + + return false; +} + +bool tooling::needParensAfterUnaryOperator(const Expr &E) { + const Expr *Expr = reallyIgnoreImplicit(E); + if (isa(Expr) || isa(Expr)) + return true; + + if (const auto *Op = dyn_cast(Expr)) + return Op->getNumArgs() == 2 && Op->getOperator() != OO_PlusPlus && + Op->getOperator() != OO_MinusMinus && Op->getOperator() != OO_Call && + Op->getOperator() != OO_Subscript; + + return false; +} + +llvm::Optional tooling::buildParens(const Expr &E, + const ASTContext &Context) { + StringRef Text = getText(E, Context); + if (Text.empty()) + return llvm::None; + if (mayEverNeedParens(E)) + return ("(" + Text + ")").str(); + return Text.str(); +} + +llvm::Optional +tooling::buildDereference(const Expr &E, const ASTContext &Context) { + if (const auto *Op = dyn_cast(&E)) + if (Op->getOpcode() == UO_AddrOf) { + // Strip leading '&'. + StringRef Text = + getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context); + if (Text.empty()) + return llvm::None; + return Text.str(); + } + + StringRef Text = getText(E, Context); + if (Text.empty()) + return llvm::None; + // Add leading '*'. + if (needParensAfterUnaryOperator(E)) + return ("*(" + Text + ")").str(); + return ("*" + Text).str(); +} + +llvm::Optional tooling::buildAddressOf(const Expr &E, + const ASTContext &Context) { + if (const auto *Op = dyn_cast(&E)) + if (Op->getOpcode() == UO_Deref) { + // Strip leading '*'. + StringRef Text = + getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context); + if (Text.empty()) + return llvm::None; + return Text.str(); + } + // Add leading '&'. + StringRef Text = getText(E, Context); + if (Text.empty()) + return llvm::None; + if (needParensAfterUnaryOperator(E)) { + return ("&(" + Text + ")").str(); + } + return ("&" + Text).str(); +} + +llvm::Optional tooling::buildDot(const Expr &E, + const ASTContext &Context) { + if (const auto *Op = llvm::dyn_cast(&E)) + if (Op->getOpcode() == UO_Deref) { + // Strip leading '*', add following '->'. + const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts(); + StringRef DerefText = getText(*SubExpr, Context); + if (DerefText.empty()) + return llvm::None; + if (needParensBeforeDotOrArrow(*SubExpr)) + return ("(" + DerefText + ")->").str(); + return (DerefText + "->").str(); + } + + // Add following '.'. + StringRef Text = getText(E, Context); + if (Text.empty()) + return llvm::None; + if (needParensBeforeDotOrArrow(E)) { + return ("(" + Text + ").").str(); + } + return (Text + ".").str(); +} + +llvm::Optional tooling::buildArrow(const Expr &E, + const ASTContext &Context) { + if (const auto *Op = llvm::dyn_cast(&E)) + if (Op->getOpcode() == UO_AddrOf) { + // Strip leading '&', add following '.'. + const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts(); + StringRef DerefText = getText(*SubExpr, Context); + if (DerefText.empty()) + return llvm::None; + if (needParensBeforeDotOrArrow(*SubExpr)) + return ("(" + DerefText + ").").str(); + return (DerefText + ".").str(); + } + + // Add following '->'. + StringRef Text = getText(E, Context); + if (Text.empty()) + return llvm::None; + if (needParensBeforeDotOrArrow(E)) + return ("(" + Text + ")->").str(); + return (Text + "->").str(); +} diff --git a/clang/lib/Tooling/Refactoring/Stencil.cpp b/clang/lib/Tooling/Refactoring/Stencil.cpp index 09eca21c3cef1..a01b81023389f 100644 --- a/clang/lib/Tooling/Refactoring/Stencil.cpp +++ b/clang/lib/Tooling/Refactoring/Stencil.cpp @@ -14,6 +14,7 @@ #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Lex/Lexer.h" #include "clang/Tooling/Refactoring/SourceCode.h" +#include "clang/Tooling/Refactoring/SourceCodeBuilders.h" #include "llvm/Support/Errc.h" #include #include @@ -23,7 +24,10 @@ using namespace clang; using namespace tooling; using ast_matchers::MatchFinder; +using llvm::errc; using llvm::Error; +using llvm::Expected; +using llvm::StringError; // A down_cast function to safely down cast a StencilPartInterface to a subclass // D. Returns nullptr if P is not an instance of D. @@ -51,28 +55,58 @@ struct RawTextData { }; // A debugging operation to dump the AST for a particular (bound) AST node. -struct DebugPrintNodeOpData { - explicit DebugPrintNodeOpData(std::string S) : Id(std::move(S)) {} +struct DebugPrintNodeData { + explicit DebugPrintNodeData(std::string S) : Id(std::move(S)) {} std::string Id; }; // The fragment of code corresponding to the selected range. -struct SelectorOpData { - explicit SelectorOpData(RangeSelector S) : Selector(std::move(S)) {} +struct SelectorData { + explicit SelectorData(RangeSelector S) : Selector(std::move(S)) {} RangeSelector Selector; }; -} // namespace + +// A stencil operation to build a member access `e.m` or `e->m`, as appropriate. +struct AccessData { + AccessData(StringRef BaseId, StencilPart Member) + : BaseId(BaseId), Member(std::move(Member)) {} + std::string BaseId; + StencilPart Member; +}; + +struct IfBoundData { + IfBoundData(StringRef Id, StencilPart TruePart, StencilPart FalsePart) + : Id(Id), TruePart(std::move(TruePart)), FalsePart(std::move(FalsePart)) { + } + std::string Id; + StencilPart TruePart; + StencilPart FalsePart; +}; bool isEqualData(const RawTextData &A, const RawTextData &B) { return A.Text == B.Text; } -bool isEqualData(const DebugPrintNodeOpData &A, const DebugPrintNodeOpData &B) { +bool isEqualData(const DebugPrintNodeData &A, const DebugPrintNodeData &B) { return A.Id == B.Id; } // Equality is not (yet) defined for \c RangeSelector. -bool isEqualData(const SelectorOpData &, const SelectorOpData &) { return false; } +bool isEqualData(const SelectorData &, const SelectorData &) { return false; } + +bool isEqualData(const AccessData &A, const AccessData &B) { + return A.BaseId == B.BaseId && A.Member == B.Member; +} + +bool isEqualData(const IfBoundData &A, const IfBoundData &B) { + return A.Id == B.Id && A.TruePart == B.TruePart && A.FalsePart == B.FalsePart; +} + +// Equality is not defined over MatchConsumers, which are opaque. +bool isEqualData(const MatchConsumer &A, + const MatchConsumer &B) { + return false; +} // The `evalData()` overloads evaluate the given stencil data to a string, given // the match result, and append it to `Result`. We define an overload for each @@ -84,7 +118,7 @@ Error evalData(const RawTextData &Data, const MatchFinder::MatchResult &, return Error::success(); } -Error evalData(const DebugPrintNodeOpData &Data, +Error evalData(const DebugPrintNodeData &Data, const MatchFinder::MatchResult &Match, std::string *Result) { std::string Output; llvm::raw_string_ostream Os(Output); @@ -96,7 +130,7 @@ Error evalData(const DebugPrintNodeOpData &Data, return Error::success(); } -Error evalData(const SelectorOpData &Data, const MatchFinder::MatchResult &Match, +Error evalData(const SelectorData &Data, const MatchFinder::MatchResult &Match, std::string *Result) { auto Range = Data.Selector(Match); if (!Range) @@ -105,6 +139,41 @@ Error evalData(const SelectorOpData &Data, const MatchFinder::MatchResult &Match return Error::success(); } +Error evalData(const AccessData &Data, const MatchFinder::MatchResult &Match, + std::string *Result) { + const auto *E = Match.Nodes.getNodeAs(Data.BaseId); + if (E == nullptr) + return llvm::make_error(errc::invalid_argument, + "Id not bound: " + Data.BaseId); + if (!E->isImplicitCXXThis()) { + if (llvm::Optional S = E->getType()->isAnyPointerType() + ? buildArrow(*E, *Match.Context) + : buildDot(*E, *Match.Context)) + *Result += *S; + else + return llvm::make_error( + errc::invalid_argument, + "Could not construct object text from ID: " + Data.BaseId); + } + return Data.Member.eval(Match, Result); +} + +Error evalData(const IfBoundData &Data, const MatchFinder::MatchResult &Match, + std::string *Result) { + auto &M = Match.Nodes.getMap(); + return (M.find(Data.Id) != M.end() ? Data.TruePart : Data.FalsePart) + .eval(Match, Result); +} + +Error evalData(const MatchConsumer &Fn, + const MatchFinder::MatchResult &Match, std::string *Result) { + Expected Value = Fn(Match); + if (!Value) + return Value.takeError(); + *Result += *Value; + return Error::success(); +} + template class StencilPartImpl : public StencilPartInterface { T Data; @@ -133,11 +202,6 @@ class StencilPartImpl : public StencilPartInterface { return false; } }; - -namespace { -using RawText = StencilPartImpl; -using DebugPrintNodeOp = StencilPartImpl; -using SelectorOp = StencilPartImpl; } // namespace StencilPart Stencil::wrap(StringRef Text) { @@ -163,13 +227,31 @@ Stencil::eval(const MatchFinder::MatchResult &Match) const { } StencilPart stencil::text(StringRef Text) { - return StencilPart(std::make_shared(Text)); + return StencilPart(std::make_shared>(Text)); } StencilPart stencil::selection(RangeSelector Selector) { - return StencilPart(std::make_shared(std::move(Selector))); + return StencilPart( + std::make_shared>(std::move(Selector))); } StencilPart stencil::dPrint(StringRef Id) { - return StencilPart(std::make_shared(Id)); + return StencilPart(std::make_shared>(Id)); +} + +StencilPart stencil::access(StringRef BaseId, StencilPart Member) { + return StencilPart( + std::make_shared>(BaseId, std::move(Member))); +} + +StencilPart stencil::ifBound(StringRef Id, StencilPart TruePart, + StencilPart FalsePart) { + return StencilPart(std::make_shared>( + Id, std::move(TruePart), std::move(FalsePart))); +} + +StencilPart stencil::run(MatchConsumer Fn) { + return StencilPart( + std::make_shared>>( + std::move(Fn))); } diff --git a/clang/lib/Tooling/Refactoring/Transformer.cpp b/clang/lib/Tooling/Refactoring/Transformer.cpp index c27739c596267..905d5944449c9 100644 --- a/clang/lib/Tooling/Refactoring/Transformer.cpp +++ b/clang/lib/Tooling/Refactoring/Transformer.cpp @@ -150,6 +150,21 @@ DynTypedMatcher tooling::detail::buildMatcher(const RewriteRule &Rule) { return Ms[0]; } +SourceLocation tooling::detail::getRuleMatchLoc(const MatchResult &Result) { + auto &NodesMap = Result.Nodes.getMap(); + auto Root = NodesMap.find(RewriteRule::RootID); + assert(Root != NodesMap.end() && "Transformation failed: missing root node."); + llvm::Optional RootRange = getRangeForEdit( + CharSourceRange::getTokenRange(Root->second.getSourceRange()), + *Result.Context); + if (RootRange) + return RootRange->getBegin(); + // The match doesn't have a coherent range, so fall back to the expansion + // location as the "beginning" of the match. + return Result.SourceManager->getExpansionLoc( + Root->second.getSourceRange().getBegin()); +} + // Finds the case that was "selected" -- that is, whose matcher triggered the // `MatchResult`. const RewriteRule::Case & @@ -178,14 +193,6 @@ void Transformer::run(const MatchResult &Result) { if (Result.Context->getDiagnostics().hasErrorOccurred()) return; - // Verify the existence and validity of the AST node that roots this rule. - auto &NodesMap = Result.Nodes.getMap(); - auto Root = NodesMap.find(RewriteRule::RootID); - assert(Root != NodesMap.end() && "Transformation failed: missing root node."); - SourceLocation RootLoc = Result.SourceManager->getExpansionLoc( - Root->second.getSourceRange().getBegin()); - assert(RootLoc.isValid() && "Invalid location for Root node of match."); - RewriteRule::Case Case = tooling::detail::findSelectedCase(Result, Rule); auto Transformations = tooling::detail::translateEdits(Result, Case.Edits); if (!Transformations) { @@ -195,14 +202,16 @@ void Transformer::run(const MatchResult &Result) { if (Transformations->empty()) { // No rewrite applied (but no error encountered either). - RootLoc.print(llvm::errs() << "note: skipping match at loc ", - *Result.SourceManager); + detail::getRuleMatchLoc(Result).print( + llvm::errs() << "note: skipping match at loc ", *Result.SourceManager); llvm::errs() << "\n"; return; } - // Record the results in the AtomicChange. - AtomicChange AC(*Result.SourceManager, RootLoc); + // Record the results in the AtomicChange, anchored at the location of the + // first change. + AtomicChange AC(*Result.SourceManager, + (*Transformations)[0].Range.getBegin()); for (const auto &T : *Transformations) { if (auto Err = AC.replace(*Result.SourceManager, T.Range, T.Replacement)) { Consumer(std::move(Err)); diff --git a/clang/test/AST/ast-dump-color.cpp b/clang/test/AST/ast-dump-color.cpp index f4bfdaa22942c..6651ef2652e02 100644 --- a/clang/test/AST/ast-dump-color.cpp +++ b/clang/test/AST/ast-dump-color.cpp @@ -49,13 +49,13 @@ struct Invalid { //CHECK: {{^}}[[Blue]]| | |-[[RESET]][[MAGENTA]]IntegerLiteral[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:10:11[[RESET]]> [[Green]]'int'[[RESET]][[Cyan:.\[0;36m]][[RESET]][[Cyan]][[RESET]][[CYAN]] 1[[RESET]]{{$}} //CHECK: {{^}}[[Blue]]| | `-[[RESET]][[MAGENTA]]CompoundStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:14[[RESET]], [[Yellow]]line:15:3[[RESET]]>{{$}} //CHECK: {{^}}[[Blue]]| | |-[[RESET]][[MAGENTA]]CaseStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:11:3[[RESET]], [[Yellow]]line:12:27[[RESET]]>{{$}} -//CHECK: {{^}}[[Blue]]| | | |-[[RESET]][[MAGENTA]]ConstantExpr[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:11:8[[RESET]]> [[Green]]'int'[[RESET]][[Cyan]][[RESET]][[Cyan]][[RESET]][[CYAN]] 1[[RESET]]{{$}} +//CHECK: {{^}}[[Blue]]| | | |-[[RESET]][[MAGENTA]]ConstantExpr[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:11:8[[RESET]]> [[Green]]'int'[[RESET]][[Cyan]][[RESET]][[Cyan]][[RESET]][[CYAN]] Int: 1[[RESET]]{{$}} //CHECK: {{^}}[[Blue]]| | | | `-[[RESET]][[MAGENTA]]IntegerLiteral[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:8[[RESET]]> [[Green]]'int'[[RESET]][[Cyan]][[RESET]][[Cyan]][[RESET]][[CYAN]] 1[[RESET]]{{$}} //CHECK: {{^}}[[Blue]]| | | `-[[RESET]][[MAGENTA]]AttributedStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:12:5[[RESET]], [[Yellow]]col:27[[RESET]]>{{$}} //CHECK: {{^}}[[Blue]]| | | |-[[RESET]][[BLUE]]FallThroughAttr[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:7[[RESET]], [[Yellow]]col:14[[RESET]]>{{$}} //CHECK: {{^}}[[Blue]]| | | `-[[RESET]][[MAGENTA]]NullStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:27[[RESET]]>{{$}} //CHECK: {{^}}[[Blue]]| | `-[[RESET]][[MAGENTA]]CaseStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:13:3[[RESET]], [[Yellow]]line:14:5[[RESET]]>{{$}} -//CHECK: {{^}}[[Blue]]| | |-[[RESET]][[MAGENTA]]ConstantExpr[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:13:8[[RESET]]> [[Green]]'int'[[RESET]][[Cyan]][[RESET]][[Cyan]][[RESET]][[CYAN]] 2[[RESET]]{{$}} +//CHECK: {{^}}[[Blue]]| | |-[[RESET]][[MAGENTA]]ConstantExpr[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:13:8[[RESET]]> [[Green]]'int'[[RESET]][[Cyan]][[RESET]][[Cyan]][[RESET]][[CYAN]] Int: 2[[RESET]]{{$}} //CHECK: {{^}}[[Blue]]| | | `-[[RESET]][[MAGENTA]]IntegerLiteral[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:8[[RESET]]> [[Green]]'int'[[RESET]][[Cyan]][[RESET]][[Cyan]][[RESET]][[CYAN]] 2[[RESET]]{{$}} //CHECK: {{^}}[[Blue]]| | `-[[RESET]][[MAGENTA]]NullStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:14:5[[RESET]]>{{$}} //CHECK: {{^}}[[Blue]]| `-[[RESET]][[Blue]]FullComment[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:8:4[[RESET]], [[Yellow]]col:11[[RESET]]>{{$}} diff --git a/clang/test/AST/ast-dump-decl.cpp b/clang/test/AST/ast-dump-decl.cpp index 0d8fadc01eda1..529733475a253 100644 --- a/clang/test/AST/ast-dump-decl.cpp +++ b/clang/test/AST/ast-dump-decl.cpp @@ -413,13 +413,13 @@ namespace testClassTemplateDecl { // CHECK: ClassTemplateDecl 0x{{.+}} <{{.+}}:275:3, col:68> col:68 TestTemplateTemplateDefaultType // CHECK-NEXT: |-TemplateTemplateParmDecl 0x{{.+}} col:37 depth 0 index 0 TT -// CHECK-NEXT: | |-TemplateTypeParmDecl 0x{{.+}} col:21 typename depth 1 index 0 +// CHECK-NEXT: | |-TemplateTypeParmDecl 0x{{.+}} col:29 typename depth 1 index 0 // CHECK-NEXT: | `-TemplateArgument template TestClassTemplate // CHECK-NEXT: `-CXXRecordDecl 0x{{.+}} col:68 struct TestTemplateTemplateDefaultType // CHECK: ClassTemplateDecl 0x{{.+}} prev 0x{{.+}} <{{.+}}:276:3, col:82> col:48 TestTemplateTemplateDefaultType // CHECK-NEXT: |-TemplateTemplateParmDecl 0x{{.+}} col:37 depth 0 index 0 TT -// CHECK-NEXT: | |-TemplateTypeParmDecl 0x{{.+}} col:21 typename depth 1 index 0 +// CHECK-NEXT: | |-TemplateTypeParmDecl 0x{{.+}} col:29 typename depth 1 index 0 // CHECK-NEXT: | `-TemplateArgument template TestClassTemplate // CHECK-NEXT: | `-inherited from TemplateTemplateParm 0x{{.+}} 'TT' // CHECK-NEXT: `-CXXRecordDecl 0x{{.+}} prev 0x{{.+}} col:48 struct TestTemplateTemplateDefaultType definition diff --git a/clang/test/AST/ast-dump-record-definition-data-json.cpp b/clang/test/AST/ast-dump-record-definition-data-json.cpp index 4ad938683e5e7..9de52868bdc16 100644 --- a/clang/test/AST/ast-dump-record-definition-data-json.cpp +++ b/clang/test/AST/ast-dump-record-definition-data-json.cpp @@ -395,14 +395,8 @@ struct DoesNotAllowConstDefaultInit { // CHECK-NEXT: "tokLen": 4 // CHECK-NEXT: }, // CHECK-NEXT: "range": { -// CHECK-NEXT: "begin": { -// CHECK-NEXT: "col": 29, -// CHECK-NEXT: "tokLen": 4 -// CHECK-NEXT: }, -// CHECK-NEXT: "end": { -// CHECK-NEXT: "col": 29, -// CHECK-NEXT: "tokLen": 4 -// CHECK-NEXT: } +// CHECK-NEXT: "begin": {}, +// CHECK-NEXT: "end": {} // CHECK-NEXT: }, // CHECK-NEXT: "isImplicit": true, // CHECK-NEXT: "tagUsed": "class", @@ -497,14 +491,8 @@ struct DoesNotAllowConstDefaultInit { // CHECK-NEXT: "tokLen": 4 // CHECK-NEXT: }, // CHECK-NEXT: "range": { -// CHECK-NEXT: "begin": { -// CHECK-NEXT: "col": 29, -// CHECK-NEXT: "tokLen": 4 -// CHECK-NEXT: }, -// CHECK-NEXT: "end": { -// CHECK-NEXT: "col": 29, -// CHECK-NEXT: "tokLen": 4 -// CHECK-NEXT: } +// CHECK-NEXT: "begin": {}, +// CHECK-NEXT: "end": {} // CHECK-NEXT: }, // CHECK-NEXT: "isImplicit": true, // CHECK-NEXT: "tagUsed": "class", @@ -563,14 +551,8 @@ struct DoesNotAllowConstDefaultInit { // CHECK-NEXT: "tokLen": 4 // CHECK-NEXT: }, // CHECK-NEXT: "range": { -// CHECK-NEXT: "begin": { -// CHECK-NEXT: "col": 29, -// CHECK-NEXT: "tokLen": 4 -// CHECK-NEXT: }, -// CHECK-NEXT: "end": { -// CHECK-NEXT: "col": 29, -// CHECK-NEXT: "tokLen": 4 -// CHECK-NEXT: } +// CHECK-NEXT: "begin": {}, +// CHECK-NEXT: "end": {} // CHECK-NEXT: }, // CHECK-NEXT: "isImplicit": true, // CHECK-NEXT: "tagUsed": "class", diff --git a/clang/test/AST/ast-dump-template-decls-json.cpp b/clang/test/AST/ast-dump-template-decls-json.cpp index 21574af2c4a22..fc9aeca5b2728 100644 --- a/clang/test/AST/ast-dump-template-decls-json.cpp +++ b/clang/test/AST/ast-dump-template-decls-json.cpp @@ -656,8 +656,8 @@ void V::f() {} // CHECK-NEXT: "id": "0x{{.*}}", // CHECK-NEXT: "kind": "TemplateTypeParmDecl", // CHECK-NEXT: "loc": { -// CHECK-NEXT: "col": 33, -// CHECK-NEXT: "tokLen": 8 +// CHECK-NEXT: "col": 41, +// CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: }, // CHECK-NEXT: "range": { // CHECK-NEXT: "begin": { @@ -1099,8 +1099,8 @@ void V::f() {} // CHECK-NEXT: "kind": "TemplateTypeParmDecl", // CHECK-NEXT: "loc": { // CHECK-NEXT: "line": 27, -// CHECK-NEXT: "col": 11, -// CHECK-NEXT: "tokLen": 8 +// CHECK-NEXT: "col": 20, +// CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: }, // CHECK-NEXT: "range": { // CHECK-NEXT: "begin": { diff --git a/clang/test/AST/ast-dump-template-decls.cpp b/clang/test/AST/ast-dump-template-decls.cpp index a1f355b4da0d8..2fa50d8f549ee 100644 --- a/clang/test/AST/ast-dump-template-decls.cpp +++ b/clang/test/AST/ast-dump-template-decls.cpp @@ -26,7 +26,7 @@ template typename Uy> // CHECK: FunctionTemplateDecl 0x{{[^ ]*}} col:6 d // CHECK-NEXT: TemplateTypeParmDecl 0x{{[^ ]*}} col:20 referenced typename depth 0 index 0 Ty // CHECK-NEXT: TemplateTemplateParmDecl 0x{{[^ ]*}} col:52 depth 0 index 1 Uy -// CHECK-NEXT: TemplateTypeParmDecl 0x{{[^ ]*}} col:33 typename depth 1 index 0 +// CHECK-NEXT: TemplateTypeParmDecl 0x{{[^ ]*}} col:41 typename depth 1 index 0 void d(Ty, Uy); template @@ -47,7 +47,7 @@ void g(Ty); template // CHECK: FunctionTemplateDecl 0x{{[^ ]*}} col:6 h -// CHECK-NEXT: TemplateTypeParmDecl 0x{{[^ ]*}} col:11 typename depth 0 index 0 +// CHECK-NEXT: TemplateTypeParmDecl 0x{{[^ ]*}} col:20 typename depth 0 index 0 // CHECK-NEXT: TemplateArgument type 'void' void h(); diff --git a/clang/test/AST/atomic-expr.cpp b/clang/test/AST/atomic-expr.cpp new file mode 100644 index 0000000000000..e7fb6ba833e5e --- /dev/null +++ b/clang/test/AST/atomic-expr.cpp @@ -0,0 +1,60 @@ +// RUN: %clang_cc1 -ast-dump %s | FileCheck %s + +template +void pr43370() { + int arr[2]; + __atomic_store_n(arr, 0, 5); +} + +template +void foo() { + int arr[2]; + (void)__atomic_compare_exchange_n(arr, arr, 1, 0, 3, 4); +} + +void useage(){ + pr43370(); + foo(); +} + +// CHECK:FunctionTemplateDecl 0x{{[0-9a-f]+}} <{{[^,]+}}, line:7:1> line:4:6 pr43370 +// CHECK: AtomicExpr +// CHECK-NEXT: ImplicitCastExpr +// CHECK-SAME: +// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} <{{[^:]+}}:20> 'int [2]' lvalue Var 0x{{[0-9a-f]+}} 'arr' 'int [2]' +// CHECK-NEXT: IntegerLiteral 0x{{[0-9a-f]+}} <{{[^:]+}}:28> 'int' 5 +// CHECK-NEXT: IntegerLiteral 0x{{[0-9a-f]+}} <{{[^:]+}}:25> 'int' 0 +// CHECK:FunctionDecl 0x{{[0-9a-f]+}} line:4:6 used pr43370 +// CHECK: AtomicExpr +// CHECK-NEXT: ImplicitCastExpr +// CHECK-SAME: +// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} <{{[^:]+}}:20> 'int [2]' lvalue Var 0x{{[0-9a-f]+}} 'arr' 'int [2]' +// CHECK-NEXT: IntegerLiteral 0x{{[0-9a-f]+}} <{{[^:]+}}:28> 'int' 5 +// CHECK-NEXT: IntegerLiteral 0x{{[0-9a-f]+}} <{{[^:]+}}:25> 'int' 0 + +// CHECK:FunctionTemplateDecl 0x{{[0-9a-f]+}} line:10:6 foo +// CHECK: AtomicExpr +// CHECK-NEXT: ImplicitCastExpr +// CHECK-SAME: +// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} <{{[^:]+}}:37> 'int [2]' lvalue Var 0x{{[0-9a-f]+}} 'arr' 'int [2]' +// CHECK-NEXT: IntegerLiteral 0x{{[0-9a-f]+}} <{{[^:]+}}:53> 'int' 3 +// CHECK-NEXT: ImplicitCastExpr +// CHECK-SAME: +// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} <{{[^:]+}}:42> 'int [2]' lvalue Var 0x{{[0-9a-f]+}} 'arr' 'int [2]' +// CHECK-NEXT: IntegerLiteral 0x{{[0-9a-f]+}} <{{[^:]+}}:56> 'int' 4 +// CHECK-NEXT: IntegerLiteral 0x{{[0-9a-f]+}} <{{[^:]+}}:47> 'int' 1 +// CHECK-NEXT: ImplicitCastExpr +// CHECK-NEXT: IntegerLiteral 0x{{[0-9a-f]+}} <{{[^:]+}}:50> 'int' 0 +// CHECK:FunctionDecl 0x{{[0-9a-f]+}} line:10:6 used foo +// CHECK: AtomicExpr +// CHECK-NEXT: ImplicitCastExpr +// CHECK-SAME: +// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} <{{[^:]+}}:37> 'int [2]' lvalue Var 0x{{[0-9a-f]+}} 'arr' 'int [2]' +// CHECK-NEXT: IntegerLiteral 0x{{[0-9a-f]+}} <{{[^:]+}}:53> 'int' 3 +// CHECK-NEXT: ImplicitCastExpr +// CHECK-SAME: +// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} <{{[^:]+}}:42> 'int [2]' lvalue Var 0x{{[0-9a-f]+}} 'arr' 'int [2]' +// CHECK-NEXT: IntegerLiteral 0x{{[0-9a-f]+}} <{{[^:]+}}:56> 'int' 4 +// CHECK-NEXT: IntegerLiteral 0x{{[0-9a-f]+}} <{{[^:]+}}:47> 'int' 1 +// CHECK-NEXT: ImplicitCastExpr +// CHECK-NEXT: IntegerLiteral 0x{{[0-9a-f]+}} <{{[^:]+}}:50> 'int' 0 diff --git a/clang/test/ASTMerge/class-template/test.cpp b/clang/test/ASTMerge/class-template/test.cpp index 27761f6983a5e..671064786d76f 100644 --- a/clang/test/ASTMerge/class-template/test.cpp +++ b/clang/test/ASTMerge/class-template/test.cpp @@ -9,13 +9,13 @@ static_assert(sizeof(X0().getValue(1)) == sizeof(int)); // CHECK: class-template2.cpp:9:15: note: declared here with type 'long' // CHECK: class-template1.cpp:12:14: warning: template parameter has different kinds in different translation units -// CHECK: class-template2.cpp:12:10: note: template parameter declared here +// CHECK: class-template2.cpp:12:18: note: template parameter declared here // CHECK: class-template1.cpp:18:23: warning: non-type template parameter declared with incompatible types in different translation units ('long' vs. 'int') // CHECK: class-template2.cpp:18:23: note: declared here with type 'int' -// CHECK: class-template1.cpp:21:10: warning: template parameter has different kinds in different translation units -// CHECK: class-template2.cpp:21:10: note: template parameter declared here +// CHECK: class-template1.cpp:21:18: warning: template parameter has different kinds in different translation units +// CHECK: class-template2.cpp:21:31: note: template parameter declared here // CHECK: class-template2.cpp:27:20: warning: external variable 'x0r' declared with incompatible types in different translation units ('X0 *' vs. 'X0 *') // CHECK: class-template1.cpp:26:19: note: declared here with type 'X0 *' diff --git a/clang/test/Analysis/array-struct-region.cpp b/clang/test/Analysis/array-struct-region.cpp index cfb57d39242db..1b9fa3e8db55c 100644 --- a/clang/test/Analysis/array-struct-region.cpp +++ b/clang/test/Analysis/array-struct-region.cpp @@ -1,20 +1,26 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core\ // RUN: -analyzer-checker=debug.ExprInspection -verify\ +// RUN: -Wno-tautological-compare\ // RUN: -x c %s // RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core\ // RUN: -analyzer-checker=debug.ExprInspection -verify\ +// RUN: -Wno-tautological-compare\ // RUN: -x c++ -std=c++14 %s // RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core\ // RUN: -analyzer-checker=debug.ExprInspection -verify\ +// RUN: -Wno-tautological-compare\ // RUN: -x c++ -std=c++17 %s // RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core\ // RUN: -analyzer-checker=debug.ExprInspection -verify\ +// RUN: -Wno-tautological-compare\ // RUN: -DINLINE -x c %s // RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core\ // RUN: -analyzer-checker=debug.ExprInspection -verify\ +// RUN: -Wno-tautological-compare\ // RUN: -DINLINE -x c++ -std=c++14 %s // RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core\ // RUN: -analyzer-checker=debug.ExprInspection -verify\ +// RUN: -Wno-tautological-compare\ // RUN: -DINLINE -x c++ -std=c++17 %s void clang_analyzer_eval(int); diff --git a/clang/test/Analysis/cfg.cpp b/clang/test/Analysis/cfg.cpp index 9b0203e99efe9..a7d707ee20d1a 100644 --- a/clang/test/Analysis/cfg.cpp +++ b/clang/test/Analysis/cfg.cpp @@ -547,6 +547,27 @@ int foo() { } } // namespace statement_expression_in_return +// CHECK-LABEL: int overlap_compare(int x) +// CHECK: [B2] +// CHECK-NEXT: 1: 1 +// CHECK-NEXT: 2: return [B2.1]; +// CHECK-NEXT: Preds (1): B3(Unreachable) +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B3] +// CHECK-NEXT: 1: x +// CHECK-NEXT: 2: [B3.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 5 +// CHECK-NEXT: 4: [B3.2] > [B3.3] +// CHECK-NEXT: T: if [B4.5] && [B3.4] +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (2): B2(Unreachable) B1 +int overlap_compare(int x) { + if (x == -1 && x > 5) + return 1; + + return 2; +} + // CHECK-LABEL: template<> int *PR18472() // CHECK: [B2 (ENTRY)] // CHECK-NEXT: Succs (1): B1 diff --git a/clang/test/Analysis/debug-CallGraph.cpp b/clang/test/Analysis/debug-CallGraph.cpp index 1d6844fad94fc..0f5a83b268a01 100644 --- a/clang/test/Analysis/debug-CallGraph.cpp +++ b/clang/test/Analysis/debug-CallGraph.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCallGraph %s -fblocks 2>&1 | FileCheck %s +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCallGraph %s -fblocks -std=c++14 2>&1 | FileCheck %s int get5() { return 5; @@ -68,8 +68,25 @@ void templUser() { } } +namespace Lambdas { + void Callee(){} + + void f1() { + [](int i) { + Callee(); + }(1); + [](auto i) { + Callee(); + }(1); + } +} + // CHECK:--- Call graph Dump --- -// CHECK-NEXT: {{Function: < root > calls: get5 add test_add mmm foo aaa < > bbb ddd ccc eee fff do_nothing test_single_call SomeNS::templ SomeNS::templ SomeNS::templUser $}} +// CHECK-NEXT: {{Function: < root > calls: get5 add test_add mmm foo aaa < > bbb ddd ccc eee fff do_nothing test_single_call SomeNS::templ SomeNS::templ SomeNS::templUser Lambdas::Callee Lambdas::f1 Lambdas::f1\(\)::\(anonymous class\)::operator\(\) Lambdas::f1\(\)::\(anonymous class\)::operator\(\) $}} +// CHECK-NEXT: {{Function: Lambdas::f1 calls: Lambdas::f1\(\)::\(anonymous class\)::operator\(\) Lambdas::f1\(\)::\(anonymous class\)::operator\(\) $}} +// CHECK-NEXT: {{Function: Lambdas::f1\(\)::\(anonymous class\)::operator\(\) calls: Lambdas::Callee $}} +// CHECK-NEXT: {{Function: Lambdas::f1\(\)::\(anonymous class\)::operator\(\) calls: Lambdas::Callee $}} +// CHECK-NEXT: {{Function: Lambdas::Callee calls: $}} // CHECK-NEXT: {{Function: SomeNS::templUser calls: SomeNS::templ SomeNS::templ $}} // CHECK-NEXT: {{Function: SomeNS::templ calls: eee $}} // CHECK-NEXT: {{Function: SomeNS::templ calls: ccc $}} diff --git a/clang/test/Analysis/loop-widening.cpp b/clang/test/Analysis/loop-widening.cpp new file mode 100644 index 0000000000000..fbcb72dee160a --- /dev/null +++ b/clang/test/Analysis/loop-widening.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_analyze_cc1 -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-config widen-loops=true \ +// RUN: -analyzer-config track-conditions=false \ +// RUN: -analyzer-max-loop 2 -analyzer-output=text + +namespace pr43102 { +class A { +public: + void m_fn1(); +}; +bool g; +void fn1() { + A a; + A *b = &a; + + for (;;) { // expected-note{{Loop condition is true. Entering loop body}} + // expected-note@-1{{Loop condition is true. Entering loop body}} + // expected-note@-2{{Value assigned to 'b'}} + // no crash during bug report construction + + g = !b; // expected-note{{Assuming 'b' is null}} + b->m_fn1(); // expected-warning{{Called C++ object pointer is null}} + // expected-note@-1{{Called C++ object pointer is null}} + } +} +} // end of namespace pr43102 diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/dtor.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/dtor.cpp new file mode 100644 index 0000000000000..3d45949430f99 --- /dev/null +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/dtor.cpp @@ -0,0 +1,68 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +// p3: if the function is a constructor or destructor, its class shall not have +// any virtual base classes; +namespace vbase { + struct A {}; + struct B : virtual A { // expected-note {{virtual}} + constexpr ~B() {} // expected-error {{constexpr member function not allowed in struct with virtual base class}} + }; +} + +// p3: its function-body shall not enclose +// -- a goto statement +// -- an identifier label +// -- a variable of non-literal type or of static or thread storage duration +namespace contents { + struct A { + constexpr ~A() { + goto x; // expected-error {{statement not allowed in constexpr function}} + x: ; + } + }; + struct B { + constexpr ~B() { + x: ; // expected-error {{statement not allowed in constexpr function}} + } + }; + struct Nonlit { Nonlit(); }; // expected-note {{not literal}} + struct C { + constexpr ~C() { + Nonlit nl; // expected-error {{non-literal}} + } + }; + struct D { + constexpr ~D() { + static int a; // expected-error {{static variable}} + } + }; + struct E { + constexpr ~E() { + thread_local int e; // expected-error {{thread_local variable}} + } + }; + struct F { + constexpr ~F() { + extern int f; + } + }; +} + +// p5: for every subobject of class type or (possibly multi-dimensional) array +// thereof, that class type shall have a constexpr destructor +namespace subobject { + struct A { + ~A(); + }; + struct B : A { // expected-note {{here}} + constexpr ~B() {} // expected-error {{destructor cannot be declared constexpr because base class 'subobject::A' does not have a constexpr destructor}} + }; + struct C { + A a; // expected-note {{here}} + constexpr ~C() {} // expected-error {{destructor cannot be declared constexpr because data member 'a' does not have a constexpr destructor}} + }; + struct D : A { + A a; + constexpr ~D() = delete; + }; +} diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp index f472e9213fbed..59e3dcf6ed657 100644 --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp @@ -1,6 +1,7 @@ // RUN: %clang_cc1 -triple %itanium_abi_triple -fsyntax-only -verify -std=c++11 %s // RUN: %clang_cc1 -triple %itanium_abi_triple -fsyntax-only -verify -std=c++14 %s -// RUN: %clang_cc1 -triple %itanium_abi_triple -fsyntax-only -verify -std=c++1z %s +// RUN: %clang_cc1 -triple %itanium_abi_triple -fsyntax-only -verify -std=c++17 %s +// RUN: %clang_cc1 -triple %itanium_abi_triple -fsyntax-only -verify -std=c++2a %s // MSVC always adopted the C++17 rule that implies that constexpr variables are // implicitly inline, so do the test again. @@ -75,7 +76,10 @@ template T f6(T); // expected-note {{here}} template constexpr T f6(T); // expected-error {{constexpr declaration of 'f6' follows non-constexpr declaration}} // destructor struct ConstexprDtor { - constexpr ~ConstexprDtor() = default; // expected-error {{destructor cannot be marked constexpr}} + constexpr ~ConstexprDtor() = default; +#if __cplusplus <= 201703L + // expected-error@-2 {{destructor cannot be declared constexpr}} +#endif }; // template stuff diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp index f72984a6a1207..59c2ee7d936e3 100644 --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp @@ -57,7 +57,10 @@ struct T : SS, NonLiteral { #ifndef CXX1Y // expected-error@-2 {{constexpr function's return type 'void' is not a literal type}} #endif - constexpr ~T(); // expected-error {{destructor cannot be marked constexpr}} + constexpr ~T(); +#ifndef CXX2A + // expected-error@-2 {{destructor cannot be declared constexpr}} +#endif typedef NonLiteral F() const; constexpr F NonLiteralReturn2; // ok until definition @@ -183,7 +186,10 @@ constexpr int DisallowedStmtsCXX1Y_6() { } constexpr int DisallowedStmtsCXX1Y_7() { // - a definition of a variable for which no initialization is performed - int n; // expected-error {{variables defined in a constexpr function must be initialized}} + int n; +#ifndef CXX2A + // expected-error@-2 {{uninitialized variable in a constexpr function}} +#endif return 0; } @@ -315,7 +321,10 @@ namespace std_example { return value; } constexpr int uninit() { - int a; // expected-error {{must be initialized}} + int a; +#ifndef CXX2A + // expected-error@-2 {{uninitialized}} +#endif return a; } constexpr int prev(int x) { diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp index 17753604b8a79..39088042251f0 100644 --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp @@ -136,29 +136,33 @@ struct V { // - every non-static data member and base class sub-object shall be initialized struct W { - int n; // expected-note {{member not initialized by constructor}} - constexpr W() {} // expected-error {{constexpr constructor must initialize all members}} + int n; + constexpr W() {} +#ifndef CXX2A + // expected-error@-2 {{constexpr constructor that does not initialize all members}} + // expected-note@-4 {{member not initialized by constructor}} +#endif }; struct AnonMembers { - int a; // expected-note {{member not initialized by constructor}} - union { // expected-note 2{{member not initialized by constructor}} + int a; // expected-note 0-1{{member not initialized by constructor}} + union { // expected-note 0-2{{member not initialized by constructor}} char b; struct { double c; - long d; // expected-note {{member not initialized by constructor}} + long d; // expected-note 0-1{{member not initialized by constructor}} }; union { char e; void *f; }; }; - struct { // expected-note {{member not initialized by constructor}} + struct { // expected-note 0-1{{member not initialized by constructor}} long long g; struct { - int h; // expected-note {{member not initialized by constructor}} - double i; // expected-note {{member not initialized by constructor}} + int h; // expected-note 0-1{{member not initialized by constructor}} + double i; // expected-note 0-1{{member not initialized by constructor}} }; - union { // expected-note 2{{member not initialized by constructor}} + union { // expected-note 0-2{{member not initialized by constructor}} char *j; AnonMembers *k; }; @@ -166,14 +170,26 @@ struct AnonMembers { constexpr AnonMembers(int(&)[1]) : a(), b(), g(), h(), i(), j() {} // ok // missing d, i, j/k union - constexpr AnonMembers(int(&)[2]) : a(), c(), g(), h() {} // expected-error {{constexpr constructor must initialize all members}} + constexpr AnonMembers(int(&)[2]) : a(), c(), g(), h() {} +#ifndef CXX2A + // expected-error@-2 {{constexpr constructor that does not initialize all members}} +#endif constexpr AnonMembers(int(&)[3]) : a(), e(), g(), h(), i(), k() {} // ok // missing h, j/k union - constexpr AnonMembers(int(&)[4]) : a(), c(), d(), g(), i() {} // expected-error {{constexpr constructor must initialize all members}} + constexpr AnonMembers(int(&)[4]) : a(), c(), d(), g(), i() {} +#ifndef CXX2A + // expected-error@-2 {{constexpr constructor that does not initialize all members}} +#endif // missing b/c/d/e/f union - constexpr AnonMembers(int(&)[5]) : a(), g(), h(), i(), k() {} // expected-error {{constexpr constructor must initialize all members}} + constexpr AnonMembers(int(&)[5]) : a(), g(), h(), i(), k() {} +#ifndef CXX2A + // expected-error@-2 {{constexpr constructor that does not initialize all members}} +#endif // missing a, b/c/d/e/f union, g/h/i/j/k struct - constexpr AnonMembers(int(&)[6]) {} // expected-error {{constexpr constructor must initialize all members}} + constexpr AnonMembers(int(&)[6]) {} +#ifndef CXX2A + // expected-error@-2 {{constexpr constructor that does not initialize all members}} +#endif }; union Empty { @@ -253,14 +269,20 @@ struct X { constexpr X(int c) : a(c) {} // ok, b initialized by 2 * c + 1 }; -union XU1 { int a; constexpr XU1() = default; }; // expected-error{{not constexpr}} +union XU1 { int a; constexpr XU1() = default; }; +#ifndef CXX2A +// expected-error@-2{{not constexpr}} +#endif union XU2 { int a = 1; constexpr XU2() = default; }; struct XU3 { union { int a; }; - constexpr XU3() = default; // expected-error{{not constexpr}} + constexpr XU3() = default; +#ifndef CXX2A + // expected-error@-2{{not constexpr}} +#endif }; struct XU4 { union { diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp index 0aaedcc07696b..8d51dbde71776 100644 --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2a %s // A constexpr specifier used in an object declaration declares the object as // const. @@ -35,3 +36,19 @@ struct pixel { }; constexpr pixel ur = { 1294, 1024 }; // ok constexpr pixel origin; // expected-error {{default initialization of an object of const type 'const pixel' without a user-provided default constructor}} + +#if __cplusplus > 201702L +// A constexpr variable shall have constant destruction. +struct A { + bool ok; + constexpr A(bool ok) : ok(ok) {} + constexpr ~A() noexcept(false) { + void oops(); // expected-note 2{{declared here}} + if (!ok) oops(); // expected-note 2{{non-constexpr function}} + } +}; + +constexpr A const_dtor(true); +constexpr A non_const_dtor(false); // expected-error {{must have constant destruction}} expected-note {{in call}} +constexpr A arr_dtor[5] = {true, true, true, false, true}; // expected-error {{must have constant destruction}} expected-note {{in call to '&arr_dtor[3]->~A()'}} +#endif diff --git a/clang/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp b/clang/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp index 2b7886d383db8..7274ee9c542fc 100644 --- a/clang/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp +++ b/clang/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp @@ -8,7 +8,7 @@ struct S1 { constexpr S1(S1&&) = default; constexpr S1 &operator=(const S1&) const = default; // expected-error {{explicitly-defaulted copy assignment operator may not have}} constexpr S1 &operator=(S1&&) const = default; // expected-error {{explicitly-defaulted move assignment operator may not have}} - constexpr ~S1() = default; // expected-error {{destructor cannot be marked constexpr}} + constexpr ~S1() = default; // expected-error {{destructor cannot be declared constexpr}} int n; }; struct NoCopyMove { diff --git a/clang/test/CXX/drs/dr14xx.cpp b/clang/test/CXX/drs/dr14xx.cpp index eb086178fc06f..52129844c4188 100644 --- a/clang/test/CXX/drs/dr14xx.cpp +++ b/clang/test/CXX/drs/dr14xx.cpp @@ -1,7 +1,8 @@ // RUN: %clang_cc1 -std=c++98 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++1z %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++2a %s -verify -fexceptions -fcxx-exceptions -pedantic-errors #if __cplusplus < 201103L // expected-no-diagnostics @@ -38,18 +39,24 @@ namespace dr1460 { // dr1460: 3.5 } union A {}; - union B { int n; }; // expected-note +{{here}} + union B { int n; }; // expected-note 0+{{here}} union C { int n = 0; }; struct D { union {}; }; // expected-error {{does not declare anything}} - struct E { union { int n; }; }; // expected-note +{{here}} + struct E { union { int n; }; }; // expected-note 0+{{here}} struct F { union { int n = 0; }; }; struct X { friend constexpr A::A() noexcept; - friend constexpr B::B() noexcept; // expected-error {{follows non-constexpr declaration}} + friend constexpr B::B() noexcept; +#if __cplusplus <= 201703L + // expected-error@-2 {{follows non-constexpr declaration}} +#endif friend constexpr C::C() noexcept; friend constexpr D::D() noexcept; - friend constexpr E::E() noexcept; // expected-error {{follows non-constexpr declaration}} + friend constexpr E::E() noexcept; +#if __cplusplus <= 201703L + // expected-error@-2 {{follows non-constexpr declaration}} +#endif friend constexpr F::F() noexcept; }; @@ -64,37 +71,61 @@ namespace dr1460 { // dr1460: 3.5 namespace Defaulted { union A { constexpr A() = default; }; - union B { int n; constexpr B() = default; }; // expected-error {{not constexpr}} + union B { int n; constexpr B() = default; }; +#if __cplusplus <= 201703L + // expected-error@-2 {{not constexpr}} +#endif union C { int n = 0; constexpr C() = default; }; struct D { union {}; constexpr D() = default; }; // expected-error {{does not declare anything}} - struct E { union { int n; }; constexpr E() = default; }; // expected-error {{not constexpr}} + struct E { union { int n; }; constexpr E() = default; }; +#if __cplusplus <= 201703L + // expected-error@-2 {{not constexpr}} +#endif struct F { union { int n = 0; }; constexpr F() = default; }; - struct G { union { int n = 0; }; union { int m; }; constexpr G() = default; }; // expected-error {{not constexpr}} + struct G { union { int n = 0; }; union { int m; }; constexpr G() = default; }; +#if __cplusplus <= 201703L + // expected-error@-2 {{not constexpr}} +#endif struct H { union { int n = 0; }; - union { // expected-note 2{{member not initialized}} + union { // expected-note 0-2{{member not initialized}} int m; }; - constexpr H() {} // expected-error {{must initialize all members}} + constexpr H() {} +#if __cplusplus <= 201703L + // expected-error@-2 {{initialize all members}} +#endif constexpr H(bool) : m(1) {} - constexpr H(char) : n(1) {} // expected-error {{must initialize all members}} + constexpr H(char) : n(1) {} +#if __cplusplus <= 201703L + // expected-error@-2 {{initialize all members}} +#endif constexpr H(double) : m(1), n(1) {} }; } #if __cplusplus > 201103L template constexpr bool check() { - T t; // expected-note-re 2{{non-constexpr constructor '{{[BE]}}'}} + T t; +#if __cplusplus <= 201703L + // expected-note-re@-2 2{{non-constexpr constructor '{{[BE]}}'}} +#endif return true; } static_assert(check(), ""); - static_assert(check(), ""); // expected-error {{constant}} expected-note {{in call}} + static_assert(check(), ""); +#if __cplusplus <= 201703L + // expected-error@-2 {{constant}} expected-note@-2 {{in call}} +#endif static_assert(check(), ""); static_assert(check(), ""); - static_assert(check(), ""); // expected-error {{constant}} expected-note {{in call}} + static_assert(check(), ""); +#if __cplusplus <= 201703L + // expected-error@-2 {{constant}} expected-note@-2 {{in call}} +#endif static_assert(check(), ""); #endif diff --git a/clang/test/CXX/drs/dr2xx.cpp b/clang/test/CXX/drs/dr2xx.cpp index c7ffa77d30dec..89e13367a474c 100644 --- a/clang/test/CXX/drs/dr2xx.cpp +++ b/clang/test/CXX/drs/dr2xx.cpp @@ -751,9 +751,12 @@ namespace dr263 { // dr263: yes #if __cplusplus < 201103L friend X::X() throw(); friend X::~X() throw(); -#else +#elif __cplusplus <= 201703L friend constexpr X::X() noexcept; friend X::~X(); +#else + friend constexpr X::X() noexcept; + friend constexpr X::~X(); #endif Y::Y(); // expected-error {{extra qualification}} Y::~Y(); // expected-error {{extra qualification}} diff --git a/clang/test/CXX/expr/expr.const/p2-0x.cpp b/clang/test/CXX/expr/expr.const/p2-0x.cpp index 091ef097255a0..e84753759c90b 100644 --- a/clang/test/CXX/expr/expr.const/p2-0x.cpp +++ b/clang/test/CXX/expr/expr.const/p2-0x.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -fsyntax-only -std=c++11 -pedantic -verify -fcxx-exceptions %s -fconstexpr-depth 128 -triple i686-pc-linux-gnu +// RUN: %clang_cc1 -fsyntax-only -std=c++11 -pedantic -verify=expected,cxx11 -fcxx-exceptions %s -fconstexpr-depth 128 -triple i686-pc-linux-gnu +// RUN: %clang_cc1 -fsyntax-only -std=c++2a -pedantic -verify=expected,cxx20 -fcxx-exceptions %s -fconstexpr-depth 128 -triple i686-pc-linux-gnu // A conditional-expression is a core constant expression unless it involves one // of the following as a potentially evaluated subexpression [...]: @@ -157,13 +158,13 @@ namespace UndefinedBehavior { constexpr int shl_unsigned_negative = unsigned(-3) << 1; // ok constexpr int shl_unsigned_into_sign = 1u << 31; // ok constexpr int shl_unsigned_overflow = 1024u << 31; // ok - constexpr int shl_signed_negative = (-3) << 1; // expected-error {{constant expression}} expected-note {{left shift of negative value -3}} + constexpr int shl_signed_negative = (-3) << 1; // cxx11-error {{constant expression}} cxx11-note {{left shift of negative value -3}} constexpr int shl_signed_ok = 1 << 30; // ok constexpr int shl_signed_into_sign = 1 << 31; // ok (DR1457) constexpr int shl_signed_into_sign_2 = 0x7fffffff << 1; // ok (DR1457) - constexpr int shl_signed_off_end = 2 << 31; // expected-error {{constant expression}} expected-note {{signed left shift discards bits}} expected-warning {{signed shift result (0x100000000) requires 34 bits to represent, but 'int' only has 32 bits}} - constexpr int shl_signed_off_end_2 = 0x7fffffff << 2; // expected-error {{constant expression}} expected-note {{signed left shift discards bits}} expected-warning {{signed shift result (0x1FFFFFFFC) requires 34 bits to represent, but 'int' only has 32 bits}} - constexpr int shl_signed_overflow = 1024 << 31; // expected-error {{constant expression}} expected-note {{signed left shift discards bits}} expected-warning {{requires 43 bits to represent}} + constexpr int shl_signed_off_end = 2 << 31; // cxx11-error {{constant expression}} cxx11-note {{signed left shift discards bits}} expected-warning {{signed shift result (0x100000000) requires 34 bits to represent, but 'int' only has 32 bits}} + constexpr int shl_signed_off_end_2 = 0x7fffffff << 2; // cxx11-error {{constant expression}} cxx11-note {{signed left shift discards bits}} expected-warning {{signed shift result (0x1FFFFFFFC) requires 34 bits to represent, but 'int' only has 32 bits}} + constexpr int shl_signed_overflow = 1024 << 31; // cxx11-error {{constant expression}} cxx11-note {{signed left shift discards bits}} expected-warning {{requires 43 bits to represent}} constexpr int shl_signed_ok2 = 1024 << 20; // ok constexpr int shr_m1 = 0 >> -1; // expected-error {{constant expression}} expected-note {{negative shift count -1}} @@ -291,7 +292,7 @@ namespace UndefinedBehavior { // - a lambda-expression (5.1.2); struct Lambda { - int n : []{ return 1; }(); // expected-error {{constant expression}} expected-error {{integral constant expression}} expected-note {{non-literal type}} + int n : []{ return 1; }(); // cxx11-error {{constant expression}} cxx11-error {{integral constant expression}} cxx11-note {{non-literal type}} }; // - an lvalue-to-rvalue conversion (4.1) unless it is applied to @@ -360,7 +361,7 @@ namespace LValueToRValueUnion { extern const U pu; constexpr const int *pua = &pu.a; constexpr const int *pub = &pu.b; - constexpr U pu = { .b = 1 }; // expected-warning {{C++20 extension}} + constexpr U pu = { .b = 1 }; // cxx11-warning {{C++20 extension}} constexpr const int a2 = *pua; // expected-error {{constant expression}} expected-note {{read of member 'a' of union with active member 'b'}} constexpr const int b2 = *pub; // ok } @@ -402,7 +403,7 @@ namespace DynamicCast { struct S { int n; }; constexpr S s { 16 }; struct T { - int n : dynamic_cast(&s)->n; // expected-warning {{constant expression}} expected-note {{dynamic_cast}} + int n : dynamic_cast(&s)->n; // cxx11-warning {{constant expression}} cxx11-note {{dynamic_cast}} }; } @@ -423,7 +424,7 @@ namespace PseudoDtor { int k; typedef int I; struct T { - int n : (k.~I(), 0); // expected-error {{constant expression}} + int n : (k.~I(), 1); // cxx11-warning {{constant expression}} cxx11-note {{pseudo-destructor}} }; } @@ -431,8 +432,8 @@ namespace PseudoDtor { namespace IncDec { int k = 2; struct T { - int n : ++k; // expected-error {{constant expression}} - int m : --k; // expected-error {{constant expression}} + int n : ++k; // expected-error {{constant expression}} cxx20-note {{visible outside}} + int m : --k; // expected-error {{constant expression}} cxx20-note {{visible outside}} }; } @@ -446,7 +447,7 @@ namespace std { namespace TypeId { struct S { virtual void f(); }; constexpr S *p = 0; - constexpr const std::type_info &ti1 = typeid(*p); // expected-error {{must be initialized by a constant expression}} expected-note {{typeid applied to expression of polymorphic type 'TypeId::S'}} + constexpr const std::type_info &ti1 = typeid(*p); // expected-error {{must be initialized by a constant expression}} cxx11-note {{typeid applied to expression of polymorphic type 'TypeId::S'}} cxx20-note {{dereferenced null pointer}} struct T {} t; constexpr const std::type_info &ti2 = typeid(t); @@ -455,10 +456,10 @@ namespace TypeId { // - a new-expression (5.3.4); // - a delete-expression (5.3.5); namespace NewDelete { - int *p = 0; + constexpr int *p = 0; struct T { - int n : *new int(4); // expected-error {{constant expression}} - int m : (delete p, 2); // expected-error {{constant expression}} + int n : *new int(4); // expected-warning {{constant expression}} cxx11-note {{until C++20}} cxx20-note {{was not deallocated}} + int m : (delete p, 2); // cxx11-warning {{constant expression}} cxx11-note {{until C++20}} }; } @@ -550,8 +551,8 @@ namespace UnspecifiedRelations { namespace Assignment { int k; struct T { - int n : (k = 9); // expected-error {{constant expression}} - int m : (k *= 2); // expected-error {{constant expression}} + int n : (k = 9); // expected-error {{constant expression}} cxx20-note {{visible outside}} + int m : (k *= 2); // expected-error {{constant expression}} cxx20-note {{visible outside}} }; struct Literal { diff --git a/clang/test/CXX/expr/expr.const/p6-2a.cpp b/clang/test/CXX/expr/expr.const/p6-2a.cpp new file mode 100644 index 0000000000000..312c28354188d --- /dev/null +++ b/clang/test/CXX/expr/expr.const/p6-2a.cpp @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +constexpr int non_class = 42; +constexpr int arr_non_class[5] = {1, 2, 3}; + +struct A { + int member = 1; + constexpr ~A() { member = member + 1; } +}; +constexpr A class_ = {}; +constexpr A arr_class[5] = {{}, {}}; + +struct Mutable { + mutable int member = 1; // expected-note {{declared here}} + constexpr ~Mutable() { member = member + 1; } // expected-note {{read of mutable member}} +}; +constexpr Mutable mut_member; // expected-error {{must have constant destruction}} expected-note {{in call}} + +struct MutableStore { + mutable int member = 1; // expected-note {{declared here}} + constexpr ~MutableStore() { member = 2; } // expected-note {{assignment to mutable member}} +}; +constexpr MutableStore mut_store; // expected-error {{must have constant destruction}} expected-note {{in call}} + +// Note: the constant destruction rules disallow this example even though hcm.n is a const object. +struct MutableConst { + struct HasConstMember { + const int n = 4; + }; + mutable HasConstMember hcm; // expected-note {{here}} + constexpr ~MutableConst() { + int q = hcm.n; // expected-note {{read of mutable}} + } +}; +constexpr MutableConst mc; // expected-error {{must have constant destruction}} expected-note {{in call}} + +struct Temporary { + int &&temp; + constexpr ~Temporary() { + int n = temp; // expected-note {{outside the expression that created the temporary}} + } +}; +constexpr Temporary t = {3}; // expected-error {{must have constant destruction}} expected-note {{created here}} expected-note {{in call}} diff --git a/clang/test/ClangScanDeps/Inputs/foodir b/clang/test/ClangScanDeps/Inputs/foodir new file mode 100644 index 0000000000000..c2e511e0cbc56 --- /dev/null +++ b/clang/test/ClangScanDeps/Inputs/foodir @@ -0,0 +1 @@ +// A C++ header with same name as that of a directory in the include path. diff --git a/clang/test/ClangScanDeps/Inputs/headerwithdirname.json b/clang/test/ClangScanDeps/Inputs/headerwithdirname.json new file mode 100644 index 0000000000000..2ae561935bec3 --- /dev/null +++ b/clang/test/ClangScanDeps/Inputs/headerwithdirname.json @@ -0,0 +1,7 @@ +[ + { + "directory": "DIR", + "command": "clang -c -IDIR -IDIR/foodir -IInputs DIR/headerwithdirname_input.cpp", + "file": "DIR/headerwithdirname_input.cpp" + } +] diff --git a/clang/test/ClangScanDeps/Inputs/strip_diag_serialize.json b/clang/test/ClangScanDeps/Inputs/strip_diag_serialize.json new file mode 100644 index 0000000000000..a774d95a3b023 --- /dev/null +++ b/clang/test/ClangScanDeps/Inputs/strip_diag_serialize.json @@ -0,0 +1,7 @@ +[ +{ + "directory": "DIR", + "command": "clang -E -fsyntax-only DIR/strip_diag_serialize_input.cpp --serialize-diagnostics /does/not/exist", + "file": "DIR/strip_diag_serialize_input.cpp" +} +] diff --git a/clang/test/ClangScanDeps/headerwithdirname.cpp b/clang/test/ClangScanDeps/headerwithdirname.cpp new file mode 100644 index 0000000000000..b0f60333aa4c3 --- /dev/null +++ b/clang/test/ClangScanDeps/headerwithdirname.cpp @@ -0,0 +1,17 @@ +// RUN: rm -rf %t.dir +// RUN: rm -rf %t.dir/foodir +// RUN: rm -rf %t.cdb +// RUN: mkdir -p %t.dir +// RUN: mkdir -p %t.dir/foodir +// RUN: cp %s %t.dir/headerwithdirname_input.cpp +// RUN: mkdir %t.dir/Inputs +// RUN: cp %S/Inputs/foodir %t.dir/Inputs/foodir +// RUN: sed -e "s|DIR|%/t.dir|g" %S/Inputs/headerwithdirname.json > %t.cdb +// +// RUN: clang-scan-deps -compilation-database %t.cdb -j 1 | FileCheck %s + +#include + +// CHECK: headerwithdirname_input.o +// CHECK-NEXT: headerwithdirname_input.cpp +// CHECK-NEXT: Inputs{{/|\\}}foodir diff --git a/clang/test/ClangScanDeps/strip_diag_serialize.cpp b/clang/test/ClangScanDeps/strip_diag_serialize.cpp new file mode 100644 index 0000000000000..ec62e75134814 --- /dev/null +++ b/clang/test/ClangScanDeps/strip_diag_serialize.cpp @@ -0,0 +1,11 @@ +// RUN: rm -rf %t.dir +// RUN: rm -rf %t.cdb +// RUN: mkdir -p %t.dir +// RUN: cp %s %t.dir/strip_diag_serialize_input.cpp +// RUN: sed -e "s|DIR|%/t.dir|g" %S/Inputs/strip_diag_serialize.json > %t.cdb +// +// RUN: clang-scan-deps -compilation-database %t.cdb 2>&1 | FileCheck %s +// CHECK-NOT: unable to open file +// CHECK: strip_diag_serialize_input.cpp + +#warning "diagnostic" diff --git a/clang/test/CodeGen/align-global-large.c b/clang/test/CodeGen/align-global-large.c index 14f5d8d9e1442..e53323f65f527 100644 --- a/clang/test/CodeGen/align-global-large.c +++ b/clang/test/CodeGen/align-global-large.c @@ -1,5 +1,5 @@ // PR13606 - Clang crashes with large alignment attribute -// RUN: %clang -cc1 -S -emit-llvm %s -o - -triple i686-pc-gnu | FileCheck %s +// RUN: %clang_cc1 -S -emit-llvm %s -o - -triple i686-pc-gnu | FileCheck %s // CHECK: x // CHECK: align diff --git a/clang/test/CodeGen/altivec-ct.c b/clang/test/CodeGen/altivec-ct.c index 1a3e14dc1fd52..e36f556eb950b 100644 --- a/clang/test/CodeGen/altivec-ct.c +++ b/clang/test/CodeGen/altivec-ct.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -triple powerpc64le-linux-gnu -S -O0 -o - %s -target-feature +altivec -target-feature +vsx | FileCheck %s -check-prefix=CHECK -check-prefix=VSX -// RUN: %clang_cc1 -triple powerpc-linux-gnu -S -O0 -o - %s -target-feature +altivec -target-feature -vsx | FileCheck %s +// RUN: %clang_cc1 -flax-vector-conversions=none -triple powerpc64le-linux-gnu -S -O0 -o - %s -target-feature +altivec -target-feature +vsx | FileCheck %s -check-prefix=CHECK -check-prefix=VSX +// RUN: %clang_cc1 -flax-vector-conversions=none -triple powerpc-linux-gnu -S -O0 -o - %s -target-feature +altivec -target-feature -vsx | FileCheck %s // REQUIRES: powerpc-registered-target diff --git a/clang/test/CodeGen/arm-target-features.c b/clang/test/CodeGen/arm-target-features.c index a0fbafc5d0c54..6200d057ad509 100644 --- a/clang/test/CodeGen/arm-target-features.c +++ b/clang/test/CodeGen/arm-target-features.c @@ -1,23 +1,23 @@ // REQUIRES: arm-registered-target // RUN: %clang_cc1 -triple thumbv7-linux-gnueabihf -target-cpu cortex-a8 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-VFP3 -// CHECK-VFP3: "target-features"="+armv7-a,+d32,+dsp,+fp64,+fpregs,+neon,+thumb-mode,+vfp2,+vfp2d16,+vfp2d16sp,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp" +// CHECK-VFP3: "target-features"="+armv7-a,+d32,+dsp,+fp64,+fpregs,+neon,+thumb-mode,+vfp2,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp" // RUN: %clang_cc1 -triple thumbv7-linux-gnueabihf -target-cpu cortex-a5 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-VFP4 -// CHECK-VFP4: "target-features"="+armv7-a,+d32,+dsp,+fp16,+fp64,+fpregs,+neon,+thumb-mode,+vfp2,+vfp2d16,+vfp2d16sp,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,+vfp4,+vfp4d16,+vfp4d16sp,+vfp4sp" +// CHECK-VFP4: "target-features"="+armv7-a,+d32,+dsp,+fp16,+fp64,+fpregs,+neon,+thumb-mode,+vfp2,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,+vfp4,+vfp4d16,+vfp4d16sp,+vfp4sp" // RUN: %clang_cc1 -triple thumbv7-linux-gnueabihf -target-cpu cortex-a7 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-VFP4-DIV // RUN: %clang_cc1 -triple thumbv7-linux-gnueabi -target-cpu cortex-a12 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-VFP4-DIV // RUN: %clang_cc1 -triple thumbv7s-linux-gnueabi -target-cpu swift -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-VFP4-DIV-2 // RUN: %clang_cc1 -triple thumbv7-linux-gnueabihf -target-cpu krait -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-VFP4-DIV -// CHECK-VFP4-DIV: "target-features"="+armv7-a,+d32,+dsp,+fp16,+fp64,+fpregs,+hwdiv,+hwdiv-arm,+neon,+thumb-mode,+vfp2,+vfp2d16,+vfp2d16sp,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,+vfp4,+vfp4d16,+vfp4d16sp,+vfp4sp" -// CHECK-VFP4-DIV-2: "target-features"="+armv7s,+d32,+dsp,+fp16,+fp64,+fpregs,+hwdiv,+hwdiv-arm,+neon,+thumb-mode,+vfp2,+vfp2d16,+vfp2d16sp,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,+vfp4,+vfp4d16,+vfp4d16sp,+vfp4sp" +// CHECK-VFP4-DIV: "target-features"="+armv7-a,+d32,+dsp,+fp16,+fp64,+fpregs,+hwdiv,+hwdiv-arm,+neon,+thumb-mode,+vfp2,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,+vfp4,+vfp4d16,+vfp4d16sp,+vfp4sp" +// CHECK-VFP4-DIV-2: "target-features"="+armv7s,+d32,+dsp,+fp16,+fp64,+fpregs,+hwdiv,+hwdiv-arm,+neon,+thumb-mode,+vfp2,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,+vfp4,+vfp4d16,+vfp4d16sp,+vfp4sp" // RUN: %clang_cc1 -triple armv7-linux-gnueabihf -target-cpu cortex-a15 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-VFP4-DIV-ARM // RUN: %clang_cc1 -triple armv7-linux-gnueabihf -target-cpu cortex-a17 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-VFP4-DIV-ARM -// CHECK-VFP4-DIV-ARM: "target-features"="+armv7-a,+d32,+dsp,+fp16,+fp64,+fpregs,+hwdiv,+hwdiv-arm,+neon,+vfp2,+vfp2d16,+vfp2d16sp,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,+vfp4,+vfp4d16,+vfp4d16sp,+vfp4sp,-thumb-mode" +// CHECK-VFP4-DIV-ARM: "target-features"="+armv7-a,+d32,+dsp,+fp16,+fp64,+fpregs,+hwdiv,+hwdiv-arm,+neon,+vfp2,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,+vfp4,+vfp4d16,+vfp4d16sp,+vfp4sp,-thumb-mode" // RUN: %clang_cc1 -triple thumbv7s-apple-ios7.0 -target-cpu cyclone -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-BASIC-V8 // RUN: %clang_cc1 -triple thumbv8-linux-gnueabihf -target-cpu cortex-a32 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-BASIC-V8 @@ -28,34 +28,34 @@ // RUN: %clang_cc1 -triple thumbv8-linux-gnueabihf -target-cpu exynos-m1 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-BASIC-V8 // RUN: %clang_cc1 -triple thumbv8-linux-gnueabihf -target-cpu exynos-m2 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-BASIC-V8 // RUN: %clang_cc1 -triple thumbv8-linux-gnueabihf -target-cpu exynos-m3 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-BASIC-V8 -// CHECK-BASIC-V8: "target-features"="+armv8-a,+crc,+crypto,+d32,+dsp,+fp-armv8,+fp-armv8d16,+fp-armv8d16sp,+fp-armv8sp,+fp16,+fp64,+fpregs,+hwdiv,+hwdiv-arm,+neon,+thumb-mode,+vfp2,+vfp2d16,+vfp2d16sp,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,+vfp4,+vfp4d16,+vfp4d16sp,+vfp4sp" +// CHECK-BASIC-V8: "target-features"="+armv8-a,+crc,+crypto,+d32,+dsp,+fp-armv8,+fp-armv8d16,+fp-armv8d16sp,+fp-armv8sp,+fp16,+fp64,+fpregs,+hwdiv,+hwdiv-arm,+neon,+thumb-mode,+vfp2,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,+vfp4,+vfp4d16,+vfp4d16sp,+vfp4sp" // RUN: %clang_cc1 -triple thumbv8-linux-gnueabihf -target-cpu exynos-m4 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-BASIC-V82 // RUN: %clang_cc1 -triple thumbv8-linux-gnueabihf -target-cpu exynos-m5 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-BASIC-V82 -// CHECK-BASIC-V82: "target-features"="+armv8.2-a,+crc,+crypto,+d32,+dotprod,+dsp,+fp-armv8,+fp-armv8d16,+fp-armv8d16sp,+fp-armv8sp,+fp16,+fp64,+fpregs,+fullfp16,+hwdiv,+hwdiv-arm,+neon,+ras,+thumb-mode,+vfp2,+vfp2d16,+vfp2d16sp,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,+vfp4,+vfp4d16,+vfp4d16sp,+vfp4sp" +// CHECK-BASIC-V82: "target-features"="+armv8.2-a,+crc,+crypto,+d32,+dotprod,+dsp,+fp-armv8,+fp-armv8d16,+fp-armv8d16sp,+fp-armv8sp,+fp16,+fp64,+fpregs,+fullfp16,+hwdiv,+hwdiv-arm,+neon,+ras,+thumb-mode,+vfp2,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,+vfp4,+vfp4d16,+vfp4d16sp,+vfp4sp" // RUN: %clang_cc1 -triple armv8-linux-gnueabi -target-cpu cortex-a53 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-BASIC-V8-ARM -// CHECK-BASIC-V8-ARM: "target-features"="+armv8-a,+crc,+crypto,+d32,+dsp,+fp-armv8,+fp-armv8d16,+fp-armv8d16sp,+fp-armv8sp,+fp16,+fp64,+fpregs,+hwdiv,+hwdiv-arm,+neon,+vfp2,+vfp2d16,+vfp2d16sp,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,+vfp4,+vfp4d16,+vfp4d16sp,+vfp4sp,-thumb-mode" +// CHECK-BASIC-V8-ARM: "target-features"="+armv8-a,+crc,+crypto,+d32,+dsp,+fp-armv8,+fp-armv8d16,+fp-armv8d16sp,+fp-armv8sp,+fp16,+fp64,+fpregs,+hwdiv,+hwdiv-arm,+neon,+vfp2,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,+vfp4,+vfp4d16,+vfp4d16sp,+vfp4sp,-thumb-mode" // RUN: %clang_cc1 -triple thumbv7-linux-gnueabi -target-cpu cortex-r5 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-VFP3-D16-DIV -// CHECK-VFP3-D16-DIV: "target-features"="+armv7-r,+dsp,+fp64,+fpregs,+hwdiv,+hwdiv-arm,+thumb-mode,+vfp2d16,+vfp2d16sp,+vfp3d16,+vfp3d16sp" +// CHECK-VFP3-D16-DIV: "target-features"="+armv7-r,+dsp,+fp64,+fpregs,+hwdiv,+hwdiv-arm,+thumb-mode,+vfp2,+vfp2sp,+vfp3d16,+vfp3d16sp" // RUN: %clang_cc1 -triple armv7-linux-gnueabi -target-cpu cortex-r4f -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-VFP3-D16-THUMB-DIV -// CHECK-VFP3-D16-THUMB-DIV: "target-features"="+armv7-r,+dsp,+fp64,+fpregs,+hwdiv,+vfp2d16,+vfp2d16sp,+vfp3d16,+vfp3d16sp,-thumb-mode" +// CHECK-VFP3-D16-THUMB-DIV: "target-features"="+armv7-r,+dsp,+fp64,+fpregs,+hwdiv,+vfp2,+vfp2sp,+vfp3d16,+vfp3d16sp,-thumb-mode" // RUN: %clang_cc1 -triple thumbv7-linux-gnueabi -target-cpu cortex-r7 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-VFP3-D16-FP16-DIV // RUN: %clang_cc1 -triple thumbv7-linux-gnueabi -target-cpu cortex-r8 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-VFP3-D16-FP16-DIV -// CHECK-VFP3-D16-FP16-DIV: "target-features"="+armv7-r,+dsp,+fp16,+fp64,+fpregs,+hwdiv,+hwdiv-arm,+thumb-mode,+vfp2d16,+vfp2d16sp,+vfp3d16,+vfp3d16sp" +// CHECK-VFP3-D16-FP16-DIV: "target-features"="+armv7-r,+dsp,+fp16,+fp64,+fpregs,+hwdiv,+hwdiv-arm,+thumb-mode,+vfp2,+vfp2sp,+vfp3d16,+vfp3d16sp" // RUN: %clang_cc1 -triple thumbv7-linux-gnueabi -target-cpu cortex-m4 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-VFP4-D16-SP-THUMB-DIV -// CHECK-VFP4-D16-SP-THUMB-DIV: "target-features"="+armv7e-m,+dsp,+fp16,+fpregs,+hwdiv,+thumb-mode,+vfp2d16sp,+vfp3d16sp,+vfp4d16sp" +// CHECK-VFP4-D16-SP-THUMB-DIV: "target-features"="+armv7e-m,+dsp,+fp16,+fpregs,+hwdiv,+thumb-mode,+vfp2sp,+vfp3d16sp,+vfp4d16sp" // RUN: %clang_cc1 -triple thumbv7-linux-gnueabi -target-cpu cortex-m7 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-VFP5-D16-THUMB-DIV -// CHECK-VFP5-D16-THUMB-DIV: "target-features"="+armv7e-m,+dsp,+fp-armv8d16,+fp-armv8d16sp,+fp16,+fp64,+fpregs,+hwdiv,+thumb-mode,+vfp2d16,+vfp2d16sp,+vfp3d16,+vfp3d16sp,+vfp4d16,+vfp4d16sp" +// CHECK-VFP5-D16-THUMB-DIV: "target-features"="+armv7e-m,+dsp,+fp-armv8d16,+fp-armv8d16sp,+fp16,+fp64,+fpregs,+hwdiv,+thumb-mode,+vfp2,+vfp2sp,+vfp3d16,+vfp3d16sp,+vfp4d16,+vfp4d16sp" // RUN: %clang_cc1 -triple armv7-linux-gnueabi -target-cpu cortex-r4 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-THUMB-DIV @@ -107,6 +107,6 @@ // CHECK-ARMV8M-M23-LINUX: "target-features"="+armv8-m.base,+hwdiv,+thumb-mode" // RUN: %clang_cc1 -triple thumb-linux-gnueabi -target-cpu cortex-m33 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-ARMV8M-MAIN-LINUX -// CHECK-ARMV8M-MAIN-LINUX: "target-features"="+armv8-m.main,+dsp,+fp-armv8d16sp,+fp16,+fpregs,+hwdiv,+thumb-mode,+vfp2d16sp,+vfp3d16sp,+vfp4d16sp" +// CHECK-ARMV8M-MAIN-LINUX: "target-features"="+armv8-m.main,+dsp,+fp-armv8d16sp,+fp16,+fpregs,+hwdiv,+thumb-mode,+vfp2sp,+vfp3d16sp,+vfp4d16sp" void foo() {} diff --git a/clang/test/CodeGen/arm_acle.c b/clang/test/CodeGen/arm_acle.c index 2f086ee70bfd9..ce2c5fac70b2c 100644 --- a/clang/test/CodeGen/arm_acle.c +++ b/clang/test/CodeGen/arm_acle.c @@ -6,6 +6,8 @@ // RUN: %clang_cc1 -ffreestanding -triple aarch64-eabi -target-cpu cortex-a57 -target-feature +v8.4a -O2 -fexperimental-new-pass-manager -S -emit-llvm -o - %s | FileCheck %s -check-prefix=AArch64-v8_3 // RUN: %clang_cc1 -ffreestanding -triple aarch64-eabi -target-cpu cortex-a57 -target-feature +v8.5a -O2 -fexperimental-new-pass-manager -S -emit-llvm -o - %s | FileCheck %s -check-prefix=AArch64-v8_3 +// REQUIRES: rewrite + #include /* 8 SYNCHRONIZATION, BARRIER AND HINT INTRINSICS */ diff --git a/clang/test/CodeGen/available-externally-suppress.c b/clang/test/CodeGen/available-externally-suppress.c index 55f85ad250d29..1e79750fb9d6d 100644 --- a/clang/test/CodeGen/available-externally-suppress.c +++ b/clang/test/CodeGen/available-externally-suppress.c @@ -30,10 +30,8 @@ inline int __attribute__((always_inline)) f1(int x) { // CHECK: @test1 // LTO: @test1 int test1(int x) { - // CHECK: br i1 // CHECK-NOT: call {{.*}} @f1 // CHECK: ret i32 - // LTO: br i1 // LTO-NOT: call {{.*}} @f1 // LTO: ret i32 return f1(x); diff --git a/clang/test/CodeGen/avr-builtins.c b/clang/test/CodeGen/avr-builtins.c index cbba6b2f2a2e5..8fa983a782393 100644 --- a/clang/test/CodeGen/avr-builtins.c +++ b/clang/test/CodeGen/avr-builtins.c @@ -1,5 +1,9 @@ // RUN: %clang_cc1 -triple avr-unknown-unknown -emit-llvm -o - %s | FileCheck %s +// Check that the parameter types match. This verifies pr43309. +// RUN: %clang_cc1 -triple avr-unknown-unknown -Wconversion -verify %s +// expected-no-diagnostics + unsigned char bitrev8(unsigned char data) { return __builtin_bitreverse8(data); } diff --git a/clang/test/CodeGen/avx-builtins.c b/clang/test/CodeGen/avx-builtins.c index 0a09c53391e57..b31a234db2eda 100644 --- a/clang/test/CodeGen/avx-builtins.c +++ b/clang/test/CodeGen/avx-builtins.c @@ -1,6 +1,6 @@ -// RUN: %clang_cc1 -ffreestanding %s -triple=x86_64-apple-darwin -target-feature +avx -emit-llvm -o - -Wall -Werror | FileCheck %s -// RUN: %clang_cc1 -ffreestanding %s -triple=x86_64-apple-darwin -target-feature +avx -fno-signed-char -emit-llvm -o - -Wall -Werror | FileCheck %s -// RUN: %clang_cc1 -fms-extensions -fms-compatibility -ffreestanding %s -triple=x86_64-windows-msvc -target-feature +avx -emit-llvm -o - -Wall -Werror | FileCheck %s +// RUN: %clang_cc1 -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-apple-darwin -target-feature +avx -emit-llvm -o - -Wall -Werror | FileCheck %s +// RUN: %clang_cc1 -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-apple-darwin -target-feature +avx -fno-signed-char -emit-llvm -o - -Wall -Werror | FileCheck %s +// RUN: %clang_cc1 -flax-vector-conversions=none -fms-extensions -fms-compatibility -ffreestanding %s -triple=x86_64-windows-msvc -target-feature +avx -emit-llvm -o - -Wall -Werror | FileCheck %s #include @@ -105,7 +105,7 @@ __m256d test_mm256_broadcast_sd(double* A) { return _mm256_broadcast_sd(A); } -__m128d test_mm_broadcast_ss(float* A) { +__m128 test_mm_broadcast_ss(float* A) { // CHECK-LABEL: test_mm_broadcast_ss // CHECK: load float, float* %{{.*}} // CHECK: insertelement <4 x float> undef, float %{{.*}}, i32 0 @@ -115,7 +115,7 @@ __m128d test_mm_broadcast_ss(float* A) { return _mm_broadcast_ss(A); } -__m256d test_mm256_broadcast_ss(float* A) { +__m256 test_mm256_broadcast_ss(float* A) { // CHECK-LABEL: test_mm256_broadcast_ss // CHECK: load float, float* %{{.*}} // CHECK: insertelement <8 x float> undef, float %{{.*}}, i32 0 @@ -1278,7 +1278,7 @@ __m128 test_mm_maskload_ps(float* A, __m128i B) { return _mm_maskload_ps(A, B); } -__m256d test_mm256_maskload_ps(float* A, __m256i B) { +__m256 test_mm256_maskload_ps(float* A, __m256i B) { // CHECK-LABEL: test_mm256_maskload_ps // CHECK: call <8 x float> @llvm.x86.avx.maskload.ps.256(i8* %{{.*}}, <8 x i32> %{{.*}}) return _mm256_maskload_ps(A, B); diff --git a/clang/test/CodeGen/avx-cmp-builtins.c b/clang/test/CodeGen/avx-cmp-builtins.c index 609f5964f31f2..adb8a86112cc2 100644 --- a/clang/test/CodeGen/avx-cmp-builtins.c +++ b/clang/test/CodeGen/avx-cmp-builtins.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -ffreestanding %s -O3 -triple=x86_64-apple-darwin -target-feature +avx -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -flax-vector-conversions=none -ffreestanding %s -O3 -triple=x86_64-apple-darwin -target-feature +avx -emit-llvm -o - | FileCheck %s // FIXME: The shufflevector instructions in test_cmpgt_sd are relying on O3 here. @@ -14,7 +14,7 @@ __m128d test_cmp_sd(__m128d a, __m128d b) { return _mm_cmp_sd(a, b, _CMP_GE_OS); } -__m128d test_cmp_ss(__m128 a, __m128 b) { +__m128 test_cmp_ss(__m128 a, __m128 b) { // Expects that the third argument in LLVM IR is immediate expression // CHECK: @llvm.x86.sse.cmp.ss({{.*}}, i8 13) return _mm_cmp_ss(a, b, _CMP_GE_OS); diff --git a/clang/test/CodeGen/avx512f-builtins.c b/clang/test/CodeGen/avx512f-builtins.c index 2a083e3e5c609..73a093c54683b 100644 --- a/clang/test/CodeGen/avx512f-builtins.c +++ b/clang/test/CodeGen/avx512f-builtins.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -fexperimental-new-pass-manager -ffreestanding %s -triple=x86_64-apple-darwin -target-feature +avx512f -emit-llvm -o - -Wall -Werror | FileCheck %s -// RUN: %clang_cc1 -fexperimental-new-pass-manager -fms-extensions -fms-compatibility -ffreestanding %s -triple=x86_64-windows-msvc -target-feature +avx512f -emit-llvm -o - -Wall -Werror | FileCheck %s +// RUN: %clang_cc1 -fexperimental-new-pass-manager -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-apple-darwin -target-feature +avx512f -emit-llvm -o - -Wall -Werror | FileCheck %s +// RUN: %clang_cc1 -fexperimental-new-pass-manager -flax-vector-conversions=none -fms-extensions -fms-compatibility -ffreestanding %s -triple=x86_64-windows-msvc -target-feature +avx512f -emit-llvm -o - -Wall -Werror | FileCheck %s #include @@ -9468,7 +9468,7 @@ __m256 test_mm512_mask_cvtpd_ps (__m256 __W, __mmask8 __U, __m512d __A) return _mm512_mask_cvtpd_ps (__W,__U,__A); } -__m512d test_mm512_cvtpd_pslo(__m512 __A) +__m512 test_mm512_cvtpd_pslo(__m512d __A) { // CHECK-LABEL: @test_mm512_cvtpd_pslo // CHECK: @llvm.x86.avx512.mask.cvtpd2ps.512 @@ -9477,7 +9477,7 @@ __m512d test_mm512_cvtpd_pslo(__m512 __A) return _mm512_cvtpd_pslo(__A); } -__m512d test_mm512_mask_cvtpd_pslo(__m512 __W, __mmask8 __U, __m512d __A) { +__m512 test_mm512_mask_cvtpd_pslo(__m512 __W, __mmask8 __U, __m512d __A) { // CHECK-LABEL: @test_mm512_mask_cvtpd_pslo // CHECK: @llvm.x86.avx512.mask.cvtpd2ps.512 // CHECK: zeroinitializer @@ -10659,7 +10659,7 @@ __m512i test_mm512_setzero_epi32() return _mm512_setzero_epi32(); } -__m512i test_mm512_setzero() +__m512 test_mm512_setzero() { // CHECK-LABEL: @test_mm512_setzero // CHECK: zeroinitializer @@ -10673,7 +10673,7 @@ __m512i test_mm512_setzero_si512() return _mm512_setzero_si512(); } -__m512i test_mm512_setzero_ps() +__m512 test_mm512_setzero_ps() { // CHECK-LABEL: @test_mm512_setzero_ps // CHECK: zeroinitializer diff --git a/clang/test/CodeGen/builtin-preserve-access-index-nonptr.c b/clang/test/CodeGen/builtin-preserve-access-index-nonptr.c new file mode 100644 index 0000000000000..bb8caf4921bef --- /dev/null +++ b/clang/test/CodeGen/builtin-preserve-access-index-nonptr.c @@ -0,0 +1,18 @@ +// RUN: %clang -target x86_64 -emit-llvm -S -g %s -o - | FileCheck %s + +#define _(x) (__builtin_preserve_access_index(x)) + +struct s1 { + char a; + int b[4]; +}; + +int unit1(struct s1 *arg) { + return _(arg->b[2]); +} +// CHECK: define dso_local i32 @unit1 +// CHECK: call [4 x i32]* @llvm.preserve.struct.access.index.p0a4i32.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 1, i32 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[STRUCT_S1:[0-9]+]] +// CHECK: call i32* @llvm.preserve.array.access.index.p0i32.p0a4i32([4 x i32]* %{{[0-9a-z]+}}, i32 1, i32 2), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[ARRAY:[0-9]+]] +// +// CHECK: ![[ARRAY]] = !DICompositeType(tag: DW_TAG_array_type +// CHECK: ![[STRUCT_S1]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s1" diff --git a/clang/test/CodeGen/builtins-ppc-error.c b/clang/test/CodeGen/builtins-ppc-error.c index 39a2458226433..80ca227eeb24c 100644 --- a/clang/test/CodeGen/builtins-ppc-error.c +++ b/clang/test/CodeGen/builtins-ppc-error.c @@ -2,10 +2,19 @@ // RUN: %clang_cc1 -target-feature +altivec -target-feature +power9-vector \ // RUN: -triple powerpc64-unknown-unknown -fsyntax-only \ +// RUN: -flax-vector-conversions=integer \ // RUN: -Wall -Werror -verify %s // RUN: %clang_cc1 -target-feature +altivec -target-feature +power9-vector \ // RUN: -triple powerpc64le-unknown-unknown -fsyntax-only \ +// RUN: -flax-vector-conversions=integer \ +// RUN: -Wall -Werror -verify %s + +// FIXME: Fix so this test also passes under +// -flax-vector-conversions=none (this last test exists to produce an error if +// we change the default to that without fixing ). +// RUN: %clang_cc1 -target-feature +altivec -target-feature +power9-vector \ +// RUN: -triple powerpc64-unknown-unknown -fsyntax-only \ // RUN: -Wall -Werror -verify %s #include diff --git a/clang/test/CodeGen/builtins-ppc-p9vector.c b/clang/test/CodeGen/builtins-ppc-p9vector.c index bfbb815854714..e920cb76f4d91 100644 --- a/clang/test/CodeGen/builtins-ppc-p9vector.c +++ b/clang/test/CodeGen/builtins-ppc-p9vector.c @@ -1,12 +1,20 @@ // REQUIRES: powerpc-registered-target // RUN: %clang_cc1 -target-feature +altivec -target-feature +power9-vector \ // RUN: -triple powerpc64-unknown-unknown -emit-llvm %s \ +// RUN: -flax-vector-conversions=integer \ // RUN: -o - | FileCheck %s -check-prefix=CHECK-BE // RUN: %clang_cc1 -target-feature +altivec -target-feature +power9-vector \ // RUN: -triple powerpc64le-unknown-unknown -emit-llvm %s \ +// RUN: -flax-vector-conversions=integer \ // RUN: -o - | FileCheck %s +// FIXME: This last test is intended to fail if the default is changed to +// -flax-vector-conversions=none and isn't fixed first. +// RUN: %clang_cc1 -target-feature +altivec -target-feature +power9-vector \ +// RUN: -triple powerpc64-unknown-unknown -emit-llvm %s \ +// RUN: -o - | FileCheck %s -check-prefix=CHECK-BE + #include vector signed char vsca, vscb; @@ -919,7 +927,7 @@ vector double test80(void) { // CHECK: insertelement <2 x double> return vec_unpackl(vfa); } -vector double test81(void) { +vector float test81(void) { // CHECK: extractelement <2 x double> // CHECK: fptrunc double // CHECK: insertelement <4 x float> diff --git a/clang/test/CodeGen/builtins-ppc-vsx.c b/clang/test/CodeGen/builtins-ppc-vsx.c index 838d94cf7de9a..0c797a389b944 100644 --- a/clang/test/CodeGen/builtins-ppc-vsx.c +++ b/clang/test/CodeGen/builtins-ppc-vsx.c @@ -882,7 +882,7 @@ void test1() { // CHECK: call void @dummy() // CHECK-LE: call void @dummy() - res_vf = vec_sel(vd, vd, vbll); + res_vd = vec_sel(vd, vd, vbll); // CHECK: xor <2 x i64> %{{[0-9]+}}, // CHECK: and <2 x i64> %{{[0-9]+}}, // CHECK: and <2 x i64> %{{[0-9]+}}, %{{[0-9]+}} diff --git a/clang/test/CodeGen/builtins-systemz-vector3-error.c b/clang/test/CodeGen/builtins-systemz-vector3-error.c index 6583857c3c178..99b171c55bb55 100644 --- a/clang/test/CodeGen/builtins-systemz-vector3-error.c +++ b/clang/test/CodeGen/builtins-systemz-vector3-error.c @@ -1,5 +1,5 @@ // REQUIRES: systemz-registered-target -// RUN: %clang_cc1 -target-cpu arch13 -triple s390x-unknown-unknown \ +// RUN: %clang_cc1 -target-cpu z15 -triple s390x-unknown-unknown \ // RUN: -Wall -Wno-unused -Werror -fsyntax-only -verify %s typedef __attribute__((vector_size(16))) signed char vec_schar; diff --git a/clang/test/CodeGen/builtins-systemz-vector3.c b/clang/test/CodeGen/builtins-systemz-vector3.c index e8194b642cd63..e4af1c1e54b86 100644 --- a/clang/test/CodeGen/builtins-systemz-vector3.c +++ b/clang/test/CodeGen/builtins-systemz-vector3.c @@ -1,5 +1,5 @@ // REQUIRES: systemz-registered-target -// RUN: %clang_cc1 -target-cpu arch13 -triple s390x-ibm-linux -flax-vector-conversions=none \ +// RUN: %clang_cc1 -target-cpu z15 -triple s390x-ibm-linux -flax-vector-conversions=none \ // RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s typedef __attribute__((vector_size(16))) signed char vec_schar; diff --git a/clang/test/CodeGen/builtins-systemz-zvector3-error.c b/clang/test/CodeGen/builtins-systemz-zvector3-error.c index 657508b261867..557b9a3a7ec1b 100644 --- a/clang/test/CodeGen/builtins-systemz-zvector3-error.c +++ b/clang/test/CodeGen/builtins-systemz-zvector3-error.c @@ -1,5 +1,5 @@ // REQUIRES: systemz-registered-target -// RUN: %clang_cc1 -target-cpu arch13 -triple s390x-linux-gnu \ +// RUN: %clang_cc1 -target-cpu z15 -triple s390x-linux-gnu \ // RUN: -fzvector -flax-vector-conversions=none \ // RUN: -Wall -Wno-unused -Werror -fsyntax-only -verify %s diff --git a/clang/test/CodeGen/builtins-systemz-zvector3.c b/clang/test/CodeGen/builtins-systemz-zvector3.c index db30f41933263..a987c6113e36b 100644 --- a/clang/test/CodeGen/builtins-systemz-zvector3.c +++ b/clang/test/CodeGen/builtins-systemz-zvector3.c @@ -1,8 +1,8 @@ // REQUIRES: systemz-registered-target -// RUN: %clang_cc1 -target-cpu arch13 -triple s390x-linux-gnu \ +// RUN: %clang_cc1 -target-cpu z15 -triple s390x-linux-gnu \ // RUN: -O -fzvector -flax-vector-conversions=none \ // RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s -// RUN: %clang_cc1 -target-cpu arch13 -triple s390x-linux-gnu \ +// RUN: %clang_cc1 -target-cpu z15 -triple s390x-linux-gnu \ // RUN: -O -fzvector -flax-vector-conversions=none \ // RUN: -Wall -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM diff --git a/clang/test/CodeGen/builtins-wasm.c b/clang/test/CodeGen/builtins-wasm.c index ee08983d9a1e7..ca407ebc0ed4f 100644 --- a/clang/test/CodeGen/builtins-wasm.c +++ b/clang/test/CodeGen/builtins-wasm.c @@ -463,3 +463,79 @@ i64x2 trunc_saturate_u_i64x2_f64x2(f64x2 f) { // WEBASSEMBLY: call <2 x i64> @llvm.wasm.trunc.saturate.unsigned.v2i64.v2f64(<2 x double> %f) // WEBASSEMBLY-NEXT: ret } + +i8x16 narrow_s_i8x16_i16x8(i16x8 low, i16x8 high) { + return __builtin_wasm_narrow_s_i8x16_i16x8(low, high); + // WEBASSEMBLY: call <16 x i8> @llvm.wasm.narrow.signed.v16i8.v8i16( + // WEBASSEMBLY-SAME: <8 x i16> %low, <8 x i16> %high) + // WEBASSEMBLY: ret +} + +i8x16 narrow_u_i8x16_i16x8(i16x8 low, i16x8 high) { + return __builtin_wasm_narrow_u_i8x16_i16x8(low, high); + // WEBASSEMBLY: call <16 x i8> @llvm.wasm.narrow.unsigned.v16i8.v8i16( + // WEBASSEMBLY-SAME: <8 x i16> %low, <8 x i16> %high) + // WEBASSEMBLY: ret +} + +i16x8 narrow_s_i16x8_i32x4(i32x4 low, i32x4 high) { + return __builtin_wasm_narrow_s_i16x8_i32x4(low, high); + // WEBASSEMBLY: call <8 x i16> @llvm.wasm.narrow.signed.v8i16.v4i32( + // WEBASSEMBLY-SAME: <4 x i32> %low, <4 x i32> %high) + // WEBASSEMBLY: ret +} + +i16x8 narrow_u_i16x8_i32x4(i32x4 low, i32x4 high) { + return __builtin_wasm_narrow_u_i16x8_i32x4(low, high); + // WEBASSEMBLY: call <8 x i16> @llvm.wasm.narrow.unsigned.v8i16.v4i32( + // WEBASSEMBLY-SAME: <4 x i32> %low, <4 x i32> %high) + // WEBASSEMBLY: ret +} + +i16x8 widen_low_s_i16x8_i8x16(i8x16 v) { + return __builtin_wasm_widen_low_s_i16x8_i8x16(v); + // WEBASSEMBLY: call <8 x i16> @llvm.wasm.widen.low.signed.v8i16.v16i8(<16 x i8> %v) + // WEBASSEMBLY: ret +} + +i16x8 widen_high_s_i16x8_i8x16(i8x16 v) { + return __builtin_wasm_widen_high_s_i16x8_i8x16(v); + // WEBASSEMBLY: call <8 x i16> @llvm.wasm.widen.high.signed.v8i16.v16i8(<16 x i8> %v) + // WEBASSEMBLY: ret +} + +i16x8 widen_low_u_i16x8_i8x16(i8x16 v) { + return __builtin_wasm_widen_low_u_i16x8_i8x16(v); + // WEBASSEMBLY: call <8 x i16> @llvm.wasm.widen.low.unsigned.v8i16.v16i8(<16 x i8> %v) + // WEBASSEMBLY: ret +} + +i16x8 widen_high_u_i16x8_i8x16(i8x16 v) { + return __builtin_wasm_widen_high_u_i16x8_i8x16(v); + // WEBASSEMBLY: call <8 x i16> @llvm.wasm.widen.high.unsigned.v8i16.v16i8(<16 x i8> %v) + // WEBASSEMBLY: ret +} + +i32x4 widen_low_s_i32x4_i16x8(i16x8 v) { + return __builtin_wasm_widen_low_s_i32x4_i16x8(v); + // WEBASSEMBLY: call <4 x i32> @llvm.wasm.widen.low.signed.v4i32.v8i16(<8 x i16> %v) + // WEBASSEMBLY: ret +} + +i32x4 widen_high_s_i32x4_i16x8(i16x8 v) { + return __builtin_wasm_widen_high_s_i32x4_i16x8(v); + // WEBASSEMBLY: call <4 x i32> @llvm.wasm.widen.high.signed.v4i32.v8i16(<8 x i16> %v) + // WEBASSEMBLY: ret +} + +i32x4 widen_low_u_i32x4_i16x8(i16x8 v) { + return __builtin_wasm_widen_low_u_i32x4_i16x8(v); + // WEBASSEMBLY: call <4 x i32> @llvm.wasm.widen.low.unsigned.v4i32.v8i16(<8 x i16> %v) + // WEBASSEMBLY: ret +} + +i32x4 widen_high_u_i32x4_i16x8(i16x8 v) { + return __builtin_wasm_widen_high_u_i32x4_i16x8(v); + // WEBASSEMBLY: call <4 x i32> @llvm.wasm.widen.high.unsigned.v4i32.v8i16(<8 x i16> %v) + // WEBASSEMBLY: ret +} diff --git a/clang/test/CodeGen/const-init.c b/clang/test/CodeGen/const-init.c index 41ac8f2eb05bb..374aa00e65893 100644 --- a/clang/test/CodeGen/const-init.c +++ b/clang/test/CodeGen/const-init.c @@ -141,8 +141,8 @@ void g28() { // CHECK: @g28.b = internal global <12 x i16> // CHECK: @g28.c = internal global <2 x x86_fp80> , align 32 static v1i64 a = (v1i64)10LL; - static v12i16 b = (v2f80){1,2}; - static v2f80 c = (v12i16){0,0,0,-32768,16383,0,0,0,0,-32768,16384,0}; + static v12i16 b = (v12i16)(v2f80){1,2}; + static v2f80 c = (v2f80)(v12i16){0,0,0,-32768,16383,0,0,0,0,-32768,16384,0}; } // PR13643 diff --git a/clang/test/CodeGen/inline.c b/clang/test/CodeGen/inline.c index ea6e9d76494b2..b511ece2813b9 100644 --- a/clang/test/CodeGen/inline.c +++ b/clang/test/CodeGen/inline.c @@ -52,7 +52,7 @@ // CHECK3-LABEL: define i32 @_Z3barv() // CHECK3-LABEL: define linkonce_odr i32 @_Z3foov() // CHECK3-NOT: unreferenced -// CHECK3-LABEL: define void @_Z10gnu_inlinev() +// CHECK3-LABEL: define available_externally void @_Z10gnu_inlinev() // CHECK3-LABEL: define available_externally void @_Z13gnu_ei_inlinev() // CHECK3-NOT: @_Z5testCv // CHECK3-LABEL: define linkonce_odr i32 @_Z2eiv() @@ -85,6 +85,7 @@ __inline void unreferenced1() {} extern __inline void unreferenced2() {} __inline __attribute((__gnu_inline__)) void gnu_inline() {} +void (*P1)() = gnu_inline; // PR3988 extern __inline __attribute__((gnu_inline)) void gnu_ei_inline() {} diff --git a/clang/test/CodeGen/ppc-smmintrin.c b/clang/test/CodeGen/ppc-smmintrin.c index e11656225a3d0..666bc0fc440cb 100644 --- a/clang/test/CodeGen/ppc-smmintrin.c +++ b/clang/test/CodeGen/ppc-smmintrin.c @@ -15,7 +15,7 @@ test_extract() { _mm_extract_epi8(mi, 0); _mm_extract_epi32(mi, 0); _mm_extract_epi64(mi, 0); - _mm_extract_ps(mi, 0); + _mm_extract_ps((__m128)mi, 0); } // CHECK-LABEL: @test_extract diff --git a/clang/test/CodeGen/sse-builtins.c b/clang/test/CodeGen/sse-builtins.c index b68a714b43b34..3891c076fa9b8 100644 --- a/clang/test/CodeGen/sse-builtins.c +++ b/clang/test/CodeGen/sse-builtins.c @@ -714,7 +714,7 @@ void test_mm_storeu_ps(float* x, __m128 y) { _mm_storeu_ps(x, y); } -void test_mm_stream_ps(float*A, __m128d B) { +void test_mm_stream_ps(float*A, __m128 B) { // CHECK-LABEL: test_mm_stream_ps // CHECK: store <4 x float> %{{.*}}, <4 x float>* %{{.*}}, align 16, !nontemporal _mm_stream_ps(A, B); diff --git a/clang/test/CodeGen/sse.c b/clang/test/CodeGen/sse.c index 1191f55f81e7a..a75b8dc77e86e 100644 --- a/clang/test/CodeGen/sse.c +++ b/clang/test/CodeGen/sse.c @@ -5,37 +5,37 @@ #include // Byte-shifts look reversed due to xmm register layout -__m128 test_mm_slli_si128(__m128 a) { +__m128i test_mm_slli_si128(__m128i a) { // CHECK-LABEL: @test_mm_slli_si128 // CHECK: shufflevector <16 x i8> <{{.*}}, i8 0, i8 0, i8 0, i8 0, i8 0>, <16 x i8> {{.*}}, <16 x i32> return _mm_slli_si128(a, 5); } -__m128 test_mm_slli_si128_0(__m128 a) { +__m128i test_mm_slli_si128_0(__m128i a) { // CHECK-LABEL: @test_mm_slli_si128_0 // CHECK-NOT: shufflevector return _mm_slli_si128(a, 0); } -__m128 test_mm_slli_si128_16(__m128 a) { +__m128i test_mm_slli_si128_16(__m128i a) { // CHECK-LABEL: @test_mm_slli_si128_16 // CHECK-NOT: shufflevector return _mm_slli_si128(a, 16); } -__m128 test_mm_srli_si128(__m128 a) { +__m128i test_mm_srli_si128(__m128i a) { // CHECK-LABEL: @test_mm_srli_si128 // CHECK: shufflevector <16 x i8> {{.*}}, <16 x i8> , <16 x i32> return _mm_srli_si128(a, 5); } -__m128 test_mm_srli_si128_0(__m128 a) { +__m128i test_mm_srli_si128_0(__m128i a) { // CHECK-LABEL: @test_mm_srli_si128_0 // CHECK-NOT: shufflevector return _mm_srli_si128(a, 0); } -__m128 test_mm_srli_si128_16(__m128 a) { +__m128i test_mm_srli_si128_16(__m128i a) { // CHECK-LABEL: @test_mm_srli_si128_16 // CHECK-NOT: shufflevector return _mm_srli_si128(a, 16); diff --git a/clang/test/CodeGen/systemz-abi-vector.c b/clang/test/CodeGen/systemz-abi-vector.c index 46c008a8d9f8e..f2e6c13c718f5 100644 --- a/clang/test/CodeGen/systemz-abi-vector.c +++ b/clang/test/CodeGen/systemz-abi-vector.c @@ -10,6 +10,8 @@ // RUN: -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-VECTOR %s // RUN: %clang_cc1 -triple s390x-linux-gnu -target-cpu arch12 \ // RUN: -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-VECTOR %s +// RUN: %clang_cc1 -triple s390x-linux-gnu -target-cpu z15 \ +// RUN: -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-VECTOR %s // RUN: %clang_cc1 -triple s390x-linux-gnu -target-cpu arch13 \ // RUN: -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-VECTOR %s diff --git a/clang/test/CodeGen/systemz-abi.c b/clang/test/CodeGen/systemz-abi.c index c04a51ff6ef44..3511983e32d76 100644 --- a/clang/test/CodeGen/systemz-abi.c +++ b/clang/test/CodeGen/systemz-abi.c @@ -10,6 +10,8 @@ // RUN: -emit-llvm -o - %s | FileCheck %s // RUN: %clang_cc1 -triple s390x-linux-gnu -target-cpu arch12 \ // RUN: -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple s390x-linux-gnu -target-cpu z15 \ +// RUN: -emit-llvm -o - %s | FileCheck %s // RUN: %clang_cc1 -triple s390x-linux-gnu -target-cpu arch13 \ // RUN: -emit-llvm -o - %s | FileCheck %s diff --git a/clang/test/CodeGen/target-builtin-error.c b/clang/test/CodeGen/target-builtin-error.c index ee41277706aee..4e44b7208a963 100644 --- a/clang/test/CodeGen/target-builtin-error.c +++ b/clang/test/CodeGen/target-builtin-error.c @@ -3,6 +3,6 @@ #include -__m128d foo(__m128d a, __m128d b) { +__m128 foo(__m128 a, __m128 b) { return __builtin_ia32_addsubps(b, a); // expected-error {{'__builtin_ia32_addsubps' needs target feature sse3}} } diff --git a/clang/test/CodeGen/target-data.c b/clang/test/CodeGen/target-data.c index 3e4d05436327d..21a8b415dc217 100644 --- a/clang/test/CodeGen/target-data.c +++ b/clang/test/CodeGen/target-data.c @@ -199,6 +199,8 @@ // RUN: FileCheck %s -check-prefix=SYSTEMZ-VECTOR // RUN: %clang_cc1 -triple s390x-unknown -target-cpu arch12 -o - -emit-llvm %s | \ // RUN: FileCheck %s -check-prefix=SYSTEMZ-VECTOR +// RUN: %clang_cc1 -triple s390x-unknown -target-cpu z15 -o - -emit-llvm %s | \ +// RUN: FileCheck %s -check-prefix=SYSTEMZ-VECTOR // RUN: %clang_cc1 -triple s390x-unknown -target-cpu arch13 -o - -emit-llvm %s | \ // RUN: FileCheck %s -check-prefix=SYSTEMZ-VECTOR // SYSTEMZ-VECTOR: target datalayout = "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-v128:64-a:8:16-n32:64" diff --git a/clang/test/CodeGen/tbaa-struct.cpp b/clang/test/CodeGen/tbaa-struct.cpp index f9eb71962f56b..0c0e4b40bfffb 100644 --- a/clang/test/CodeGen/tbaa-struct.cpp +++ b/clang/test/CodeGen/tbaa-struct.cpp @@ -17,7 +17,7 @@ typedef A __attribute__((may_alias)) AA; void copy(A *a1, A *a2) { // CHECK-LABEL: _Z4copyP1AS0_ -// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 dereferenceable(16) %{{.*}}, i8* align 4 dereferenceable(16) %{{.*}}, i64 16, i1 false) +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 4 dereferenceable(16) %{{.*}}, i8* nonnull align 4 dereferenceable(16) %{{.*}}, i64 16, i1 false) // CHECK-OLD-SAME: !tbaa.struct [[TS:!.*]] // CHECK-NEW-SAME: !tbaa [[TAG_A:![0-9]*]] *a1 = *a2; @@ -31,7 +31,7 @@ struct B { void copy2(B *b1, B *b2) { // CHECK-LABEL: _Z5copy2P1BS0_ -// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 dereferenceable(24) %{{.*}}, i8* align 4 dereferenceable(24) %{{.*}}, i64 24, i1 false) +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 4 dereferenceable(24) %{{.*}}, i8* nonnull align 4 dereferenceable(24) %{{.*}}, i64 24, i1 false) // CHECK-OLD-SAME: !tbaa.struct [[TS2:!.*]] // CHECK-NEW-SAME: !tbaa [[TAG_B:![0-9]*]] *b1 = *b2; @@ -49,7 +49,7 @@ union U { void copy3(U *u1, U *u2) { // CHECK-LABEL: _Z5copy3P1US0_ -// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 dereferenceable(12) %{{.*}}, i8* align 4 dereferenceable(12) %{{.*}}, i64 12, i1 false) +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 4 dereferenceable(12) %{{.*}}, i8* nonnull align 4 dereferenceable(12) %{{.*}}, i64 12, i1 false) // CHECK-OLD-SAME: !tbaa.struct [[TS3:!.*]] // CHECK-NEW-SAME: !tbaa [[TAG_U:![0-9]*]] *u1 = *u2; @@ -65,7 +65,7 @@ struct C { void copy4(C *c1, C *c2) { // CHECK-LABEL: _Z5copy4P1CS0_ -// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 dereferenceable(3) {{.*}}, i8* align 1 dereferenceable(3) {{.*}}, i64 3, i1 false) +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 1 dereferenceable(3) {{.*}}, i8* nonnull align 1 dereferenceable(3) {{.*}}, i64 3, i1 false) // CHECK-OLD-SAME: !tbaa.struct [[TS4:!.*]] // CHECK-NEW-SAME: !tbaa [[TAG_C:![0-9]*]] *c1 = *c2; @@ -80,7 +80,7 @@ struct D { void copy5(D *d1, D *d2) { // CHECK-LABEL: _Z5copy5P1DS0_ -// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 dereferenceable(6) {{.*}}, i8* align 1 dereferenceable(6) {{.*}}, i64 6, i1 false) +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 1 dereferenceable(6) {{.*}}, i8* nonnull align 1 dereferenceable(6) {{.*}}, i64 6, i1 false) // CHECK-OLD-SAME: !tbaa.struct [[TS5:!.*]] // CHECK-NEW-SAME: !tbaa [[TAG_D:![0-9]*]] *d1 = *d2; @@ -88,7 +88,7 @@ void copy5(D *d1, D *d2) { void copy6(AA *a1, A *a2) { // CHECK-LABEL: _Z5copy6P1AS0_ -// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 dereferenceable(16) %{{.*}}, i8* align 4 dereferenceable(16) %{{.*}}, i64 16, i1 false) +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 4 dereferenceable(16) %{{.*}}, i8* nonnull align 4 dereferenceable(16) %{{.*}}, i64 16, i1 false) // CHECK-OLD-SAME: !tbaa.struct [[TS]] // CHECK-NEW-SAME: !tbaa [[TAG_char:![0-9]*]] *a1 = *a2; @@ -96,7 +96,7 @@ void copy6(AA *a1, A *a2) { void copy7(A *a1, AA *a2) { // CHECK-LABEL: _Z5copy7P1AS0_ -// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 dereferenceable(16) %{{.*}}, i8* align 4 dereferenceable(16) %{{.*}}, i64 16, i1 false) +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 4 dereferenceable(16) %{{.*}}, i8* nonnull align 4 dereferenceable(16) %{{.*}}, i64 16, i1 false) // CHECK-OLD-SAME: !tbaa.struct [[TS]] // CHECK-NEW-SAME: !tbaa [[TAG_char]] *a1 = *a2; diff --git a/clang/test/CodeGen/x86-builtins-vector-width.c b/clang/test/CodeGen/x86-builtins-vector-width.c index 62f5929e4fda9..dca3e5874ea9c 100644 --- a/clang/test/CodeGen/x86-builtins-vector-width.c +++ b/clang/test/CodeGen/x86-builtins-vector-width.c @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -triple i686-linux-gnu -target-cpu i686 -emit-llvm %s -o - | FileCheck %s -typedef signed long long V2LLi __attribute__((vector_size(16))); -typedef signed long long V4LLi __attribute__((vector_size(32))); +typedef double V2LLi __attribute__((vector_size(16))); +typedef double V4LLi __attribute__((vector_size(32))); // Make sure builtin forces a min-legal-width attribute void foo(void) { diff --git a/clang/test/CodeGen/x86-builtins.c b/clang/test/CodeGen/x86-builtins.c new file mode 100644 index 0000000000000..fa2530b2cd15f --- /dev/null +++ b/clang/test/CodeGen/x86-builtins.c @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -ffreestanding %s -triple=x86_64-unknown-unknown -emit-llvm -o - -Wall -Werror | FileCheck %s -check-prefix=CHECK-64 +// RUN: %clang_cc1 -ffreestanding %s -triple=i386-unknown-unknown -emit-llvm -o - -Wall -Werror | FileCheck %s -check-prefix=CHECK-32 + +#include + +unsigned int test_castf32_u32 (float __A){ + // CHECK-64-LABEL: @test_castf32_u32 + // CHECK-64: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %{{.*}}, i8* align 4 %{{.*}}, i64 4, i1 false) + // CHECK-64: %{{.*}} = load i32, i32* %{{.*}}, align 4 + // CHECK-32-LABEL: @test_castf32_u32 + // CHECK-32: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %{{.*}}, i8* align 4 %{{.*}}, i32 4, i1 false) + // CHECK-32: %{{.*}} = load i32, i32* %{{.*}}, align 4 + return _castf32_u32(__A); +} + +unsigned long long test_castf64_u64 (double __A){ + // CHECK-64-LABEL: @test_castf64_u64 + // CHECK-64: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %{{.*}}, i8* align 8 %{{.*}}, i64 8, i1 false) + // CHECK-64: %{{.*}} = load i64, i64* %{{.*}}, align 8 + // CHECK-32-LABEL: @test_castf64_u64 + // CHECK-32: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %{{.*}}, i8* align 8 %{{.*}}, i32 8, i1 false) + // CHECK-32: %{{.*}} = load i64, i64* %{{.*}}, align 8 + return _castf64_u64(__A); +} + +float test_castu32_f32 (unsigned int __A){ + // CHECK-64-LABEL: @test_castu32_f32 + // CHECK-64: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %{{.*}}, i8* align 4 %{{.*}}, i64 4, i1 false) + // CHECK-64: %{{.*}} = load float, float* %{{.*}}, align 4 + // CHECK-32-LABEL: @test_castu32_f32 + // CHECK-32: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %{{.*}}, i8* align 4 %{{.*}}, i32 4, i1 false) + // CHECK-32: %{{.*}} = load float, float* %{{.*}}, align 4 + return _castu32_f32(__A); +} + +double test_castu64_f64 (unsigned long long __A){ + // CHECK-64-LABEL: @test_castu64_f64 + // CHECK-64: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %{{.*}}, i8* align 8 %{{.*}}, i64 8, i1 false) + // CHECK-64: %{{.*}} = load double, double* %{{.*}}, align 8 + // CHECK-32-LABEL: @test_castu64_f64 + // CHECK-32: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %{{.*}}, i8* align 8 %{{.*}}, i32 8, i1 false) + // CHECK-32: %{{.*}} = load double, double* %{{.*}}, align 8 + return _castu64_f64(__A); +} + diff --git a/clang/test/CodeGen/x86_64-mno-sse2.c b/clang/test/CodeGen/x86_64-mno-sse2.c new file mode 100644 index 0000000000000..0c8e78edb9951 --- /dev/null +++ b/clang/test/CodeGen/x86_64-mno-sse2.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -triple x86_64-linux -target-feature -sse2 -S -o /dev/null -verify %s +// REQUIRES: x86-registered-target + +double f1(void) { // expected-error {{SSE2 register return with SSE2 disabled}} + return 1.4; +} +extern double g; +void f2(void) { // expected-error {{SSE2 register return with SSE2 disabled}} + g = f1(); +} +void take_double(double); +void pass_double(void) { + // FIXME: Still asserts. + //take_double(1.5); +} + +double return_double(); +void call_double(double *a) { // expected-error {{SSE2 register return with SSE2 disabled}} + *a = return_double(); +} diff --git a/clang/test/CodeGenCUDA/Inputs/cuda.h b/clang/test/CodeGenCUDA/Inputs/cuda.h index 0fd175765a205..5d73b81041ab5 100644 --- a/clang/test/CodeGenCUDA/Inputs/cuda.h +++ b/clang/test/CodeGenCUDA/Inputs/cuda.h @@ -14,12 +14,21 @@ struct dim3 { __host__ __device__ dim3(unsigned x, unsigned y = 1, unsigned z = 1) : x(x), y(y), z(z) {} }; -typedef struct cudaStream *cudaStream_t; -typedef enum cudaError {} cudaError_t; #ifdef __HIP__ +typedef struct hipStream *hipStream_t; +typedef enum hipError {} hipError_t; int hipConfigureCall(dim3 gridSize, dim3 blockSize, size_t sharedSize = 0, - cudaStream_t stream = 0); + hipStream_t stream = 0); +extern "C" hipError_t __hipPushCallConfiguration(dim3 gridSize, dim3 blockSize, + size_t sharedSize = 0, + hipStream_t stream = 0); +extern "C" hipError_t hipLaunchKernel(const void *func, dim3 gridDim, + dim3 blockDim, void **args, + size_t sharedMem, + hipStream_t stream); #else +typedef struct cudaStream *cudaStream_t; +typedef enum cudaError {} cudaError_t; extern "C" int cudaConfigureCall(dim3 gridSize, dim3 blockSize, size_t sharedSize = 0, cudaStream_t stream = 0); diff --git a/clang/test/CodeGenCUDA/kernel-call.cu b/clang/test/CodeGenCUDA/kernel-call.cu index ed48a6cc81384..b76f2c1883576 100644 --- a/clang/test/CodeGenCUDA/kernel-call.cu +++ b/clang/test/CodeGenCUDA/kernel-call.cu @@ -3,14 +3,17 @@ // RUN: %clang_cc1 -target-sdk-version=9.2 -emit-llvm %s -o - \ // RUN: | FileCheck %s --check-prefixes=CUDA-NEW,CHECK // RUN: %clang_cc1 -x hip -emit-llvm %s -o - \ -// RUN: | FileCheck %s --check-prefixes=HIP,CHECK - +// RUN: | FileCheck %s --check-prefixes=HIP-OLD,CHECK +// RUN: %clang_cc1 -fhip-new-launch-api -x hip -emit-llvm %s -o - \ +// RUN: | FileCheck %s --check-prefixes=HIP-NEW,CHECK #include "Inputs/cuda.h" // CHECK-LABEL: define{{.*}}g1 -// HIP: call{{.*}}hipSetupArgument -// HIP: call{{.*}}hipLaunchByPtr +// HIP-OLD: call{{.*}}hipSetupArgument +// HIP-OLD: call{{.*}}hipLaunchByPtr +// HIP-NEW: call{{.*}}__hipPopCallConfiguration +// HIP-NEW: call{{.*}}hipLaunchKernel // CUDA-OLD: call{{.*}}cudaSetupArgument // CUDA-OLD: call{{.*}}cudaLaunch // CUDA-NEW: call{{.*}}__cudaPopCallConfiguration @@ -19,7 +22,8 @@ __global__ void g1(int x) {} // CHECK-LABEL: define{{.*}}main int main(void) { - // HIP: call{{.*}}hipConfigureCall + // HIP-OLD: call{{.*}}hipConfigureCall + // HIP-NEW: call{{.*}}__hipPushCallConfiguration // CUDA-OLD: call{{.*}}cudaConfigureCall // CUDA-NEW: call{{.*}}__cudaPushCallConfiguration // CHECK: icmp diff --git a/clang/test/CodeGenCXX/attr-no-destroy-d54344.cpp b/clang/test/CodeGenCXX/attr-no-destroy-d54344.cpp index 2e004d6426d94..b03791e5135df 100644 --- a/clang/test/CodeGenCXX/attr-no-destroy-d54344.cpp +++ b/clang/test/CodeGenCXX/attr-no-destroy-d54344.cpp @@ -14,6 +14,7 @@ class a { public: + a(); ~a(); }; class logger_base { diff --git a/clang/test/CodeGenCXX/const-init-cxx2a.cpp b/clang/test/CodeGenCXX/const-init-cxx2a.cpp new file mode 100644 index 0000000000000..1195b912c2556 --- /dev/null +++ b/clang/test/CodeGenCXX/const-init-cxx2a.cpp @@ -0,0 +1,58 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s -std=c++2a | FileCheck %s --implicit-check-not=cxx_global_var_init --implicit-check-not=cxa_atexit + +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-pch -o %t.pch %s -std=c++2a +// RUN: %clang_cc1 -triple x86_64-linux-gnu -include-pch %t.pch -x c++ /dev/null -emit-llvm -o - -std=c++2a | FileCheck %s --implicit-check-not=cxx_global_var_init --implicit-check-not=cxa_atexit + +// CHECK: @a = global i32 123, +int a = (delete new int, 123); + +struct B { + constexpr B() {} + constexpr ~B() { n *= 5; } + int n = 123; +}; +// CHECK: @b = global {{.*}} i32 123 +extern constexpr B b = B(); + +// CHECK: @_ZL1c = internal global {{.*}} i32 123 +const B c; +int use_c() { return c.n; } + +struct D { + int n; + constexpr ~D() {} +}; +D d; +// CHECK: @d = global {{.*}} zeroinitializer + +D d_arr[3]; +// CHECK: @d_arr = global {{.*}} zeroinitializer + +thread_local D d_tl; +// CHECK: @d_tl = thread_local global {{.*}} zeroinitializer + +// CHECK-NOT: @llvm.global_ctors + +// CHECK-LABEL: define {{.*}} @_Z1fv( +void f() { + // CHECK-NOT: call + // CHECK: call {{.*}}memcpy + // CHECK-NOT: call + // CHECK: call {{.*}}memset + // CHECK-NOT: call + // CHECK: } + constexpr B b; + D d = D(); +} + +// CHECK-LABEL: define {{.*}} @_Z1gv( +void g() { + // CHECK-NOT: call + // CHECK-NOT: cxa_guard + // CHECK-NOT: _ZGV + // CHECK: } + static constexpr B b1; + static const B b2; + static D d; + thread_local D d_tl; +} diff --git a/clang/test/CodeGenCXX/cxx2a-thread-local-constinit.cpp b/clang/test/CodeGenCXX/cxx2a-thread-local-constinit.cpp index 149d5b99d4628..af46a92c7dc2d 100644 --- a/clang/test/CodeGenCXX/cxx2a-thread-local-constinit.cpp +++ b/clang/test/CodeGenCXX/cxx2a-thread-local-constinit.cpp @@ -33,11 +33,6 @@ extern thread_local int c; // CHECK: } int h() { return c; } -thread_local int c = 0; - -int d_init(); -thread_local int d = d_init(); - // Note: use of 'c' does not trigger initialization of 'd', because 'c' has a // constant initializer. // CHECK-LABEL: define weak_odr {{.*}} @_ZTW1c() @@ -45,3 +40,30 @@ thread_local int d = d_init(); // CHECK-NOT: call // CHECK: ret i32* @c // CHECK: } + +thread_local int c = 0; + +int d_init(); + +// CHECK: define {{.*}}[[D_INIT:@__cxx_global_var_init[^(]*]]( +// CHECK: call {{.*}} @_Z6d_initv() +thread_local int d = d_init(); + +struct Destructed { + int n; + ~Destructed(); +}; + +extern thread_local constinit Destructed e; +// CHECK-LABEL: define i32 @_Z1iv() +// CHECK: call {{.*}}* @_ZTW1e() +// CHECK: } +int i() { return e.n; } + +// CHECK: define {{.*}}[[E2_INIT:@__cxx_global_var_init[^(]*]]( +// CHECK: call {{.*}} @__cxa_thread_atexit({{.*}} @_ZN10DestructedD1Ev {{.*}} @e2 +thread_local constinit Destructed e2; + +// CHECK-LABEL: define {{.*}}__tls_init +// CHECK: call {{.*}} [[D_INIT]] +// CHECK: call {{.*}} [[E2_INIT]] diff --git a/clang/test/CodeGenCXX/ms-constexpr-var-template.cpp b/clang/test/CodeGenCXX/ms-constexpr-var-template.cpp deleted file mode 100644 index a40f7aacac03b..0000000000000 --- a/clang/test/CodeGenCXX/ms-constexpr-var-template.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// RUN: %clang_cc1 -emit-llvm -triple=x86_64-windows-msvc -fms-compatibility %s -o - | FileCheck %s - -template constexpr bool _Is_integer = false; -template <> constexpr bool _Is_integer = true; -template <> constexpr bool _Is_integer = false; -extern "C" const bool *escape = &_Is_integer; - -// CHECK: @"??$_Is_integer@H@@3_NB" = linkonce_odr dso_local constant i8 1, comdat, align 1 -// Should not emit _Is_integer, since it's not referenced. -// CHECK-NOT: @"??$_Is_integer@D@@3_NB" -// CHECK: @escape = dso_local global i8* @"??$_Is_integer@H@@3_NB", align 8 diff --git a/clang/test/CodeGenCXX/no_destroy.cpp b/clang/test/CodeGenCXX/no_destroy.cpp index 3400b6080b53f..607cbfb3a1fca 100644 --- a/clang/test/CodeGenCXX/no_destroy.cpp +++ b/clang/test/CodeGenCXX/no_destroy.cpp @@ -5,10 +5,8 @@ struct NonTrivial { ~NonTrivial(); }; -// CHECK-LABEL: define internal void @__cxx_global_var_init // CHECK-NOT: __cxa_atexit{{.*}}_ZN10NonTrivialD1Ev [[clang::no_destroy]] NonTrivial nt1; -// CHECK-LABEL: define internal void @__cxx_global_var_init // CHECK-NOT: _tlv_atexit{{.*}}_ZN10NonTrivialD1Ev [[clang::no_destroy]] thread_local NonTrivial nt2; @@ -16,11 +14,9 @@ struct NonTrivial2 { ~NonTrivial2(); }; -// CHECK-LABEL: define internal void @__cxx_global_var_init -// CHECK: __cxa_atexit{{.*}}_ZN11NonTrivial2D1Ev +// CHECK: __cxa_atexit{{.*}}_ZN11NonTrivial2D1Ev{{.*}}nt21 NonTrivial2 nt21; -// CHECK-LABEL: define internal void @__cxx_global_var_init -// CHECK: _tlv_atexit{{.*}}_ZN11NonTrivial2D1Ev +// CHECK: _tlv_atexit{{.*}}_ZN11NonTrivial2D1Ev{{.*}}nt22 thread_local NonTrivial2 nt22; // CHECK-LABEL: define void @_Z1fv diff --git a/clang/test/CodeGenCXX/non-const-init-cxx2a.cpp b/clang/test/CodeGenCXX/non-const-init-cxx2a.cpp new file mode 100644 index 0000000000000..120b32090fb4a --- /dev/null +++ b/clang/test/CodeGenCXX/non-const-init-cxx2a.cpp @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s -std=c++2a | FileCheck %s + +// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-pch -o %t.pch %s -std=c++2a +// RUN: %clang_cc1 -triple x86_64-apple-darwin -include-pch %t.pch -x c++ /dev/null -emit-llvm -o - -std=c++2a | FileCheck %s + +struct B { + constexpr B() {} + constexpr ~B() { n *= 5; } + int n = 123; +}; + +// We emit a dynamic destructor here because b.n might have been modified +// before b is destroyed. +// +// CHECK: @b = global {{.*}} i32 123 +B b = B(); + +// CHECK: define {{.*}}cxx_global_var_init +// CHECK: call {{.*}} @__cxa_atexit({{.*}} @_ZN1BD1Ev {{.*}} @b diff --git a/clang/test/CodeGenCXX/wasm-args-returns.cpp b/clang/test/CodeGenCXX/wasm-args-returns.cpp index 50ebdb96848f5..5718223f9f740 100644 --- a/clang/test/CodeGenCXX/wasm-args-returns.cpp +++ b/clang/test/CodeGenCXX/wasm-args-returns.cpp @@ -46,7 +46,7 @@ struct copy_ctor { copy_ctor(copy_ctor const &); }; test(copy_ctor); -// CHECK: define void @_Z7forward9copy_ctor(%struct.copy_ctor* noalias sret %{{.*}}, %struct.copy_ctor* %{{.*}}) +// CHECK: define void @_Z7forward9copy_ctor(%struct.copy_ctor* noalias sret %{{.*}}, %struct.copy_ctor* nonnull %{{.*}}) // // CHECK: declare %struct.copy_ctor* @_ZN9copy_ctorC1ERKS_(%struct.copy_ctor* returned, %struct.copy_ctor* dereferenceable(8)) // @@ -64,7 +64,7 @@ struct __attribute__((aligned(16))) aligned_copy_ctor { aligned_copy_ctor(aligned_copy_ctor const &); }; test(aligned_copy_ctor); -// CHECK: define void @_Z7forward17aligned_copy_ctor(%struct.aligned_copy_ctor* noalias sret %{{.*}}, %struct.aligned_copy_ctor* %{{.*}}) +// CHECK: define void @_Z7forward17aligned_copy_ctor(%struct.aligned_copy_ctor* noalias sret %{{.*}}, %struct.aligned_copy_ctor* nonnull %{{.*}}) // // CHECK: declare %struct.aligned_copy_ctor* @_ZN17aligned_copy_ctorC1ERKS_(%struct.aligned_copy_ctor* returned, %struct.aligned_copy_ctor* dereferenceable(16)) // diff --git a/clang/test/CodeGenObjC/protocol-comdat.m b/clang/test/CodeGenObjC/protocol-comdat.m index 1402ad0a045a1..79a1d5535576b 100644 --- a/clang/test/CodeGenObjC/protocol-comdat.m +++ b/clang/test/CodeGenObjC/protocol-comdat.m @@ -1,4 +1,4 @@ -// RUN: %clang -cc1 -triple thumbv7--windows-itanium -fobjc-runtime=ios -emit-llvm -o - %s -Wno-objc-root-class | FileCheck %s +// RUN: %clang_cc1 -triple thumbv7--windows-itanium -fobjc-runtime=ios -emit-llvm -o - %s -Wno-objc-root-class | FileCheck %s @protocol P - (void) method; diff --git a/clang/test/Driver/arm-mfpu.c b/clang/test/Driver/arm-mfpu.c index 4b0289b73b93a..9a87b63856794 100644 --- a/clang/test/Driver/arm-mfpu.c +++ b/clang/test/Driver/arm-mfpu.c @@ -35,7 +35,7 @@ // CHECK-SOFT-ABI-FP-2-DAG: "-target-feature" "-fp-armv8d16sp" // CHECK-SOFT-ABI-FP-2-DAG: "-target-feature" "-neon" // CHECK-SOFT-ABI-FP-2-DAG: "-target-feature" "-crypto" -// CHECK-SOFT-ABI-FP-2-DAG: "-target-feature" "-vfp2d16sp" +// CHECK-SOFT-ABI-FP-2-DAG: "-target-feature" "-vfp2sp" // RUN: %clang -target arm-linux-eabi -mfpu=vfp3 %s -### -o %t.o 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-VFP3 %s @@ -50,7 +50,7 @@ // CHECK-VFP3-DAG: "-target-feature" "-fp-armv8d16sp" // CHECK-VFP3-DAG: "-target-feature" "-neon" // CHECK-SOFT-ABI-FP-3-DAG: "-target-feature" "+soft-float-abi" -// CHECK-SOFT-ABI-FP-3-DAG: "-target-feature" "-vfp2d16sp" +// CHECK-SOFT-ABI-FP-3-DAG: "-target-feature" "-vfp2sp" // CHECK-SOFT-ABI-FP-3-DAG: "-target-feature" "-vfp4d16sp" // CHECK-SOFT-ABI-FP-3-DAG: "-target-feature" "-fp-armv8d16sp" // CHECK-SOFT-ABI-FP-3-DAG: "-target-feature" "-neon" @@ -144,7 +144,7 @@ // CHECK-VFP4-DAG: "-target-feature" "-fp-armv8d16sp" // CHECK-VFP4-DAG: "-target-feature" "-neon" // CHECK-SOFT-ABI-FP-4-DAG: "-target-feature" "+soft-float-abi" -// CHECK-SOFT-ABI-FP-4-DAG: "-target-feature" "-vfp2d16sp" +// CHECK-SOFT-ABI-FP-4-DAG: "-target-feature" "-vfp2sp" // CHECK-SOFT-ABI-FP-4-DAG: "-target-feature" "-vfp3d16sp" // CHECK-SOFT-ABI-FP-4-DAG: "-target-feature" "-fp-armv8d16sp" // CHECK-SOFT-ABI-FP-4-DAG: "-target-feature" "-neon" @@ -208,7 +208,7 @@ // CHECK-FP5-DP-D16-DAG: "-target-feature" "-crypto" // CHECK-SOFT-ABI-FP-5-DAG: "-target-feature" "+soft-float" // CHECK-SOFT-ABI-FP-5-DAG: "-target-feature" "+soft-float-abi" -// CHECK-SOFT-ABI-FP-5-DAG: "-target-feature" "-vfp2d16sp" +// CHECK-SOFT-ABI-FP-5-DAG: "-target-feature" "-vfp2sp" // CHECK-SOFT-ABI-FP-5-DAG: "-target-feature" "-vfp3d16sp" // CHECK-SOFT-ABI-FP-5-DAG: "-target-feature" "-vfp4d16sp" // CHECK-SOFT-ABI-FP-5-DAG: "-target-feature" "-neon" @@ -222,7 +222,7 @@ // CHECK-NEON-NOT: "-target-feature" "+soft-float" // CHECK-NEON-DAG: "-target-feature" "+neon" // CHECK-SOFT-ABI-FP-6-DAG: "-target-feature" "+soft-float-abi" -// CHECK-SOFT-ABI-FP-6-DAG: "-target-feature" "-vfp2d16sp" +// CHECK-SOFT-ABI-FP-6-DAG: "-target-feature" "-vfp2sp" // CHECK-SOFT-ABI-FP-6-DAG: "-target-feature" "-vfp4d16sp" // CHECK-SOFT-ABI-FP-6-DAG: "-target-feature" "-fp-armv8d16sp" // CHECK-SOFT-ABI-FP-6-DAG: "-target-feature" "-crypto" @@ -262,7 +262,7 @@ // CHECK-NEON-VFPV4-DAG: "-target-feature" "+vfp4" // CHECK-NEON-VFPV4-DAG: "-target-feature" "+neon" // CHECK-SOFT-ABI-FP-7-DAG: "-target-feature" "+soft-float-abi" -// CHECK-SOFT-ABI-FP-7-DAG: "-target-feature" "-vfp2d16sp" +// CHECK-SOFT-ABI-FP-7-DAG: "-target-feature" "-vfp2sp" // CHECK-SOFT-ABI-FP-7-DAG: "-target-feature" "-vfp3d16sp" // CHECK-SOFT-ABI-FP-7-DAG: "-target-feature" "-fp-armv8d16sp" // CHECK-SOFT-ABI-FP-7-DAG: "-target-feature" "-crypto" @@ -276,7 +276,7 @@ // RUN: %clang -target armv8a -mfpu=neon %s -### -c 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-SOFT-ABI-FP-8 %s // CHECK-SOFT-ABI-FP-8-DAG: "-target-feature" "+soft-float-abi" -// CHECK-SOFT-ABI-FP-8-DAG: "-target-feature" "-vfp2d16sp" +// CHECK-SOFT-ABI-FP-8-DAG: "-target-feature" "-vfp2sp" // CHECK-SOFT-ABI-FP-8-DAG: "-target-feature" "-vfp4d16sp" // CHECK-SOFT-ABI-FP-8-DAG: "-target-feature" "-fp-armv8d16sp" // CHECK-SOFT-ABI-FP-8-DAG: "-target-feature" "-crypto" @@ -319,7 +319,7 @@ // CHECK-NO-FP-NOT: "-target-feature" "+soft-float" // CHECK-NO-FP-DAG: "-target-feature" "+soft-float-abi" // CHECK-NO-FP-DAG: "-target-feature" "-fpregs" -// CHECK-NO-FP-DAG: "-target-feature" "-vfp2d16sp" +// CHECK-NO-FP-DAG: "-target-feature" "-vfp2sp" // CHECK-NO-FP-DAG: "-target-feature" "-vfp3d16sp" // CHECK-NO-FP-DAG: "-target-feature" "-vfp4d16sp" // CHECK-NO-FP-DAG: "-target-feature" "-fp-armv8d16sp" @@ -358,7 +358,7 @@ // RUN: | FileCheck --check-prefix=CHECK-SOFT-ABI-FP %s // CHECK-SOFT-ABI-FP-DAG: "-target-feature" "+soft-float" // CHECK-SOFT-ABI-FP-DAG: "-target-feature" "+soft-float-abi" -// CHECK-SOFT-ABI-FP-DAG: "-target-feature" "-vfp2d16sp" +// CHECK-SOFT-ABI-FP-DAG: "-target-feature" "-vfp2sp" // CHECK-SOFT-ABI-FP-DAG: "-target-feature" "-vfp3d16sp" // CHECK-SOFT-ABI-FP-DAG: "-target-feature" "-vfp4d16sp" // CHECK-SOFT-ABI-FP-DAG: "-target-feature" "-fp-armv8d16sp" diff --git a/clang/test/Driver/compilation_database.c b/clang/test/Driver/compilation_database.c index 017178d60cde5..343b76aa44f3a 100644 --- a/clang/test/Driver/compilation_database.c +++ b/clang/test/Driver/compilation_database.c @@ -1,8 +1,8 @@ -// RUN: mkdir -p %t && cd %t +// RUN: mkdir -p %t.workdir && cd %t.workdir // RUN: %clang -MD -MP --sysroot=somewhere -c -x c %s -xc++ %s -Wall -MJ - -no-canonical-prefixes 2>&1 | FileCheck %s // RUN: not %clang -c -x c %s -MJ %s/non-existant -no-canonical-prefixes 2>&1 | FileCheck --check-prefix=ERROR %s -// CHECK: { "directory": "{{.*}}", "file": "[[SRC:[^"]+[/|\\]compilation_database.c]]", "output": "compilation_database.o", "arguments": ["{{[^"]*}}clang{{[^"]*}}", "-xc", "[[SRC]]", "--sysroot=somewhere", "-c", "-Wall",{{.*}} "--target={{[^"]+}}"]}, +// CHECK: { "directory": "{{[^"]*}}workdir", "file": "[[SRC:[^"]+[/|\\]compilation_database.c]]", "output": "compilation_database.o", "arguments": ["{{[^"]*}}clang{{[^"]*}}", "-xc", "[[SRC]]", "--sysroot=somewhere", "-c", "-Wall",{{.*}} "--target={{[^"]+}}"]}, // CHECK: { "directory": "{{.*}}", "file": "[[SRC:[^"]+[/|\\]compilation_database.c]]", "output": "compilation_database.o", "arguments": ["{{[^"]*}}clang{{[^"]*}}", "-xc++", "[[SRC]]", "--sysroot=somewhere", "-c", "-Wall",{{.*}} "--target={{[^"]+}}"]}, // ERROR: error: compilation database '{{.*}}/non-existant' could not be opened: diff --git a/clang/test/Driver/coverage_no_integrated_as.c b/clang/test/Driver/coverage_no_integrated_as.c index d7689e591ff00..fc9af6bd9458b 100644 --- a/clang/test/Driver/coverage_no_integrated_as.c +++ b/clang/test/Driver/coverage_no_integrated_as.c @@ -1,21 +1,26 @@ // REQUIRES: clang-driver -// XFAIL: windows-msvc + +// RUN: %clang -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-NO-COV %s // RUN: %clang -### -S -fprofile-arcs %s 2>&1 | FileCheck -check-prefix=CHECK-GCNO-DEFAULT-LOCATION %s -// RUN: %clang -### -S -fprofile-arcs -no-integrated-as %s 2>&1 | FileCheck -check-prefix=CHECK-GCNO-DEFAULT-LOCATION %s +// RUN: %clang -### -S -fprofile-arcs --target=%itanium_abi_triple -no-integrated-as %s 2>&1 | FileCheck -check-prefix=CHECK-GCNO-DEFAULT-LOCATION %s // RUN: %clang -### -c -fprofile-arcs %s 2>&1 | FileCheck -check-prefix=CHECK-GCNO-DEFAULT-LOCATION %s -// RUN: %clang -### -c -fprofile-arcs -no-integrated-as %s 2>&1 | FileCheck -check-prefix=CHECK-GCNO-DEFAULT-LOCATION %s +// RUN: %clang -### -c -fprofile-arcs --target=%itanium_abi_triple -no-integrated-as %s 2>&1 | FileCheck -check-prefix=CHECK-GCNO-DEFAULT-LOCATION %s // RUN: %clang -### -S -fprofile-arcs %s -o /foo/bar.o 2>&1 | FileCheck -check-prefix=CHECK-GCNO-LOCATION %s -// RUN: %clang -### -S -fprofile-arcs -no-integrated-as %s -o /foo/bar.o 2>&1 | FileCheck -check-prefix=CHECK-GCNO-LOCATION %s +// RUN: %clang -### -S -fprofile-arcs --target=%itanium_abi_triple -no-integrated-as %s -o /foo/bar.o 2>&1 | FileCheck -check-prefix=CHECK-GCNO-LOCATION %s // RUN: %clang -### -c -fprofile-arcs %s -o /foo/bar.o 2>&1 | FileCheck -check-prefix=CHECK-GCNO-LOCATION %s -// RUN: %clang -### -c -fprofile-arcs -no-integrated-as %s -o /foo/bar.o 2>&1 | FileCheck -check-prefix=CHECK-GCNO-LOCATION %s +// RUN: %clang -### -c -fprofile-arcs --target=%itanium_abi_triple -no-integrated-as %s -o /foo/bar.o 2>&1 | FileCheck -check-prefix=CHECK-GCNO-LOCATION %s // RUN: %clang -### -S -fprofile-arcs %s -o foo/bar.o 2>&1 | FileCheck -check-prefix=CHECK-GCNO-LOCATION-REL-PATH %s -// RUN: %clang -### -S -fprofile-arcs -no-integrated-as %s -o foo/bar.o 2>&1 | FileCheck -check-prefix=CHECK-GCNO-LOCATION-REL-PATH %s +// RUN: %clang -### -S -fprofile-arcs --target=%itanium_abi_triple -no-integrated-as %s -o foo/bar.o 2>&1 | FileCheck -check-prefix=CHECK-GCNO-LOCATION-REL-PATH %s // RUN: %clang -### -c -fprofile-arcs %s -o foo/bar.o 2>&1 | FileCheck -check-prefix=CHECK-GCNO-LOCATION-REL-PATH %s -// RUN: %clang -### -c -fprofile-arcs -no-integrated-as %s -o foo/bar.o 2>&1 | FileCheck -check-prefix=CHECK-GCNO-LOCATION-REL-PATH %s +// RUN: %clang -### -c -fprofile-arcs --target=%itanium_abi_triple -no-integrated-as %s -o foo/bar.o 2>&1 | FileCheck -check-prefix=CHECK-GCNO-LOCATION-REL-PATH %s +// These should only get passed if any of --coverage, -ftest-coverage, or +// -fprofile-arcs is passed. +// CHECK-NO-COV-NOT: "-coverage-notes-file" +// CHECK-NO-COV-NOT: "-coverage-data-file" // CHECK-GCNO-DEFAULT-LOCATION: "-coverage-notes-file" "{{.*}}{{/|\\\\}}coverage_no_integrated_as.c" // CHECK-GCNO-DEFAULT-LOCATION-NOT: "-coverage-notes-file" "/tmp/{{.*}}/coverage_no_integrated_as.c" diff --git a/clang/test/Driver/darwin-sdkroot.c b/clang/test/Driver/darwin-sdkroot.c index 8b3782ea22a71..700993fa96a66 100644 --- a/clang/test/Driver/darwin-sdkroot.c +++ b/clang/test/Driver/darwin-sdkroot.c @@ -2,7 +2,7 @@ // // RUN: rm -rf %t.tmpdir // RUN: mkdir -p %t.tmpdir -// RUN: env SDKROOT=%t.tmpdir %clang -target x86_64-apple-darwin10 \ +// RUN: env SDKROOT=%t.tmpdir %clang -target x86_64-apple-darwin10 --sysroot="" \ // RUN: -c %s -### 2> %t.log // RUN: FileCheck --check-prefix=CHECK-BASIC < %t.log %s // @@ -13,7 +13,7 @@ // Check that we don't use SDKROOT as the default if it is not a valid path. // // RUN: rm -rf %t.nonpath -// RUN: env SDKROOT=%t.nonpath %clang -target x86_64-apple-darwin10 \ +// RUN: env SDKROOT=%t.nonpath %clang -target x86_64-apple-darwin10 --sysroot="" \ // RUN: -c %s -### 2> %t.log // RUN: FileCheck --check-prefix=CHECK-NONPATH < %t.log %s // @@ -23,7 +23,7 @@ // Check that we don't use SDKROOT as the default if it is just "/" // -// RUN: env SDKROOT=/ %clang -target x86_64-apple-darwin10 \ +// RUN: env SDKROOT=/ %clang -target x86_64-apple-darwin10 --sysroot="" \ // RUN: -c %s -### 2> %t.log // RUN: FileCheck --check-prefix=CHECK-NONROOT < %t.log %s // @@ -43,7 +43,7 @@ // // RUN: rm -rf %t/SDKs/iPhoneOS8.0.0.sdk // RUN: mkdir -p %t/SDKs/iPhoneOS8.0.0.sdk -// RUN: env SDKROOT=%t/SDKs/iPhoneOS8.0.0.sdk %clang -target arm64-apple-darwin %s -### 2>&1 \ +// RUN: env SDKROOT=%t/SDKs/iPhoneOS8.0.0.sdk %clang -target arm64-apple-darwin --sysroot="" %s -### 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-IPHONE %s // // CHECK-IPHONE: clang @@ -55,7 +55,7 @@ // // RUN: rm -rf %t/SDKs/iPhoneSimulator8.0.sdk // RUN: mkdir -p %t/SDKs/iPhoneSimulator8.0.sdk -// RUN: env SDKROOT=%t/SDKs/iPhoneSimulator8.0.sdk %clang -target x86_64-apple-darwin %s -### 2>&1 \ +// RUN: env SDKROOT=%t/SDKs/iPhoneSimulator8.0.sdk %clang -target x86_64-apple-darwin --sysroot="" %s -### 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-SIMULATOR %s // // CHECK-SIMULATOR: clang @@ -66,7 +66,7 @@ // // RUN: rm -rf %t/SDKs/MacOSX10.10.0.sdk // RUN: mkdir -p %t/SDKs/MacOSX10.10.0.sdk -// RUN: env SDKROOT=%t/SDKs/MacOSX10.10.0.sdk %clang -target x86_64-apple-darwin %s -### 2>&1 \ +// RUN: env SDKROOT=%t/SDKs/MacOSX10.10.0.sdk %clang -target x86_64-apple-darwin --sysroot="" %s -### 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-MACOSX %s // // CHECK-MACOSX: clang diff --git a/clang/test/Driver/fuchsia.c b/clang/test/Driver/fuchsia.c index ec664fb3b5a8d..bf8e5a04dc91b 100644 --- a/clang/test/Driver/fuchsia.c +++ b/clang/test/Driver/fuchsia.c @@ -16,7 +16,7 @@ // CHECK: "-fsanitize=safe-stack" // CHECK: "-stack-protector" "2" // CHECK: "-fno-common" -// CHECK: {{.*}}ld.lld{{.*}}" "-z" "rodynamic" "-z" "separate-code" +// CHECK: {{.*}}ld.lld{{.*}}" "-z" "rodynamic" "-z" "separate-loadable-segments" // CHECK: "--sysroot=[[SYSROOT]]" // CHECK: "-pie" // CHECK: "--build-id" diff --git a/clang/test/Driver/gcc-toolchain.cpp b/clang/test/Driver/gcc-toolchain.cpp index ca96757a2bbcf..6c872f4255c34 100644 --- a/clang/test/Driver/gcc-toolchain.cpp +++ b/clang/test/Driver/gcc-toolchain.cpp @@ -3,12 +3,14 @@ // RUN: %clangxx -no-canonical-prefixes %s -### -o %t 2>&1 \ // RUN: --target=i386-unknown-linux -stdlib=libstdc++ \ // RUN: --gcc-toolchain=%S/Inputs/ubuntu_11.04_multiarch_tree/usr \ +// RUN: --sysroot="" \ // RUN: | FileCheck %s // // Additionally check that the legacy spelling of the flag works. // RUN: %clangxx -no-canonical-prefixes %s -### -o %t 2>&1 \ // RUN: --target=i386-unknown-linux -stdlib=libstdc++ \ // RUN: -gcc-toolchain %S/Inputs/ubuntu_11.04_multiarch_tree/usr \ +// RUN: --sysroot="" \ // RUN: | FileCheck %s // // Test for header search toolchain detection. diff --git a/clang/test/Driver/m-and-mm.c b/clang/test/Driver/m-and-mm.c new file mode 100644 index 0000000000000..cb719a6c97fe8 --- /dev/null +++ b/clang/test/Driver/m-and-mm.c @@ -0,0 +1,35 @@ +// RUN: %clang -M %s 2>&1 | FileCheck %s --implicit-check-not=warning +// RUN: %clang -MM %s 2>&1 | FileCheck %s --implicit-check-not=warning + +// CHECK: m-and-mm.o: +// TEST-I: {{.*}}test.i: +// TEST: {{.*}}test: + +// RUN: mkdir -p %t.dir + +/// if -MD and -MMD are not specified, -o specifies the dependency file name. +// RUN: rm -f %t.dir/test.i +// RUN: %clang -M %s -o %t.dir/test.i +// RUN: FileCheck %s < %t.dir/test.i +// RUN: rm -f %t.dir/test.i +// RUN: %clang -MM %s -o %t.dir/test.i +// RUN: FileCheck %s < %t.dir/test.i + +// RUN: rm -f %t.dir/test.d +// RUN: %clang -fsyntax-only -MD %s -o %t.dir/test.i +// RUN: FileCheck --check-prefix=TEST-I %s < %t.dir/test.d + +// RUN: rm -f %t.dir/test.d +// RUN: %clang -M -MD %s -o %t.dir/test.i +// RUN: FileCheck --check-prefix=TEST-I %s < %t.dir/test.d + +/// If the output file name does not have a suffix, just append `.d`. +// RUN: rm -f %t.dir/test.d +// RUN: %clang -fsyntax-only -MD %s -o %t.dir/test +// RUN: FileCheck --check-prefix=TEST %s < %t.dir/test.d + +#warning "-M and -MM suppresses warnings, thus this warning shouldn't show up" +int main(void) +{ + return 0; +} diff --git a/clang/test/Driver/m_and_mm.c b/clang/test/Driver/m_and_mm.c deleted file mode 100644 index 6e40c82cb16cf..0000000000000 --- a/clang/test/Driver/m_and_mm.c +++ /dev/null @@ -1,15 +0,0 @@ -// RUN: %clang -### \ -// RUN: -M -MM %s 2> %t -// RUN: not grep '"-sys-header-deps"' %t - -// RUN: %clang -M -MM %s 2> %t -// RUN: not grep "warning" %t - -// RUN: %clang -MMD -MD %s 2> %t || true -// RUN: grep "warning" %t - -#warning "This warning shouldn't show up with -M and -MM" -int main (void) -{ - return 0; -} diff --git a/clang/test/Driver/metadata-with-dots.c b/clang/test/Driver/metadata-with-dots.c deleted file mode 100644 index 371b48bd2cc6e..0000000000000 --- a/clang/test/Driver/metadata-with-dots.c +++ /dev/null @@ -1,11 +0,0 @@ -// REQUIRES: shell -// RUN: mkdir -p out.dir -// RUN: cat %s > out.dir/test.c -// RUN: %clang -E -MMD %s -o out.dir/test -// RUN: test ! -f %out.d -// RUN: test -f out.dir/test.d -// RUN: rm -rf out.dir/test.d out.dir/ out.d -int main (void) -{ - return 0; -} diff --git a/clang/test/Driver/mips-features.c b/clang/test/Driver/mips-features.c index 19725bc096b5d..5ae566774959f 100644 --- a/clang/test/Driver/mips-features.c +++ b/clang/test/Driver/mips-features.c @@ -268,13 +268,13 @@ // RUN: %clang -target mips-linux-gnu -### -c %s \ // RUN: -mno-xgot -mxgot 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-XGOT %s -// CHECK-XGOT: "-mllvm" "-mxgot" +// CHECK-XGOT: "-target-feature" "+xgot" // // -mno-xgot // RUN: %clang -target mips-linux-gnu -### -c %s \ // RUN: -mxgot -mno-xgot 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-NOXGOT %s -// CHECK-NOXGOT-NOT: "-mllvm" "-mxgot" +// CHECK-NOXGOT: "-target-feature" "-xgot" // // -mldc1-sdc1 // RUN: %clang -target mips-linux-gnu -### -c %s \ diff --git a/clang/test/Driver/mips-integrated-as.s b/clang/test/Driver/mips-integrated-as.s index 0e128770678fd..46ce5b6871f4e 100644 --- a/clang/test/Driver/mips-integrated-as.s +++ b/clang/test/Driver/mips-integrated-as.s @@ -293,3 +293,13 @@ // IMG-SINGLEFLOAT-EXPLICIT-FPXX: "-target-feature" "+single-float" // IMG-SINGLEFLOAT-EXPLICIT-FPXX: "-target-feature" "+fpxx" // IMG-SINGLEFLOAT-EXPLICIT-FPXX: "-target-feature" "+nooddspreg" + +// RUN: %clang -target mips-linux-gnu -### -fintegrated-as -mxgot -c %s 2>&1 | \ +// RUN: FileCheck -check-prefix=XGOT %s +// XGOT: -cc1as +// XGOT: "-target-feature" "+xgot" + +// RUN: %clang -target mips-linux-gnu -### -fintegrated-as -mno-xgot -c %s 2>&1 | \ +// RUN: FileCheck -check-prefix=NOXGOT %s +// NOXGOT: -cc1as +// NOXGOT: "-target-feature" "-xgot" diff --git a/clang/test/Driver/mips-mti.cpp b/clang/test/Driver/mips-mti.cpp index 147239cf3759e..94c3c5568e332 100644 --- a/clang/test/Driver/mips-mti.cpp +++ b/clang/test/Driver/mips-mti.cpp @@ -4,6 +4,7 @@ // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: --target=mips-mti-linux-gnu \ // RUN: --gcc-toolchain=%S/Inputs/mips_mti_tree \ +// RUN: --sysroot="" \ // RUN: -stdlib=libstdc++ \ // RUN: -EB -mhard-float -mabi=32 \ // RUN: | FileCheck --check-prefix=EB-HARD-O32 %s @@ -32,6 +33,7 @@ // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: --target=mips-mti-linux-gnu \ // RUN: --gcc-toolchain=%S/Inputs/mips_mti_tree \ +// RUN: --sysroot="" \ // RUN: -stdlib=libstdc++ \ // RUN: -EB -mhard-float -mabi=n32 \ // RUN: | FileCheck --check-prefix=EB-HARD-N32 %s @@ -60,6 +62,7 @@ // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: --target=mips64-mti-linux-gnu \ // RUN: --gcc-toolchain=%S/Inputs/mips_mti_tree \ +// RUN: --sysroot="" \ // RUN: -stdlib=libstdc++ \ // RUN: -EB -mhard-float -mabi=64 \ // RUN: | FileCheck --check-prefix=EB-HARD-N64 %s @@ -88,6 +91,7 @@ // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: --target=mips-mti-linux-gnu \ // RUN: --gcc-toolchain=%S/Inputs/mips_mti_tree \ +// RUN: --sysroot="" \ // RUN: -stdlib=libstdc++ \ // RUN: -EL -mhard-float -mabi=32 \ // RUN: | FileCheck --check-prefix=EL-HARD-O32 %s @@ -116,6 +120,7 @@ // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: --target=mips-mti-linux-gnu \ // RUN: --gcc-toolchain=%S/Inputs/mips_mti_tree \ +// RUN: --sysroot="" \ // RUN: -stdlib=libstdc++ \ // RUN: -EL -mhard-float -mabi=n32 \ // RUN: | FileCheck --check-prefix=EL-HARD-N32 %s @@ -144,6 +149,7 @@ // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: --target=mips64-mti-linux-gnu \ // RUN: --gcc-toolchain=%S/Inputs/mips_mti_tree \ +// RUN: --sysroot="" \ // RUN: -stdlib=libstdc++ \ // RUN: -EL -mhard-float -mabi=64 \ // RUN: | FileCheck --check-prefix=EL-HARD-N64 %s @@ -172,6 +178,7 @@ // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: --target=mips-mti-linux-gnu \ // RUN: --gcc-toolchain=%S/Inputs/mips_mti_tree \ +// RUN: --sysroot="" \ // RUN: -stdlib=libstdc++ \ // RUN: -EB -msoft-float \ // RUN: | FileCheck --check-prefix=EB-SOFT %s @@ -200,6 +207,7 @@ // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: --target=mips-mti-linux-gnu \ // RUN: --gcc-toolchain=%S/Inputs/mips_mti_tree \ +// RUN: --sysroot="" \ // RUN: -stdlib=libstdc++ \ // RUN: -EL -msoft-float \ // RUN: | FileCheck --check-prefix=EL-SOFT %s @@ -228,6 +236,7 @@ // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: --target=mips-mti-linux-gnu \ // RUN: --gcc-toolchain=%S/Inputs/mips_mti_tree \ +// RUN: --sysroot="" \ // RUN: -stdlib=libstdc++ \ // RUN: -EB -mhard-float -muclibc \ // RUN: | FileCheck --check-prefix=EB-HARD-UCLIBC %s @@ -256,6 +265,7 @@ // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: --target=mips-mti-linux-gnu \ // RUN: --gcc-toolchain=%S/Inputs/mips_mti_tree \ +// RUN: --sysroot="" \ // RUN: -stdlib=libstdc++ \ // RUN: -EL -mhard-float -muclibc \ // RUN: | FileCheck --check-prefix=EL-HARD-UCLIBC %s @@ -284,6 +294,7 @@ // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: --target=mips-mti-linux-gnu \ // RUN: --gcc-toolchain=%S/Inputs/mips_mti_tree \ +// RUN: --sysroot="" \ // RUN: -stdlib=libstdc++ \ // RUN: -EB -mhard-float -mnan=2008 \ // RUN: | FileCheck --check-prefix=EB-HARD-NAN2008 %s @@ -312,6 +323,7 @@ // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: --target=mips-mti-linux-gnu \ // RUN: --gcc-toolchain=%S/Inputs/mips_mti_tree \ +// RUN: --sysroot="" \ // RUN: -stdlib=libstdc++ \ // RUN: -EL -mhard-float -mnan=2008 \ // RUN: | FileCheck --check-prefix=EL-HARD-NAN2008 %s @@ -340,6 +352,7 @@ // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: --target=mips-mti-linux-gnu \ // RUN: --gcc-toolchain=%S/Inputs/mips_mti_tree \ +// RUN: --sysroot="" \ // RUN: -stdlib=libstdc++ \ // RUN: -EB -mhard-float -muclibc -mnan=2008 \ // RUN: | FileCheck --check-prefix=EB-HARD-UCLIBC-NAN2008 %s @@ -368,6 +381,7 @@ // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: --target=mips-mti-linux-gnu \ // RUN: --gcc-toolchain=%S/Inputs/mips_mti_tree \ +// RUN: --sysroot="" \ // RUN: -stdlib=libstdc++ \ // RUN: -EL -mhard-float -muclibc -mnan=2008 \ // RUN: | FileCheck --check-prefix=EL-HARD-UCLIBC-NAN2008 %s @@ -396,6 +410,7 @@ // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: --target=mips-mti-linux-gnu \ // RUN: --gcc-toolchain=%S/Inputs/mips_mti_tree \ +// RUN: --sysroot="" \ // RUN: -stdlib=libstdc++ \ // RUN: -EL -msoft-float -mmicromips \ // RUN: | FileCheck --check-prefix=EL-SOFT-MICRO %s @@ -424,6 +439,7 @@ // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: --target=mips-mti-linux-gnu \ // RUN: --gcc-toolchain=%S/Inputs/mips_mti_tree \ +// RUN: --sysroot="" \ // RUN: -stdlib=libstdc++ \ // RUN: -EL -mhard-float -mmicromips -mnan=2008 \ // RUN: | FileCheck --check-prefix=EL-SOFT-MICRO-NAN2008 %s diff --git a/clang/test/Driver/modules.cpp b/clang/test/Driver/modules.cpp index 7c549c1300ff3..4f4e3a414002d 100644 --- a/clang/test/Driver/modules.cpp +++ b/clang/test/Driver/modules.cpp @@ -15,7 +15,7 @@ // RUN: %clang -std=c++2a %t/module.pcm -S -o %t/module.pcm.o -v 2>&1 | FileCheck %s --check-prefix=CHECK-COMPILE // // CHECK-COMPILE: -cc1 {{.*}} {{-emit-obj|-S}} -// CHECK-COMPILE-SAME: -o {{.*}}.{{pcm.o|s}} +// CHECK-COMPILE-SAME: -o {{.*}}module{{2*}}.pcm.o // CHECK-COMPILE-SAME: -x pcm // CHECK-COMPILE-SAME: {{.*}}.pcm diff --git a/clang/test/Driver/msp430-toolchain.c b/clang/test/Driver/msp430-toolchain.c index 62ef1c0c1f150..f741491c94e8c 100644 --- a/clang/test/Driver/msp430-toolchain.c +++ b/clang/test/Driver/msp430-toolchain.c @@ -1,11 +1,11 @@ // A basic clang -cc1 command-line, and simple environment check. -// RUN: %clang %s -### -no-canonical-prefixes -target msp430 2>&1 \ +// RUN: %clang %s -### -no-canonical-prefixes -target msp430 --sysroot="" 2>&1 \ // RUN: | FileCheck -check-prefix=CC1 %s // CC1: clang{{.*}} "-cc1" "-triple" "msp430" // RUN: %clang %s -### -no-canonical-prefixes -target msp430 \ -// RUN: --gcc-toolchain=%S/Inputs/basic_msp430_tree 2>&1 \ +// RUN: --gcc-toolchain=%S/Inputs/basic_msp430_tree --sysroot="" 2>&1 \ // RUN: | FileCheck -check-prefix=MSP430 %s // MSP430: "{{.*}}Inputs/basic_msp430_tree/lib/gcc/msp430-elf/7.3.1/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}msp430-elf-ld" @@ -18,7 +18,7 @@ // MSP430: "{{.*}}/Inputs/basic_msp430_tree/lib/gcc/msp430-elf/7.3.1/../../..{{/|\\\\}}..{{/|\\\\}}msp430-elf{{/|\\\\}}lib/430{{/|\\\\}}crtn.o" // RUN: %clang %s -### -no-canonical-prefixes -target msp430 -nodefaultlibs \ -// RUN: --gcc-toolchain=%S/Inputs/basic_msp430_tree 2>&1 \ +// RUN: --gcc-toolchain=%S/Inputs/basic_msp430_tree --sysroot="" 2>&1 \ // RUN: | FileCheck -check-prefix=MSP430-NO-DFT-LIB %s // MSP430-NO-DFT-LIB: "{{.*}}Inputs/basic_msp430_tree/lib/gcc/msp430-elf/7.3.1/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}msp430-elf-ld" @@ -31,7 +31,7 @@ // MSP430-NO-DFT-LIB: "{{.*}}/Inputs/basic_msp430_tree/lib/gcc/msp430-elf/7.3.1/../../..{{/|\\\\}}..{{/|\\\\}}msp430-elf{{/|\\\\}}lib/430{{/|\\\\}}crtn.o" // RUN: %clang %s -### -no-canonical-prefixes -target msp430 -nostartfiles \ -// RUN: --gcc-toolchain=%S/Inputs/basic_msp430_tree 2>&1 \ +// RUN: --gcc-toolchain=%S/Inputs/basic_msp430_tree --sysroot="" 2>&1 \ // RUN: | FileCheck -check-prefix=MSP430-NO-START %s // MSP430-NO-START: "{{.*}}Inputs/basic_msp430_tree/lib/gcc/msp430-elf/7.3.1/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}msp430-elf-ld" @@ -40,7 +40,7 @@ // MSP430-NO-START: "--start-group" "-lmul_none" "-lgcc" "-lc" "-lcrt" "-lnosys" "--end-group" // RUN: %clang %s -### -no-canonical-prefixes -target msp430 -nostdlib \ -// RUN: --gcc-toolchain=%S/Inputs/basic_msp430_tree 2>&1 \ +// RUN: --gcc-toolchain=%S/Inputs/basic_msp430_tree --sysroot="" 2>&1 \ // RUN: | FileCheck -check-prefix=MSP430-NO-STD-LIB %s // MSP430-NO-STD-LIB: "{{.*}}Inputs/basic_msp430_tree/lib/gcc/msp430-elf/7.3.1/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}msp430-elf-ld" @@ -48,31 +48,31 @@ // MSP430-NO-STD-LIB: "-L{{.*}}/Inputs/basic_msp430_tree/lib/gcc/msp430-elf/7.3.1/../../..{{/|\\\\}}..{{/|\\\\}}msp430-elf{{/|\\\\}}lib/430" // MSP430-NO-STD-LIB: "--start-group" "-lmul_none" "-lgcc" "--end-group" -// RUN: %clang %s -### -no-canonical-prefixes -target msp430 -mmcu=msp430f147 2>&1 \ +// RUN: %clang %s -### -no-canonical-prefixes -target msp430 -mmcu=msp430f147 --sysroot="" 2>&1 \ // RUN: | FileCheck -check-prefix=MSP430-HWMult-16BIT %s -// RUN: %clang %s -### -no-canonical-prefixes -target msp430 -mmcu=msp430f147 -mhwmult=auto 2>&1 \ +// RUN: %clang %s -### -no-canonical-prefixes -target msp430 -mmcu=msp430f147 -mhwmult=auto --sysroot="" 2>&1 \ // RUN: | FileCheck -check-prefix=MSP430-HWMult-16BIT %s -// RUN: %clang %s -### -no-canonical-prefixes -target msp430 -mhwmult=16bit 2>&1 \ +// RUN: %clang %s -### -no-canonical-prefixes -target msp430 -mhwmult=16bit --sysroot="" 2>&1 \ // RUN: | FileCheck -check-prefix=MSP430-HWMult-16BIT %s // MSP430-HWMult-16BIT: "--start-group" "-lmul_16" -// RUN: %clang %s -### -no-canonical-prefixes -target msp430 -mmcu=msp430f4783 2>&1 \ +// RUN: %clang %s -### -no-canonical-prefixes -target msp430 -mmcu=msp430f4783 --sysroot="" 2>&1 \ // RUN: | FileCheck -check-prefix=MSP430-HWMult-32BIT %s -// RUN: %clang %s -### -no-canonical-prefixes -target msp430 -mmcu=msp430f4783 -mhwmult=auto 2>&1 \ +// RUN: %clang %s -### -no-canonical-prefixes -target msp430 -mmcu=msp430f4783 -mhwmult=auto --sysroot="" 2>&1 \ // RUN: | FileCheck -check-prefix=MSP430-HWMult-32BIT %s -// RUN: %clang %s -### -no-canonical-prefixes -target msp430 -mhwmult=32bit 2>&1 \ +// RUN: %clang %s -### -no-canonical-prefixes -target msp430 -mhwmult=32bit --sysroot="" 2>&1 \ // RUN: | FileCheck -check-prefix=MSP430-HWMult-32BIT %s // MSP430-HWMult-32BIT: "--start-group" "-lmul_32" -// RUN: %clang %s -### -no-canonical-prefixes -target msp430 -mhwmult=f5series 2>&1 \ +// RUN: %clang %s -### -no-canonical-prefixes -target msp430 -mhwmult=f5series --sysroot="" 2>&1 \ // RUN: | FileCheck -check-prefix=MSP430-HWMult-F5 %s // MSP430-HWMult-F5: "--start-group" "-lmul_f5" -// RUN: %clang %s -### -no-canonical-prefixes -target msp430 -mhwmult=none 2>&1 \ +// RUN: %clang %s -### -no-canonical-prefixes -target msp430 -mhwmult=none --sysroot="" 2>&1 \ // RUN: | FileCheck -check-prefix=MSP430-HWMult-NONE %s -// RUN: %clang %s -### -no-canonical-prefixes -target msp430 -mhwmult=none -mmcu=msp430f4783 2>&1 \ +// RUN: %clang %s -### -no-canonical-prefixes -target msp430 -mhwmult=none -mmcu=msp430f4783 --sysroot="" 2>&1 \ // RUN: | FileCheck -check-prefix=MSP430-HWMult-NONE %s // MSP430-HWMult-NONE: "--start-group" "-lmul_none" diff --git a/clang/test/Driver/openmp-offload.c b/clang/test/Driver/openmp-offload.c index 2f8992c594015..86121d2f75369 100644 --- a/clang/test/Driver/openmp-offload.c +++ b/clang/test/Driver/openmp-offload.c @@ -280,14 +280,6 @@ // CHK-LKS: [[T2BIN]] // CHK-LKS: PROVIDE_HIDDEN(.omp_offloading.img_end.x86_64-pc-linux-gnu = .); // CHK-LKS: } -// CHK-LKS: .omp_offloading.entries : -// CHK-LKS: ALIGN(0x10) -// CHK-LKS: SUBALIGN(0x01) -// CHK-LKS: { -// CHK-LKS: PROVIDE_HIDDEN(.omp_offloading.entries_begin = .); -// CHK-LKS: *(.omp_offloading.entries) -// CHK-LKS: PROVIDE_HIDDEN(.omp_offloading.entries_end = .); -// CHK-LKS: } // CHK-LKS: } // CHK-LKS: INSERT BEFORE .data diff --git a/clang/test/Driver/split-lto-unit.c b/clang/test/Driver/split-lto-unit.c index fab5790c26b95..d2ed253ca201a 100644 --- a/clang/test/Driver/split-lto-unit.c +++ b/clang/test/Driver/split-lto-unit.c @@ -6,5 +6,5 @@ // UNIT: "-fsplit-lto-unit" // NOUNIT-NOT: "-fsplit-lto-unit" -// ERROR1: error: invalid argument '-fno-split-lto-unit' not allowed with '-fwhole-program-vtables' +// ERROR1-NOT: error: invalid argument // ERROR2: error: invalid argument '-fno-split-lto-unit' not allowed with '-fsanitize=cfi' diff --git a/clang/test/Driver/systemz-march.c b/clang/test/Driver/systemz-march.c index 6b751523c34f9..f07a2c50131ad 100644 --- a/clang/test/Driver/systemz-march.c +++ b/clang/test/Driver/systemz-march.c @@ -11,6 +11,7 @@ // RUN: %clang -target s390x -### -S -emit-llvm -march=arch11 %s 2>&1 | FileCheck --check-prefix=CHECK-ARCH11 %s // RUN: %clang -target s390x -### -S -emit-llvm -march=z14 %s 2>&1 | FileCheck --check-prefix=CHECK-Z14 %s // RUN: %clang -target s390x -### -S -emit-llvm -march=arch12 %s 2>&1 | FileCheck --check-prefix=CHECK-ARCH12 %s +// RUN: %clang -target s390x -### -S -emit-llvm -march=z15 %s 2>&1 | FileCheck --check-prefix=CHECK-Z15 %s // RUN: %clang -target s390x -### -S -emit-llvm -march=arch13 %s 2>&1 | FileCheck --check-prefix=CHECK-ARCH13 %s // CHECK-Z9: error: unknown target CPU 'z9' @@ -24,6 +25,7 @@ // CHECK-ARCH11: "-target-cpu" "arch11" // CHECK-Z14: "-target-cpu" "z14" // CHECK-ARCH12: "-target-cpu" "arch12" +// CHECK-Z15: "-target-cpu" "z15" // CHECK-ARCH13: "-target-cpu" "arch13" int x; diff --git a/clang/test/Frontend/Inputs/NextIncludes/rewrite-includes9.h b/clang/test/Frontend/Inputs/NextIncludes/rewrite-includes9.h index b074bd1a4468c..75e4c963fd40a 100644 --- a/clang/test/Frontend/Inputs/NextIncludes/rewrite-includes9.h +++ b/clang/test/Frontend/Inputs/NextIncludes/rewrite-includes9.h @@ -1 +1 @@ -included_line9 +int included_line9; diff --git a/clang/test/Frontend/Inputs/rewrite-includes1.h b/clang/test/Frontend/Inputs/rewrite-includes1.h index 1b6c80d5f4358..3312f9ff9025e 100644 --- a/clang/test/Frontend/Inputs/rewrite-includes1.h +++ b/clang/test/Frontend/Inputs/rewrite-includes1.h @@ -1,3 +1,3 @@ #pragma clang system_header -included_line1 +int included_line1; #include "rewrite-includes2.h" diff --git a/clang/test/Frontend/Inputs/rewrite-includes2.h b/clang/test/Frontend/Inputs/rewrite-includes2.h index 1114e51cc1351..d1097c8e17a94 100644 --- a/clang/test/Frontend/Inputs/rewrite-includes2.h +++ b/clang/test/Frontend/Inputs/rewrite-includes2.h @@ -1 +1 @@ -included_line2 +int included_line2; diff --git a/clang/test/Frontend/Inputs/rewrite-includes3.h b/clang/test/Frontend/Inputs/rewrite-includes3.h index 3757bc8b3416d..2abf780590a44 100644 --- a/clang/test/Frontend/Inputs/rewrite-includes3.h +++ b/clang/test/Frontend/Inputs/rewrite-includes3.h @@ -1 +1 @@ -included_line3 +unsigned int included_line3 = -10; diff --git a/clang/test/Frontend/Inputs/rewrite-includes4.h b/clang/test/Frontend/Inputs/rewrite-includes4.h index b4e25d26f62df..7a442013f7227 100644 --- a/clang/test/Frontend/Inputs/rewrite-includes4.h +++ b/clang/test/Frontend/Inputs/rewrite-includes4.h @@ -1 +1 @@ -included_line4 +int included_line4; diff --git a/clang/test/Frontend/Inputs/rewrite-includes5.h b/clang/test/Frontend/Inputs/rewrite-includes5.h index 934bf413e50ff..0de12113cb647 100644 --- a/clang/test/Frontend/Inputs/rewrite-includes5.h +++ b/clang/test/Frontend/Inputs/rewrite-includes5.h @@ -1 +1 @@ -included_line5 +int included_line5; diff --git a/clang/test/Frontend/Inputs/rewrite-includes6.h b/clang/test/Frontend/Inputs/rewrite-includes6.h index c18e5013affc0..5d891446234e6 100644 --- a/clang/test/Frontend/Inputs/rewrite-includes6.h +++ b/clang/test/Frontend/Inputs/rewrite-includes6.h @@ -1,2 +1,2 @@ #pragma once -included_line6 +int included_line6; diff --git a/clang/test/Frontend/Inputs/rewrite-includes7.h b/clang/test/Frontend/Inputs/rewrite-includes7.h index da00d4fb97d82..221bab82df304 100644 --- a/clang/test/Frontend/Inputs/rewrite-includes7.h +++ b/clang/test/Frontend/Inputs/rewrite-includes7.h @@ -1,4 +1,4 @@ #ifndef REWRITE_INCLUDES_7 #define REWRITE_INCLUDES_7 -included_line7 +int included_line7; #endif diff --git a/clang/test/Frontend/Inputs/sysroot_x86_64_cross_linux_tree/lib/.keep b/clang/test/Frontend/Inputs/sysroot_x86_64_cross_linux_tree/lib/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Frontend/Inputs/sysroot_x86_64_cross_linux_tree/usr/include/c++/.keep b/clang/test/Frontend/Inputs/sysroot_x86_64_cross_linux_tree/usr/include/c++/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Frontend/Inputs/sysroot_x86_64_cross_linux_tree/usr/lib/gcc/.keep b/clang/test/Frontend/Inputs/sysroot_x86_64_cross_linux_tree/usr/lib/gcc/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Frontend/Inputs/sysroot_x86_64_cross_linux_tree/usr/local/include/.keep b/clang/test/Frontend/Inputs/sysroot_x86_64_cross_linux_tree/usr/local/include/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Frontend/Inputs/sysroot_x86_64_cross_linux_tree/usr/local/lib/.keep b/clang/test/Frontend/Inputs/sysroot_x86_64_cross_linux_tree/usr/local/lib/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Frontend/cc1-return-codes.c b/clang/test/Frontend/cc1-return-codes.c index da329b9342654..fde1b4adf2ad5 100644 --- a/clang/test/Frontend/cc1-return-codes.c +++ b/clang/test/Frontend/cc1-return-codes.c @@ -1,4 +1,4 @@ // cc1 immediate arguments (arguments which displays information and exits) // shall exit indicating success (return code 0) -// RUN: %clang -cc1 -help -// RUN: %clang -cc1 -version +// RUN: %clang_cc1 -help +// RUN: %clang_cc1 -version diff --git a/clang/test/Frontend/nostdlib-for-asmpp.s b/clang/test/Frontend/nostdlib-for-asmpp.s index 330fee32a361d..c1be5a2777907 100644 --- a/clang/test/Frontend/nostdlib-for-asmpp.s +++ b/clang/test/Frontend/nostdlib-for-asmpp.s @@ -1,4 +1,4 @@ -// RUN: %clang -cc1 -x assembler-with-cpp -triple arm64-apple-ios6.0.0 -isysroot %S/doesnotexist -std=c++11 -v %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x assembler-with-cpp -triple arm64-apple-ios6.0.0 -isysroot %S/doesnotexist -std=c++11 -v %s 2>&1 | FileCheck %s // The C++ stdlib path should not be included for an assembly source. // CHECK-NOT: usr/include/c++/ diff --git a/clang/test/Frontend/rewrite-includes-cli-include.c b/clang/test/Frontend/rewrite-includes-cli-include.c index ba96039bdd5ad..d63f966f79ca6 100644 --- a/clang/test/Frontend/rewrite-includes-cli-include.c +++ b/clang/test/Frontend/rewrite-includes-cli-include.c @@ -2,7 +2,7 @@ main_file_line // CHECK: {{^}}# 1 ""{{$}} // CHECK-NEXT: {{^}}# 1 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes2.h" 1{{$}} -// CHECK-NEXT: {{^}}included_line2{{$}} +// CHECK-NEXT: {{^}}int included_line2;{{$}} // CHECK-NEXT: {{^}}# 1 "" 2{{$}} // CHECK-NEXT: {{^}}# 1 "{{.*}}rewrite-includes-cli-include.c"{{$}} // CHECK-NEXT: FileCheck diff --git a/clang/test/Frontend/rewrite-includes-conditions.c b/clang/test/Frontend/rewrite-includes-conditions.c new file mode 100644 index 0000000000000..37a55f31de9ec --- /dev/null +++ b/clang/test/Frontend/rewrite-includes-conditions.c @@ -0,0 +1,113 @@ +// RUN: %clang_cc1 -E -frewrite-includes -I %S/Inputs %s -o - | FileCheck -strict-whitespace %s +// RUN: %clang_cc1 -E -frewrite-includes -I %S/Inputs %s -o - | %clang_cc1 -Wall -Wextra -Wconversion -x c -fsyntax-only 2>&1 | FileCheck -check-prefix=COMPILE --implicit-check-not warning: %s + +#define value1 1 +#if value1 +int line1; +#else +int line2; +#endif + +#define value2 2 + +#if value1 == value2 +int line3; +#elif value1 > value2 +int line4; +#elif value1 < value2 +int line5; +#else +int line6; +#endif + +#if __has_include() +#include +#endif + +#define HAS_INCLUDE(x) __has_include(x) + +#if HAS_INCLUDE() +#endif + +/* +#if value1 +commented out +*/ + +#if value1 < value2 \ +|| value1 != value2 +int line7; +#endif + +#if value1 /* +*/ +#endif + +static int unused; + +// ENDCOMPARE + +// CHECK: #if 0 /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if value1 +// CHECK-NEXT: #endif +// CHECK-NEXT: #endif /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if 1 /* evaluated by -frewrite-includes */ +// CHECK-NEXT: # 6 "{{.*}}rewrite-includes-conditions.c" + +// CHECK: #if 0 /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if value1 == value2 +// CHECK-NEXT: #endif +// CHECK-NEXT: #endif /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if 0 /* evaluated by -frewrite-includes */ +// CHECK-NEXT: # 14 "{{.*}}rewrite-includes-conditions.c" + +// CHECK: #if 0 /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if 0 +// CHECK-NEXT: #elif value1 > value2 +// CHECK-NEXT: #endif +// CHECK-NEXT: #endif /* disabled by -frewrite-includes */ +// CHECK-NEXT: #elif 0 /* evaluated by -frewrite-includes */ +// CHECK-NEXT: # 16 "{{.*}}rewrite-includes-conditions.c" + +// CHECK: #if 0 /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if 0 +// CHECK-NEXT: #elif value1 < value2 +// CHECK-NEXT: #endif +// CHECK-NEXT: #endif /* disabled by -frewrite-includes */ +// CHECK-NEXT: #elif 1 /* evaluated by -frewrite-includes */ +// CHECK-NEXT: # 18 "{{.*}}rewrite-includes-conditions.c" + +// CHECK: #if 0 /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if __has_include() +// CHECK-NEXT: #endif +// CHECK-NEXT: #endif /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if 1 /* evaluated by -frewrite-includes */ +// CHECK-NEXT: # 24 "{{.*}}rewrite-includes-conditions.c" + +// CHECK: #if 0 /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if HAS_INCLUDE() +// CHECK-NEXT: #endif +// CHECK-NEXT: #endif /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if 1 /* evaluated by -frewrite-includes */ +// CHECK-NEXT: # 30 "{{.*}}rewrite-includes-conditions.c" + +// CHECK: #if 0 /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if value1 < value2 \ +// CHECK-NEXT: || value1 != value2 +// CHECK-NEXT: #endif +// CHECK-NEXT: #endif /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if 1 /* evaluated by -frewrite-includes */ +// CHECK-NEXT: # 39 "{{.*}}rewrite-includes-conditions.c" + +// CHECK: #if 0 /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if value1 /* +// CHECK-NEXT: */ +// CHECK-NEXT: #endif +// CHECK-NEXT: #endif /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if 1 /* evaluated by -frewrite-includes */ +// CHECK-NEXT: # 44 "{{.*}}rewrite-includes-conditions.c" + +// CHECK: {{^}}// ENDCOMPARE{{$}} + +// COMPILE: Inputs{{[/\\]}}rewrite-includes3.h:1:31: warning: implicit conversion changes signedness: +// COMPILE: rewrite-includes-conditions.c:46:12: warning: unused variable 'unused' diff --git a/clang/test/Frontend/rewrite-includes-warnings.c b/clang/test/Frontend/rewrite-includes-warnings.c index 1fb98db30147e..d955f86f826a8 100644 --- a/clang/test/Frontend/rewrite-includes-warnings.c +++ b/clang/test/Frontend/rewrite-includes-warnings.c @@ -1,4 +1,7 @@ -// RUN: %clang_cc1 -verify -Wall -Wextra -E -frewrite-includes %s +// RUN: %clang_cc1 -verify -Wall -Wextra -Wunused-macros -E -frewrite-includes %s // expected-no-diagnostics #pragma GCC visibility push (default) + +#define USED_MACRO 1 +int test() { return USED_MACRO; } diff --git a/clang/test/Frontend/rewrite-includes.c b/clang/test/Frontend/rewrite-includes.c index 630e761cab2ff..15254931250c1 100644 --- a/clang/test/Frontend/rewrite-includes.c +++ b/clang/test/Frontend/rewrite-includes.c @@ -1,8 +1,9 @@ -// RUN: not %clang_cc1 -verify -E -frewrite-includes -DFIRST -I %S/Inputs -I %S/Inputs/NextIncludes %s -o - | FileCheck -strict-whitespace %s -// RUN: not %clang_cc1 -verify -E -frewrite-includes -P -DFIRST -I %S/Inputs -I %S/Inputs/NextIncludes %s -o - | FileCheck -check-prefix=CHECKNL -strict-whitespace %s +// RUN: %clang_cc1 -E -frewrite-includes -DFIRST -I %S/Inputs -I %S/Inputs/NextIncludes %s -o - | FileCheck -strict-whitespace %s +// RUN: %clang_cc1 -E -frewrite-includes -P -DFIRST -I %S/Inputs -I %S/Inputs/NextIncludes %s -o - | FileCheck -check-prefix=CHECKNL -strict-whitespace %s +// RUN: %clang_cc1 -E -frewrite-includes -DFIRST -I %S/Inputs -I %S/Inputs/NextIncludes %s -o - | %clang_cc1 -Wall -Wextra -Wconversion -DFIRST -x c -fsyntax-only 2>&1 | FileCheck -check-prefix=COMPILE --implicit-check-not warning: %s // STARTCOMPARE #define A(a,b) a ## b -A(1,2) +A(in,t) a; #include "rewrite-includes1.h" #ifdef FIRST #define HEADER "rewrite-includes3.h" @@ -21,142 +22,164 @@ A(1,2) #include "rewrite-includes7.h" #include "rewrite-includes8.h" #include "rewrite-includes9.h" +static int unused; // ENDCOMPARE // CHECK: {{^}}# 1 "{{.*}}rewrite-includes.c"{{$}} // CHECK: {{^}}// STARTCOMPARE{{$}} // CHECK-NEXT: {{^}}#define A(a,b) a ## b{{$}} -// CHECK-NEXT: {{^}}A(1,2){{$}} +// CHECK-NEXT: {{^}}A(in,t) a;{{$}} // CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}#include "rewrite-includes1.h"{{$}} // CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} -// CHECK-NEXT: {{^}}# 6 "{{.*}}rewrite-includes.c"{{$}} +// CHECK-NEXT: {{^}}# 7 "{{.*}}rewrite-includes.c"{{$}} // CHECK-NEXT: {{^}}# 1 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes1.h" 1{{$}} // CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}#pragma clang system_header{{$}} // CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}# 2 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes1.h" 3{{$}} -// CHECK-NEXT: {{^}}included_line1{{$}} +// CHECK-NEXT: {{^}}int included_line1;{{$}} // CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}#include "rewrite-includes2.h"{{$}} // CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}# 3 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes1.h" 3{{$}} // CHECK-NEXT: {{^}}# 1 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes2.h" 1 3{{$}} -// CHECK-NEXT: {{^}}included_line2{{$}} +// CHECK-NEXT: {{^}}int included_line2;{{$}} // CHECK-NEXT: {{^}}# 4 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes1.h" 2 3{{$}} -// CHECK-NEXT: {{^}}# 7 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-NEXT: {{^}}# 8 "{{.*}}rewrite-includes.c" 2{{$}} // CHECK-NEXT: {{^}}#ifdef FIRST{{$}} // CHECK-NEXT: {{^}}#define HEADER "rewrite-includes3.h"{{$}} // CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}#include HEADER{{$}} // CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} -// CHECK-NEXT: {{^}}# 9 "{{.*}}rewrite-includes.c"{{$}} +// CHECK-NEXT: {{^}}# 10 "{{.*}}rewrite-includes.c"{{$}} // CHECK-NEXT: {{^}}# 1 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes3.h" 1{{$}} -// CHECK-NEXT: {{^}}included_line3{{$}} -// CHECK-NEXT: {{^}}# 10 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-NEXT: {{^}}unsigned int included_line3 = -10;{{$}} +// CHECK-NEXT: {{^}}# 11 "{{.*}}rewrite-includes.c" 2{{$}} // CHECK-NEXT: {{^}}#else{{$}} -// CHECK-NEXT: {{^}}# 11 "{{.*}}rewrite-includes.c"{{$}} +// CHECK-NEXT: {{^}}# 12 "{{.*}}rewrite-includes.c"{{$}} // CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}#include "rewrite-includes4.h"{{$}} // CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} -// CHECK-NEXT: {{^}}# 11 "{{.*}}rewrite-includes.c"{{$}} // CHECK-NEXT: {{^}}# 12 "{{.*}}rewrite-includes.c"{{$}} -// CHECK-NEXT: {{^}}#endif{{$}} // CHECK-NEXT: {{^}}# 13 "{{.*}}rewrite-includes.c"{{$}} +// CHECK-NEXT: {{^}}#endif{{$}} +// CHECK-NEXT: {{^}}# 14 "{{.*}}rewrite-includes.c"{{$}} // CHECK-NEXT: {{^}} // indented{{$}} // CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}#/**/include /**/ "rewrite-includes5.h" /**/ {{\\}}{{$}} // CHECK-NEXT: {{^}} {{$}} // CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} -// CHECK-NEXT: {{^}}# 15 "{{.*}}rewrite-includes.c"{{$}} +// CHECK-NEXT: {{^}}# 16 "{{.*}}rewrite-includes.c"{{$}} // CHECK-NEXT: {{^}}# 1 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes5.h" 1{{$}} -// CHECK-NEXT: {{^}}included_line5{{$}} -// CHECK-NEXT: {{^}}# 16 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-NEXT: {{^}}int included_line5;{{$}} +// CHECK-NEXT: {{^}}# 17 "{{.*}}rewrite-includes.c" 2{{$}} // CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}#include "rewrite-includes6.h" // comment{{$}} // CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} -// CHECK-NEXT: {{^}}# 16 "{{.*}}rewrite-includes.c"{{$}} +// CHECK-NEXT: {{^}}# 17 "{{.*}}rewrite-includes.c"{{$}} // CHECK-NEXT: {{^}}# 1 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes6.h" 1{{$}} // CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}#pragma once{{$}} // CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}# 2 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes6.h"{{$}} -// CHECK-NEXT: {{^}}included_line6{{$}} -// CHECK-NEXT: {{^}}# 17 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-NEXT: {{^}}int included_line6;{{$}} +// CHECK-NEXT: {{^}}# 18 "{{.*}}rewrite-includes.c" 2{{$}} // CHECK-NEXT: {{^}} {{$}} // CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}#include "rewrite-includes6.h" /* comment{{$}} // CHECK-NEXT: {{^}} continues */{{$}} // CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} -// CHECK-NEXT: {{^}}# 19 "{{.*}}rewrite-includes.c"{{$}} // CHECK-NEXT: {{^}}# 20 "{{.*}}rewrite-includes.c"{{$}} +// CHECK-NEXT: {{^}}# 21 "{{.*}}rewrite-includes.c"{{$}} // CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}#include "rewrite-includes7.h"{{$}} // CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} -// CHECK-NEXT: {{^}}# 20 "{{.*}}rewrite-includes.c"{{$}} +// CHECK-NEXT: {{^}}# 21 "{{.*}}rewrite-includes.c"{{$}} // CHECK-NEXT: {{^}}# 1 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes7.h" 1{{$}} // CHECK-NEXT: {{^}}#ifndef REWRITE_INCLUDES_7{{$}} // CHECK-NEXT: {{^}}#define REWRITE_INCLUDES_7{{$}} -// CHECK-NEXT: {{^}}included_line7{{$}} +// CHECK-NEXT: {{^}}int included_line7;{{$}} // CHECK-NEXT: {{^}}#endif{{$}} // CHECK-NEXT: {{^}}# 5 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes7.h"{{$}} -// CHECK-NEXT: {{^}}# 21 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-NEXT: {{^}}# 22 "{{.*}}rewrite-includes.c" 2{{$}} // CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}#include "rewrite-includes7.h"{{$}} // CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} -// CHECK-NEXT: {{^}}# 21 "{{.*}}rewrite-includes.c"{{$}} // CHECK-NEXT: {{^}}# 22 "{{.*}}rewrite-includes.c"{{$}} +// CHECK-NEXT: {{^}}# 23 "{{.*}}rewrite-includes.c"{{$}} // CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}#include "rewrite-includes8.h"{{$}} // CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} -// CHECK-NEXT: {{^}}# 22 "{{.*}}rewrite-includes.c"{{$}} +// CHECK-NEXT: {{^}}# 23 "{{.*}}rewrite-includes.c"{{$}} // CHECK-NEXT: {{^}}# 1 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes8.h" 1{{$}} -// CHECK-NEXT: {{^}}#if (0)/*__has_include_next()*/{{$}} -// CHECK-NEXT: {{^}}#elif (0)/*__has_include()*/{{$}} +// CHECK-NEXT: {{^}}#if 0 /* disabled by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}#if __has_include_next(){{$}} +// CHECK-NEXT: {{^}}#endif{{$}} +// CHECK-NEXT: {{^}}#endif /* disabled by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}#if 0 /* evaluated by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}# 2 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes8.h"{{$}} +// CHECK-NEXT: {{^}}#if 0 /* disabled by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}#if 0{{$}} +// CHECK-NEXT: {{^}}#elif __has_include(){{$}} +// CHECK-NEXT: {{^}}#endif{{$}} +// CHECK-NEXT: {{^}}#endif /* disabled by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}#elif 0 /* evaluated by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}# 3 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes8.h"{{$}} // CHECK-NEXT: {{^}}#endif{{$}} // CHECK-NEXT: {{^}}# 4 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes8.h"{{$}} -// CHECK-NEXT: {{^}}#if !(1)/*__has_include("rewrite-includes8.h")*/{{$}} +// CHECK-NEXT: {{^}}#if 0 /* disabled by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}#if !__has_include("rewrite-includes8.h"){{$}} +// CHECK-NEXT: {{^}}#endif{{$}} +// CHECK-NEXT: {{^}}#endif /* disabled by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}#if 0 /* evaluated by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}# 5 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes8.h"{{$}} // CHECK-NEXT: {{^}}#endif{{$}} // CHECK-NEXT: {{^}}# 6 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes8.h"{{$}} -// CHECK-NEXT: {{^}}# 23 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-NEXT: {{^}}# 24 "{{.*}}rewrite-includes.c" 2{{$}} // CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}#include "rewrite-includes9.h"{{$}} // CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} -// CHECK-NEXT: {{^}}# 23 "{{.*}}rewrite-includes.c"{{$}} +// CHECK-NEXT: {{^}}# 24 "{{.*}}rewrite-includes.c"{{$}} // CHECK-NEXT: {{^}}# 1 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes9.h" 1{{$}} -// CHECK-NEXT: {{^}}#if (1)/*__has_include_next()*/{{$}} +// CHECK-NEXT: {{^}}#if 0 /* disabled by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}#if __has_include_next(){{$}} +// CHECK-NEXT: {{^}}#endif{{$}} +// CHECK-NEXT: {{^}}#endif /* disabled by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}#if 1 /* evaluated by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}# 2 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes9.h"{{$}} // CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}#include_next {{$}} // CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}# 2 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes9.h"{{$}} // CHECK-NEXT: {{^}}# 1 "{{.*[/\\]Inputs(/|\\\\)NextIncludes(/|\\\\)}}rewrite-includes9.h" 1{{$}} -// CHECK-NEXT: {{^}}included_line9{{$}} +// CHECK-NEXT: {{^}}int included_line9;{{$}} // CHECK-NEXT: {{^}}# 3 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes9.h" 2{{$}} // CHECK-NEXT: {{^}}#endif{{$}} // CHECK-NEXT: {{^}}# 4 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes9.h"{{$}} -// CHECK-NEXT: {{^}}# 24 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-NEXT: {{^}}# 25 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-NEXT: {{^}}static int unused;{{$}} // CHECK-NEXT: {{^}}// ENDCOMPARE{{$}} // CHECKNL: {{^}}// STARTCOMPARE{{$}} // CHECKNL-NEXT: {{^}}#define A(a,b) a ## b{{$}} -// CHECKNL-NEXT: {{^}}A(1,2){{$}} +// CHECKNL-NEXT: {{^}}A(in,t) a;{{$}} // CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#include "rewrite-includes1.h"{{$}} // CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#pragma clang system_header{{$}} // CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} -// CHECKNL-NEXT: {{^}}included_line1{{$}} +// CHECKNL-NEXT: {{^}}int included_line1;{{$}} // CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#include "rewrite-includes2.h"{{$}} // CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} -// CHECKNL-NEXT: {{^}}included_line2{{$}} +// CHECKNL-NEXT: {{^}}int included_line2;{{$}} // CHECKNL-NEXT: {{^}}#ifdef FIRST{{$}} // CHECKNL-NEXT: {{^}}#define HEADER "rewrite-includes3.h"{{$}} // CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#include HEADER{{$}} // CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} -// CHECKNL-NEXT: {{^}}included_line3{{$}} +// CHECKNL-NEXT: {{^}}unsigned int included_line3 = -10;{{$}} // CHECKNL-NEXT: {{^}}#else{{$}} // CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#include "rewrite-includes4.h"{{$}} @@ -167,14 +190,14 @@ A(1,2) // CHECKNL-NEXT: {{^}}#/**/include /**/ "rewrite-includes5.h" /**/ {{\\}}{{$}} // CHECKNL-NEXT: {{^}} {{$}} // CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} -// CHECKNL-NEXT: {{^}}included_line5{{$}} +// CHECKNL-NEXT: {{^}}int included_line5;{{$}} // CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#include "rewrite-includes6.h" // comment{{$}} // CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#pragma once{{$}} // CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} -// CHECKNL-NEXT: {{^}}included_line6{{$}} +// CHECKNL-NEXT: {{^}}int included_line6;{{$}} // CHECKNL-NEXT: {{^}} {{$}} // CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#include "rewrite-includes6.h" /* comment{{$}} @@ -185,7 +208,7 @@ A(1,2) // CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#ifndef REWRITE_INCLUDES_7{{$}} // CHECKNL-NEXT: {{^}}#define REWRITE_INCLUDES_7{{$}} -// CHECKNL-NEXT: {{^}}included_line7{{$}} +// CHECKNL-NEXT: {{^}}int included_line7;{{$}} // CHECKNL-NEXT: {{^}}#endif{{$}} // CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#include "rewrite-includes7.h"{{$}} @@ -193,18 +216,39 @@ A(1,2) // CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#include "rewrite-includes8.h"{{$}} // CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} -// CHECKNL-NEXT: {{^}}#if (0)/*__has_include_next()*/{{$}} -// CHECKNL-NEXT: {{^}}#elif (0)/*__has_include()*/{{$}} +// CHECKNL-NEXT: {{^}}#if 0 /* disabled by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#if __has_include_next(){{$}} +// CHECKNL-NEXT: {{^}}#endif{{$}} +// CHECKNL-NEXT: {{^}}#endif /* disabled by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#if 0 /* evaluated by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#if 0 /* disabled by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#if 0{{$}} +// CHECKNL-NEXT: {{^}}#elif __has_include(){{$}} +// CHECKNL-NEXT: {{^}}#endif{{$}} +// CHECKNL-NEXT: {{^}}#endif /* disabled by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#elif 0 /* evaluated by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#endif{{$}} -// CHECKNL-NEXT: {{^}}#if !(1)/*__has_include("rewrite-includes8.h")*/{{$}} +// CHECKNL-NEXT: {{^}}#if 0 /* disabled by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#if !__has_include("rewrite-includes8.h"){{$}} +// CHECKNL-NEXT: {{^}}#endif{{$}} +// CHECKNL-NEXT: {{^}}#endif /* disabled by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#if 0 /* evaluated by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#endif{{$}} // CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#include "rewrite-includes9.h"{{$}} // CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} -// CHECKNL-NEXT: {{^}}#if (1)/*__has_include_next()*/{{$}} +// CHECKNL-NEXT: {{^}}#if 0 /* disabled by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#if __has_include_next(){{$}} +// CHECKNL-NEXT: {{^}}#endif{{$}} +// CHECKNL-NEXT: {{^}}#endif /* disabled by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#if 1 /* evaluated by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#include_next {{$}} // CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} -// CHECKNL-NEXT: {{^}}included_line9{{$}} +// CHECKNL-NEXT: {{^}}int included_line9;{{$}} // CHECKNL-NEXT: {{^}}#endif{{$}} +// CHECKNL-NEXT: {{^}}static int unused;{{$}} // CHECKNL-NEXT: {{^}}// ENDCOMPARE{{$}} + +// COMPILE: Inputs{{[/\\]}}rewrite-includes3.h:1:31: warning: implicit conversion changes signedness: +// COMPILE: rewrite-includes.c:25:12: warning: unused variable 'unused' diff --git a/clang/test/Frontend/stdin-input.c b/clang/test/Frontend/stdin-input.c new file mode 100644 index 0000000000000..7b15482b8c513 --- /dev/null +++ b/clang/test/Frontend/stdin-input.c @@ -0,0 +1,7 @@ +// RUN: cat %s | %clang -emit-llvm -g -S \ +// RUN: -Xclang -main-file-name -Xclang test/foo.c -x c - -o - | FileCheck %s +// CHECK: ; ModuleID = 'test/foo.c' +// CHECK: source_filename = "test/foo.c" +// CHECK: !DIFile(filename: "test/foo.c" + +int main() {} diff --git a/clang/test/Frontend/warning-poison-system-directories.c b/clang/test/Frontend/warning-poison-system-directories.c new file mode 100644 index 0000000000000..ba4c0e1990ed3 --- /dev/null +++ b/clang/test/Frontend/warning-poison-system-directories.c @@ -0,0 +1,29 @@ +// REQUIRES: x86-registered-target + +// System directory and sysroot option causes warning. +// RUN: %clang -Wpoison-system-directories -target x86_64 -I/usr/include --sysroot %S/Inputs/sysroot_x86_64_cross_linux_tree -c -o - %s 2> %t.1.stderr +// RUN: FileCheck -check-prefix=WARN < %t.1.stderr %s +// RUN: %clang -Wpoison-system-directories -target x86_64 -cxx-isystem/usr/include --sysroot %S/Inputs/sysroot_x86_64_cross_linux_tree -c -o - %s 2> %t.1.stderr +// RUN: FileCheck -check-prefix=WARN < %t.1.stderr %s +// RUN: %clang -Wpoison-system-directories -target x86_64 -iquote/usr/local/include --sysroot %S/Inputs/sysroot_x86_64_cross_linux_tree -c -o - %s 2> %t.1.stderr +// RUN: FileCheck -check-prefix=WARN < %t.1.stderr %s +// RUN: %clang -Wpoison-system-directories -target x86_64 -isystem/usr/local/include --sysroot %S/Inputs/sysroot_x86_64_cross_linux_tree -c -o - %s 2> %t.1.stderr +// RUN: FileCheck -check-prefix=WARN < %t.1.stderr %s + +// Missing target but included sysroot still causes the warning. +// RUN: %clang -Wpoison-system-directories -I/usr/include --sysroot %S/Inputs/sysroot_x86_64_cross_linux_tree -c -o - %s 2> %t.2.stderr +// RUN: FileCheck -check-prefix=WARN < %t.2.stderr %s + +// With -Werror the warning causes the failure. +// RUN: not %clang -Werror=poison-system-directories -target x86_64 -I/usr/include --sysroot %S/Inputs/sysroot_x86_64_cross_linux_tree -c -o - %s 2> %t.3.stderr +// RUN: FileCheck -check-prefix=ERROR < %t.3.stderr %s + +// Cros target without sysroot causes no warning. +// RUN: %clang -Wpoison-system-directories -Werror -target x86_64 -I/usr/include -c -o - %s + +// By default the warning is off. +// RUN: %clang -Werror -target x86_64 -I/usr/include --sysroot %S/Inputs/sysroot_x86_64_cross_linux_tree -c -o - %s + +// WARN: warning: include location {{[^ ]+}} is unsafe for cross-compilation [-Wpoison-system-directories] + +// ERROR: error: include location {{[^ ]+}} is unsafe for cross-compilation [-Werror,-Wpoison-system-directories] diff --git a/clang/test/Headers/arm-fp16-header.c b/clang/test/Headers/arm-fp16-header.c index 781113debda0b..85068bb892779 100644 --- a/clang/test/Headers/arm-fp16-header.c +++ b/clang/test/Headers/arm-fp16-header.c @@ -6,14 +6,14 @@ // RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding --target=aarch64_be-none-eabi -march=armv8.2-a+fp16 -std=c99 -xc %s // RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding --target=aarch64_be-none-eabi -march=armv8.2-a+fp16 -std=c11 -xc %s -// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding --target=aarch64-none-eabi -march=armv8.2-a+fp16 -std=c++98 -xc++ %s -// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding --target=aarch64-none-eabi -march=armv8.2-a+fp16 -std=c++11 -xc++ %s -// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding --target=aarch64-none-eabi -march=armv8.2-a+fp16 -std=c++14 -xc++ %s -// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding --target=aarch64-none-eabi -march=armv8.2-a+fp16 -std=c++17 -xc++ %s +// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding -nostdinc++ --target=aarch64-none-eabi -march=armv8.2-a+fp16 -std=c++98 -xc++ %s +// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding -nostdinc++ --target=aarch64-none-eabi -march=armv8.2-a+fp16 -std=c++11 -xc++ %s +// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding -nostdinc++ --target=aarch64-none-eabi -march=armv8.2-a+fp16 -std=c++14 -xc++ %s +// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding -nostdinc++ --target=aarch64-none-eabi -march=armv8.2-a+fp16 -std=c++17 -xc++ %s -// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding --target=aarch64_be-none-eabi -march=armv8.2-a+fp16 -std=c++98 -xc++ %s -// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding --target=aarch64_be-none-eabi -march=armv8.2-a+fp16 -std=c++11 -xc++ %s -// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding --target=aarch64_be-none-eabi -march=armv8.2-a+fp16 -std=c++14 -xc++ %s -// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding --target=aarch64_be-none-eabi -march=armv8.2-a+fp16 -std=c++17 -xc++ %s +// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding -nostdinc++ --target=aarch64_be-none-eabi -march=armv8.2-a+fp16 -std=c++98 -xc++ %s +// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding -nostdinc++ --target=aarch64_be-none-eabi -march=armv8.2-a+fp16 -std=c++11 -xc++ %s +// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding -nostdinc++ --target=aarch64_be-none-eabi -march=armv8.2-a+fp16 -std=c++14 -xc++ %s +// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding -nostdinc++ --target=aarch64_be-none-eabi -march=armv8.2-a+fp16 -std=c++17 -xc++ %s #include diff --git a/clang/test/Headers/arm-neon-header.c b/clang/test/Headers/arm-neon-header.c index 1da5ed052c329..e8ab33d7702c9 100644 --- a/clang/test/Headers/arm-neon-header.c +++ b/clang/test/Headers/arm-neon-header.c @@ -10,14 +10,14 @@ // RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding --target=aarch64_be-none-eabi -march=armv8.2-a+fp16 -std=c99 -xc %s // RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding --target=aarch64_be-none-eabi -march=armv8.2-a+fp16 -std=c11 -xc %s -// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding --target=aarch64-none-eabi -march=armv8.2-a+fp16 -std=c++98 -xc++ %s -// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding --target=aarch64-none-eabi -march=armv8.2-a+fp16 -std=c++11 -xc++ %s -// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding --target=aarch64-none-eabi -march=armv8.2-a+fp16 -std=c++14 -xc++ %s -// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding --target=aarch64-none-eabi -march=armv8.2-a+fp16 -std=c++17 -xc++ %s +// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding -nostdinc++ --target=aarch64-none-eabi -march=armv8.2-a+fp16 -std=c++98 -xc++ %s +// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding -nostdinc++ --target=aarch64-none-eabi -march=armv8.2-a+fp16 -std=c++11 -xc++ %s +// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding -nostdinc++ --target=aarch64-none-eabi -march=armv8.2-a+fp16 -std=c++14 -xc++ %s +// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding -nostdinc++ --target=aarch64-none-eabi -march=armv8.2-a+fp16 -std=c++17 -xc++ %s -// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding --target=aarch64_be-none-eabi -march=armv8.2-a+fp16 -std=c++98 -xc++ %s -// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding --target=aarch64_be-none-eabi -march=armv8.2-a+fp16 -std=c++11 -xc++ %s -// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding --target=aarch64_be-none-eabi -march=armv8.2-a+fp16 -std=c++14 -xc++ %s -// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding --target=aarch64_be-none-eabi -march=armv8.2-a+fp16 -std=c++17 -xc++ %s +// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding -nostdinc++ --target=aarch64_be-none-eabi -march=armv8.2-a+fp16 -std=c++98 -xc++ %s +// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding -nostdinc++ --target=aarch64_be-none-eabi -march=armv8.2-a+fp16 -std=c++11 -xc++ %s +// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding -nostdinc++ --target=aarch64_be-none-eabi -march=armv8.2-a+fp16 -std=c++14 -xc++ %s +// RUN: %clang -fsyntax-only -Wall -Werror -ffreestanding -nostdinc++ --target=aarch64_be-none-eabi -march=armv8.2-a+fp16 -std=c++17 -xc++ %s #include diff --git a/clang/test/Headers/xmmintrin.c b/clang/test/Headers/xmmintrin.c index c617504b85da5..9b8993e6bb679 100644 --- a/clang/test/Headers/xmmintrin.c +++ b/clang/test/Headers/xmmintrin.c @@ -22,7 +22,7 @@ __m64 test_mm_cvtps_pi16(__m128 a) { // Make sure that including also makes 's content available. // This is an ugly hack for GCC compatibility. -__m128 test_xmmintrin_provides_emmintrin(__m128d __a, __m128d __b) { +__m128d test_xmmintrin_provides_emmintrin(__m128d __a, __m128d __b) { return _mm_add_sd(__a, __b); } diff --git a/clang/test/Import/cxx-anon-namespace/test.cpp b/clang/test/Import/cxx-anon-namespace/test.cpp index 0cbf08c181eea..7331a6bc4315e 100644 --- a/clang/test/Import/cxx-anon-namespace/test.cpp +++ b/clang/test/Import/cxx-anon-namespace/test.cpp @@ -2,9 +2,13 @@ // The implicit UsingDirectiveDecls for the anonymous namespaces are created by the Sema. -// CHECK: NamespaceDecl +// There might be another builtin namespace before our first namespace, so we can't +// just look for NamespaceDecl. Instead look for the first line of F.cpp (which only +// contains the namespace we are looking for but no other decl). +// CHECK: F.cpp:1:1 // The nested anonymous namespace. // CHECK-NEXT: NamespaceDecl +// CHECK-SAME: // CHECK: FunctionDecl // CHECK-SAME: func4 // CHECK-NEXT: CompoundStmt diff --git a/clang/test/Index/Core/index-source.cpp b/clang/test/Index/Core/index-source.cpp index b57b156072828..f159b1c884304 100644 --- a/clang/test/Index/Core/index-source.cpp +++ b/clang/test/Index/Core/index-source.cpp @@ -461,12 +461,12 @@ struct StaticAssertRef { }; static_assert(StaticAssertRef::constVar, "index static asserts"); -// CHECK: [[@LINE-1]]:32 | static-property/C++ | constVar | c:@S@StaticAssertRef@constVar | __ZN15StaticAssertRef8constVarE | Ref | rel: 0 +// CHECK: [[@LINE-1]]:32 | static-property/C++ | constVar | c:@S@StaticAssertRef@constVar | __ZN15StaticAssertRef8constVarE | Ref,Read | rel: 0 // CHECK: [[@LINE-2]]:15 | struct/C++ | StaticAssertRef | c:@S@StaticAssertRef | | Ref | rel: 0 void staticAssertInFn() { static_assert(StaticAssertRef::constVar, "index static asserts"); -// CHECK: [[@LINE-1]]:34 | static-property/C++ | constVar | c:@S@StaticAssertRef@constVar | __ZN15StaticAssertRef8constVarE | Ref,RelCont | rel: 1 +// CHECK: [[@LINE-1]]:34 | static-property/C++ | constVar | c:@S@StaticAssertRef@constVar | __ZN15StaticAssertRef8constVarE | Ref,Read,RelCont | rel: 1 // CHECK-NEXT: RelCont | staticAssertInFn | c:@F@staticAssertInFn# // CHECK: [[@LINE-3]]:17 | struct/C++ | StaticAssertRef | c:@S@StaticAssertRef | | Ref,RelCont | rel: 1 // CHECK-NEXT: RelCont | staticAssertInFn | c:@F@staticAssertInFn# diff --git a/clang/test/Index/crash-recovery-modules.m b/clang/test/Index/crash-recovery-modules.m index 85b39cf58df74..82979f794bea8 100644 --- a/clang/test/Index/crash-recovery-modules.m +++ b/clang/test/Index/crash-recovery-modules.m @@ -31,5 +31,5 @@ void test() { // ...and with module building successful. // RUN: env CINDEXTEST_FAILONERROR=1 not c-index-test -test-load-source all -fmodules -fmodules-cache-path=%t -Xclang -fdisable-module-hash -I %S/Inputs/Headers -DLIBCLANG_CRASH %s > /dev/null 2> %t.err // RUN: FileCheck < %t.err -check-prefix=CHECK-LIBCLANG-CRASH %s -// CHECK-LIBCLANG-CRASH: libclang: crash detected during parsing -// CHECK-LIBCLANG-CRASH: Unable to load translation unit! +// CHECK-LIBCLANG-CRASH-DAG: libclang: crash detected during parsing +// CHECK-LIBCLANG-CRASH-DAG: Unable to load translation unit! diff --git a/clang/test/Index/index-templates.cpp b/clang/test/Index/index-templates.cpp index 424a638ffbb8f..b06ea7fed7a84 100644 --- a/clang/test/Index/index-templates.cpp +++ b/clang/test/Index/index-templates.cpp @@ -155,7 +155,7 @@ using alias = T; // CHECK-LOAD: index-templates.cpp:36:44: DeclRefExpr=OneDimension:35:16 Extent=[36:44 - 36:56] // CHECK-LOAD: index-templates.cpp:40:8: ClassTemplate=storage:40:8 (Definition) Extent=[39:1 - 40:19] // CHECK-LOAD: index-templates.cpp:39:45: TemplateTemplateParameter=DataStructure:39:45 (Definition) Extent=[39:10 - 39:66] -// CHECK-LOAD: index-templates.cpp:39:19: TemplateTypeParameter=:39:19 (Definition) Extent=[39:19 - 39:27] +// CHECK-LOAD: index-templates.cpp:39:27: TemplateTypeParameter=:39:27 (Definition) Extent=[39:19 - 39:27] // CHECK-LOAD: index-templates.cpp:39:37: NonTypeTemplateParameter=:39:37 (Definition) Extent=[39:29 - 39:37] // CHECK-LOAD: index-templates.cpp:39:61: TemplateRef=array:37:8 Extent=[39:61 - 39:66] // CHECK-LOAD: index-templates.cpp:42:18: TypedefDecl=Unsigned:42:18 (Definition) Extent=[42:1 - 42:26] diff --git a/clang/test/Lexer/cross-windows-on-linux-default.cpp b/clang/test/Lexer/cross-windows-on-linux-default.cpp index 520b419a71003..bcd4999a663a1 100644 --- a/clang/test/Lexer/cross-windows-on-linux-default.cpp +++ b/clang/test/Lexer/cross-windows-on-linux-default.cpp @@ -1,4 +1,4 @@ -// RUN: not %clang_cc1 -fsyntax-only -fms-compatibility -triple i686-win32 %s 2>&1 \ +// RUN: not %clang_cc1 -fsyntax-only -fms-extensions -triple i686-win32 %s 2>&1 \ // RUN: | FileCheck %s #include "Inputs\success.h" diff --git a/clang/test/Lexer/cross-windows-on-linux.cpp b/clang/test/Lexer/cross-windows-on-linux.cpp index c6dcbca540561..3932ffcb33c82 100644 --- a/clang/test/Lexer/cross-windows-on-linux.cpp +++ b/clang/test/Lexer/cross-windows-on-linux.cpp @@ -6,10 +6,8 @@ // CHECK: #include "Inputs\success.h" // CHECK: ^ -// expected to fail on windows as the inclusion would succeed and the -// compilation will fail due to the '#error success'. -// XFAIL: windows-msvc - -// This test may or may not fail since 'Inputs\success.h' is passed -// to Win32 APIs on Windows. -// REQUIRES: disabled +// This test is really checking that we *don't* replace backslashes with slashes +// on non-Windows unless -fms-extensions is passed. It won't fail in this way on +// Windows because the filesystem will interpret the backslash as a directory +// separator. +// UNSUPPORTED: system-windows diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp index cda6f888cbce5..868ec24a1d9f7 100644 --- a/clang/test/Lexer/cxx-features.cpp +++ b/clang/test/Lexer/cxx-features.cpp @@ -34,6 +34,10 @@ #error "wrong value for __cpp_char8_t" #endif +#if check(constexpr_dynamic_alloc, 0, 0, 0, 0, 201907) +#error "wrong value for __cpp_constexpr_dynamic_alloc" +#endif + #if check(constinit, 0, 0, 0, 0, 201907) #error "wrong value for __cpp_constinit" #endif diff --git a/clang/test/Lexer/has_feature_leak_sanitizer.cpp b/clang/test/Lexer/has_feature_leak_sanitizer.cpp new file mode 100644 index 0000000000000..00ca96f4ba73f --- /dev/null +++ b/clang/test/Lexer/has_feature_leak_sanitizer.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -E -fsanitize=leak %s -o - | FileCheck --check-prefix=CHECK-LSAN %s +// RUN: %clang_cc1 -E %s -o - | FileCheck --check-prefix=CHECK-NO-LSAN %s + +#if __has_feature(leak_sanitizer) +int LeakSanitizerEnabled(); +#else +int LeakSanitizerDisabled(); +#endif + +// CHECK-LSAN: LeakSanitizerEnabled +// CHECK-NO-LSAN: LeakSanitizerDisabled diff --git a/clang/test/Misc/diag-macro-backtrace2.c b/clang/test/Misc/diag-macro-backtrace2.c index 64fc5f6307278..ca13dca074d83 100644 --- a/clang/test/Misc/diag-macro-backtrace2.c +++ b/clang/test/Misc/diag-macro-backtrace2.c @@ -1,4 +1,4 @@ -// RUN: not %clang -cc1 -fsyntax-only %s 2>&1 | FileCheck %s +// RUN: not %clang_cc1 -fsyntax-only %s 2>&1 | FileCheck %s #define a b #define b c diff --git a/clang/test/Misc/driver-verify.c b/clang/test/Misc/driver-verify.c index fa31f820b1087..f858a3db85cb5 100644 --- a/clang/test/Misc/driver-verify.c +++ b/clang/test/Misc/driver-verify.c @@ -1,5 +1,5 @@ // RUN: not %clang -verify %s 2>&1 | FileCheck %s -// RUN: %clang -cc1 -verify %s +// RUN: %clang_cc1 -verify %s // expected-no-diagnostics // Test that -verify is strictly rejected as unknown by the driver. diff --git a/clang/test/Misc/target-invalid-cpu-note.c b/clang/test/Misc/target-invalid-cpu-note.c index e7dc6ed478b6d..4f8158e50229e 100644 --- a/clang/test/Misc/target-invalid-cpu-note.c +++ b/clang/test/Misc/target-invalid-cpu-note.c @@ -60,7 +60,7 @@ // RUN: not %clang_cc1 -triple systemz--- -target-cpu not-a-cpu -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix SYSTEMZ // SYSTEMZ: error: unknown target CPU 'not-a-cpu' // SYSTEMZ: note: valid target CPU values are: arch8, z10, arch9, z196, arch10, -// SYSTEMZ-SAME: zEC12, arch11, z13, arch12, z14, arch13 +// SYSTEMZ-SAME: zEC12, arch11, z13, arch12, z14, arch13, z15 // RUN: not %clang_cc1 -triple sparc--- -target-cpu not-a-cpu -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix SPARC // SPARC: error: unknown target CPU 'not-a-cpu' diff --git a/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/Headers/Bar.h b/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/Headers/Bar.h new file mode 100644 index 0000000000000..3ce482e6f243a --- /dev/null +++ b/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/Headers/Bar.h @@ -0,0 +1 @@ +// interface-diagnose-missing-import/Foo.framework/Headers/Bar.h diff --git a/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/Headers/Foo.h b/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/Headers/Foo.h new file mode 100644 index 0000000000000..c9c40986d9ed3 --- /dev/null +++ b/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/Headers/Foo.h @@ -0,0 +1,2 @@ +#import +#import diff --git a/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/Modules/module.modulemap b/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/Modules/module.modulemap new file mode 100644 index 0000000000000..ebb4fa6e90248 --- /dev/null +++ b/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/Modules/module.modulemap @@ -0,0 +1,6 @@ +// interface-diagnose-missing-import/Foo.framework/Modules/module.modulemap +framework module Foo { + umbrella header "Foo.h" + export * + module * { export * } +} diff --git a/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/PrivateHeaders/RandoPriv.h b/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/PrivateHeaders/RandoPriv.h new file mode 100644 index 0000000000000..3e195fd85b36e --- /dev/null +++ b/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/PrivateHeaders/RandoPriv.h @@ -0,0 +1,4 @@ +@interface NSObject +@end +@interface Buggy : NSObject +@end diff --git a/clang/test/Modules/ModuleDebugInfo.cpp b/clang/test/Modules/ModuleDebugInfo.cpp index 6fe546f7013e5..40136978017cb 100644 --- a/clang/test/Modules/ModuleDebugInfo.cpp +++ b/clang/test/Modules/ModuleDebugInfo.cpp @@ -12,7 +12,7 @@ // PCH: // RUN: %clang_cc1 -triple %itanium_abi_triple -x c++ -std=c++11 -debugger-tuning=lldb -emit-pch -fmodule-format=obj -I %S/Inputs -o %t.pch %S/Inputs/DebugCXX.h -mllvm -debug-only=pchcontainer &>%t-pch.ll -// RUN: cat %t-pch.ll | FileCheck %s +// RUN: cat %t-pch.ll | FileCheck --check-prefix=CHECK-CXX %s // RUN: cat %t-pch.ll | FileCheck --check-prefix=CHECK-NEG %s #ifdef MODULES @@ -23,6 +23,7 @@ // CHECK-MOD: distinct !DICompileUnit(language: DW_LANG_{{.*}}C_plus_plus, // CHECK: distinct !DICompileUnit(language: DW_LANG_{{.*}}C_plus_plus, +// CHECK-CXX: distinct !DICompileUnit(language: DW_LANG_C_plus_plus_11, // CHECK-SAME: isOptimized: false, // CHECK-NOT: splitDebugFilename: // CHECK-SAME: dwoId: diff --git a/clang/test/Modules/builtin-import.mm b/clang/test/Modules/builtin-import.mm index cbc312b059042..8a27cb358484c 100644 --- a/clang/test/Modules/builtin-import.mm +++ b/clang/test/Modules/builtin-import.mm @@ -1,5 +1,5 @@ // RUN: rm -rf %t -// RUN: %clang -cc1 -fsyntax-only -nobuiltininc -nostdinc++ -isysroot %S/Inputs/libc-libcxx/sysroot -isystem %S/Inputs/libc-libcxx/sysroot/usr/include/c++/v1 -isystem %S/Inputs/libc-libcxx/sysroot/usr/include -std=c++11 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -x objective-c++ -fmodules-local-submodule-visibility %s +// RUN: %clang_cc1 -fsyntax-only -nobuiltininc -nostdinc++ -isysroot %S/Inputs/libc-libcxx/sysroot -isystem %S/Inputs/libc-libcxx/sysroot/usr/include/c++/v1 -isystem %S/Inputs/libc-libcxx/sysroot/usr/include -std=c++11 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -x objective-c++ -fmodules-local-submodule-visibility %s #include #include diff --git a/clang/test/Modules/interface-diagnose-missing-import.m b/clang/test/Modules/interface-diagnose-missing-import.m new file mode 100644 index 0000000000000..5bbac36423006 --- /dev/null +++ b/clang/test/Modules/interface-diagnose-missing-import.m @@ -0,0 +1,11 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 %s -fsyntax-only -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -F%S/Inputs/interface-diagnose-missing-import -verify +@interface Buggy +@end + +@import Foo.Bar; + +@interface Buggy (MyExt) // expected-error {{definition of 'Buggy' must be imported from module 'Foo' before it is required}} +@end + +// expected-note@Foo/RandoPriv.h:3{{previous definition is here}} diff --git a/clang/test/Modules/preprocess-module.cpp b/clang/test/Modules/preprocess-module.cpp index b0cbac18e7807..ee909a811836f 100644 --- a/clang/test/Modules/preprocess-module.cpp +++ b/clang/test/Modules/preprocess-module.cpp @@ -51,7 +51,7 @@ // RUN: %clang_cc1 -fmodules -fmodule-file=%t/file.rewrite.pcm %s -I%t -verify -fno-modules-error-recovery -DFILE_REWRITE -DINCLUDE -I%S/Inputs/preprocess // // Check that we can preprocess this user of the .pcm file. -// RUN: %clang_cc1 -fmodules -fmodule-file=%t/file.pcm %s -I%t -E -frewrite-imports -o %t/preprocess-module.ii +// RUN: %clang_cc1 -fmodules -fmodule-file=%t/file.pcm %s -I%t -E -frewrite-imports -DFILE_REWRITE_FULL -o %t/preprocess-module.ii // RUN: %clang_cc1 -fmodules %t/preprocess-module.ii -verify -fno-modules-error-recovery -DFILE_REWRITE_FULL // // Check that language / header search options are ignored when preprocessing from a .pcm file. diff --git a/clang/test/Modules/umbrella-header-include-builtin.mm b/clang/test/Modules/umbrella-header-include-builtin.mm index b14c73705ca86..9f8a45e1790e7 100644 --- a/clang/test/Modules/umbrella-header-include-builtin.mm +++ b/clang/test/Modules/umbrella-header-include-builtin.mm @@ -1,5 +1,5 @@ // RUN: rm -rf %t -// RUN: %clang -cc1 -fsyntax-only -nobuiltininc -nostdinc++ -isysroot %S/Inputs/libc-libcxx/sysroot -isystem %S/Inputs/libc-libcxx/sysroot/usr/include/c++/v1 -isystem %S/Inputs/libc-libcxx/sysroot/usr/include -F%S/Inputs/libc-libcxx/sysroot/Frameworks -std=c++11 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -x objective-c++ %s +// RUN: %clang_cc1 -fsyntax-only -nobuiltininc -nostdinc++ -isysroot %S/Inputs/libc-libcxx/sysroot -isystem %S/Inputs/libc-libcxx/sysroot/usr/include/c++/v1 -isystem %S/Inputs/libc-libcxx/sysroot/usr/include -F%S/Inputs/libc-libcxx/sysroot/Frameworks -std=c++11 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -x objective-c++ %s #include diff --git a/clang/test/OpenMP/declare_mapper_messages.c b/clang/test/OpenMP/declare_mapper_messages.c index 51b761e224204..a7400b682f8ae 100644 --- a/clang/test/OpenMP/declare_mapper_messages.c +++ b/clang/test/OpenMP/declare_mapper_messages.c @@ -36,6 +36,8 @@ int fun(int arg) { { #pragma omp declare mapper(id: struct vec v) map(v.len) allocate(v) // expected-error {{unexpected OpenMP clause 'allocate' in directive '#pragma omp declare mapper'}} struct vec vv, v1; + struct vec arr[10]; + double d; #pragma omp target map(mapper) // expected-error {{use of undeclared identifier 'mapper'}} {} #pragma omp target map(mapper:vv) // expected-error {{expected '(' after 'mapper'}} @@ -46,9 +48,13 @@ int fun(int arg) { {} #pragma omp target map(mapper(ab) :vv) // expected-error {{missing map type}} expected-error {{cannot find a valid user-defined mapper for type 'struct vec' with name 'ab'}} {} +#pragma omp target map(mapper(ab) :arr[0:2]) // expected-error {{missing map type}} expected-error {{cannot find a valid user-defined mapper for type 'struct vec' with name 'ab'}} + {} #pragma omp target map(mapper(aa) :vv) // expected-error {{missing map type}} {} -#pragma omp target map(mapper(aa) to:vv) map(close mapper(aa) from:v1) +#pragma omp target map(mapper(aa) to:d) // expected-error {{mapper type must be of struct, union or class type}} + {} +#pragma omp target map(mapper(aa) to:vv) map(close mapper(aa) from:v1) map(mapper(aa) to:arr[0]) {} #pragma omp target update to(mapper) // expected-error {{expected '(' after 'mapper'}} expected-error {{expected expression}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} @@ -57,7 +63,10 @@ int fun(int arg) { #pragma omp target update to(mapper(:vv) // expected-error {{illegal OpenMP user-defined mapper identifier}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} #pragma omp target update to(mapper(aa :vv) // expected-error {{expected ')'}} expected-note {{to match this '('}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} #pragma omp target update to(mapper(ab):vv) // expected-error {{cannot find a valid user-defined mapper for type 'struct vec' with name 'ab'}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} -#pragma omp target update to(mapper(aa):vv) +#pragma omp target update to(mapper(ab):arr[0:2]) // expected-error {{cannot find a valid user-defined mapper for type 'struct vec' with name 'ab'}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} +#pragma omp target update to(mapper(aa) a:vv) // expected-warning {{missing ':' after ) - ignoring}} +#pragma omp target update to(mapper(aa):d) // expected-error {{mapper type must be of struct, union or class type}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} +#pragma omp target update to(mapper(aa):vv) to(mapper(aa):arr[0]) #pragma omp target update from(mapper) // expected-error {{expected '(' after 'mapper'}} expected-error {{expected expression}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} #pragma omp target update from(mapper() // expected-error {{illegal OpenMP user-defined mapper identifier}} expected-error {{expected expression}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} @@ -65,8 +74,10 @@ int fun(int arg) { #pragma omp target update from(mapper(:vv) // expected-error {{illegal OpenMP user-defined mapper identifier}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} #pragma omp target update from(mapper(aa :vv) // expected-error {{expected ')'}} expected-note {{to match this '('}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} #pragma omp target update from(mapper(ab):vv) // expected-error {{cannot find a valid user-defined mapper for type 'struct vec' with name 'ab'}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} +#pragma omp target update from(mapper(ab):arr[0:2]) // expected-error {{cannot find a valid user-defined mapper for type 'struct vec' with name 'ab'}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} #pragma omp target update from(mapper(aa) a:vv) // expected-warning {{missing ':' after ) - ignoring}} -#pragma omp target update from(mapper(aa):vv) +#pragma omp target update from(mapper(aa):d) // expected-error {{mapper type must be of struct, union or class type}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} +#pragma omp target update from(mapper(aa):vv) from(mapper(aa):arr[0]) } } return arg; diff --git a/clang/test/OpenMP/declare_mapper_messages.cpp b/clang/test/OpenMP/declare_mapper_messages.cpp index bcb5ac463a0db..7b50fb2a52c27 100644 --- a/clang/test/OpenMP/declare_mapper_messages.cpp +++ b/clang/test/OpenMP/declare_mapper_messages.cpp @@ -64,6 +64,7 @@ int fun(int arg) { { #pragma omp declare mapper(id: vec v) map(v.len) vec vv, v1; + vec arr[10]; #pragma omp target map(mapper) // expected-error {{use of undeclared identifier 'mapper'}} {} #pragma omp target map(mapper:vv) // expected-error {{expected '(' after 'mapper'}} @@ -82,7 +83,9 @@ int fun(int arg) { {} #pragma omp target map(mapper(N1::aa) alloc:vv) // expected-error {{cannot find a valid user-defined mapper for type 'vec' with name 'aa'}} {} -#pragma omp target map(mapper(aa) to:vv) map(close mapper(aa) from:v1) +#pragma omp target map(mapper(N1::aa) alloc:arr[0:2]) // expected-error {{cannot find a valid user-defined mapper for type 'vec' with name 'aa'}} + {} +#pragma omp target map(mapper(aa) to:vv) map(close mapper(aa) from:v1) map(mapper(aa) to:arr[0]) {} #pragma omp target map(mapper(N1::stack::id) to:vv) {} @@ -96,8 +99,9 @@ int fun(int arg) { #pragma omp target update to(mapper(N1:: :vv) // expected-error {{illegal OpenMP user-defined mapper identifier}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} #pragma omp target update to(mapper(N1::aa) :vv) // expected-error {{cannot find a valid user-defined mapper for type 'vec' with name 'aa'}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} #pragma omp target update to(mapper(ab):vv) // expected-error {{cannot find a valid user-defined mapper for type 'vec' with name 'ab'}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} +#pragma omp target update to(mapper(ab):arr[0:2]) // expected-error {{cannot find a valid user-defined mapper for type 'vec' with name 'ab'}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} #pragma omp target update to(mapper(aa) a:vv) // expected-warning {{missing ':' after ) - ignoring}} -#pragma omp target update to(mapper(aa):vv) +#pragma omp target update to(mapper(aa):vv) to(mapper(aa):arr[0]) #pragma omp target update to(mapper(N1::stack::id) :vv) #pragma omp target update from(mapper) // expected-error {{expected '(' after 'mapper'}} expected-error {{expected expression}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} @@ -109,8 +113,9 @@ int fun(int arg) { #pragma omp target update from(mapper(N1:: :vv) // expected-error {{illegal OpenMP user-defined mapper identifier}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} #pragma omp target update from(mapper(N1::aa) :vv) // expected-error {{cannot find a valid user-defined mapper for type 'vec' with name 'aa'}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} #pragma omp target update from(mapper(ab):vv) // expected-error {{cannot find a valid user-defined mapper for type 'vec' with name 'ab'}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} +#pragma omp target update from(mapper(ab):arr[0:2]) // expected-error {{cannot find a valid user-defined mapper for type 'vec' with name 'ab'}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}} #pragma omp target update from(mapper(aa) a:vv) // expected-warning {{missing ':' after ) - ignoring}} -#pragma omp target update from(mapper(aa):vv) +#pragma omp target update from(mapper(aa):vv) from(mapper(aa):arr[0]) #pragma omp target update from(mapper(N1::stack::id) :vv) } #pragma omp declare mapper(id: vec v) map(v.len) // expected-error {{redefinition of user-defined mapper for type 'vec' with name 'id'}} diff --git a/clang/test/OpenMP/declare_simd_ast_print.cpp b/clang/test/OpenMP/declare_simd_ast_print.cpp index c09f8b42e6d53..565dc2dfc04d1 100644 --- a/clang/test/OpenMP/declare_simd_ast_print.cpp +++ b/clang/test/OpenMP/declare_simd_ast_print.cpp @@ -21,6 +21,15 @@ void add_1(float *d) __attribute__((cold)); // CHECK-NEXT: void add_1(float *d) __attribute__((cold)); // +#pragma omp declare simd aligned(hp, hp2:V) +#pragma omp declare simd aligned(hp, hp2:V) +template void h(C *hp, C *hp2, C *hq, C *lin) { +} +// CHECK-NEXT: #pragma omp declare simd aligned(hp: V) aligned(hp2: V) +// CHECK-NEXT: #pragma omp declare simd aligned(hp: V) aligned(hp2: V) +// CHECK-NEXT: template void h(C *hp, C *hp2, C *hq, C *lin) { +// CHECK-NEXT: } + #pragma omp declare simd aligned(hp, hp2) template void h(C *hp, C *hp2, C *hq, C *lin) { } diff --git a/clang/test/OpenMP/declare_target_codegen.cpp b/clang/test/OpenMP/declare_target_codegen.cpp index e24e58e10aa24..dd4ab3c8248e6 100644 --- a/clang/test/OpenMP/declare_target_codegen.cpp +++ b/clang/test/OpenMP/declare_target_codegen.cpp @@ -7,6 +7,15 @@ // RUN: %clang_cc1 -verify -fopenmp -x c++ -triple powerpc64le-unknown-unknown -fopenmp-targets=powerpc64le-ibm-linux-gnu -emit-llvm-bc %s -o %t-ppc-host.bc -fopenmp-version=50 -DOMP5 // RUN: %clang_cc1 -verify -fopenmp -x c++ -triple powerpc64le-unknown-unknown -fopenmp-targets=powerpc64le-ibm-linux-gnu -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-ppc-host.bc -o - -fopenmp-version=50 -DOMP5 | FileCheck %s --check-prefix DEV5 +// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple powerpc64le-unknown-unknown -emit-llvm %s -o - -fopenmp-version=50 -DOMP5 | FileCheck %s --check-prefix KMPC-ONLY +// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple powerpc64le-unknown-unknown -emit-llvm-bc %s -o %t-ppc-host.bc -fopenmp-version=50 -DOMP5 +// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple powerpc64le-unknown-unknown -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-ppc-host.bc -o - -fopenmp-version=50 -DOMP5 | FileCheck %s --check-prefix KMPC-ONLY + +// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple powerpc64le-unknown-unknown -emit-llvm-bc %s -o %t-ppc-host.bc +// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple powerpc64le-unknown-unknown -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-ppc-host.bc -o -| FileCheck %s --check-prefix KMPC-ONLY +// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple powerpc64le-unknown-unknown -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-ppc-host.bc -emit-pch -o %t +// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple powerpc64le-unknown-unknown -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-ppc-host.bc -include-pch %t -verify -o - | FileCheck %s --check-prefix KMPC-ONLY + // RUN: %clang_cc1 -verify -fopenmp-simd -x c++ -triple powerpc64le-unknown-unknown -fopenmp-targets=powerpc64le-ibm-linux-gnu -emit-llvm %s -o - -fopenmp-version=50 -DOMP5 | FileCheck %s --check-prefix SIMD-ONLY // RUN: %clang_cc1 -verify -fopenmp-simd -x c++ -triple powerpc64le-unknown-unknown -fopenmp-targets=powerpc64le-ibm-linux-gnu -emit-llvm-bc %s -o %t-ppc-host.bc -fopenmp-version=50 -DOMP5 // RUN: %clang_cc1 -verify -fopenmp-simd -x c++ -triple powerpc64le-unknown-unknown -fopenmp-targets=powerpc64le-ibm-linux-gnu -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-ppc-host.bc -o - -fopenmp-version=50 -DOMP5 | FileCheck %s --check-prefix SIMD-ONLY @@ -19,6 +28,7 @@ // expected-no-diagnostics // SIMD-ONLY-NOT: {{__kmpc|__tgt}} +// KMPC-ONLY-NOT: __tgt // CHECK-NOT: define {{.*}}{{baz1|baz4|maini1|Base|virtual_}} // CHECK-DAG: Bake diff --git a/clang/test/OpenMP/declare_target_link_codegen.cpp b/clang/test/OpenMP/declare_target_link_codegen.cpp index 041dc6d02cebe..613c837afad09 100644 --- a/clang/test/OpenMP/declare_target_link_codegen.cpp +++ b/clang/test/OpenMP/declare_target_link_codegen.cpp @@ -26,7 +26,7 @@ // HOST: [[SIZES:@.+]] = private unnamed_addr constant [3 x i64] [i64 4, i64 4, i64 4] // HOST: [[MAPTYPES:@.+]] = private unnamed_addr constant [3 x i64] [i64 35, i64 531, i64 531] // HOST: @.omp_offloading.entry_name{{.*}} = internal unnamed_addr constant [{{[0-9]+}} x i8] c"c_decl_tgt_ref_ptr\00" -// HOST: @.omp_offloading.entry.c_decl_tgt_ref_ptr = weak constant %struct.__tgt_offload_entry { i8* bitcast (i32** @c_decl_tgt_ref_ptr to i8*), i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @.omp_offloading.entry_name, i32 0, i32 0), i64 8, i32 1, i32 0 }, section ".omp_offloading.entries", align 1 +// HOST: @.omp_offloading.entry.c_decl_tgt_ref_ptr = weak constant %struct.__tgt_offload_entry { i8* bitcast (i32** @c_decl_tgt_ref_ptr to i8*), i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @.omp_offloading.entry_name, i32 0, i32 0), i64 8, i32 1, i32 0 }, section "omp_offloading_entries", align 1 // DEVICE-NOT: internal unnamed_addr constant [{{[0-9]+}} x i8] c"c_{{.*}}_decl_tgt_ref_ptr\00" // HOST: @.omp_offloading.entry_name{{.*}} = internal unnamed_addr constant [{{[0-9]+}} x i8] c"_{{.*}}d_{{.*}}_decl_tgt_ref_ptr\00" // HOST: @.omp_offloading.entry.[[D_PTR]] = weak constant %struct.__tgt_offload_entry { i8* bitcast (i32** @[[D_PTR]] to i8*), i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @.omp_offloading.entry_name{{.*}}, i32 0, i32 0 diff --git a/clang/test/OpenMP/declare_variant_ast_print.c b/clang/test/OpenMP/declare_variant_ast_print.c new file mode 100644 index 0000000000000..ad1ef91eca14c --- /dev/null +++ b/clang/test/OpenMP/declare_variant_ast_print.c @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -verify -fopenmp -x c -std=c99 -ast-print %s -o - | FileCheck %s + +// RUN: %clang_cc1 -verify -fopenmp-simd -x c -std=c99 -ast-print %s -o - | FileCheck %s + +// expected-no-diagnostics + +int foo(void); + +#pragma omp declare variant(foo) match(xxx={}, yyy={ccc}) +#pragma omp declare variant(foo) match(xxx={vvv}) +#pragma omp declare variant(foo) match(implementation={vendor(ibm)}, implementation={vendor(llvm)}) +#pragma omp declare variant(foo) match(implementation={vendor(unknown)}) +#pragma omp declare variant(foo) match(implementation={vendor(score(5): ibm)}) +int bar(void); + +// CHECK: int foo(); +// CHECK-NEXT: #pragma omp declare variant(foo) match(implementation={vendor(score(5):ibm)}) +// CHECK-NEXT: #pragma omp declare variant(foo) match(implementation={vendor(unknown)}) +// CHECK-NEXT: #pragma omp declare variant(foo) match(implementation={vendor(ibm)}) +// CHECK-NEXT: #pragma omp declare variant(foo) match(implementation={vendor(llvm)}) +// CHECK-NEXT: int bar(); diff --git a/clang/test/OpenMP/declare_variant_ast_print.cpp b/clang/test/OpenMP/declare_variant_ast_print.cpp new file mode 100644 index 0000000000000..77f03f518552f --- /dev/null +++ b/clang/test/OpenMP/declare_variant_ast_print.cpp @@ -0,0 +1,201 @@ +// RUN: %clang_cc1 -verify -fopenmp -x c++ -std=c++14 -fexceptions -fcxx-exceptions %s -ast-print -o - -Wno-source-uses-openmp | FileCheck %s + +// RUN: %clang_cc1 -verify -fopenmp-simd -x c++ -std=c++14 -fexceptions -fcxx-exceptions %s -ast-print -o - -Wno-source-uses-openmp | FileCheck %s + +// expected-no-diagnostics + +// CHECK: int foo(); +int foo(); + +// CHECK: template T foofoo() { +// CHECK-NEXT: return T(); +// CHECK-NEXT: } +template +T foofoo() { return T(); } + +// CHECK: template<> int foofoo() { +// CHECK-NEXT: return int(); +// CHECK-NEXT: } + +// CHECK: #pragma omp declare variant(foofoo) match(implementation={vendor(score(5):ibm)}) +// CHECK-NEXT: #pragma omp declare variant(foofoo) match(implementation={vendor(unknown)}) +// CHECK-NEXT: #pragma omp declare variant(foofoo) match(implementation={vendor(ibm)}) +// CHECK-NEXT: #pragma omp declare variant(foofoo) match(implementation={vendor(llvm)}) +// CHECK-NEXT: int bar(); +#pragma omp declare variant(foofoo ) match(xxx = {}) +#pragma omp declare variant(foofoo ) match(xxx = {vvv}) +#pragma omp declare variant(foofoo ) match(implementation={vendor(ibm)}, implementation={vendor(llvm)}) +#pragma omp declare variant(foofoo ) match(implementation={vendor(unknown)}) +#pragma omp declare variant(foofoo ) match(implementation={vendor(score(5): ibm)}) +int bar(); + +// CHECK: #pragma omp declare variant(foofoo) match(implementation={vendor(score(C + 5):ibm)}) +// CHECK-NEXT: #pragma omp declare variant(foofoo) match(implementation={vendor(unknown)}) +// CHECK-NEXT: #pragma omp declare variant(foofoo) match(implementation={vendor(ibm)}) +// CHECK-NEXT: #pragma omp declare variant(foofoo) match(implementation={vendor(llvm)}) +// CHECK-NEXT: template T barbar(); +#pragma omp declare variant(foofoo ) match(xxx = {}) +#pragma omp declare variant(foofoo ) match(xxx = {vvv}) +#pragma omp declare variant(foofoo ) match(user = {score() : condition()}) +#pragma omp declare variant(foofoo ) match(user = {score() : condition()}, user = {condition()}) +#pragma omp declare variant(foofoo ) match(user = {condition()}) +#pragma omp declare variant(foofoo ) match(user = {condition()}) +#pragma omp declare variant(foofoo ) match(implementation={vendor(ibm)}, implementation={vendor(llvm)}) +#pragma omp declare variant(foofoo ) match(implementation={vendor(unknown)}) +#pragma omp declare variant(foofoo ) match(implementation={vendor(score(C+5): ibm)}) +template +T barbar(); + +// CHECK: #pragma omp declare variant(foofoo) match(implementation={vendor(score(3 + 5):ibm)}) +// CHECK-NEXT: #pragma omp declare variant(foofoo) match(implementation={vendor(unknown)}) +// CHECK-NEXT: #pragma omp declare variant(foofoo) match(implementation={vendor(ibm)}) +// CHECK-NEXT: #pragma omp declare variant(foofoo) match(implementation={vendor(llvm)}) +// CHECK-NEXT: template<> int barbar(); + +// CHECK-NEXT: int baz() { +// CHECK-NEXT: return barbar(); +// CHECK-NEXT: } +int baz() { + return barbar(); +} + +// CHECK: template void h_ref(C *hp, C *hp2, C *hq, C *lin) { +// CHECK-NEXT: } +// CHECK-NEXT: template<> void h_ref(double *hp, double *hp2, double *hq, double *lin) { +// CHECK-NEXT: } +// CHECK-NEXT: template<> void h_ref(float *hp, float *hp2, float *hq, float *lin) { +// CHECK-NEXT: } +template +void h_ref(C *hp, C *hp2, C *hq, C *lin) { +} + +// CHECK: #pragma omp declare variant(h_ref) match(implementation={vendor(unknown)}) +// CHECK-NEXT: #pragma omp declare variant(h_ref) match(implementation={vendor(ibm)}) +// CHECK-NEXT: #pragma omp declare variant(h_ref) match(implementation={vendor(llvm)}) +// CHECK-NEXT: template void h(C *hp, C *hp2, C *hq, C *lin) { +// CHECK-NEXT: } +#pragma omp declare variant(h_ref ) match(xxx = {}) +#pragma omp declare variant(h_ref ) match(implementation={vendor(ibm)}, implementation={vendor(llvm)}) +#pragma omp declare variant(h_ref ) match(implementation={vendor(unknown)}) +template +void h(C *hp, C *hp2, C *hq, C *lin) { +} + +// CHECK: #pragma omp declare variant(h_ref) match(implementation={vendor(unknown)}) +// CHECK-NEXT: #pragma omp declare variant(h_ref) match(implementation={vendor(ibm)}) +// CHECK-NEXT: #pragma omp declare variant(h_ref) match(implementation={vendor(llvm)}) +// CHECK-NEXT: template<> void h(float *hp, float *hp2, float *hq, float *lin) { +// CHECK-NEXT: } + +// CHECK-NEXT: template<> void h(double *hp, double *hp2, double *hq, double *lin) { +// CHECK-NEXT: h((float *)hp, (float *)hp2, (float *)hq, (float *)lin); +// CHECK-NEXT: } +#pragma omp declare variant(h_ref ) match(xxx = {}) +#pragma omp declare variant(h_ref ) match(implementation={vendor(ibm)}, implementation={vendor(llvm)}) +#pragma omp declare variant(h_ref ) match(implementation={vendor(unknown)}) +template <> +void h(double *hp, double *hp2, double *hq, double *lin) { + h((float *)hp, (float *)hp2, (float *)hq, (float *)lin); +} + +// CHECK: int fn(); +int fn(); +// CHECK: int fn(int); +int fn(int); +// CHECK: #pragma omp declare variant(fn) match(implementation={vendor(unknown)}) +// CHECK-NEXT: #pragma omp declare variant(fn) match(implementation={vendor(ibm)}) +// CHECK-NEXT: #pragma omp declare variant(fn) match(implementation={vendor(llvm)}) +// CHECK-NEXT: int overload(); +#pragma omp declare variant(fn) match(xxx = {}) +#pragma omp declare variant(fn) match(implementation={vendor(ibm)}, implementation={vendor(llvm)}) +#pragma omp declare variant(fn) match(implementation={vendor(unknown)}) +int overload(void); + +// CHECK: int fn_deduced_variant() { +// CHECK-NEXT: return 0; +// CHECK-NEXT: } +auto fn_deduced_variant() { return 0; } +// CHECK: #pragma omp declare variant(fn_deduced_variant) match(implementation={vendor(unknown)}) +// CHECK-NEXT: #pragma omp declare variant(fn_deduced_variant) match(implementation={vendor(ibm)}) +// CHECK-NEXT: #pragma omp declare variant(fn_deduced_variant) match(implementation={vendor(llvm)}) +// CHECK-NEXT: int fn_deduced(); +#pragma omp declare variant(fn_deduced_variant) match(xxx = {}) +#pragma omp declare variant(fn_deduced_variant) match(implementation={vendor(ibm)}, implementation={vendor(llvm)}) +#pragma omp declare variant(fn_deduced_variant) match(implementation={vendor(unknown)}) +int fn_deduced(); + +// CHECK: int fn_deduced_variant1(); +int fn_deduced_variant1(); +// CHECK: #pragma omp declare variant(fn_deduced_variant1) match(implementation={vendor(unknown)}) +// CHECK-NEXT: #pragma omp declare variant(fn_deduced_variant1) match(implementation={vendor(ibm)}) +// CHECK-NEXT: #pragma omp declare variant(fn_deduced_variant1) match(implementation={vendor(llvm)}) +// CHECK-NEXT: int fn_deduced1() { +// CHECK-NEXT: return 0; +// CHECK-NEXT: } +#pragma omp declare variant(fn_deduced_variant1) match(xxx = {}) +#pragma omp declare variant(fn_deduced_variant1) match(implementation={vendor(ibm)}, implementation={vendor(llvm)}) +#pragma omp declare variant(fn_deduced_variant1) match(implementation={vendor(unknown)}) +auto fn_deduced1() { return 0; } + +// CHECK: struct SpecialFuncs { +// CHECK-NEXT: void vd() { +// CHECK-NEXT: } +// CHECK-NEXT: SpecialFuncs(); +// CHECK-NEXT: ~SpecialFuncs() noexcept; +// CHECK-NEXT: void baz() { +// CHECK-NEXT: } +// CHECK-NEXT: void bar() { +// CHECK-NEXT: } +// CHECK-NEXT: void bar(int) { +// CHECK-NEXT: } +// CHECK-NEXT: #pragma omp declare variant(SpecialFuncs::baz) match(implementation={vendor(unknown)}) +// CHECK-NEXT: #pragma omp declare variant(SpecialFuncs::bar) match(implementation={vendor(ibm)}) +// CHECK-NEXT: #pragma omp declare variant(SpecialFuncs::bar) match(implementation={vendor(llvm)}) +// CHECK-NEXT: void foo1() { +// CHECK-NEXT: } +// CHECK-NEXT: #pragma omp declare variant(SpecialFuncs::baz) match(implementation={vendor(unknown)}) +// CHECK-NEXT: void xxx(); +// CHECK-NEXT: } s; +struct SpecialFuncs { + void vd() {} + SpecialFuncs(); + ~SpecialFuncs(); + + void baz() {} + void bar() {} + void bar(int) {} +#pragma omp declare variant(SpecialFuncs::baz) match(xxx = {}) +#pragma omp declare variant(SpecialFuncs::bar) match(xxx = {}) +#pragma omp declare variant(SpecialFuncs::bar) match(implementation={vendor(ibm)}, implementation={vendor(llvm)}) +#pragma omp declare variant(SpecialFuncs::baz) match(implementation={vendor(unknown)}) + void foo1() {} +#pragma omp declare variant(SpecialFuncs::baz) match(implementation={vendor(unknown)}) + void xxx(); +} s; + +// CHECK: #pragma omp declare variant(SpecialFuncs::baz) match(implementation={vendor(unknown)}) +// CHECK-NEXT: void SpecialFuncs::xxx() { +// CHECK-NEXT: } +void SpecialFuncs::xxx() {} + +// CHECK: static void static_f_variant() { +// CHECK-NEXT: } +static void static_f_variant() {} +// CHECK: #pragma omp declare variant(static_f_variant) match(implementation={vendor(unknown)}) +// CHECK-NEXT: #pragma omp declare variant(static_f_variant) match(implementation={vendor(ibm)}) +// CHECK-NEXT: #pragma omp declare variant(static_f_variant) match(implementation={vendor(llvm)}) +// CHECK-NEXT: static void static_f() { +// CHECK-NEXT: } +#pragma omp declare variant(static_f_variant) match(xxx = {}) +#pragma omp declare variant(static_f_variant) match(implementation={vendor(ibm)}, implementation={vendor(llvm)}) +#pragma omp declare variant(static_f_variant) match(implementation={vendor(unknown)}) +static void static_f() {} + +// CHECK: void bazzzz() { +// CHECK-NEXT: s.foo1(); +// CHECK-NEXT: static_f(); +// CHECK-NEXT: } +void bazzzz() { + s.foo1(); + static_f(); +} diff --git a/clang/test/OpenMP/declare_variant_implementation_vendor_codegen.cpp b/clang/test/OpenMP/declare_variant_implementation_vendor_codegen.cpp new file mode 100644 index 0000000000000..ed814b7b28950 --- /dev/null +++ b/clang/test/OpenMP/declare_variant_implementation_vendor_codegen.cpp @@ -0,0 +1,91 @@ +// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple %itanium_abi_triple -emit-llvm %s -fexceptions -fcxx-exceptions -o - -fsanitize-address-use-after-scope | FileCheck %s +// RUN: %clang_cc1 -fopenmp -x c++ -std=c++11 -triple %itanium_abi_triple -fexceptions -fcxx-exceptions -emit-pch -o %t -fopenmp-version=50 %s +// RUN: %clang_cc1 -fopenmp -x c++ -triple %itanium_abi_triple -fexceptions -fcxx-exceptions -std=c++11 -include-pch %t -verify %s -emit-llvm -o - -fopenmp-version=50 | FileCheck %s +// expected-no-diagnostics + +// CHECK-NOT: ret i32 {{1|4}} +// CHECK-DAG: @_Z3barv = {{.*}}alias i32 (), i32 ()* @_Z3foov +// CHECK-DAG: @_ZN16SpecSpecialFuncs6MethodEv = {{.*}}alias i32 (%struct.SpecSpecialFuncs*), i32 (%struct.SpecSpecialFuncs*)* @_ZN16SpecSpecialFuncs7method_Ev +// CHECK-DAG: @_ZN16SpecSpecialFuncs6methodEv = linkonce_odr {{.*}}alias i32 (%struct.SpecSpecialFuncs*), i32 (%struct.SpecSpecialFuncs*)* @_ZN16SpecSpecialFuncs7method_Ev +// CHECK-DAG: @_ZN12SpecialFuncs6methodEv = linkonce_odr {{.*}}alias i32 (%struct.SpecialFuncs*), i32 (%struct.SpecialFuncs*)* @_ZN12SpecialFuncs7method_Ev +// CHECK-DAG: @_Z4callv = {{.*}}alias i32 (), i32 ()* @_Z4testv +// CHECK-DAG: @_ZL9stat_usedv = internal alias i32 (), i32 ()* @_ZL10stat_used_v +// CHECK-DAG: @_ZN12SpecialFuncs6MethodEv = {{.*}}alias i32 (%struct.SpecialFuncs*), i32 (%struct.SpecialFuncs*)* @_ZN12SpecialFuncs7method_Ev +// CHECK-DAG: declare {{.*}}i32 @_Z5bazzzv() +// CHECK-DAG: declare {{.*}}i32 @_Z3bazv() +// CHECK-DAG: ret i32 2 +// CHECK-DAG: ret i32 3 +// CHECK-DAG: ret i32 5 +// CHECK-DAG: ret i32 6 +// CHECK-DAG: ret i32 7 +// CHECK-NOT: ret i32 {{1|4}} + +#ifndef HEADER +#define HEADER + +int foo() { return 2; } + +#pragma omp declare variant(foo) match(implementation = {vendor(llvm)}) +int bar() { return 1; } + +int bazzz(); +#pragma omp declare variant(bazzz) match(implementation = {vendor(llvm)}) +int baz() { return 1; } + +int test(); +#pragma omp declare variant(test) match(implementation = {vendor(llvm)}) +int call() { return 1; } + +static int stat_unused_(); +#pragma omp declare variant(stat_unused_) match(implementation = {vendor(llvm)}) +static int stat_unused() { return 1; } + +static int stat_used_(); +#pragma omp declare variant(stat_used_) match(implementation = {vendor(llvm)}) +static int stat_used() { return 1; } + +int main() { return bar() + baz() + call() + stat_used(); } + +int test() { return 3; } +static int stat_unused_() { return 4; } +static int stat_used_() { return 5; } + +struct SpecialFuncs { + void vd() {} + SpecialFuncs(); + ~SpecialFuncs(); + + int method_() { return 6; } +#pragma omp declare variant(SpecialFuncs::method_) \ + match(implementation = {vendor(llvm)}) + int method() { return 1; } +#pragma omp declare variant(SpecialFuncs::method_) \ + match(implementation = {vendor(llvm)}) + int Method(); +} s; + +int SpecialFuncs::Method() { return 1; } + +struct SpecSpecialFuncs { + void vd() {} + SpecSpecialFuncs(); + ~SpecSpecialFuncs(); + + int method_(); +#pragma omp declare variant(SpecSpecialFuncs::method_) \ + match(implementation = {vendor(llvm)}) + int method() { return 1; } +#pragma omp declare variant(SpecSpecialFuncs::method_) \ + match(implementation = {vendor(llvm)}) + int Method(); +} s1; + +int SpecSpecialFuncs::method_() { return 7; } +int SpecSpecialFuncs::Method() { return 1; } + +void xxx() { + (void)s.method(); + (void)s1.method(); +} + +#endif // HEADER diff --git a/clang/test/OpenMP/declare_variant_messages.c b/clang/test/OpenMP/declare_variant_messages.c new file mode 100644 index 0000000000000..7e69cec1f1b0c --- /dev/null +++ b/clang/test/OpenMP/declare_variant_messages.c @@ -0,0 +1,117 @@ +// RUN: %clang_cc1 -triple=x86_64-pc-win32 -verify -fopenmp -x c -std=c99 -fms-extensions -Wno-pragma-pack %s + +// RUN: %clang_cc1 -triple=x86_64-pc-win32 -verify -fopenmp-simd -x c -std=c99 -fms-extensions -Wno-pragma-pack %s + +// expected-error@+1 {{expected an OpenMP directive}} +#pragma omp declare + +int foo(void); + +#pragma omp declare variant // expected-error {{expected '(' after 'declare variant'}} +#pragma omp declare variant( // expected-error {{expected expression}} expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma omp declare variant(foo // expected-error {{expected ')'}} expected-error {{expected 'match' clause on 'omp declare variant' directive}} expected-note {{to match this '('}} +#pragma omp declare variant(x) // expected-error {{use of undeclared identifier 'x'}} +#pragma omp declare variant(foo) // expected-error {{expected 'match' clause on 'omp declare variant' directive}} +#pragma omp declare variant(foo) // expected-error {{expected 'match' clause on 'omp declare variant' directive}} +#pragma omp declare variant(foo) xxx // expected-error {{expected 'match' clause on 'omp declare variant' directive}} +#pragma omp declare variant(foo) match // expected-error {{expected '(' after 'match'}} +#pragma omp declare variant(foo) match( // expected-error {{expected context selector in 'match' clause on 'omp declare variant' directive}} +#pragma omp declare variant(foo) match() // expected-error {{expected context selector in 'match' clause on 'omp declare variant' directive}} +#pragma omp declare variant(foo) match(xxx) // expected-error {{expected '=' after 'xxx' context selector set name on 'omp declare variant' directive}} +#pragma omp declare variant(foo) match(xxx=) // expected-error {{expected '{' after '='}} +#pragma omp declare variant(foo) match(xxx=yyy) // expected-error {{expected '{' after '='}} +#pragma omp declare variant(foo) match(xxx=yyy}) // expected-error {{expected '{' after '='}} +#pragma omp declare variant(foo) match(xxx={) // expected-error {{expected '}'}} expected-note {{to match this '{'}} +#pragma omp declare variant(foo) match(xxx={}) +#pragma omp declare variant(foo) match(xxx={vvv}) +#pragma omp declare variant(foo) match(xxx={vvv} xxx) // expected-error {{expected ','}} expected-error {{expected '=' after 'xxx' context selector set name on 'omp declare variant' directive}} +#pragma omp declare variant(foo) match(xxx={vvv}) xxx // expected-warning {{extra tokens at the end of '#pragma omp declare variant' are ignored}} +#pragma omp declare variant(foo) match(implementation={xxx}) // expected-warning {{unknown context selector in 'implementation' context selector set of 'omp declare variant' directive, ignored}} +#pragma omp declare variant(foo) match(implementation={vendor}) // expected-error {{expected '(' after 'vendor'}} expected-error {{expected vendor identifier in 'vendor' context selector of 'implementation' selector set of 'omp declare variant' directive}} expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma omp declare variant(foo) match(implementation={vendor(}) // expected-error {{expected vendor identifier in 'vendor' context selector of 'implementation' selector set of 'omp declare variant' directive}} expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma omp declare variant(foo) match(implementation={vendor()}) // expected-error {{expected vendor identifier in 'vendor' context selector of 'implementation' selector set of 'omp declare variant' directive}} +#pragma omp declare variant(foo) match(implementation={vendor(score ibm)}) // expected-error {{expected '(' after 'score'}} expected-warning {{missing ':' after context selector score clause - ignoring}} +#pragma omp declare variant(foo) match(implementation={vendor(score( ibm)}) // expected-error {{expected ')'}} expected-error {{use of undeclared identifier 'ibm'}} expected-error {{expected vendor identifier in 'vendor' context selector of 'implementation' selector set of 'omp declare variant' directive}} expected-warning {{missing ':' after context selector score clause - ignoring}} expected-note {{to match this '('}} +#pragma omp declare variant(foo) match(implementation={vendor(score(2 ibm)}) // expected-error 2 {{expected ')'}} expected-error {{expected vendor identifier in 'vendor' context selector of 'implementation' selector set of 'omp declare variant' directive}} expected-warning {{missing ':' after context selector score clause - ignoring}} expected-note 2 {{to match this '('}} +#pragma omp declare variant(foo) match(implementation={vendor(score(foo()) ibm)}) // expected-warning {{missing ':' after context selector score clause - ignoring}} expected-error {{expression is not an integer constant expression}} +#pragma omp declare variant(foo) match(implementation={vendor(score(5): ibm)}) +int bar(void); + +// expected-error@+2 {{'#pragma omp declare variant' can only be applied to functions}} +#pragma omp declare variant(foo) match(xxx={}) +int a; +// expected-error@+2 {{'#pragma omp declare variant' can only be applied to functions}} +#pragma omp declare variant(foo) match(xxx={}) +#pragma omp threadprivate(a) +int var; +#pragma omp threadprivate(var) + +// expected-error@+2 {{expected an OpenMP directive}} expected-error@+1 {{function declaration is expected after 'declare variant' directive}} +#pragma omp declare variant(foo) match(xxx={}) +#pragma omp declare + +// expected-error@+3 {{function declaration is expected after 'declare variant' directive}} +// expected-error@+1 {{function declaration is expected after 'declare variant' directive}} +#pragma omp declare variant(foo) match(xxx={}) +#pragma omp declare variant(foo) match(xxx={}) +#pragma options align=packed +int main(); + +// expected-error@+3 {{function declaration is expected after 'declare variant' directive}} +// expected-error@+1 {{function declaration is expected after 'declare variant' directive}} +#pragma omp declare variant(foo) match(xxx={}) +#pragma omp declare variant(foo) match(xxx={}) +#pragma init_seg(compiler) +int main(); + +// expected-error@+1 {{single declaration is expected after 'declare variant' directive}} +#pragma omp declare variant(foo) match(xxx={}) +int b, c; + +int no_proto(); + +// expected-error@+3 {{function with '#pragma omp declare variant' must have a prototype}} +// expected-note@+1 {{'#pragma omp declare variant' for function specified here}} +#pragma omp declare variant(no_proto) match(xxx={}) +int no_proto_too(); + +int after_use_variant(void); +int after_use(); +int bar() { + return after_use(); +} + +// expected-warning@+1 {{'#pragma omp declare variant' cannot be applied for function after first usage; the original function might be used}} +#pragma omp declare variant(after_use_variant) match(xxx={}) +int after_use(void); +#pragma omp declare variant(after_use_variant) match(xxx={}) +int defined(void) { return 0; } +int defined1(void) { return 0; } +// expected-warning@+1 {{#pragma omp declare variant' cannot be applied to the function that was defined already; the original function might be used}} +#pragma omp declare variant(after_use_variant) match(xxx={}) +int defined1(void); + + +int diff_cc_variant(void); +// expected-error@+1 {{function with '#pragma omp declare variant' has a different calling convention}} +#pragma omp declare variant(diff_cc_variant) match(xxx={}) +__vectorcall int diff_cc(void); + +int diff_ret_variant(void); +// expected-error@+1 {{function with '#pragma omp declare variant' has a different return type}} +#pragma omp declare variant(diff_ret_variant) match(xxx={}) +void diff_ret(void); + +void marked(void); +void not_marked(void); +// expected-note@+1 {{marked as 'declare variant' here}} +#pragma omp declare variant(not_marked) match(implementation={vendor(unknown)}) +void marked_variant(void); +// expected-warning@+1 {{variant function in '#pragma omp declare variant' is itself marked as '#pragma omp declare variant'}} +#pragma omp declare variant(marked_variant) match(xxx={}) +void marked(void); + +// expected-error@+1 {{function declaration is expected after 'declare variant' directive}} +#pragma omp declare variant +// expected-error@+1 {{function declaration is expected after 'declare variant' directive}} +#pragma omp declare variant diff --git a/clang/test/OpenMP/declare_variant_messages.cpp b/clang/test/OpenMP/declare_variant_messages.cpp new file mode 100644 index 0000000000000..88e724d12470d --- /dev/null +++ b/clang/test/OpenMP/declare_variant_messages.cpp @@ -0,0 +1,242 @@ +// RUN: %clang_cc1 -triple=x86_64-pc-win32 -verify -fopenmp -x c++ -std=c++14 -fms-extensions -Wno-pragma-pack -fexceptions -fcxx-exceptions %s + +// RUN: %clang_cc1 -triple=x86_64-pc-win32 -verify -fopenmp-simd -x c++ -std=c++14 -fms-extensions -Wno-pragma-pack -fexceptions -fcxx-exceptions %s + +// expected-error@+1 {{expected an OpenMP directive}} +#pragma omp declare + +int foo(); + +template +T foofoo(); // expected-note 2 {{declared here}} + +#pragma omp declare variant // expected-error {{expected '(' after 'declare variant'}} +#pragma omp declare variant( // expected-error {{expected expression}} expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma omp declare variant(foo // expected-error {{expected ')'}} expected-error {{expected 'match' clause on 'omp declare variant' directive}} expected-note {{to match this '('}} +#pragma omp declare variant(x) // expected-error {{use of undeclared identifier 'x'}} +#pragma omp declare variant(foo) // expected-error {{expected 'match' clause on 'omp declare variant' directive}} +#pragma omp declare variant(foofoo ) // expected-error {{expected 'match' clause on 'omp declare variant' directive}} +#pragma omp declare variant(foofoo ) xxx // expected-error {{expected 'match' clause on 'omp declare variant' directive}} +#pragma omp declare variant(foofoo ) match // expected-error {{expected '(' after 'match'}} +#pragma omp declare variant(foofoo ) match( // expected-error {{expected context selector in 'match' clause on 'omp declare variant' directive}} +#pragma omp declare variant(foofoo ) match() // expected-error {{expected context selector in 'match' clause on 'omp declare variant' directive}} +#pragma omp declare variant(foofoo ) match(xxx) // expected-error {{expected '=' after 'xxx' context selector set name on 'omp declare variant' directive}} +#pragma omp declare variant(foofoo ) match(xxx =) // expected-error {{expected '{' after '='}} +#pragma omp declare variant(foofoo ) match(xxx = yyy) // expected-error {{expected '{' after '='}} +#pragma omp declare variant(foofoo ) match(xxx = yyy }) // expected-error {{expected '{' after '='}} +#pragma omp declare variant(foofoo ) match(xxx = {) // expected-error {{expected '}'}} expected-note {{to match this '{'}} +#pragma omp declare variant(foofoo ) match(xxx = {}) +#pragma omp declare variant(foofoo ) match(xxx = {vvv}) +#pragma omp declare variant(foofoo ) match(xxx = {vvv} xxx) // expected-error {{expected ','}} expected-error {{expected '=' after 'xxx' context selector set name on 'omp declare variant' directive}} +#pragma omp declare variant(foofoo ) match(xxx = {vvv}) xxx // expected-warning {{extra tokens at the end of '#pragma omp declare variant' are ignored}} +#pragma omp declare variant(foofoo ) match(implementation={xxx}) // expected-warning {{unknown context selector in 'implementation' context selector set of 'omp declare variant' directive, ignored}} +#pragma omp declare variant(foofoo ) match(implementation={vendor}) // expected-error {{expected '(' after 'vendor'}} expected-error {{expected vendor identifier in 'vendor' context selector of 'implementation' selector set of 'omp declare variant' directive}} expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma omp declare variant(foofoo ) match(implementation={vendor(}) // expected-error {{expected vendor identifier in 'vendor' context selector of 'implementation' selector set of 'omp declare variant' directive}} expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma omp declare variant(foofoo ) match(implementation={vendor()}) // expected-error {{expected vendor identifier in 'vendor' context selector of 'implementation' selector set of 'omp declare variant' directive}} +#pragma omp declare variant(foofoo ) match(implementation={vendor(score ibm)}) // expected-error {{expected '(' after 'score'}} expected-warning {{missing ':' after context selector score clause - ignoring}} +#pragma omp declare variant(foofoo ) match(implementation={vendor(score( ibm)}) // expected-error {{expected ')'}} expected-error {{use of undeclared identifier 'ibm'}} expected-error {{expected vendor identifier in 'vendor' context selector of 'implementation' selector set of 'omp declare variant' directive}} expected-warning {{missing ':' after context selector score clause - ignoring}} expected-note {{to match this '('}} +#pragma omp declare variant(foofoo ) match(implementation={vendor(score(2 ibm)}) // expected-error 2 {{expected ')'}} expected-error {{expected vendor identifier in 'vendor' context selector of 'implementation' selector set of 'omp declare variant' directive}} expected-warning {{missing ':' after context selector score clause - ignoring}} expected-note 2 {{to match this '('}} +#pragma omp declare variant(foofoo ) match(implementation={vendor(score(foofoo ()) ibm)}) // expected-warning {{missing ':' after context selector score clause - ignoring}} expected-error {{expression is not an integral constant expression}} expected-note {{non-constexpr function 'foofoo' cannot be used in a constant expression}} +#pragma omp declare variant(foofoo ) match(implementation={vendor(score(5): ibm)}) +int bar(); + +#pragma omp declare variant // expected-error {{expected '(' after 'declare variant'}} +#pragma omp declare variant( // expected-error {{expected expression}} expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma omp declare variant(foofoo // expected-error {{expected ')'}} expected-error {{expected 'match' clause on 'omp declare variant' directive}} expected-note {{to match this '('}} +#pragma omp declare variant(x) // expected-error {{use of undeclared identifier 'x'}} +#pragma omp declare variant(foo) // expected-error {{expected 'match' clause on 'omp declare variant' directive}} +#pragma omp declare variant(foofoo) // expected-error {{expected 'match' clause on 'omp declare variant' directive}} +#pragma omp declare variant(foofoo ) // expected-error {{expected 'match' clause on 'omp declare variant' directive}} +#pragma omp declare variant(foofoo ) xxx // expected-error {{expected 'match' clause on 'omp declare variant' directive}} +#pragma omp declare variant(foofoo ) match // expected-error {{expected '(' after 'match'}} +#pragma omp declare variant(foofoo ) match( // expected-error {{expected context selector in 'match' clause on 'omp declare variant' directive}} +#pragma omp declare variant(foofoo ) match() // expected-error {{expected context selector in 'match' clause on 'omp declare variant' directive}} +#pragma omp declare variant(foofoo ) match(xxx) // expected-error {{expected '=' after 'xxx' context selector set name on 'omp declare variant' directive}} +#pragma omp declare variant(foofoo ) match(xxx =) // expected-error {{expected '{' after '='}} +#pragma omp declare variant(foofoo ) match(xxx = {) // expected-error {{expected '}'}} expected-note {{to match this '{'}} +#pragma omp declare variant(foofoo ) match(xxx = {}) +#pragma omp declare variant(foofoo ) match(xxx = {vvv}) +#pragma omp declare variant(foofoo ) match(user = {score() : condition()}) +#pragma omp declare variant(foofoo ) match(user = {score() : condition()}) +#pragma omp declare variant(foofoo ) match(user = {condition()}) +#pragma omp declare variant(foofoo ) match(user = {condition()}) +#pragma omp declare variant(foofoo ) match(xxx = {vvv} xxx) // expected-error {{expected ','}} expected-error {{expected '=' after 'xxx' context selector set name on 'omp declare variant' directive}} +#pragma omp declare variant(foofoo ) match(xxx = {vvv}) xxx // expected-warning {{extra tokens at the end of '#pragma omp declare variant' are ignored}} +#pragma omp declare variant(foofoo ) match(implementation={vendor(score ibm)}) // expected-error {{expected '(' after 'score'}} expected-warning {{missing ':' after context selector score clause - ignoring}} +#pragma omp declare variant(foofoo ) match(implementation={vendor(score( ibm)}) // expected-error {{expected ')'}} expected-error {{use of undeclared identifier 'ibm'}} expected-error {{expected vendor identifier in 'vendor' context selector of 'implementation' selector set of 'omp declare variant' directive}} expected-warning {{missing ':' after context selector score clause - ignoring}} expected-note {{to match this '('}} +#pragma omp declare variant(foofoo ) match(implementation={vendor(score(C ibm)}) // expected-error 2 {{expected ')'}} expected-error {{expected vendor identifier in 'vendor' context selector of 'implementation' selector set of 'omp declare variant' directive}} expected-warning {{missing ':' after context selector score clause - ignoring}} expected-note 2 {{to match this '('}} +#pragma omp declare variant(foofoo ) match(implementation={vendor(score(foofoo ()) ibm)}) // expected-warning {{missing ':' after context selector score clause - ignoring}} expected-error {{expression is not an integral constant expression}} expected-note {{non-constexpr function 'foofoo' cannot be used in a constant expression}} +#pragma omp declare variant(foofoo ) match(implementation={vendor(score(C+5): ibm)}) +template +T barbar(); + +// expected-error@+2 {{'#pragma omp declare variant' can only be applied to functions}} +#pragma omp declare variant(barbar ) match(xxx = {}) +int a; +// expected-error@+2 {{'#pragma omp declare variant' can only be applied to functions}} +#pragma omp declare variant(barbar ) match(xxx = {}) +#pragma omp threadprivate(a) +int var; +#pragma omp threadprivate(var) + +// expected-error@+2 {{expected an OpenMP directive}} expected-error@+1 {{function declaration is expected after 'declare variant' directive}} +#pragma omp declare variant(barbar ) match(xxx = {}) +#pragma omp declare + +// expected-error@+3 {{function declaration is expected after 'declare variant' directive}} +// expected-error@+1 {{function declaration is expected after 'declare variant' directive}} +#pragma omp declare variant(barbar ) match(xxx = {}) +#pragma omp declare variant(barbar ) match(xxx = {}) +#pragma options align = packed +int main(); + +// expected-error@+3 {{function declaration is expected after 'declare variant' directive}} +// expected-error@+1 {{function declaration is expected after 'declare variant' directive}} +#pragma omp declare variant(barbar ) match(xxx = {}) +#pragma omp declare variant(barbar ) match(xxx = {}) +#pragma init_seg(compiler) +int main(); + +// expected-error@+1 {{single declaration is expected after 'declare variant' directive}} +#pragma omp declare variant(barbar ) match(xxx = {}) +int b, c; + +// expected-error@+1 {{'C' does not refer to a value}} +#pragma omp declare variant(C) match(xxx = {}) +// expected-note@+1 {{declared here}} +template +void h(C *hp, C *hp2, C *hq, C *lin) { + b = 0; +} + +// expected-error@+1 {{variant in '#pragma omp declare variant' with type '' is incompatible with type 'void (*)(int *, int *, int *, int *)'}} +#pragma omp declare variant(barbar ) match(xxx = {}) +template <> +void h(int *hp, int *hp2, int *hq, int *lin); + +int after_use_variant(void); +int after_use(); +int bar() { + return after_use(); +} + +// expected-warning@+1 {{'#pragma omp declare variant' cannot be applied for function after first usage; the original function might be used}} +#pragma omp declare variant(after_use_variant) match(xxx = {}) +int after_use(void); + +int fn(); +int fn(int); +#pragma omp declare variant(fn) match(xxx = {}) +int overload(void); + +int fn1(); +int fn1(int); +// expected-error@+1 {{variant in '#pragma omp declare variant' with type '' is incompatible with type 'int (*)(float)'}} +#pragma omp declare variant(fn1) match(xxx = {}) +int overload1(float); + +int fn_constexpr_variant(); +// expected-error@+2 {{'#pragma omp declare variant' does not support constexpr functions}} +#pragma omp declare variant(fn_constexpr_variant) match(xxx = {}) +constexpr int fn_constexpr(); + +constexpr int fn_constexpr_variant1(); +// expected-error@+1 {{'#pragma omp declare variant' does not support constexpr functions}} +#pragma omp declare variant(fn_constexpr_variant1) match(xxx = {}) +int fn_constexpr1(); + +int fn_sc_variant(); +// expected-error@+1 {{function with '#pragma omp declare variant' has a different storage class}} +#pragma omp declare variant(fn_sc_variant) match(xxx = {}) +static int fn_sc(); + +static int fn_sc_variant1(); +// expected-error@+1 {{function with '#pragma omp declare variant' has a different storage class}} +#pragma omp declare variant(fn_sc_variant1) match(xxx = {}) +int fn_sc1(); + +int fn_inline_variant(); +// expected-error@+1 {{function with '#pragma omp declare variant' has a different inline specification}} +#pragma omp declare variant(fn_inline_variant) match(xxx = {}) +inline int fn_inline(); + +inline int fn_inline_variant1(); +// expected-error@+1 {{function with '#pragma omp declare variant' has a different inline specification}} +#pragma omp declare variant(fn_inline_variant1) match(xxx = {}) +int fn_inline1(); + +int fn_linkage_variant(); +extern "C" { +// expected-error@+1 {{function with '#pragma omp declare variant' has a different linkage}} +#pragma omp declare variant(fn_linkage_variant) match(xxx = {}) +int fn_linkage(); +} + +extern "C" int fn_linkage_variant1(); +// expected-error@+1 {{function with '#pragma omp declare variant' has a different linkage}} +#pragma omp declare variant(fn_linkage_variant1) match(xxx = {}) +int fn_linkage1(); + +auto fn_deduced_variant() { return 0; } +#pragma omp declare variant(fn_deduced_variant) match(xxx = {}) +int fn_deduced(); + +int fn_deduced_variant1(); +#pragma omp declare variant(fn_deduced_variant1) match(xxx = {}) +auto fn_deduced1() { return 0; } + +auto fn_deduced3() { return 0; } +// expected-warning@+1 {{'#pragma omp declare variant' cannot be applied to the function that was defined already; the original function might be used}} +#pragma omp declare variant(fn_deduced_variant1) match(xxx = {}) +auto fn_deduced3(); + +auto fn_deduced_variant2() { return 0; } +// expected-error@+1 {{variant in '#pragma omp declare variant' with type 'int ()' is incompatible with type 'float (*)()'}} +#pragma omp declare variant(fn_deduced_variant2) match(xxx = {}) +float fn_deduced2(); + +// expected-error@+1 {{exception specification in declaration does not match previous declaration}} +int fn_except_variant() noexcept(true); +// expected-note@+2 {{previous declaration is here}} +#pragma omp declare variant(fn_except_variant) match(xxx = {}) +int fn_except() noexcept(false); + +// expected-error@+1 {{exception specification in declaration does not match previous declaration}} +int fn_except_variant1() noexcept(false); +// expected-note@+2 {{previous declaration is here}} +#pragma omp declare variant(fn_except_variant1) match(xxx = {}) +int fn_except1() noexcept(true); + +struct SpecialFuncs { + void vd(); + // expected-error@+2 {{'#pragma omp declare variant' does not support constructors}} +#pragma omp declare variant(SpecialFuncs::vd) match(xxx = {}) + SpecialFuncs(); + // expected-error@+2 {{'#pragma omp declare variant' does not support destructors}} +#pragma omp declare variant(SpecialFuncs::vd) match(xxx = {}) + ~SpecialFuncs(); + + void baz(); + void bar(); + void bar(int); +#pragma omp declare variant(SpecialFuncs::baz) match(xxx = {}) +#pragma omp declare variant(SpecialFuncs::bar) match(xxx = {}) + void foo1(); + SpecialFuncs& foo(const SpecialFuncs&); + SpecialFuncs& bar(SpecialFuncs&&); + // expected-error@+2 {{'#pragma omp declare variant' does not support defaulted functions}} +#pragma omp declare variant(SpecialFuncs::foo) match(xxx = {}) + SpecialFuncs& operator=(const SpecialFuncs&) = default; + // expected-error@+2 {{'#pragma omp declare variant' does not support deleted functions}} +#pragma omp declare variant(SpecialFuncs::bar) match(xxx = {}) + SpecialFuncs& operator=(SpecialFuncs&&) = delete; +}; + +namespace N { +// expected-error@+1 {{function declaration is expected after 'declare variant' directive}} +#pragma omp declare variant +} // namespace N +// expected-error@+1 {{function declaration is expected after 'declare variant' directive}} +#pragma omp declare variant +// expected-error@+1 {{function declaration is expected after 'declare variant' directive}} +#pragma omp declare variant diff --git a/clang/test/OpenMP/for_codegen.cpp b/clang/test/OpenMP/for_codegen.cpp index 756d6ccebf424..925235ef960e5 100644 --- a/clang/test/OpenMP/for_codegen.cpp +++ b/clang/test/OpenMP/for_codegen.cpp @@ -89,23 +89,6 @@ void loop_with_counter_collapse() { // CHECK: [[NUM_ITERS_VAL:%.+]] = sub nsw i64 [[MUL]], 1 // CHECK: store i64 [[NUM_ITERS_VAL]], i64* [[NUM_ITERS:%.+]], - // Initialization - // CHECK: store i32 0, i32* [[I:%.+]], - // CHECK: [[I_INIT:%.+]] = load i32, i32* [[I]], - // CHECK: store i32 [[I_INIT]], i32* [[J:%.+]], - - // LIFETIME: call void @llvm.lifetime.end - // LIFETIME: call void @llvm.lifetime.end - - // Precondition for j counter - // CHECK: store i32 0, i32* [[TMP_I:%.+]], - // CHECK: [[J_LB_VAL:%.+]] = load i32, i32* [[TMP_I]], - // CHECK: [[I_VAL:%.+]] = load i32, i32* [[TMP_I]], - // CHECK: [[J_UB_VAL:%.+]] = add nsw i32 4, [[I_VAL]] - // CHECK: [[CMP:%.+]] = icmp slt i32 [[J_LB_VAL]], [[J_UB_VAL]] - // CHECK: br i1 [[CMP]], label %[[THEN:[^,]+]], label %[[ELSE:[^,]+]] - - // CHECK: [[THEN]]: // CHECK: store i64 0, i64* [[LB:%.+]], // CHECK: [[NUM_ITERS_VAL:%.+]] = load i64, i64* [[NUM_ITERS]], // CHECK: store i64 [[NUM_ITERS_VAL]], i64* [[UB:%.+]], @@ -633,6 +616,22 @@ void for_with_references() { k = cnt; } +// CHECK-LABEL: for_with_references_dep_cond +void for_with_references_dep_cond() { +// CHECK: [[I:%.+]] = alloca i8, +// CHECK: [[CNT:%.+]] = alloca i8*, +// CHECK: [[CNT_PRIV:%.+]] = alloca i8, +// CHECK: call void @__kmpc_for_static_init_8( +// CHECK-NOT: load i8, i8* [[CNT]], +// CHECK: call void @__kmpc_for_static_fini( + char i = 0; + char &cnt = i; +#pragma omp for collapse(2) + for (cnt = 0; cnt < 2; ++cnt) + for (int j = 0; j < 4 + cnt; j++) + k = cnt; +} + struct Bool { Bool(bool b) : b(b) {} operator bool() const { return b; } diff --git a/clang/test/OpenMP/nvptx_declare_target_var_ctor_dtor_codegen.cpp b/clang/test/OpenMP/nvptx_declare_target_var_ctor_dtor_codegen.cpp index 03520e17a614c..48039469489c1 100644 --- a/clang/test/OpenMP/nvptx_declare_target_var_ctor_dtor_codegen.cpp +++ b/clang/test/OpenMP/nvptx_declare_target_var_ctor_dtor_codegen.cpp @@ -73,15 +73,15 @@ S cd = doo() + car() + caz() + baz(); // DEVICE-DAG: ret void // HOST-DAG: @.omp_offloading.entry_name{{.*}} = internal unnamed_addr constant [{{[0-9]+}} x i8] c"[[C_ADDR]]\00" -// HOST-DAG: @.omp_offloading.entry.[[C_ADDR]] = weak constant %struct.__tgt_offload_entry { i8* bitcast (i32* @[[C_ADDR]] to i8*), i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @.omp_offloading.entry_name{{.*}}, i32 0, i32 0), i64 4, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// HOST-DAG: @.omp_offloading.entry.[[C_ADDR]] = weak constant %struct.__tgt_offload_entry { i8* bitcast (i32* @[[C_ADDR]] to i8*), i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @.omp_offloading.entry_name{{.*}}, i32 0, i32 0), i64 4, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // HOST-DAG: @.omp_offloading.entry_name{{.*}} = internal unnamed_addr constant [{{[0-9]+}} x i8] c"[[CD_ADDR]]\00" -// HOST-DAG: @.omp_offloading.entry.[[CD_ADDR]] = weak constant %struct.__tgt_offload_entry { i8* bitcast (%struct.S* @[[CD_ADDR]] to i8*), i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @.omp_offloading.entry_name{{.*}}, i32 0, i32 0), i64 4, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// HOST-DAG: @.omp_offloading.entry.[[CD_ADDR]] = weak constant %struct.__tgt_offload_entry { i8* bitcast (%struct.S* @[[CD_ADDR]] to i8*), i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @.omp_offloading.entry_name{{.*}}, i32 0, i32 0), i64 4, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // HOST-DAG: @.omp_offloading.entry_name{{.*}} = internal unnamed_addr constant [{{[0-9]+}} x i8] c"[[C_CTOR]]\00" -// HOST-DAG: @.omp_offloading.entry.[[C_CTOR]] = weak constant %struct.__tgt_offload_entry { i8* @[[C_CTOR]], i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @.omp_offloading.entry_name{{.*}}, i32 0, i32 0), i64 0, i32 2, i32 0 }, section ".omp_offloading.entries", align 1 +// HOST-DAG: @.omp_offloading.entry.[[C_CTOR]] = weak constant %struct.__tgt_offload_entry { i8* @[[C_CTOR]], i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @.omp_offloading.entry_name{{.*}}, i32 0, i32 0), i64 0, i32 2, i32 0 }, section "omp_offloading_entries", align 1 // HOST-DAG: @.omp_offloading.entry_name{{.*}}= internal unnamed_addr constant [{{[0-9]+}} x i8] c"[[CD_CTOR]]\00" -// HOST-DAG: @.omp_offloading.entry.[[CD_CTOR]] = weak constant %struct.__tgt_offload_entry { i8* @[[CD_CTOR]], i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @.omp_offloading.entry_name{{.*}}, i32 0, i32 0), i64 0, i32 2, i32 0 }, section ".omp_offloading.entries", align 1 +// HOST-DAG: @.omp_offloading.entry.[[CD_CTOR]] = weak constant %struct.__tgt_offload_entry { i8* @[[CD_CTOR]], i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @.omp_offloading.entry_name{{.*}}, i32 0, i32 0), i64 0, i32 2, i32 0 }, section "omp_offloading_entries", align 1 // HOST-DAG: @.omp_offloading.entry_name{{.*}}= internal unnamed_addr constant [{{[0-9]+}} x i8] c"[[CD_DTOR]]\00" -// HOST-DAG: @.omp_offloading.entry.[[CD_DTOR]] = weak constant %struct.__tgt_offload_entry { i8* @[[CD_DTOR]], i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @.omp_offloading.entry_name{{.*}}, i32 0, i32 0), i64 0, i32 4, i32 0 }, section ".omp_offloading.entries", align 1 +// HOST-DAG: @.omp_offloading.entry.[[CD_DTOR]] = weak constant %struct.__tgt_offload_entry { i8* @[[CD_DTOR]], i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @.omp_offloading.entry_name{{.*}}, i32 0, i32 0), i64 0, i32 4, i32 0 }, section "omp_offloading_entries", align 1 int maini1() { int a; #pragma omp target map(tofrom : a) diff --git a/clang/test/OpenMP/nvptx_target_requires_unified_shared_memory.cpp b/clang/test/OpenMP/nvptx_target_requires_unified_shared_memory.cpp index c676f77a0bc63..877aa7ab0b622 100644 --- a/clang/test/OpenMP/nvptx_target_requires_unified_shared_memory.cpp +++ b/clang/test/OpenMP/nvptx_target_requires_unified_shared_memory.cpp @@ -40,10 +40,10 @@ int bar(int n){ // CHECK-HOST: [[OFFLOAD_MAPTYPES:@.+]] = private unnamed_addr constant [2 x i64] [i64 800, i64 800] // CHECK-HOST: [[OMP_OFFLOAD_ENTRY_LINK_VAR_PTR_NAME:@.+]] = internal unnamed_addr constant [21 x i8] -// CHECK-HOST: [[OMP_OFFLOAD_ENTRY_LINK_VAR_PTR:@.+]] = weak constant %struct.__tgt_offload_entry { i8* bitcast (double** [[VAR_DECL_TGT_LINK_PTR]] to i8*), i8* getelementptr inbounds ([21 x i8], [21 x i8]* [[OMP_OFFLOAD_ENTRY_LINK_VAR_PTR_NAME]], i32 0, i32 0), i64 8, i32 1, i32 0 }, section ".omp_offloading.entries" +// CHECK-HOST: [[OMP_OFFLOAD_ENTRY_LINK_VAR_PTR:@.+]] = weak constant %struct.__tgt_offload_entry { i8* bitcast (double** [[VAR_DECL_TGT_LINK_PTR]] to i8*), i8* getelementptr inbounds ([21 x i8], [21 x i8]* [[OMP_OFFLOAD_ENTRY_LINK_VAR_PTR_NAME]], i32 0, i32 0), i64 8, i32 1, i32 0 }, section "omp_offloading_entries" // CHECK-HOST: [[OMP_OFFLOAD_ENTRY_TO_VAR_PTR_NAME:@.+]] = internal unnamed_addr constant [24 x i8] -// CHECK-HOST: [[OMP_OFFLOAD_ENTRY_TO_VAR_PTR:@.+]] = weak constant %struct.__tgt_offload_entry { i8* bitcast (double** [[VAR_DECL_TGT_TO_PTR]] to i8*), i8* getelementptr inbounds ([24 x i8], [24 x i8]* [[OMP_OFFLOAD_ENTRY_TO_VAR_PTR_NAME]], i32 0, i32 0), i64 8, i32 0, i32 0 }, section ".omp_offloading.entries" +// CHECK-HOST: [[OMP_OFFLOAD_ENTRY_TO_VAR_PTR:@.+]] = weak constant %struct.__tgt_offload_entry { i8* bitcast (double** [[VAR_DECL_TGT_TO_PTR]] to i8*), i8* getelementptr inbounds ([24 x i8], [24 x i8]* [[OMP_OFFLOAD_ENTRY_TO_VAR_PTR_NAME]], i32 0, i32 0), i64 8, i32 0, i32 0 }, section "omp_offloading_entries" // CHECK-HOST: [[N_CASTED:%.+]] = alloca i64 // CHECK-HOST: [[SUM_CASTED:%.+]] = alloca i64 diff --git a/clang/test/OpenMP/openmp_offload_registration.cpp b/clang/test/OpenMP/openmp_offload_registration.cpp index 4705cb6c0dc3d..4b2e4830dc1fb 100644 --- a/clang/test/OpenMP/openmp_offload_registration.cpp +++ b/clang/test/OpenMP/openmp_offload_registration.cpp @@ -16,8 +16,8 @@ void foo() { // CHECK-DAG: $[[REGFN:\.omp_offloading\..+\.powerpc64le-ibm-linux-gnu\.x86_64-pc-linux-gnu+]] = comdat any // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEV1BEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEV1END:@.+]] = extern_weak constant i8 // CHECK: [[DEV2BEGIN:@.+]] = extern_weak constant i8 diff --git a/clang/test/OpenMP/parallel_for_codegen.cpp b/clang/test/OpenMP/parallel_for_codegen.cpp index e4e76060e7073..9e3390214d78d 100644 --- a/clang/test/OpenMP/parallel_for_codegen.cpp +++ b/clang/test/OpenMP/parallel_for_codegen.cpp @@ -15,10 +15,13 @@ // RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=50 -DOMP5 -x c++ -triple x86_64-unknown-unknown -emit-llvm %s -fexceptions -fcxx-exceptions -o - | FileCheck --check-prefix=OMP5 %s // RUN: %clang_cc1 -fopenmp -fopenmp-version=50 -DOMP5 -x c++ -std=c++11 -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -emit-pch -o %t %s // RUN: %clang_cc1 -fopenmp -fopenmp-version=50 -DOMP5 -x c++ -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -std=c++11 -include-pch %t -verify %s -emit-llvm -o - | FileCheck --check-prefix=OMP5 %s -// RUN: %clang_cc1 -fopenmp-simd -fopenmp-version=50 -x c++ -std=c++11 -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -emit-pch -o %t %s +// RUN: %clang_cc1 -verify -fopenmp-simd -fopenmp-version=50 -DOMP5 -x c++ -triple x86_64-unknown-unknown -emit-llvm %s -fexceptions -fcxx-exceptions -o - | FileCheck --check-prefix SIMD-ONLY0 %s +// RUN: %clang_cc1 -fopenmp-simd -fopenmp-version=50 -DOMP5 -x c++ -std=c++11 -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -emit-pch -o %t %s +// RUN: %clang_cc1 -fopenmp-simd -fopenmp-version=50 -DOMP5 -x c++ -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -std=c++11 -include-pch %t -verify %s -emit-llvm -o - | FileCheck --check-prefix SIMD-ONLY0 %s #ifndef HEADER #define HEADER +#ifndef OMP5 // CHECK-DAG: [[IDENT_T_TY:%.+]] = type { i32, i32, i32, i32, i8* } // CHECK-DAG: [[LOOP_LOC:@.+]] = private unnamed_addr global [[IDENT_T_TY]] { i32 0, i32 514, i32 0, i32 0, i8* @@ -32,12 +35,14 @@ void with_var_schedule() { // CHECK: [[CHUNK:%.+]] = load i64, i64* % // CHECK: call void {{.+}} @__kmpc_fork_call({{.+}}, i64 [[CHUNK]]) +// CHECK: [[UNDEF_A:%.+]] = load double, double* undef +// CHECK: fadd double 2.000000e+00, [[UNDEF_A]] // CHECK: [[CHUNK_VAL:%.+]] = load i8, i8* % // CHECK: [[CHUNK_SIZE:%.+]] = sext i8 [[CHUNK_VAL]] to i64 // CHECK: call void @__kmpc_for_static_init_8u([[IDENT_T_TY]]* [[LOOP_LOC]], i32 [[GTID:%[^,]+]], i32 33, i32* [[IS_LAST:%[^,]+]], i64* [[OMP_LB:%[^,]+]], i64* [[OMP_UB:%[^,]+]], i64* [[OMP_ST:%[^,]+]], i64 1, i64 [[CHUNK_SIZE]]) // CHECK: call void @__kmpc_for_static_fini([[IDENT_T_TY]]* [[LOOP_LOC]], i32 [[GTID]]) -#pragma omp parallel for schedule(static, char(a)) - for (unsigned long long i = 1; i < 2; ++i) { +#pragma omp parallel for schedule(static, char(a)) private(a) + for (unsigned long long i = 1; i < 2 + a; ++i) { } } @@ -383,16 +388,13 @@ void parallel_for(float *a, const int n) { // TERM_DEBUG-DAG: [[DBG_LOC_START]] = !DILocation(line: [[@LINE-4]], // TERM_DEBUG-DAG: [[DBG_LOC_END]] = !DILocation(line: [[@LINE-18]], -#ifdef OMP5 -// OMP5-DAG: [[IDENT_T_TY:%.+]] = type { i32, i32, i32, i32, i8* } -// OMP5-DAG: [[LOOP_LOC:@.+]] = private unnamed_addr global [[IDENT_T_TY]] { i32 0, i32 514, i32 0, i32 0, i8* - +#else // OMP5 // OMP5-LABEL: increment int increment () { -// OMP5: [[GTID:%.+]] = call i32 @__kmpc_global_thread_num([[IDENT_T_TY]]* [[DEFAULT_LOC:[@%].+]]) +// OMP5: [[GTID:%.+]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* [[DEFAULT_LOC:[@%].+]]) #pragma omp for // Determine UB = min(UB, GlobalUB) -// OMP5: call void @__kmpc_for_static_init_4([[IDENT_T_TY]]* [[LOOP_LOC]], i32 [[GTID]], i32 34, i32* [[IS_LAST:%[^,]+]], i32* [[OMP_LB:%[^,]+]], i32* [[OMP_UB:%[^,]+]], i32* [[OMP_ST:%[^,]+]], i32 1, i32 1) +// OMP5: call void @__kmpc_for_static_init_4(%struct.ident_t* [[LOOP_LOC:[@%].+]], i32 [[GTID]], i32 34, i32* [[IS_LAST:%[^,]+]], i32* [[OMP_LB:%[^,]+]], i32* [[OMP_UB:%[^,]+]], i32* [[OMP_ST:%[^,]+]], i32 1, i32 1) // OMP5-NEXT: [[UB:%.+]] = load i32, i32* [[OMP_UB]] // OMP5-NEXT: [[UBCMP:%.+]] = icmp sgt i32 [[UB]], 4 // OMP5-NEXT: br i1 [[UBCMP]], label [[UB_TRUE:%[^,]+]], label [[UB_FALSE:%[^,]+]] @@ -422,7 +424,7 @@ int increment () { // OMP5-NEXT: br label %[[LOOP1_HEAD]] ; // OMP5: [[LOOP1_END]] -// OMP5: call void @__kmpc_for_static_fini([[IDENT_T_TY]]* [[LOOP_LOC]], i32 [[GTID]]) +// OMP5: call void @__kmpc_for_static_fini(%struct.ident_t* [[LOOP_LOC]], i32 [[GTID]]) // OMP5: __kmpc_barrier return 0; // OMP5: ret i32 0 @@ -430,10 +432,10 @@ int increment () { // OMP5-LABEL: decrement_nowait int decrement_nowait () { -// OMP5: [[GTID:%.+]] = call i32 @__kmpc_global_thread_num([[IDENT_T_TY]]* [[DEFAULT_LOC:[@%].+]]) +// OMP5: [[GTID:%.+]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* [[DEFAULT_LOC:[@%].+]]) #pragma omp for nowait // Determine UB = min(UB, GlobalUB) -// OMP5: call void @__kmpc_for_static_init_4([[IDENT_T_TY]]* [[LOOP_LOC]], i32 [[GTID]], i32 34, i32* [[IS_LAST:%[^,]+]], i32* [[OMP_LB:%[^,]+]], i32* [[OMP_UB:%[^,]+]], i32* [[OMP_ST:%[^,]+]], i32 1, i32 1) +// OMP5: call void @__kmpc_for_static_init_4(%struct.ident_t* [[LOOP_LOC]], i32 [[GTID]], i32 34, i32* [[IS_LAST:%[^,]+]], i32* [[OMP_LB:%[^,]+]], i32* [[OMP_UB:%[^,]+]], i32* [[OMP_ST:%[^,]+]], i32 1, i32 1) // OMP5-NEXT: [[UB:%.+]] = load i32, i32* [[OMP_UB]] // OMP5-NEXT: [[UBCMP:%.+]] = icmp sgt i32 [[UB]], 4 // OMP5-NEXT: br i1 [[UBCMP]], label [[UB_TRUE:%[^,]+]], label [[UB_FALSE:%[^,]+]] @@ -462,12 +464,12 @@ int decrement_nowait () { // OMP5-NEXT: br label %[[LOOP1_HEAD]] ; // OMP5: [[LOOP1_END]] -// OMP5: call void @__kmpc_for_static_fini([[IDENT_T_TY]]* [[LOOP_LOC]], i32 [[GTID]]) +// OMP5: call void @__kmpc_for_static_fini(%struct.ident_t* [[LOOP_LOC]], i32 [[GTID]]) // OMP5-NOT: __kmpc_barrier return 0; // OMP5: ret i32 0 } -#endif +#endif // OMP5 #endif // HEADER diff --git a/clang/test/OpenMP/target_codegen.cpp b/clang/test/OpenMP/target_codegen.cpp index a6f0da171b2a6..e1db9c9d49a6f 100644 --- a/clang/test/OpenMP/target_codegen.cpp +++ b/clang/test/OpenMP/target_codegen.cpp @@ -88,8 +88,8 @@ // TCHECK-NOT: @{{.+}} = weak constant [[ENTTY]] // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_codegen_registration.cpp b/clang/test/OpenMP/target_codegen_registration.cpp index 31a1c004a8306..cc56f7a8dc57a 100644 --- a/clang/test/OpenMP/target_codegen_registration.cpp +++ b/clang/test/OpenMP/target_codegen_registration.cpp @@ -123,57 +123,57 @@ // CHECK-NTARGET-NOT: private unnamed_addr constant [1 x i // CHECK-DAG: [[NAMEPTR1:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME1:__omp_offloading_[0-9a-f]+_[0-9a-f]+__Z.+_l[0-9]+]]\00" -// CHECK-DAG: @.omp_offloading.entry.[[NAME1]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: @.omp_offloading.entry.[[NAME1]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR2:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME2:.+]]\00" -// CHECK-DAG: @.omp_offloading.entry.[[NAME2]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: @.omp_offloading.entry.[[NAME2]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR3:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME3:.+]]\00" -// CHECK-DAG: @.omp_offloading.entry.[[NAME3]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: @.omp_offloading.entry.[[NAME3]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR4:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME4:.+]]\00" -// CHECK-DAG: @.omp_offloading.entry.[[NAME4]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: @.omp_offloading.entry.[[NAME4]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR5:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME5:.+]]\00" -// CHECK-DAG: @.omp_offloading.entry.[[NAME5]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: @.omp_offloading.entry.[[NAME5]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR6:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME6:.+]]\00" -// CHECK-DAG: @.omp_offloading.entry.[[NAME6]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: @.omp_offloading.entry.[[NAME6]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR7:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME7:.+]]\00" -// CHECK-DAG: @.omp_offloading.entry.[[NAME7]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: @.omp_offloading.entry.[[NAME7]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR8:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME8:.+]]\00" -// CHECK-DAG: @.omp_offloading.entry.[[NAME8]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: @.omp_offloading.entry.[[NAME8]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR9:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME9:.+]]\00" -// CHECK-DAG: @.omp_offloading.entry.[[NAME9]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: @.omp_offloading.entry.[[NAME9]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR10:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME10:.+]]\00" -// CHECK-DAG: @.omp_offloading.entry.[[NAME10]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: @.omp_offloading.entry.[[NAME10]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR11:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME11:.+]]\00" -// CHECK-DAG: @.omp_offloading.entry.[[NAME11]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: @.omp_offloading.entry.[[NAME11]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR12:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME12:.+]]\00" -// CHECK-DAG: @.omp_offloading.entry.[[NAME12]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: @.omp_offloading.entry.[[NAME12]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR1:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME1:__omp_offloading_[0-9a-f]+_[0-9a-f]+__Z.+_l[0-9]+]]\00" -// TCHECK-DAG: @.omp_offloading.entry.[[NAME1]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: @.omp_offloading.entry.[[NAME1]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR2:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME2:.+]]\00" -// TCHECK-DAG: @.omp_offloading.entry.[[NAME2]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: @.omp_offloading.entry.[[NAME2]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR3:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME3:.+]]\00" -// TCHECK-DAG: @.omp_offloading.entry.[[NAME3]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: @.omp_offloading.entry.[[NAME3]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR4:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME4:.+]]\00" -// TCHECK-DAG: @.omp_offloading.entry.[[NAME4]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: @.omp_offloading.entry.[[NAME4]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR5:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME5:.+]]\00" -// TCHECK-DAG: @.omp_offloading.entry.[[NAME5]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: @.omp_offloading.entry.[[NAME5]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR6:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME6:.+]]\00" -// TCHECK-DAG: @.omp_offloading.entry.[[NAME6]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: @.omp_offloading.entry.[[NAME6]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR7:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME7:.+]]\00" -// TCHECK-DAG: @.omp_offloading.entry.[[NAME7]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: @.omp_offloading.entry.[[NAME7]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR8:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME8:.+]]\00" -// TCHECK-DAG: @.omp_offloading.entry.[[NAME8]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: @.omp_offloading.entry.[[NAME8]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR9:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME9:.+]]\00" -// TCHECK-DAG: @.omp_offloading.entry.[[NAME9]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: @.omp_offloading.entry.[[NAME9]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR10:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME10:.+]]\00" -// TCHECK-DAG: @.omp_offloading.entry.[[NAME10]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: @.omp_offloading.entry.[[NAME10]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR11:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME11:.+]]\00" -// TCHECK-DAG: @.omp_offloading.entry.[[NAME11]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: @.omp_offloading.entry.[[NAME11]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR12:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME12:.+]]\00" -// TCHECK-DAG: @.omp_offloading.entry.[[NAME12]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: @.omp_offloading.entry.[[NAME12]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_depend_codegen.cpp b/clang/test/OpenMP/target_depend_codegen.cpp index a5b839047f408..735fb763c762c 100644 --- a/clang/test/OpenMP/target_depend_codegen.cpp +++ b/clang/test/OpenMP/target_depend_codegen.cpp @@ -56,8 +56,8 @@ // TCHECK-NOT: @{{.+}} = weak constant [[ENTTY]] // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_map_codegen.cpp b/clang/test/OpenMP/target_map_codegen.cpp index ca6680237ad04..0a8198a904178 100644 --- a/clang/test/OpenMP/target_map_codegen.cpp +++ b/clang/test/OpenMP/target_map_codegen.cpp @@ -1323,172 +1323,176 @@ void implicit_maps_template_type_capture (int a){ // SIMD-ONLY18-NOT: {{__kmpc|__tgt}} #ifdef CK19 -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1510.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1514.region_id = weak constant i8 0 // CK19: [[SIZE00:@.+]] = private {{.*}}constant [1 x i64] [i64 4] // CK19: [[MTYPE00:@.+]] = private {{.*}}constant [1 x i64] [i64 32] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1531.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1535.region_id = weak constant i8 0 // CK19: [[SIZE00n:@.+]] = private {{.*}}constant [1 x i64] [i64 4] // CK19: [[MTYPE00n:@.+]] = private {{.*}}constant [1 x i64] [i64 32] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1553.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1557.region_id = weak constant i8 0 // CK19: [[SIZE01:@.+]] = private {{.*}}constant [1 x i64] [i64 400] // CK19: [[MTYPE01:@.+]] = private {{.*}}constant [1 x i64] [i64 33] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1572.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1576.region_id = weak constant i8 0 // CK19: [[SIZE02:@.+]] = private {{.*}}constant [1 x i64] [i64 240] // CK19: [[MTYPE02:@.+]] = private {{.*}}constant [1 x i64] [i64 34] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1591.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1595.region_id = weak constant i8 0 // CK19: [[SIZE03:@.+]] = private {{.*}}constant [1 x i64] [i64 240] // CK19: [[MTYPE03:@.+]] = private {{.*}}constant [1 x i64] [i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1610.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1614.region_id = weak constant i8 0 // CK19: [[SIZE04:@.+]] = private {{.*}}constant [1 x i64] [i64 400] // CK19: [[MTYPE04:@.+]] = private {{.*}}constant [1 x i64] [i64 32] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1629.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1633.region_id = weak constant i8 0 // CK19: [[SIZE05:@.+]] = private {{.*}}constant [1 x i64] [i64 4] // CK19: [[MTYPE05:@.+]] = private {{.*}}constant [1 x i64] [i64 33] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1652.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1656.region_id = weak constant i8 0 // CK19: [[MTYPE06:@.+]] = private {{.*}}constant [1 x i64] [i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1675.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1679.region_id = weak constant i8 0 // CK19: [[MTYPE07:@.+]] = private {{.*}}constant [1 x i64] [i64 32] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1694.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1698.region_id = weak constant i8 0 // CK19: [[SIZE08:@.+]] = private {{.*}}constant [1 x i64] [i64 4] // CK19: [[MTYPE08:@.+]] = private {{.*}}constant [1 x i64] [i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1715.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1719.region_id = weak constant i8 0 // CK19: [[SIZE09:@.+]] = private {{.*}}constant [1 x i64] [i64 {{8|4}}] // CK19: [[MTYPE09:@.+]] = private {{.*}}constant [1 x i64] [i64 34] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1736.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1740.region_id = weak constant i8 0 // CK19: [[SIZE10:@.+]] = private {{.*}}constant [1 x i64] [i64 240] // CK19: [[MTYPE10:@.+]] = private {{.*}}constant [1 x i64] [i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1757.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1761.region_id = weak constant i8 0 // CK19: [[SIZE11:@.+]] = private {{.*}}constant [1 x i64] [i64 240] // CK19: [[MTYPE11:@.+]] = private {{.*}}constant [1 x i64] [i64 32] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1778.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1782.region_id = weak constant i8 0 // CK19: [[SIZE12:@.+]] = private {{.*}}constant [1 x i64] [i64 4] // CK19: [[MTYPE12:@.+]] = private {{.*}}constant [1 x i64] [i64 33] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1803.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1807.region_id = weak constant i8 0 // CK19: [[MTYPE13:@.+]] = private {{.*}}constant [1 x i64] [i64 32] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1828.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1832.region_id = weak constant i8 0 // CK19: [[MTYPE14:@.+]] = private {{.*}}constant [1 x i64] [i64 33] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1849.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1853.region_id = weak constant i8 0 // CK19: [[SIZE15:@.+]] = private {{.*}}constant [1 x i64] [i64 4] // CK19: [[MTYPE15:@.+]] = private {{.*}}constant [1 x i64] [i64 34] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1883.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1887.region_id = weak constant i8 0 // CK19: [[MTYPE16:@.+]] = private {{.*}}constant [2 x i64] [i64 800, i64 33] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1909.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1913.region_id = weak constant i8 0 // CK19: [[SIZE17:@.+]] = private {{.*}}constant [2 x i64] [i64 {{8|4}}, i64 240] // CK19: [[MTYPE17:@.+]] = private {{.*}}constant [2 x i64] [i64 800, i64 34] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1935.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1939.region_id = weak constant i8 0 // CK19: [[SIZE18:@.+]] = private {{.*}}constant [2 x i64] [i64 {{8|4}}, i64 240] // CK19: [[MTYPE18:@.+]] = private {{.*}}constant [2 x i64] [i64 800, i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1967.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1971.region_id = weak constant i8 0 // CK19: [[MTYPE19:@.+]] = private {{.*}}constant [2 x i64] [i64 800, i64 32] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1993.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l1997.region_id = weak constant i8 0 // CK19: [[SIZE20:@.+]] = private {{.*}}constant [2 x i64] [i64 {{8|4}}, i64 4] // CK19: [[MTYPE20:@.+]] = private {{.*}}constant [2 x i64] [i64 800, i64 33] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2025.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2029.region_id = weak constant i8 0 // CK19: [[MTYPE21:@.+]] = private {{.*}}constant [2 x i64] [i64 800, i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2051.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2055.region_id = weak constant i8 0 // CK19: [[SIZE22:@.+]] = private {{.*}}constant [2 x i64] [i64 {{8|4}}, i64 4] // CK19: [[MTYPE22:@.+]] = private {{.*}}constant [2 x i64] [i64 800, i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2070.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2074.region_id = weak constant i8 0 // CK19: [[SIZE23:@.+]] = private {{.*}}constant [1 x i64] [i64 4] // CK19: [[MTYPE23:@.+]] = private {{.*}}constant [1 x i64] [i64 39] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2092.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2096.region_id = weak constant i8 0 // CK19: [[SIZE24:@.+]] = private {{.*}}constant [1 x i64] [i64 480] // CK19: [[MTYPE24:@.+]] = private {{.*}}constant [1 x i64] [i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2113.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2117.region_id = weak constant i8 0 // CK19: [[SIZE25:@.+]] = private {{.*}}constant [1 x i64] [i64 16] // CK19: [[MTYPE25:@.+]] = private {{.*}}constant [1 x i64] [i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2134.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2138.region_id = weak constant i8 0 // CK19: [[SIZE26:@.+]] = private {{.*}}constant [1 x i64] [i64 24] // CK19: [[MTYPE26:@.+]] = private {{.*}}constant [1 x i64] [i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2155.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2159.region_id = weak constant i8 0 // CK19: [[SIZE27:@.+]] = private {{.*}}constant [1 x i64] [i64 4] // CK19: [[MTYPE27:@.+]] = private {{.*}}constant [1 x i64] [i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2200.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2204.region_id = weak constant i8 0 // CK19: [[SIZE28:@.+]] = private {{.*}}constant [3 x i64] [i64 {{8|4}}, i64 {{8|4}}, i64 16] // CK19: [[MTYPE28:@.+]] = private {{.*}}constant [3 x i64] [i64 35, i64 16, i64 19] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2245.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2249.region_id = weak constant i8 0 // CK19: [[SIZE29:@.+]] = private {{.*}}constant [3 x i64] [i64 {{8|4}}, i64 {{8|4}}, i64 4] // CK19: [[MTYPE29:@.+]] = private {{.*}}constant [3 x i64] [i64 35, i64 16, i64 19] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2301.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2305.region_id = weak constant i8 0 // CK19: [[MTYPE30:@.+]] = private {{.*}}constant [4 x i64] [i64 800, i64 800, i64 800, i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2345.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2349.region_id = weak constant i8 0 // CK19: [[SIZE31:@.+]] = private {{.*}}constant [4 x i64] [i64 {{8|4}}, i64 {{8|4}}, i64 {{8|4}}, i64 40] // CK19: [[MTYPE31:@.+]] = private {{.*}}constant [4 x i64] [i64 800, i64 800, i64 800, i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2368.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2372.region_id = weak constant i8 0 // CK19: [[SIZE32:@.+]] = private {{.*}}constant [1 x i64] [i64 13728] // CK19: [[MTYPE32:@.+]] = private {{.*}}constant [1 x i64] [i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2387.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2391.region_id = weak constant i8 0 // CK19: [[SIZE33:@.+]] = private {{.*}}constant [1 x i64] [i64 13728] // CK19: [[MTYPE33:@.+]] = private {{.*}}constant [1 x i64] [i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2406.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2410.region_id = weak constant i8 0 // CK19: [[SIZE34:@.+]] = private {{.*}}constant [1 x i64] [i64 13728] // CK19: [[MTYPE34:@.+]] = private {{.*}}constant [1 x i64] [i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2431.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2435.region_id = weak constant i8 0 // CK19: [[MTYPE35:@.+]] = private {{.*}}constant [1 x i64] [i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2452.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2456.region_id = weak constant i8 0 // CK19: [[SIZE36:@.+]] = private {{.*}}constant [1 x i64] [i64 208] // CK19: [[MTYPE36:@.+]] = private {{.*}}constant [1 x i64] [i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2492.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2496.region_id = weak constant i8 0 // CK19: [[MTYPE37:@.+]] = private {{.*}}constant [3 x i64] [i64 800, i64 800, i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2534.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2538.region_id = weak constant i8 0 // CK19: [[MTYPE38:@.+]] = private {{.*}}constant [3 x i64] [i64 800, i64 800, i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2576.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2580.region_id = weak constant i8 0 // CK19: [[MTYPE39:@.+]] = private {{.*}}constant [3 x i64] [i64 800, i64 800, i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2618.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2622.region_id = weak constant i8 0 // CK19: [[MTYPE40:@.+]] = private {{.*}}constant [3 x i64] [i64 800, i64 800, i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2653.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2657.region_id = weak constant i8 0 // CK19: [[SIZE41:@.+]] = private {{.*}}constant [3 x i64] [i64 {{8|4}}, i64 {{8|4}}, i64 208] // CK19: [[MTYPE41:@.+]] = private {{.*}}constant [3 x i64] [i64 800, i64 800, i64 35] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2698.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2702.region_id = weak constant i8 0 // CK19: [[SIZE42:@.+]] = private {{.*}}constant [3 x i64] [i64 {{8|4}}, i64 {{8|4}}, i64 104] // CK19: [[MTYPE42:@.+]] = private {{.*}}constant [3 x i64] [i64 35, i64 16, i64 19] -// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2723.region_id = weak constant i8 0 +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2727.region_id = weak constant i8 0 // CK19: [[MTYPE43:@.+]] = private {{.*}}constant [1 x i64] [i64 35] +// CK19-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l2746.region_id = weak constant i8 0 +// CK19: [[SIZE44:@.+]] = private {{.*}}constant [1 x i64] [i64 320] +// CK19: [[MTYPE44:@.+]] = private {{.*}}constant [1 x i64] [i64 34] + // CK19-LABEL: explicit_maps_single{{.*}}( void explicit_maps_single (int ii){ // Map of a scalar. @@ -2725,6 +2729,25 @@ void explicit_maps_single (int ii){ marras[1][2][3]++; } + // Region 44 + // CK19-DAG: call i32 @__tgt_target(i64 {{[^,]+}}, i8* {{[^,]+}}, i32 1, i8** [[GEPBP:%.+]], i8** [[GEPP:%.+]], {{.+}}getelementptr {{.+}}[1 x i{{.+}}]* [[SIZE44]], {{.+}}getelementptr {{.+}}[1 x i{{.+}}]* [[MTYPE44]]{{.+}}) + // CK19-DAG: [[GEPBP]] = getelementptr inbounds {{.+}}[[BP:%[^,]+]] + // CK19-DAG: [[GEPP]] = getelementptr inbounds {{.+}}[[P:%[^,]+]] + + // CK19-DAG: [[BP0:%.+]] = getelementptr inbounds {{.+}}[[BP]], i{{.+}} 0, i{{.+}} 0 + // CK19-DAG: [[P0:%.+]] = getelementptr inbounds {{.+}}[[P]], i{{.+}} 0, i{{.+}} 0 + // CK19-DAG: [[CBP0:%.+]] = bitcast i8** [[BP0]] to [100 x i32]** + // CK19-DAG: [[CP0:%.+]] = bitcast i8** [[P0]] to i32** + // CK19-DAG: store [100 x i32]* [[VAR0:%.+]], [100 x i32]** [[CBP0]] + // CK19-DAG: store i32* [[SEC0:%[^,]+]], i32** [[CP0]] + // CK19-DAG: [[SEC0]] = getelementptr {{.*}}[100 x i32]* [[VAR0]], i{{.+}} 0, i{{.+}} 20 + + // CK19: call void [[CALL44:@.+]]([100 x i32]* {{[^,]+}}) + #pragma omp target map(from:arra[20:]) + { + arra[50]++; + } + } // CK19: define {{.+}}[[CALL00]] @@ -2771,6 +2794,7 @@ void explicit_maps_single (int ii){ // CK19: define {{.+}}[[CALL41]] // CK19: define {{.+}}[[CALL42]] // CK19: define {{.+}}[[CALL43]] +// CK19: define {{.+}}[[CALL44]] #endif ///==========================================================================/// @@ -2790,19 +2814,19 @@ void explicit_maps_single (int ii){ // SIMD-ONLY19-NOT: {{__kmpc|__tgt}} #ifdef CK20 -// CK20-LABEL: @.__omp_offloading_{{.*}}explicit_maps_references_and_function_args{{.*}}_l2832.region_id = weak constant i8 0 +// CK20-LABEL: @.__omp_offloading_{{.*}}explicit_maps_references_and_function_args{{.*}}_l2856.region_id = weak constant i8 0 // CK20: [[SIZE00:@.+]] = private {{.*}}constant [1 x i64] [i64 4] // CK20: [[MTYPE00:@.+]] = private {{.*}}constant [1 x i64] [i64 33] -// CK20-LABEL: @.__omp_offloading_{{.*}}explicit_maps_references_and_function_args{{.*}}_l2853.region_id = weak constant i8 0 +// CK20-LABEL: @.__omp_offloading_{{.*}}explicit_maps_references_and_function_args{{.*}}_l2877.region_id = weak constant i8 0 // CK20: [[SIZE01:@.+]] = private {{.*}}constant [1 x i64] [i64 20] // CK20: [[MTYPE01:@.+]] = private {{.*}}constant [1 x i64] [i64 33] -// CK20-LABEL: @.__omp_offloading_{{.*}}explicit_maps_references_and_function_args{{.*}}_l2871.region_id = weak constant i8 0 +// CK20-LABEL: @.__omp_offloading_{{.*}}explicit_maps_references_and_function_args{{.*}}_l2895.region_id = weak constant i8 0 // CK20: [[SIZE02:@.+]] = private {{.*}}constant [1 x i64] [i64 4] // CK20: [[MTYPE02:@.+]] = private {{.*}}constant [1 x i64] [i64 34] -// CK20-LABEL: @.__omp_offloading_{{.*}}explicit_maps_references_and_function_args{{.*}}_l2892.region_id = weak constant i8 0 +// CK20-LABEL: @.__omp_offloading_{{.*}}explicit_maps_references_and_function_args{{.*}}_l2916.region_id = weak constant i8 0 // CK20: [[SIZE03:@.+]] = private {{.*}}constant [1 x i64] [i64 12] // CK20: [[MTYPE03:@.+]] = private {{.*}}constant [1 x i64] [i64 34] @@ -5275,11 +5299,11 @@ void map_with_deep_copy() { // SIMD-ONLY18-NOT: {{__kmpc|__tgt}} #ifdef CK31 -// CK31-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l5305.region_id = weak constant i8 0 +// CK31-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l5329.region_id = weak constant i8 0 // CK31: [[SIZE00:@.+]] = private {{.*}}constant [1 x i[[Z:64|32]]] [i[[Z:64|32]] 4] // CK31: [[MTYPE00:@.+]] = private {{.*}}constant [1 x i64] [i64 1059] -// CK31-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l5324.region_id = weak constant i8 0 +// CK31-LABEL: @.__omp_offloading_{{.*}}explicit_maps_single{{.*}}_l5348.region_id = weak constant i8 0 // CK31: [[SIZE01:@.+]] = private {{.*}}constant [1 x i[[Z:64|32]]] [i[[Z:64|32]] 4] // CK31: [[MTYPE01:@.+]] = private {{.*}}constant [1 x i64] [i64 1063] diff --git a/clang/test/OpenMP/target_parallel_codegen.cpp b/clang/test/OpenMP/target_parallel_codegen.cpp index ca12a4c30ba85..48cea928ce972 100644 --- a/clang/test/OpenMP/target_parallel_codegen.cpp +++ b/clang/test/OpenMP/target_parallel_codegen.cpp @@ -83,8 +83,8 @@ // TCHECK-NOT: @{{.+}} = weak constant [[ENTTY]] // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_parallel_codegen_registration.cpp b/clang/test/OpenMP/target_parallel_codegen_registration.cpp index a1aa66eb5697b..380e00e55b422 100644 --- a/clang/test/OpenMP/target_parallel_codegen_registration.cpp +++ b/clang/test/OpenMP/target_parallel_codegen_registration.cpp @@ -123,57 +123,57 @@ // CHECK-NTARGET-NOT: private unnamed_addr constant [1 x i // CHECK-DAG: [[NAMEPTR1:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME1:__omp_offloading_[0-9a-f]+_[0-9a-f]+__Z.+_l[0-9]+]]\00" -// CHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR2:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME2:.+]]\00" -// CHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR3:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME3:.+]]\00" -// CHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR4:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME4:.+]]\00" -// CHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR5:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME5:.+]]\00" -// CHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR6:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME6:.+]]\00" -// CHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR7:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME7:.+]]\00" -// CHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR8:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME8:.+]]\00" -// CHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR9:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME9:.+]]\00" -// CHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR10:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME10:.+]]\00" -// CHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR11:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME11:.+]]\00" -// CHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR12:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME12:.+]]\00" -// CHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR1:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME1:__omp_offloading_[0-9a-f]+_[0-9a-f]+__Z.+_l[0-9]+]]\00" -// TCHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR2:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME2:.+]]\00" -// TCHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR3:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME3:.+]]\00" -// TCHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR4:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME4:.+]]\00" -// TCHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR5:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME5:.+]]\00" -// TCHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR6:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME6:.+]]\00" -// TCHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR7:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME7:.+]]\00" -// TCHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR8:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME8:.+]]\00" -// TCHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR9:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME9:.+]]\00" -// TCHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR10:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME10:.+]]\00" -// TCHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR11:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME11:.+]]\00" -// TCHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR12:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME12:.+]]\00" -// TCHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_parallel_depend_codegen.cpp b/clang/test/OpenMP/target_parallel_depend_codegen.cpp index b7d338c1f272e..6217de39c0933 100644 --- a/clang/test/OpenMP/target_parallel_depend_codegen.cpp +++ b/clang/test/OpenMP/target_parallel_depend_codegen.cpp @@ -56,8 +56,8 @@ // TCHECK-NOT: @{{.+}} = weak constant [[ENTTY]] // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_parallel_for_codegen.cpp b/clang/test/OpenMP/target_parallel_for_codegen.cpp index 66d2a233a83b2..3d80d990e87a5 100644 --- a/clang/test/OpenMP/target_parallel_for_codegen.cpp +++ b/clang/test/OpenMP/target_parallel_for_codegen.cpp @@ -84,8 +84,8 @@ // TCHECK-NOT: @{{.+}} = weak constant [[ENTTY]] // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_parallel_for_codegen_registration.cpp b/clang/test/OpenMP/target_parallel_for_codegen_registration.cpp index aac7795c046ce..a2625c3447068 100644 --- a/clang/test/OpenMP/target_parallel_for_codegen_registration.cpp +++ b/clang/test/OpenMP/target_parallel_for_codegen_registration.cpp @@ -123,57 +123,57 @@ // CHECK-NTARGET-NOT: private unnamed_addr constant [1 x i // CHECK-DAG: [[NAMEPTR1:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME1:__omp_offloading_[0-9a-f]+_[0-9a-f]+__Z.+_l[0-9]+]]\00" -// CHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR2:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME2:.+]]\00" -// CHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR3:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME3:.+]]\00" -// CHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR4:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME4:.+]]\00" -// CHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR5:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME5:.+]]\00" -// CHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR6:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME6:.+]]\00" -// CHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR7:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME7:.+]]\00" -// CHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR8:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME8:.+]]\00" -// CHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR9:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME9:.+]]\00" -// CHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR10:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME10:.+]]\00" -// CHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR11:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME11:.+]]\00" -// CHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR12:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME12:.+]]\00" -// CHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR1:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME1:__omp_offloading_[0-9a-f]+_[0-9a-f]+__Z.+_l[0-9]+]]\00" -// TCHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR2:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME2:.+]]\00" -// TCHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR3:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME3:.+]]\00" -// TCHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR4:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME4:.+]]\00" -// TCHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR5:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME5:.+]]\00" -// TCHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR6:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME6:.+]]\00" -// TCHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR7:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME7:.+]]\00" -// TCHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR8:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME8:.+]]\00" -// TCHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR9:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME9:.+]]\00" -// TCHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR10:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME10:.+]]\00" -// TCHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR11:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME11:.+]]\00" -// TCHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR12:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME12:.+]]\00" -// TCHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_parallel_for_depend_codegen.cpp b/clang/test/OpenMP/target_parallel_for_depend_codegen.cpp index f05bb6e66905e..a6b434e137a92 100644 --- a/clang/test/OpenMP/target_parallel_for_depend_codegen.cpp +++ b/clang/test/OpenMP/target_parallel_for_depend_codegen.cpp @@ -56,8 +56,8 @@ // TCHECK-NOT: @{{.+}} = weak constant [[ENTTY]] // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_parallel_for_simd_codegen.cpp b/clang/test/OpenMP/target_parallel_for_simd_codegen.cpp index a6087c0cbbe8f..89fcf0a1f977f 100644 --- a/clang/test/OpenMP/target_parallel_for_simd_codegen.cpp +++ b/clang/test/OpenMP/target_parallel_for_simd_codegen.cpp @@ -83,8 +83,8 @@ // TCHECK-NOT: @{{.+}} = weak constant [[ENTTY]] // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_parallel_for_simd_codegen_registration.cpp b/clang/test/OpenMP/target_parallel_for_simd_codegen_registration.cpp index 26f6b1ec522d4..072fb44d7b474 100644 --- a/clang/test/OpenMP/target_parallel_for_simd_codegen_registration.cpp +++ b/clang/test/OpenMP/target_parallel_for_simd_codegen_registration.cpp @@ -123,57 +123,57 @@ // CHECK-NTARGET-NOT: private unnamed_addr constant [1 x i // CHECK-DAG: [[NAMEPTR1:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME1:__omp_offloading_[0-9a-f]+_[0-9a-f]+__Z.+_l[0-9]+]]\00" -// CHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR2:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME2:.+]]\00" -// CHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR3:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME3:.+]]\00" -// CHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR4:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME4:.+]]\00" -// CHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR5:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME5:.+]]\00" -// CHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR6:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME6:.+]]\00" -// CHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR7:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME7:.+]]\00" -// CHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR8:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME8:.+]]\00" -// CHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR9:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME9:.+]]\00" -// CHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR10:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME10:.+]]\00" -// CHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR11:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME11:.+]]\00" -// CHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR12:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME12:.+]]\00" -// CHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR1:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME1:__omp_offloading_[0-9a-f]+_[0-9a-f]+__Z.+_l[0-9]+]]\00" -// TCHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR2:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME2:.+]]\00" -// TCHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR3:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME3:.+]]\00" -// TCHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR4:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME4:.+]]\00" -// TCHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR5:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME5:.+]]\00" -// TCHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR6:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME6:.+]]\00" -// TCHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR7:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME7:.+]]\00" -// TCHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR8:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME8:.+]]\00" -// TCHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR9:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME9:.+]]\00" -// TCHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR10:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME10:.+]]\00" -// TCHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR11:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME11:.+]]\00" -// TCHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR12:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME12:.+]]\00" -// TCHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_parallel_for_simd_depend_codegen.cpp b/clang/test/OpenMP/target_parallel_for_simd_depend_codegen.cpp index 57b91f2a0c82b..7c1f2bc7bcc74 100644 --- a/clang/test/OpenMP/target_parallel_for_simd_depend_codegen.cpp +++ b/clang/test/OpenMP/target_parallel_for_simd_depend_codegen.cpp @@ -56,8 +56,8 @@ // TCHECK-NOT: @{{.+}} = weak constant [[ENTTY]] // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_parallel_if_codegen.cpp b/clang/test/OpenMP/target_parallel_if_codegen.cpp index ae862320f8d03..e3ffe58aac862 100644 --- a/clang/test/OpenMP/target_parallel_if_codegen.cpp +++ b/clang/test/OpenMP/target_parallel_if_codegen.cpp @@ -68,8 +68,8 @@ // TCHECK: @{{.+}} = weak constant [[ENTTY]] // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_parallel_num_threads_codegen.cpp b/clang/test/OpenMP/target_parallel_num_threads_codegen.cpp index f3bc5a441a932..8a3d45054873d 100644 --- a/clang/test/OpenMP/target_parallel_num_threads_codegen.cpp +++ b/clang/test/OpenMP/target_parallel_num_threads_codegen.cpp @@ -68,8 +68,8 @@ // TCHECK: @{{.+}} = weak constant [[ENTTY]] // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_simd_codegen.cpp b/clang/test/OpenMP/target_simd_codegen.cpp index b68745e1ade96..f561e545f6590 100644 --- a/clang/test/OpenMP/target_simd_codegen.cpp +++ b/clang/test/OpenMP/target_simd_codegen.cpp @@ -80,8 +80,8 @@ // TCHECK-NOT: @{{.+}} = weak constant [[ENTTY]] // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_simd_codegen_registration.cpp b/clang/test/OpenMP/target_simd_codegen_registration.cpp index b27fb106c0668..61c0ae5bce562 100644 --- a/clang/test/OpenMP/target_simd_codegen_registration.cpp +++ b/clang/test/OpenMP/target_simd_codegen_registration.cpp @@ -123,57 +123,57 @@ // CHECK-NTARGET-NOT: private unnamed_addr constant [1 x i // CHECK-DAG: [[NAMEPTR1:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME1:__omp_offloading_[0-9a-f]+_[0-9a-f]+__Z.+_l[0-9]+]]\00" -// CHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR2:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME2:.+]]\00" -// CHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR3:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME3:.+]]\00" -// CHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR4:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME4:.+]]\00" -// CHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR5:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME5:.+]]\00" -// CHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR6:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME6:.+]]\00" -// CHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR7:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME7:.+]]\00" -// CHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR8:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME8:.+]]\00" -// CHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR9:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME9:.+]]\00" -// CHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR10:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME10:.+]]\00" -// CHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR11:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME11:.+]]\00" -// CHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR12:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME12:.+]]\00" -// CHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR1:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME1:__omp_offloading_[0-9a-f]+_[0-9a-f]+__Z.+_l[0-9]+]]\00" -// TCHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR2:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME2:.+]]\00" -// TCHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR3:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME3:.+]]\00" -// TCHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR4:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME4:.+]]\00" -// TCHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR5:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME5:.+]]\00" -// TCHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR6:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME6:.+]]\00" -// TCHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR7:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME7:.+]]\00" -// TCHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR8:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME8:.+]]\00" -// TCHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR9:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME9:.+]]\00" -// TCHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR10:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME10:.+]]\00" -// TCHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR11:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME11:.+]]\00" -// TCHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR12:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME12:.+]]\00" -// TCHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_simd_depend_codegen.cpp b/clang/test/OpenMP/target_simd_depend_codegen.cpp index 5845daee495cb..f07de8f9d671d 100644 --- a/clang/test/OpenMP/target_simd_depend_codegen.cpp +++ b/clang/test/OpenMP/target_simd_depend_codegen.cpp @@ -56,8 +56,8 @@ // TCHECK-NOT: @{{.+}} = weak constant [[ENTTY]] // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_teams_codegen.cpp b/clang/test/OpenMP/target_teams_codegen.cpp index 4713f0766dc78..c788a20966fe6 100644 --- a/clang/test/OpenMP/target_teams_codegen.cpp +++ b/clang/test/OpenMP/target_teams_codegen.cpp @@ -89,8 +89,8 @@ // TCHECK-NOT: @{{.+}} = weak constant [[ENTTY]] // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_teams_codegen_registration.cpp b/clang/test/OpenMP/target_teams_codegen_registration.cpp index 7c6428b154be4..bc0eecae4807b 100644 --- a/clang/test/OpenMP/target_teams_codegen_registration.cpp +++ b/clang/test/OpenMP/target_teams_codegen_registration.cpp @@ -123,57 +123,57 @@ // CHECK-NTARGET-NOT: private unnamed_addr constant [1 x i // CHECK-DAG: [[NAMEPTR1:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME1:__omp_offloading_[0-9a-f]+_[0-9a-f]+__Z.+_l[0-9]+]]\00" -// CHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR2:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME2:.+]]\00" -// CHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR3:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME3:.+]]\00" -// CHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR4:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME4:.+]]\00" -// CHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR5:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME5:.+]]\00" -// CHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR6:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME6:.+]]\00" -// CHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR7:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME7:.+]]\00" -// CHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR8:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME8:.+]]\00" -// CHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR9:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME9:.+]]\00" -// CHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR10:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME10:.+]]\00" -// CHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR11:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME11:.+]]\00" -// CHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR12:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME12:.+]]\00" -// CHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR1:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME1:__omp_offloading_[0-9a-f]+_[0-9a-f]+__Z.+_l[0-9]+]]\00" -// TCHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR2:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME2:.+]]\00" -// TCHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR3:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME3:.+]]\00" -// TCHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR4:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME4:.+]]\00" -// TCHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR5:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME5:.+]]\00" -// TCHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR6:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME6:.+]]\00" -// TCHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR7:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME7:.+]]\00" -// TCHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR8:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME8:.+]]\00" -// TCHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR9:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME9:.+]]\00" -// TCHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR10:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME10:.+]]\00" -// TCHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR11:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME11:.+]]\00" -// TCHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR12:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME12:.+]]\00" -// TCHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_teams_depend_codegen.cpp b/clang/test/OpenMP/target_teams_depend_codegen.cpp index f47122eab4e27..8591d65f29dba 100644 --- a/clang/test/OpenMP/target_teams_depend_codegen.cpp +++ b/clang/test/OpenMP/target_teams_depend_codegen.cpp @@ -56,8 +56,8 @@ // TCHECK-NOT: @{{.+}} = weak constant [[ENTTY]] // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_teams_distribute_codegen.cpp b/clang/test/OpenMP/target_teams_distribute_codegen.cpp index 28ea08ff1bd33..feb6b46d187c9 100644 --- a/clang/test/OpenMP/target_teams_distribute_codegen.cpp +++ b/clang/test/OpenMP/target_teams_distribute_codegen.cpp @@ -86,8 +86,8 @@ // TCHECK-NOT: @{{.+}} = weak constant [[ENTTY]] // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_teams_distribute_codegen_registration.cpp b/clang/test/OpenMP/target_teams_distribute_codegen_registration.cpp index 4db52b21598ce..b10823aa04333 100644 --- a/clang/test/OpenMP/target_teams_distribute_codegen_registration.cpp +++ b/clang/test/OpenMP/target_teams_distribute_codegen_registration.cpp @@ -123,57 +123,57 @@ // CHECK-NTARGET-NOT: private unnamed_addr constant [1 x i // CHECK-DAG: [[NAMEPTR1:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME1:__omp_offloading_[0-9a-f]+_[0-9a-f]+__Z.+_l[0-9]+]]\00" -// CHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR2:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME2:.+]]\00" -// CHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR3:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME3:.+]]\00" -// CHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR4:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME4:.+]]\00" -// CHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR5:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME5:.+]]\00" -// CHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR6:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME6:.+]]\00" -// CHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR7:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME7:.+]]\00" -// CHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR8:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME8:.+]]\00" -// CHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR9:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME9:.+]]\00" -// CHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR10:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME10:.+]]\00" -// CHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR11:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME11:.+]]\00" -// CHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR12:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME12:.+]]\00" -// CHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR1:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME1:__omp_offloading_[0-9a-f]+_[0-9a-f]+__Z.+_l[0-9]+]]\00" -// TCHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR2:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME2:.+]]\00" -// TCHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR3:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME3:.+]]\00" -// TCHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR4:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME4:.+]]\00" -// TCHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR5:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME5:.+]]\00" -// TCHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR6:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME6:.+]]\00" -// TCHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR7:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME7:.+]]\00" -// TCHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR8:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME8:.+]]\00" -// TCHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR9:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME9:.+]]\00" -// TCHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR10:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME10:.+]]\00" -// TCHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR11:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME11:.+]]\00" -// TCHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR12:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME12:.+]]\00" -// TCHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_teams_distribute_depend_codegen.cpp b/clang/test/OpenMP/target_teams_distribute_depend_codegen.cpp index e6aee65e20710..e10f8002a97c6 100644 --- a/clang/test/OpenMP/target_teams_distribute_depend_codegen.cpp +++ b/clang/test/OpenMP/target_teams_distribute_depend_codegen.cpp @@ -56,8 +56,8 @@ // TCHECK-NOT: @{{.+}} = weak constant [[ENTTY]] // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_teams_distribute_parallel_for_codegen.cpp b/clang/test/OpenMP/target_teams_distribute_parallel_for_codegen.cpp index 4c7ac2441cc5c..1617f8510a379 100644 --- a/clang/test/OpenMP/target_teams_distribute_parallel_for_codegen.cpp +++ b/clang/test/OpenMP/target_teams_distribute_parallel_for_codegen.cpp @@ -54,17 +54,14 @@ int target_teams_fun(int *g){ // discard capture expressions for te and th // HCK1: = alloca i32, // HCK1: = alloca i32, - // HCK1: = alloca i32, - // HCK1: = alloca i32, - // HCK1: = alloca i32, // HCK1: [[N_CAST:%.+]] = alloca i{{32|64}}, // HCK1: [[TE_CAST:%.+]] = alloca i{{32|64}}, // HCK1: [[TH_CAST:%.+]] = alloca i{{32|64}}, - // HCK1: call void @__kmpc_push_target_tripcount(i64 -1, i64 %{{.+}}) // HCK1: [[N_PAR:%.+]] = load{{.+}}, {{.+}} [[N_CAST]], // HCK1: [[TE_PAR:%.+]] = load{{.+}}, {{.+}} [[TE_CAST]], // HCK1: [[TH_PAR:%.+]] = load{{.+}}, {{.+}} [[TH_CAST]], - // HCK1: call i32 @__tgt_target_teams(i64 -1, i8* @{{[^,]+}}, i32 4, i8** %{{[^,]+}}, i8** %{{[^,]+}}, + // HCK1: call void @__kmpc_push_target_tripcount(i64 -1, i64 %{{.+}}) + // HCK1: call i32 @__tgt_target_teams(i64 -1, i8* @{{[^,]+}}, i32 4, i8** %{{[^,]+}}, i8** %{{[^,]+}}, // HCK1: call void @[[OFFL1:.+]](i{{32|64}} [[N_PAR]], {{.+}}, i{{32|64}} [[TE_PAR]], i{{32|64}} [[TH_PAR]]) #pragma omp target teams distribute parallel for num_teams(te), thread_limit(th) diff --git a/clang/test/OpenMP/target_teams_distribute_parallel_for_depend_codegen.cpp b/clang/test/OpenMP/target_teams_distribute_parallel_for_depend_codegen.cpp index c8615a9fc5b55..e2cdd39145d03 100644 --- a/clang/test/OpenMP/target_teams_distribute_parallel_for_depend_codegen.cpp +++ b/clang/test/OpenMP/target_teams_distribute_parallel_for_depend_codegen.cpp @@ -56,8 +56,8 @@ // TCHECK-NOT: @{{.+}} = weak constant [[ENTTY]] // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_teams_distribute_parallel_for_simd_codegen.cpp b/clang/test/OpenMP/target_teams_distribute_parallel_for_simd_codegen.cpp index 6846aaf434d2e..2c74b18fbd00c 100644 --- a/clang/test/OpenMP/target_teams_distribute_parallel_for_simd_codegen.cpp +++ b/clang/test/OpenMP/target_teams_distribute_parallel_for_simd_codegen.cpp @@ -52,18 +52,15 @@ int target_teams_fun(int *g){ // discard capture expressions for te and th // HCK1: = alloca i32, // HCK1: = alloca i32, -// HCK1: = alloca i32, -// HCK1: = alloca i32, -// HCK1: = alloca i32, // HCK1: [[I_CAST:%.+]] = alloca i{{32|64}}, // HCK1: [[N_CAST:%.+]] = alloca i{{32|64}}, // HCK1: [[TE_CAST:%.+]] = alloca i{{32|64}}, // HCK1: [[TH_CAST:%.+]] = alloca i{{32|64}}, -// HCK1: call void @__kmpc_push_target_tripcount(i64 -1, i64 %{{.+}}) // HCK1: [[I_PAR:%.+]] = load{{.+}}, {{.+}} [[I_CAST]], // HCK1: [[N_PAR:%.+]] = load{{.+}}, {{.+}} [[N_CAST]], // HCK1: [[TE_PAR:%.+]] = load{{.+}}, {{.+}} [[TE_CAST]], // HCK1: [[TH_PAR:%.+]] = load{{.+}}, {{.+}} [[TH_CAST]], +// HCK1: call void @__kmpc_push_target_tripcount(i64 -1, i64 %{{.+}}) // HCK1: call i32 @__tgt_target_teams(i64 -1, i8* @{{[^,]+}}, i32 5, i8** %{{[^,]+}}, i8** %{{[^,]+}}, // HCK1: call void @[[OFFL1:.+]](i{{32|64}} [[I_PAR]], i{{32|64}} [[N_PAR]], {{.+}}, i{{32|64}} [[TE_PAR]], i{{32|64}} [[TH_PAR]]) @@ -77,8 +74,7 @@ int target_teams_fun(int *g){ // HCK1: call void @[[OFFL2:.+]](i{{64|32}} %{{.+}}) {{{ #pragma omp target teams distribute parallel for simd is_device_ptr(g) simdlen(8) - for( - int i = 0; i < n; i++) { + for(int i = 0; i < n; i++) { a[i] = g[0]; } }}} diff --git a/clang/test/OpenMP/target_teams_distribute_parallel_for_simd_codegen_registration.cpp b/clang/test/OpenMP/target_teams_distribute_parallel_for_simd_codegen_registration.cpp index 147ead404e1f3..3d12ad3b85a33 100644 --- a/clang/test/OpenMP/target_teams_distribute_parallel_for_simd_codegen_registration.cpp +++ b/clang/test/OpenMP/target_teams_distribute_parallel_for_simd_codegen_registration.cpp @@ -123,57 +123,57 @@ // CHECK-NTARGET-NOT: private unnamed_addr constant [1 x i // CHECK-DAG: [[NAMEPTR1:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME1:__omp_offloading_[0-9a-f]+_[0-9a-f]+__Z.+_l[0-9]+]]\00" -// CHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR2:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME2:.+]]\00" -// CHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR3:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME3:.+]]\00" -// CHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR4:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME4:.+]]\00" -// CHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR5:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME5:.+]]\00" -// CHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR6:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME6:.+]]\00" -// CHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR7:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME7:.+]]\00" -// CHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR8:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME8:.+]]\00" -// CHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR9:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME9:.+]]\00" -// CHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR10:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME10:.+]]\00" -// CHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR11:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME11:.+]]\00" -// CHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR12:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME12:.+]]\00" -// CHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR1:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME1:__omp_offloading_[0-9a-f]+_[0-9a-f]+__Z.+_l[0-9]+]]\00" -// TCHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR2:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME2:.+]]\00" -// TCHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR3:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME3:.+]]\00" -// TCHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR4:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME4:.+]]\00" -// TCHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR5:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME5:.+]]\00" -// TCHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR6:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME6:.+]]\00" -// TCHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR7:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME7:.+]]\00" -// TCHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR8:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME8:.+]]\00" -// TCHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR9:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME9:.+]]\00" -// TCHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR10:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME10:.+]]\00" -// TCHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR11:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME11:.+]]\00" -// TCHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR12:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME12:.+]]\00" -// TCHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_teams_distribute_parallel_for_simd_depend_codegen.cpp b/clang/test/OpenMP/target_teams_distribute_parallel_for_simd_depend_codegen.cpp index 8d46979a1207b..1b2800606a270 100644 --- a/clang/test/OpenMP/target_teams_distribute_parallel_for_simd_depend_codegen.cpp +++ b/clang/test/OpenMP/target_teams_distribute_parallel_for_simd_depend_codegen.cpp @@ -56,8 +56,8 @@ // TCHECK-NOT: @{{.+}} = weak constant [[ENTTY]] // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_teams_distribute_simd_codegen.cpp b/clang/test/OpenMP/target_teams_distribute_simd_codegen.cpp index 37615f0f032c9..de05d82db4a58 100644 --- a/clang/test/OpenMP/target_teams_distribute_simd_codegen.cpp +++ b/clang/test/OpenMP/target_teams_distribute_simd_codegen.cpp @@ -86,8 +86,8 @@ // TCHECK-NOT: @{{.+}} = weak constant [[ENTTY]] // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_teams_distribute_simd_codegen_registration.cpp b/clang/test/OpenMP/target_teams_distribute_simd_codegen_registration.cpp index 843f377f6e9da..36c3aaec2b096 100644 --- a/clang/test/OpenMP/target_teams_distribute_simd_codegen_registration.cpp +++ b/clang/test/OpenMP/target_teams_distribute_simd_codegen_registration.cpp @@ -123,57 +123,57 @@ // CHECK-NTARGET-NOT: private unnamed_addr constant [1 x i // CHECK-DAG: [[NAMEPTR1:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME1:__omp_offloading_[0-9a-f]+_[0-9a-f]+__Z.+_l[0-9]+]]\00" -// CHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR2:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME2:.+]]\00" -// CHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR3:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME3:.+]]\00" -// CHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR4:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME4:.+]]\00" -// CHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR5:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME5:.+]]\00" -// CHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR6:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME6:.+]]\00" -// CHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR7:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME7:.+]]\00" -// CHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR8:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME8:.+]]\00" -// CHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR9:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME9:.+]]\00" -// CHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR10:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME10:.+]]\00" -// CHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR11:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME11:.+]]\00" -// CHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // CHECK-DAG: [[NAMEPTR12:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME12:.+]]\00" -// CHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// CHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* @{{.*}}, i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR1:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME1:__omp_offloading_[0-9a-f]+_[0-9a-f]+__Z.+_l[0-9]+]]\00" -// TCHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY1:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR1]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR2:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME2:.+]]\00" -// TCHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY2:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR2]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR3:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME3:.+]]\00" -// TCHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY3:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR3]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR4:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME4:.+]]\00" -// TCHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY4:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR4]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR5:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME5:.+]]\00" -// TCHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY5:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR5]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR6:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME6:.+]]\00" -// TCHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY6:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR6]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR7:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME7:.+]]\00" -// TCHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY7:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR7]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR8:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME8:.+]]\00" -// TCHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY8:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR8]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR9:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME9:.+]]\00" -// TCHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY9:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR9]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR10:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME10:.+]]\00" -// TCHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY10:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR10]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR11:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME11:.+]]\00" -// TCHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY11:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR11]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 // TCHECK-DAG: [[NAMEPTR12:@.+]] = internal unnamed_addr constant [{{.*}} x i8] c"[[NAME12:.+]]\00" -// TCHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section ".omp_offloading.entries", align 1 +// TCHECK-DAG: [[ENTRY12:@.+]] = weak constant [[ENTTY]] { i8* bitcast (void (i[[SZ]])* @{{.*}} to i8*), i8* getelementptr inbounds ([{{.*}} x i8], [{{.*}} x i8]* [[NAMEPTR12]], i32 0, i32 0), i[[SZ]] 0, i32 0, i32 0 }, section "omp_offloading_entries", align 1 -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_teams_distribute_simd_depend_codegen.cpp b/clang/test/OpenMP/target_teams_distribute_simd_depend_codegen.cpp index b7bb85df77bba..5789d1cd394b1 100644 --- a/clang/test/OpenMP/target_teams_distribute_simd_depend_codegen.cpp +++ b/clang/test/OpenMP/target_teams_distribute_simd_depend_codegen.cpp @@ -56,8 +56,8 @@ // TCHECK-NOT: @{{.+}} = weak constant [[ENTTY]] // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_teams_num_teams_codegen.cpp b/clang/test/OpenMP/target_teams_num_teams_codegen.cpp index 3d2eb55f1d5ff..35e8bbc11ca6c 100644 --- a/clang/test/OpenMP/target_teams_num_teams_codegen.cpp +++ b/clang/test/OpenMP/target_teams_num_teams_codegen.cpp @@ -68,8 +68,8 @@ // TCHECK: @{{.+}} = weak constant [[ENTTY]] // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/target_teams_thread_limit_codegen.cpp b/clang/test/OpenMP/target_teams_thread_limit_codegen.cpp index 3dfd7532190a4..6f0e27e9e6340 100644 --- a/clang/test/OpenMP/target_teams_thread_limit_codegen.cpp +++ b/clang/test/OpenMP/target_teams_thread_limit_codegen.cpp @@ -68,8 +68,8 @@ // TCHECK: @{{.+}} = weak constant [[ENTTY]] // Check if offloading descriptor is created. -// CHECK: [[ENTBEGIN:@.+]] = external constant [[ENTTY]] -// CHECK: [[ENTEND:@.+]] = external constant [[ENTTY]] +// CHECK: [[ENTBEGIN:@.+]] = external hidden constant [[ENTTY]] +// CHECK: [[ENTEND:@.+]] = external hidden constant [[ENTTY]] // CHECK: [[DEVBEGIN:@.+]] = extern_weak constant i8 // CHECK: [[DEVEND:@.+]] = extern_weak constant i8 // CHECK: [[IMAGES:@.+]] = internal unnamed_addr constant [1 x [[DEVTY]]] [{{.+}} { i8* [[DEVBEGIN]], i8* [[DEVEND]], [[ENTTY]]* [[ENTBEGIN]], [[ENTTY]]* [[ENTEND]] }], comdat($[[REGFN]]) diff --git a/clang/test/OpenMP/teams_distribute_codegen.cpp b/clang/test/OpenMP/teams_distribute_codegen.cpp index ea299bc346a66..0065c6cbd51cb 100644 --- a/clang/test/OpenMP/teams_distribute_codegen.cpp +++ b/clang/test/OpenMP/teams_distribute_codegen.cpp @@ -21,22 +21,19 @@ int a[100]; // CK1: define {{.*}}i32 @{{.+}}teams_argument_globali( -int teams_argument_global(int n){ +int teams_argument_global(int n) { int te = n / 128; int th = 128; // discard n_addr // CK1: alloca i32, // CK1: [[TE:%.+]] = alloca i32, // CK1: [[TH:%.+]] = alloca i32, - // CK1: alloca i32, - // CK1: alloca i32, - // CK1: alloca i32, // CK1: [[TE_CAST:%.+]] = alloca i{{32|64}}, // CK1: [[TH_CAST:%.+]] = alloca i{{32|64}}, - // CK1: call void @__kmpc_push_target_tripcount(i64 -1, i64 %{{.+}}) // CK1: [[TE_PAR:%.+]] = load{{.+}}, {{.+}} [[TE_CAST]], // CK1: [[TH_PAR:%.+]] = load{{.+}}, {{.+}} [[TH_CAST]], + // CK1: call void @__kmpc_push_target_tripcount(i64 -1, i64 %{{.+}}) // CK1: call i32 @__tgt_target_teams(i64 -1, i8* @{{[^,]+}}, i32 4, i8** %{{[^,]+}}, i8** %{{[^,]+}}, i{{64|32}}* {{.+}}@{{[^,]+}}, i32 0, i32 0), i64* {{.+}}@{{[^,]+}}, i32 0, i32 0), i32 {{.+}}, i32 {{.+}}) // CK1: call void @[[OFFL1:.+]](i{{32|64}} [[TE_PAR]], i{{32|64}} [[TH_PAR]], diff --git a/clang/test/OpenMP/teams_distribute_parallel_for_codegen.cpp b/clang/test/OpenMP/teams_distribute_parallel_for_codegen.cpp index fa425b3ecab0c..bc42f740ad741 100644 --- a/clang/test/OpenMP/teams_distribute_parallel_for_codegen.cpp +++ b/clang/test/OpenMP/teams_distribute_parallel_for_codegen.cpp @@ -28,14 +28,11 @@ int teams_argument_global(int n){ // CK1: alloca i32, // CK1: [[TE:%.+]] = alloca i32, // CK1: [[TH:%.+]] = alloca i32, - // CK1: alloca i32, - // CK1: alloca i32, - // CK1: alloca i32, // CK1: [[TE_CAST:%.+]] = alloca i{{32|64}}, // CK1: [[TH_CAST:%.+]] = alloca i{{32|64}}, - // CK1: call void @__kmpc_push_target_tripcount(i64 -1, i64 %{{.+}}) // CK1: [[TE_PAR:%.+]] = load{{.+}}, {{.+}} [[TE_CAST]], // CK1: [[TH_PAR:%.+]] = load{{.+}}, {{.+}} [[TH_CAST]], + // CK1: call void @__kmpc_push_target_tripcount(i64 -1, i64 %{{.+}}) // CK1: call i32 @__tgt_target_teams(i64 -1, i8* @{{[^,]+}}, i32 4, i8** %{{[^,]+}}, i8** %{{[^,]+}}, i{{64|32}}* {{.+}}@{{[^,]+}}, i32 0, i32 0), i64* {{.+}}@{{[^,]+}}, i32 0, i32 0), i32 {{.+}}, i32 {{.+}}) // CK1: call void @[[OFFL1:.+]](i{{32|64}} [[TE_PAR]], i{{32|64}} [[TH_PAR]], diff --git a/clang/test/OpenMP/teams_distribute_parallel_for_simd_codegen.cpp b/clang/test/OpenMP/teams_distribute_parallel_for_simd_codegen.cpp index 45793419d7c4b..fd2b2da5333ba 100644 --- a/clang/test/OpenMP/teams_distribute_parallel_for_simd_codegen.cpp +++ b/clang/test/OpenMP/teams_distribute_parallel_for_simd_codegen.cpp @@ -28,15 +28,12 @@ int teams_argument_global(int n){ // CK1: alloca i32, // CK1: [[TE:%.+]] = alloca i32, // CK1: [[TH:%.+]] = alloca i32, - // CK1: alloca i32, - // CK1: alloca i32, - // CK1: alloca i32, // CK1: [[TE_CAST:%.+]] = alloca i{{32|64}}, // CK1: [[TH_CAST:%.+]] = alloca i{{32|64}}, - // CK1: call void @__kmpc_push_target_tripcount(i64 -1, i64 %{{.+}}) // CK1: [[TE_PAR:%.+]] = load{{.+}}, {{.+}} [[TE_CAST]], // CK1: [[TH_PAR:%.+]] = load{{.+}}, {{.+}} [[TH_CAST]], + // CK1: call void @__kmpc_push_target_tripcount(i64 -1, i64 %{{.+}}) // CK1: call i32 @__tgt_target_teams(i64 -1, i8* @{{[^,]+}}, i32 4, i8** %{{[^,]+}}, i8** %{{[^,]+}}, i{{64|32}}* {{.+}}@{{[^,]+}}, i32 0, i32 0), i64* {{.+}}@{{[^,]+}}, i32 0, i32 0) // CK1: call void @[[OFFL1:.+]](i{{32|64}} [[TE_PAR]], i{{32|64}} [[TH_PAR]], diff --git a/clang/test/OpenMP/teams_distribute_simd_codegen.cpp b/clang/test/OpenMP/teams_distribute_simd_codegen.cpp index ab1482855e750..ff2a752cfa348 100644 --- a/clang/test/OpenMP/teams_distribute_simd_codegen.cpp +++ b/clang/test/OpenMP/teams_distribute_simd_codegen.cpp @@ -30,15 +30,12 @@ int teams_argument_global(int n) { // CK1: alloca i32, // CK1: [[TE:%.+]] = alloca i32, // CK1: [[TH:%.+]] = alloca i32, - // CK1: alloca i32, - // CK1: alloca i32, - // CK1: alloca i32, // CK1: [[TE_CAST:%.+]] = alloca i{{32|64}}, // CK1: [[TH_CAST:%.+]] = alloca i{{32|64}}, - // CK1: call void @__kmpc_push_target_tripcount(i64 -1, i64 %{{.+}}) // CK1: [[TE_PAR:%.+]] = load{{.+}}, {{.+}} [[TE_CAST]], // CK1: [[TH_PAR:%.+]] = load{{.+}}, {{.+}} [[TH_CAST]], + // CK1: call void @__kmpc_push_target_tripcount(i64 -1, i64 %{{.+}}) // CK1: call i32 @__tgt_target_teams(i64 -1, i8* @{{[^,]+}}, i32 5, i8** %{{[^,]+}}, i8** %{{[^,]+}}, i{{64|32}}* {{.+}}@{{[^,]+}}, i32 0, i32 0), i64* {{.+}}@{{[^,]+}}, i32 0, i32 0), i32 {{.+}}, i32 1) // CK1: call void @[[OFFL1:.+]](i{{32|64}} [[TE_PAR]], i{{32|64}} [[TH_PAR]], diff --git a/clang/test/Preprocessor/pragma_module.c b/clang/test/Preprocessor/pragma_module.c index 90aa9481fb13c..c52d42537573f 100644 --- a/clang/test/Preprocessor/pragma_module.c +++ b/clang/test/Preprocessor/pragma_module.c @@ -1,9 +1,9 @@ // RUN: rm -rf %t // RUN: mkdir %t // RUN: echo 'module foo { module a {} module b {} } module bar {} module if {}' > %t/module.map -// RUN: %clang -cc1 -fmodules -fmodule-name=if -x c %t/module.map -emit-module -o %t/if.pcm -// RUN: %clang -cc1 -E -fmodules %s -fmodule-file=%t/if.pcm -verify -fmodule-name=foo -fmodule-map-file=%t/module.map -// RUN: %clang -cc1 -E -fmodules %s -fmodule-file=%t/if.pcm -verify -fmodule-name=foo -fmodule-map-file=%t/module.map -fmodules-local-submodule-visibility -DLOCAL_VIS +// RUN: %clang_cc1 -fmodules -fmodule-name=if -x c %t/module.map -emit-module -o %t/if.pcm +// RUN: %clang_cc1 -E -fmodules %s -fmodule-file=%t/if.pcm -verify -fmodule-name=foo -fmodule-map-file=%t/module.map +// RUN: %clang_cc1 -E -fmodules %s -fmodule-file=%t/if.pcm -verify -fmodule-name=foo -fmodule-map-file=%t/module.map -fmodules-local-submodule-visibility -DLOCAL_VIS // Just checking the syntax here; the semantics are tested elsewhere. #pragma clang module import // expected-error {{expected module name}} diff --git a/clang/test/Preprocessor/predefined-arch-macros.c b/clang/test/Preprocessor/predefined-arch-macros.c index 5981c80ae3a1a..95bea654fe220 100644 --- a/clang/test/Preprocessor/predefined-arch-macros.c +++ b/clang/test/Preprocessor/predefined-arch-macros.c @@ -3242,6 +3242,9 @@ // RUN: %clang -march=arch13 -E -dM %s -o - 2>&1 \ // RUN: -target s390x-unknown-linux \ // RUN: | FileCheck -match-full-lines %s -check-prefix=CHECK_SYSTEMZ_ARCH13 +// RUN: %clang -march=z15 -E -dM %s -o - 2>&1 \ +// RUN: -target s390x-unknown-linux \ +// RUN: | FileCheck -match-full-lines %s -check-prefix=CHECK_SYSTEMZ_ARCH13 // CHECK_SYSTEMZ_ARCH13: #define __ARCH__ 13 // CHECK_SYSTEMZ_ARCH13: #define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_1 1 // CHECK_SYSTEMZ_ARCH13: #define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 1 diff --git a/clang/test/Preprocessor/riscv-cmodel.c b/clang/test/Preprocessor/riscv-cmodel.c new file mode 100644 index 0000000000000..c0b85eba16bda --- /dev/null +++ b/clang/test/Preprocessor/riscv-cmodel.c @@ -0,0 +1,30 @@ +// RUN: %clang -target riscv32-unknown-linux-gnu -march=rv32i -x c -E -dM %s \ +// RUN: -o - | FileCheck --check-prefix=CHECK-MEDLOW %s +// RUN: %clang -target riscv64-unknown-linux-gnu -march=rv64i -x c -E -dM %s \ +// RUN: -o - | FileCheck --check-prefix=CHECK-MEDLOW %s + +// RUN: %clang -target riscv32-unknown-linux-gnu -march=rv32i -x c -E -dM %s \ +// RUN: -mcmodel=small -o - | FileCheck --check-prefix=CHECK-MEDLOW %s +// RUN: %clang -target riscv64-unknown-linux-gnu -march=rv64i -x c -E -dM %s \ +// RUN: -mcmodel=small -o - | FileCheck --check-prefix=CHECK-MEDLOW %s + +// RUN: %clang -target riscv32-unknown-linux-gnu -march=rv32i -x c -E -dM %s \ +// RUN: -mcmodel=medlow -o - | FileCheck --check-prefix=CHECK-MEDLOW %s +// RUN: %clang -target riscv64-unknown-linux-gnu -march=rv64i -x c -E -dM %s \ +// RUN: -mcmodel=medlow -o - | FileCheck --check-prefix=CHECK-MEDLOW %s + +// CHECK-MEDLOW: #define __riscv_cmodel_medlow 1 +// CHECK-MEDLOW-NOT: __riscv_cmodel_medany + +// RUN: %clang -target riscv32-unknown-linux-gnu -march=rv32i -x c -E -dM %s \ +// RUN: -mcmodel=medium -o - | FileCheck --check-prefix=CHECK-MEDANY %s +// RUN: %clang -target riscv64-unknown-linux-gnu -march=rv64i -x c -E -dM %s \ +// RUN: -mcmodel=medium -o - | FileCheck --check-prefix=CHECK-MEDANY %s + +// RUN: %clang -target riscv32-unknown-linux-gnu -march=rv32i -x c -E -dM %s \ +// RUN: -mcmodel=medany -o - | FileCheck --check-prefix=CHECK-MEDANY %s +// RUN: %clang -target riscv64-unknown-linux-gnu -march=rv64i -x c -E -dM %s \ +// RUN: -mcmodel=medany -o - | FileCheck --check-prefix=CHECK-MEDANY %s + +// CHECK-MEDANY: #define __riscv_cmodel_medany 1 +// CHECK-MEDANY-NOT: __riscv_cmodel_medlow diff --git a/clang/test/Sema/builtin-preserve-access-index.c b/clang/test/Sema/builtin-preserve-access-index.c index c10ceb5145b8c..8cd829da200b1 100644 --- a/clang/test/Sema/builtin-preserve-access-index.c +++ b/clang/test/Sema/builtin-preserve-access-index.c @@ -4,10 +4,36 @@ const void *invalid1(const int *arg) { return __builtin_preserve_access_index(&arg[1], 1); // expected-error {{too many arguments to function call, expected 1, have 2}} } -void *invalid2(const int *arg) { - return __builtin_preserve_access_index(&arg[1]); // expected-warning {{returning 'const void *' from a function with result type 'void *' discards qualifiers}} +int valid2(void) { + return __builtin_preserve_access_index(1); } -const void *invalid3(const int *arg) { - return __builtin_preserve_access_index(1); // expected-warning {{incompatible integer to pointer conversion passing 'int' to parameter of type 'const void *'}} +void *invalid3(const int *arg) { + return __builtin_preserve_access_index(&arg[1]); // expected-warning {{returning 'const int *' from a function with result type 'void *' discards qualifiers}} +} + +const void *invalid4(volatile const int *arg) { + return __builtin_preserve_access_index(arg); // expected-warning {{returning 'const volatile int *' from a function with result type 'const void *' discards qualifiers}} +} + +int *valid5(int *arg) { + return __builtin_preserve_access_index(arg); +} + +int valid6(const volatile int *arg) { + return *__builtin_preserve_access_index(arg); +} + +struct s { int a; int b; }; + +int valid7(struct s *arg) { + return *__builtin_preserve_access_index(&arg->b); +} + +int valid8(struct s *arg) { + return __builtin_preserve_access_index(arg->a + arg->b); +} + +int valid9(struct s *arg) { + return __builtin_preserve_access_index(({arg->a = 2; arg->b = 3; })); } diff --git a/clang/test/Sema/builtins-x86.c b/clang/test/Sema/builtins-x86.c index dca0bdc720a0e..cbaf7bcde871e 100644 --- a/clang/test/Sema/builtins-x86.c +++ b/clang/test/Sema/builtins-x86.c @@ -37,7 +37,7 @@ __m128d test__builtin_ia32_cmpsd(__m128d __a, __m128d __b) { return __builtin_ia32_cmpsd(__a, __b, 32); // expected-error {{argument value 32 is outside the valid range [0, 31]}} } -__mmask16 test__builtin_ia32_cmpps512_mask(__m512d __a, __m512d __b) { +__mmask16 test__builtin_ia32_cmpps512_mask(__m512 __a, __m512 __b) { return __builtin_ia32_cmpps512_mask(__a, __b, 32, -1, 4); // expected-error {{argument value 32 is outside the valid range [0, 31]}} } @@ -185,3 +185,19 @@ __m256i test_mm256_shrdi_epi16(__m256i __A, __m256i __B) { __m128i test_mm128_shrdi_epi16(__m128i __A, __m128i __B) { return __builtin_ia32_vpshrdw128(__A, __B, 1024); // expected-error {{argument value 1024 is outside the valid range [0, 255]}} } + +unsigned char test_lwpins32(unsigned int data2, unsigned int data1, unsigned int flags) { + return __builtin_ia32_lwpins32(data2, data1, flags); // expected-error {{argument to '__builtin_ia32_lwpins32' must be a constant integer}} +} + +void test_lwpval32(unsigned int data2, unsigned int data1, unsigned int flags) { + __builtin_ia32_lwpval32(data2, data1, flags); // expected-error {{argument to '__builtin_ia32_lwpval32' must be a constant integer}} +} + +unsigned char test_lwpins64(unsigned long long data2, unsigned long long data1, unsigned int flags) { + return __builtin_ia32_lwpins64(data2, data1, flags); // expected-error {{argument to '__builtin_ia32_lwpins64' must be a constant integer}} +} + +void test_lwpval64(unsigned long long data2, unsigned long long data1, unsigned int flags) { + __builtin_ia32_lwpval64(data2, data1, flags); // expected-error {{argument to '__builtin_ia32_lwpval64' must be a constant integer}} +} diff --git a/clang/test/Sema/div-sizeof-array.cpp b/clang/test/Sema/div-sizeof-array.cpp index 15da1da0f9377..7c76a5265b85e 100644 --- a/clang/test/Sema/div-sizeof-array.cpp +++ b/clang/test/Sema/div-sizeof-array.cpp @@ -9,21 +9,35 @@ int f(Ty (&Array)[N]) { typedef int int32; void test(void) { - int arr[12]; // expected-note 2 {{array 'arr' declared here}} + int arr[12]; // expected-note 2 {{array 'arr' declared here}} unsigned long long arr2[4]; int *p = &arr[0]; int a1 = sizeof(arr) / sizeof(*arr); int a2 = sizeof arr / sizeof p; // expected-warning {{expression does not compute the number of elements in this array; element type is 'int', not 'int *'}} + // expected-note@-1 {{place parentheses around the 'sizeof p' expression to silence this warning}} int a4 = sizeof arr2 / sizeof p; int a5 = sizeof(arr) / sizeof(short); // expected-warning {{expression does not compute the number of elements in this array; element type is 'int', not 'short'}} + // expected-note@-1 {{place parentheses around the 'sizeof(short)' expression to silence this warning}} int a6 = sizeof(arr) / sizeof(int32); int a7 = sizeof(arr) / sizeof(int); int a9 = sizeof(arr) / sizeof(unsigned int); const char arr3[2] = "A"; int a10 = sizeof(arr3) / sizeof(char); + int a11 = sizeof(arr2) / (sizeof(unsigned)); + int a12 = sizeof(arr) / (sizeof(short)); - int arr4[10][12]; // expected-note 3 {{array 'arr4' declared here}} - int b1 = sizeof(arr4) / sizeof(arr2[12]); // expected-warning {{expression does not compute the number of elements in this array; element type is 'int [12]', not 'unsigned long long'}} - int b2 = sizeof(arr4) / sizeof(int *); // expected-warning {{expression does not compute the number of elements in this array; element type is 'int [12]', not 'int *'}} - int b3 = sizeof(arr4) / sizeof(short *); // expected-warning {{expression does not compute the number of elements in this array; element type is 'int [12]', not 'short *'}} + int arr4[10][12]; + int b1 = sizeof(arr4) / sizeof(arr2[12]); + int b2 = sizeof(arr4) / sizeof(int *); + int b3 = sizeof(arr4) / sizeof(short *); + int arr5[][5] = { + {1, 2, 3, 4, 5}, + {6, 7, 8, 9, 0}, + }; + int c1 = sizeof(arr5) / sizeof(*arr5); + int c2 = sizeof(arr5) / sizeof(**arr5); + int c3 = sizeof(arr5) / sizeof(int); + + float m[4][4]; + int d1 = sizeof(m) / sizeof(**m); } diff --git a/clang/test/Sema/format-bool.c b/clang/test/Sema/format-bool.c new file mode 100644 index 0000000000000..53c2c7fd126a6 --- /dev/null +++ b/clang/test/Sema/format-bool.c @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -xc %s -verify -DBOOL=_Bool +// RUN: %clang_cc1 -xc++ %s -verify -DBOOL=bool +// RUN: %clang_cc1 -xobjective-c %s -verify -DBOOL=_Bool +// RUN: %clang_cc1 -xc %s -verify -DBOOL=_Bool -Wformat-pedantic -DPEDANTIC +// RUN: %clang_cc1 -xc++ %s -verify -DBOOL=bool -Wformat-pedantic -DPEDANTIC + +__attribute__((format(__printf__, 1, 2))) +int p(const char *fmt, ...); + +BOOL b; + +#ifdef __OBJC__ +@interface NSString ++(NSString *)stringWithFormat:(NSString *)fmt, ... + __attribute__((format(__NSString__, 1, 2))); +@end + +#define YES __objc_yes +#define NO __objc_no +#endif + +int main() { + p("%d", b); + p("%hd", b); +#ifdef PEDANTIC + // expected-warning@-2 {{format specifies type 'short' but the argument has type}} +#endif + p("%hhd", b); + p("%u", b); + p("%hu", b); +#ifdef PEDANTIC + // expected-warning@-2 {{format specifies type 'unsigned short' but the argument has type}} +#endif + p("%hhu", b); + p("%c", b); // expected-warning {{using '%c' format specifier, but argument has boolean value}} + p("%lc", b); // expected-warning {{using '%lc' format specifier, but argument has boolean value}} + p("%c", 1 == 1); // expected-warning {{using '%c' format specifier, but argument has boolean value}} + p("%f", b); // expected-warning{{format specifies type 'double' but the argument has type}} + p("%ld", b); // expected-warning{{format specifies type 'long' but the argument has type}} + p("%lld", b); // expected-warning{{format specifies type 'long long' but the argument has type}} + +#ifdef __OBJC__ + [NSString stringWithFormat: @"%c", 0]; // probably fine? + [NSString stringWithFormat: @"%c", NO]; // expected-warning {{using '%c' format specifier, but argument has boolean value}} +#endif +} diff --git a/clang/test/Sema/objc-bool-constant-conversion-fixit.m b/clang/test/Sema/objc-bool-constant-conversion-fixit.m index 57f575222ee87..08325c1441961 100644 --- a/clang/test/Sema/objc-bool-constant-conversion-fixit.m +++ b/clang/test/Sema/objc-bool-constant-conversion-fixit.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -Werror=constant-conversion %s -fixit-recompile -fixit-to-temporary -E -o - | FileCheck %s +// RUN: %clang_cc1 -Werror=objc-signed-char-bool %s -fixit-recompile -fixit-to-temporary -E -o - | FileCheck %s typedef signed char BOOL; @@ -25,6 +25,17 @@ int main() { b = 1 << 1; // CHECK: b = (1 << 1) ? YES : NO; + + int i; + + b = i; + // CHECK: b = i ? YES : NO; + + b = i * 2; + // CHECK b = (i * 2) ? YES : NO; + + b = 1 ? 2 : 3; + // CHECK: b = 1 ? 2 ? YES : NO : 3 ? YES : NO; } @interface BoolProp @@ -37,4 +48,12 @@ void f(BoolProp *bp) { [bp setB:43]; // CHECK: [bp setB:43 ? YES : NO]; + + int i; + + bp.b = i; + // CHECK: bp.b = i ? YES : NO; + + bp.b = i + 1; + // CHECK: bp.b = (i + 1) ? YES : NO; } diff --git a/clang/test/Sema/objc-bool-constant-conversion.m b/clang/test/Sema/objc-bool-constant-conversion.m index 3638e2f8a9dcf..25546ab0a647d 100644 --- a/clang/test/Sema/objc-bool-constant-conversion.m +++ b/clang/test/Sema/objc-bool-constant-conversion.m @@ -12,20 +12,20 @@ int main() { B = YES; B = NO; - B = -1; // expected-warning{{implicit conversion from constant value -1 to BOOL; the only well defined values for BOOL are YES and NO}} - B = 0 - 1; // expected-warning{{implicit conversion from constant value -1 to BOOL; the only well defined values for BOOL are YES and NO}} - B = YES + YES; // expected-warning {{implicit conversion from constant value 2 to BOOL; the only well defined values for BOOL are YES and NO}} + B = -1; // expected-warning{{implicit conversion from constant value -1 to 'BOOL'; the only well defined values for 'BOOL' are YES and NO}} + B = 0 - 1; // expected-warning{{implicit conversion from constant value -1 to 'BOOL'; the only well defined values for 'BOOL' are YES and NO}} + B = YES + YES; // expected-warning {{implicit conversion from constant value 2 to 'BOOL'; the only well defined values for 'BOOL' are YES and NO}} B = YES | YES; - B = B ? 2 : 2; // expected-warning 2 {{implicit conversion from constant value 2 to BOOL; the only well defined values for BOOL are YES and NO}} + B = B ? 2 : 2; // expected-warning 2 {{implicit conversion from constant value 2 to 'BOOL'; the only well defined values for 'BOOL' are YES and NO}} - BOOL Init = -1; // expected-warning{{implicit conversion from constant value -1 to BOOL; the only well defined values for BOOL are YES and NO}} - BOOL Init2 = B ? 2 : 2; // expected-warning 2 {{implicit conversion from constant value 2 to BOOL; the only well defined values for BOOL are YES and NO}} + BOOL Init = -1; // expected-warning{{implicit conversion from constant value -1 to 'BOOL'; the only well defined values for 'BOOL' are YES and NO}} + BOOL Init2 = B ? 2 : 2; // expected-warning 2 {{implicit conversion from constant value 2 to 'BOOL'; the only well defined values for 'BOOL' are YES and NO}} void takesbool(BOOL); - takesbool(43); // expected-warning {{implicit conversion from constant value 43 to BOOL; the only well defined values for BOOL are YES and NO}} + takesbool(43); // expected-warning {{implicit conversion from constant value 43 to 'BOOL'; the only well defined values for 'BOOL' are YES and NO}} - BOOL OutOfRange = 400; // expected-warning{{implicit conversion from constant value 400 to BOOL; the only well defined values for BOOL are YES and NO}} + BOOL OutOfRange = 400; // expected-warning{{implicit conversion from constant value 400 to 'BOOL'; the only well defined values for 'BOOL' are YES and NO}} } @interface BoolProp @@ -33,6 +33,6 @@ @interface BoolProp @end void f(BoolProp *bp) { - bp.b = 43; // expected-warning {{implicit conversion from constant value 43 to BOOL; the only well defined values for BOOL are YES and NO}} - [bp setB:43]; // expected-warning {{implicit conversion from constant value 43 to BOOL; the only well defined values for BOOL are YES and NO}} + bp.b = 43; // expected-warning {{implicit conversion from constant value 43 to 'BOOL'; the only well defined values for 'BOOL' are YES and NO}} + [bp setB:43]; // expected-warning {{implicit conversion from constant value 43 to 'BOOL'; the only well defined values for 'BOOL' are YES and NO}} } diff --git a/clang/test/Sema/tautological-objc-bool-compare.m b/clang/test/Sema/tautological-objc-bool-compare.m index 525862ed9ea28..5fd7b9e1289a6 100644 --- a/clang/test/Sema/tautological-objc-bool-compare.m +++ b/clang/test/Sema/tautological-objc-bool-compare.m @@ -9,16 +9,16 @@ void test() { int r; r = B > 0; - r = B > 1; // expected-warning {{result of comparison of constant 1 with expression of type BOOL is always false, as the only well defined values for BOOL are YES and NO}} + r = B > 1; // expected-warning {{result of comparison of constant 1 with expression of type 'BOOL' is always false, as the only well defined values for 'BOOL' are YES and NO}} r = B < 1; - r = B < 0; // expected-warning {{result of comparison of constant 0 with expression of type BOOL is always false, as the only well defined values for BOOL are YES and NO}} - r = B >= 0; // expected-warning {{result of comparison of constant 0 with expression of type BOOL is always true, as the only well defined values for BOOL are YES and NO}} + r = B < 0; // expected-warning {{result of comparison of constant 0 with expression of type 'BOOL' is always false, as the only well defined values for 'BOOL' are YES and NO}} + r = B >= 0; // expected-warning {{result of comparison of constant 0 with expression of type 'BOOL' is always true, as the only well defined values for 'BOOL' are YES and NO}} r = B <= 0; - r = B > YES; // expected-warning {{result of comparison of constant YES with expression of type BOOL is always false, as the only well defined values for BOOL are YES and NO}} + r = B > YES; // expected-warning {{result of comparison of constant YES with expression of type 'BOOL' is always false, as the only well defined values for 'BOOL' are YES and NO}} r = B > NO; - r = B < NO; // expected-warning {{result of comparison of constant NO with expression of type BOOL is always false, as the only well defined values for BOOL are YES and NO}} + r = B < NO; // expected-warning {{result of comparison of constant NO with expression of type 'BOOL' is always false, as the only well defined values for 'BOOL' are YES and NO}} r = B < YES; - r = B >= NO; // expected-warning {{result of comparison of constant NO with expression of type BOOL is always true, as the only well defined values for BOOL are YES and NO}} + r = B >= NO; // expected-warning {{result of comparison of constant NO with expression of type 'BOOL' is always true, as the only well defined values for 'BOOL' are YES and NO}} r = B <= NO; } diff --git a/clang/test/Sema/warn-conditional-emum-types-mismatch.c b/clang/test/Sema/warn-conditional-emum-types-mismatch.c new file mode 100644 index 0000000000000..12264ff89270d --- /dev/null +++ b/clang/test/Sema/warn-conditional-emum-types-mismatch.c @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -x c -fsyntax-only -verify -Wenum-compare-conditional %s +// RUN: %clang_cc1 -x c++ -fsyntax-only -verify -Wenum-compare-conditional %s + +enum ro { A = 0x10 }; +enum rw { B = 0xFF }; +enum { C = 0x1A}; + +enum { + STATUS_SUCCESS, + STATUS_FAILURE, + MAX_BASE_STATUS_CODE +}; + +enum ExtendedStatusCodes { + STATUS_SOMETHING_INTERESTING = MAX_BASE_STATUS_CODE + 1000, +}; + + +int get_flag(int cond) { + return cond ? A : B; + #ifdef __cplusplus + // expected-warning@-2 {{enumeration type mismatch in conditional expression ('ro' and 'rw')}} + #else + // expected-no-diagnostics + #endif +} + +// In the following cases we purposefully differ from GCC and dont warn because +// this code pattern is quite sensitive and we dont want to produce so many false positives. + +int get_flag_anon_enum(int cond) { + return cond ? A : C; +} + +int foo(int c) { + return c ? STATUS_SOMETHING_INTERESTING : STATUS_SUCCESS; +} diff --git a/clang/test/Sema/warn-int-in-bool-context.c b/clang/test/Sema/warn-int-in-bool-context.c new file mode 100644 index 0000000000000..0c94ebb391f3c --- /dev/null +++ b/clang/test/Sema/warn-int-in-bool-context.c @@ -0,0 +1,74 @@ +// RUN: %clang_cc1 -x c -fsyntax-only -verify -Wint-in-bool-context %s +// RUN: %clang_cc1 -x c -fsyntax-only -verify -Wall %s +// RUN: %clang_cc1 -x c++ -fsyntax-only -verify -Wint-in-bool-context %s +// RUN: %clang_cc1 -x c++ -fsyntax-only -verify -Wall %s + +#define ONE 1 +#define TWO 2 + +#define SHIFT(l, r) l << r +#define MM a << a +#define AF 1 << 7 + +#ifdef __cplusplus +typedef bool boolean; +#else +typedef _Bool boolean; +#endif + +enum num { + zero, + one, + two, +}; + +int test(int a, unsigned b, enum num n) { + boolean r; + r = a << a; // expected-warning {{converting the result of '<<' to a boolean; did you mean '(a << a) != 0'?}} + r = MM; // expected-warning {{converting the result of '<<' to a boolean; did you mean '(a << a) != 0'?}} + r = (1 << 7); // expected-warning {{converting the result of '<<' to a boolean always evaluates to true}} + r = 2UL << 2; // expected-warning {{converting the result of '<<' to a boolean always evaluates to true}} + r = 0 << a; // expected-warning {{converting the result of '<<' to a boolean always evaluates to false}} + r = 0 << 2; // expected-warning {{converting the result of '<<' to a boolean always evaluates to false}} + r = 1 << 0; // expected-warning {{converting the result of '<<' to a boolean always evaluates to true}} + r = 1 << 2; // expected-warning {{converting the result of '<<' to a boolean always evaluates to true}} + r = 1ULL << 2; // expected-warning {{converting the result of '<<' to a boolean always evaluates to true}} + r = 2 << b; // expected-warning {{converting the result of '<<' to a boolean; did you mean '(2 << b) != 0'?}} + r = (unsigned)(2 << b); + r = b << 7; + r = (1 << a); // expected-warning {{converting the result of '<<' to a boolean; did you mean '(1 << a) != 0'?}} + r = TWO << a; // expected-warning {{converting the result of '<<' to a boolean; did you mean '(2 << a) != 0'?}} + r = a << 7; // expected-warning {{converting the result of '<<' to a boolean; did you mean '(a << 7) != 0'?}} + r = ONE << a; // expected-warning {{converting the result of '<<' to a boolean; did you mean '(1 << a) != 0'?}} + if (TWO << a) // expected-warning {{converting the result of '<<' to a boolean; did you mean '(2 << a) != 0'?}} + return a; + + for (a = 0; 1 << a; a++) // expected-warning {{converting the result of '<<' to a boolean; did you mean '(1 << a) != 0'?}} + ; + + if (a << TWO) // expected-warning {{converting the result of '<<' to a boolean; did you mean '(a << 2) != 0'?}} + return a; + + if (n || two) + // expected-warning@-1 {{converting the enum constant to a boolean}} + return a; + + if (n == one || two) + // expected-warning@-1 {{converting the enum constant to a boolean}} + return a; + + if (r && two) + // expected-warning@-1 {{converting the enum constant to a boolean}} + return a; + + if (two && r) + // expected-warning@-1 {{converting the enum constant to a boolean}} + return a; + + if (n == one && two) + // expected-warning@-1 {{converting the enum constant to a boolean}} + return a; + + // Don't warn in macros. + return SHIFT(1, a); +} diff --git a/clang/test/Sema/warn-integer-constants-in-ternary.c b/clang/test/Sema/warn-integer-constants-in-ternary.c new file mode 100644 index 0000000000000..287b91ecdb723 --- /dev/null +++ b/clang/test/Sema/warn-integer-constants-in-ternary.c @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -x c -fsyntax-only -verify -Wtautological-constant-compare %s +// RUN: %clang_cc1 -x c -fsyntax-only -verify %s +// RUN: %clang_cc1 -x c++ -fsyntax-only -verify -Wtautological-constant-compare %s +// RUN: %clang_cc1 -x c++ -fsyntax-only -verify %s + +#define ONE 1 +#define TWO 2 + +#define TERN(c, l, r) c ? l : r + +#ifdef __cplusplus +typedef bool boolean; +#else +typedef _Bool boolean; +#endif + +void test(boolean a) { + boolean r; + r = a ? (1) : TWO; + r = a ? 3 : TWO; // expected-warning {{converting the result of '?:' with integer constants to a boolean always evaluates to 'true'}} + r = a ? -2 : 0; // expected-warning {{converting the result of '?:' with integer constants to a boolean always evaluates to 'true'}} + r = a ? 3 : -2; // expected-warning {{converting the result of '?:' with integer constants to a boolean always evaluates to 'true'}} + r = a ? 0 : TWO; + r = a ? 3 : ONE; // expected-warning {{converting the result of '?:' with integer constants to a boolean always evaluates to 'true'}} + r = a ? ONE : 0; + r = a ? 0 : -0; + r = a ? 1 : 0; + r = a ? ONE : 0; + r = a ? ONE : ONE; + r = TERN(a, 4, 8); // expected-warning {{converting the result of '?:' with integer constants to a boolean always evaluates to 'true'}} + r = TERN(a, -1, -8); // expected-warning {{converting the result of '?:' with integer constants to a boolean always evaluates to 'true'}} +} diff --git a/clang/test/Sema/warn-overlap.c b/clang/test/Sema/warn-overlap.c index 6299c511fe230..066312591c1eb 100644 --- a/clang/test/Sema/warn-overlap.c +++ b/clang/test/Sema/warn-overlap.c @@ -141,3 +141,34 @@ int returns(int x) { return x < 1 || x != 0; // expected-warning@-1{{overlapping comparisons always evaluate to true}} } + +int integer_conversion(unsigned x, int y) { + return x > 4 || x < 10; + // expected-warning@-1{{overlapping comparisons always evaluate to true}} + return y > 4u || y < 10u; + // expected-warning@-1{{overlapping comparisons always evaluate to true}} +} + +int negative_compare(int x) { + return x > -1 || x < 1; + // expected-warning@-1{{overlapping comparisons always evaluate to true}} +} + +int no_warning(unsigned x) { + return x >= 0 || x == 1; + // no warning since "x >= 0" is caught by a different tautological warning. +} + +struct A { + int x; + int y; +}; + +int struct_test(struct A a) { + return a.x > 5 && a.y < 1; // no warning, different variables + + return a.x > 5 && a.x < 1; + // expected-warning@-1{{overlapping comparisons always evaluate to false}} + return a.y == 1 || a.y != 1; + // expected-warning@-1{{overlapping comparisons always evaluate to true}} +} diff --git a/clang/test/Sema/warn-unreachable.c b/clang/test/Sema/warn-unreachable.c index aec3b070213c7..cfac36a131869 100644 --- a/clang/test/Sema/warn-unreachable.c +++ b/clang/test/Sema/warn-unreachable.c @@ -433,7 +433,7 @@ void wrapOneInFixit(struct StructWithPointer *s) { } void unaryOpNoFixit() { - if (- 1) + if (~ 1) return; // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]] unaryOpNoFixit(); // expected-warning {{code will never be executed}} } diff --git a/clang/test/SemaCUDA/autoret-global.cu b/clang/test/SemaCUDA/autoret-global.cu new file mode 100644 index 0000000000000..ae2f1ca7598bb --- /dev/null +++ b/clang/test/SemaCUDA/autoret-global.cu @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify %s + +#include "Inputs/cuda.h" + +template +__global__ T foo() { + // expected-note@-1 {{kernel function type 'T ()' must have void return type}} +} + +void f0() { + foo<<<0, 0>>>(); + foo<<<0, 0>>>(); + // expected-error@-1 {{no matching function for call to 'foo'}} +} + +__global__ auto f1() { +} + +__global__ auto f2(int x) { + return x + 1; + // expected-error@-2 {{kernel function type 'auto (int)' must have void return type}} +} + +template struct enable_if { typedef T type; }; +template struct enable_if {}; + +template +__global__ +auto bar() -> typename enable_if::type { + // expected-note@-1 {{requirement '3 == 1' was not satisfied [with N = 3]}} +} + +template +__global__ +auto bar() -> typename enable_if::type { + // expected-note@-1 {{requirement '3 == 2' was not satisfied [with N = 3]}} +} + +void f3() { + bar<1><<<0, 0>>>(); + bar<2><<<0, 0>>>(); + bar<3><<<0, 0>>>(); + // expected-error@-1 {{no matching function for call to 'bar'}} +} diff --git a/clang/test/SemaCUDA/default-ctor.cu b/clang/test/SemaCUDA/default-ctor.cu new file mode 100644 index 0000000000000..cbad7a1774c15 --- /dev/null +++ b/clang/test/SemaCUDA/default-ctor.cu @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -std=c++11 -triple nvptx64-nvidia-cuda -fsyntax-only \ +// RUN: -fcuda-is-device -verify -verify-ignore-unexpected=note %s +// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -fsyntax-only \ +// RUN: -verify -verify-ignore-unexpected=note %s + +#include "Inputs/cuda.h" + +struct In { In() = default; }; +struct InD { __device__ InD() = default; }; +struct InH { __host__ InH() = default; }; +struct InHD { __host__ __device__ InHD() = default; }; + +struct Out { Out(); }; +struct OutD { __device__ OutD(); }; +struct OutH { __host__ OutH(); }; +struct OutHD { __host__ __device__ OutHD(); }; + +Out::Out() = default; +__device__ OutD::OutD() = default; +__host__ OutH::OutH() = default; +__host__ __device__ OutHD::OutHD() = default; + +__device__ void fd() { + In in; + InD ind; + InH inh; // expected-error{{no matching constructor for initialization of 'InH'}} + InHD inhd; + Out out; // expected-error{{no matching constructor for initialization of 'Out'}} + OutD outd; + OutH outh; // expected-error{{no matching constructor for initialization of 'OutH'}} + OutHD outhd; +} + +__host__ void fh() { + In in; + InD ind; // expected-error{{no matching constructor for initialization of 'InD'}} + InH inh; + InHD inhd; + Out out; + OutD outd; // expected-error{{no matching constructor for initialization of 'OutD'}} + OutH outh; + OutHD outhd; +} diff --git a/clang/test/SemaCUDA/function-overload.cu b/clang/test/SemaCUDA/function-overload.cu index 1d78636a7089b..b9efd1c09e699 100644 --- a/clang/test/SemaCUDA/function-overload.cu +++ b/clang/test/SemaCUDA/function-overload.cu @@ -402,3 +402,20 @@ __host__ void test_host_template_overload() { __device__ void test_device_template_overload() { template_overload(1); // OK. Attribute-based overloading picks __device__ variant. } + +// Two classes with `operator-` defined. One of them is device only. +struct C1; +struct C2; +__device__ +int operator-(const C1 &x, const C1 &y); +int operator-(const C2 &x, const C2 &y); + +template +__host__ __device__ int constexpr_overload(const T &x, const T &y) { + return x - y; +} + +// Verify that function overloading doesn't prune candidate wrongly. +int test_constexpr_overload(C2 &x, C2 &y) { + return constexpr_overload(x, y); +} diff --git a/clang/test/SemaCUDA/gnu-inline.cu b/clang/test/SemaCUDA/gnu-inline.cu index 0ed543326be80..e383082f2a773 100644 --- a/clang/test/SemaCUDA/gnu-inline.cu +++ b/clang/test/SemaCUDA/gnu-inline.cu @@ -7,4 +7,4 @@ // Check that we can handle gnu_inline functions when compiling in CUDA mode. void foo(); -inline __attribute__((gnu_inline)) void bar() { foo(); } +extern inline __attribute__((gnu_inline)) void bar() { foo(); } diff --git a/clang/test/SemaCUDA/implicit-member-target-collision-cxx11.cu b/clang/test/SemaCUDA/implicit-member-target-collision-cxx11.cu index 7aa1dd3f20880..06015ed0d6d8e 100644 --- a/clang/test/SemaCUDA/implicit-member-target-collision-cxx11.cu +++ b/clang/test/SemaCUDA/implicit-member-target-collision-cxx11.cu @@ -74,11 +74,13 @@ struct B4_with_device_copy_ctor { struct C4_with_collision : A4_with_host_copy_ctor, B4_with_device_copy_ctor { }; -// expected-note@-3 {{copy constructor of 'C4_with_collision' is implicitly deleted because base class 'B4_with_device_copy_ctor' has no copy constructor}} +// expected-note@-3 {{candidate constructor (the implicit copy constructor) not viable: call to invalid function from __host__ function}} +// expected-note@-4 {{implicit copy constructor inferred target collision: call to both __host__ and __device__ members}} +// expected-note@-5 {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} void hostfoo4() { C4_with_collision c; - C4_with_collision c2 = c; // expected-error {{call to implicitly-deleted copy constructor of 'C4_with_collision'}} + C4_with_collision c2 = c; // expected-error {{no matching constructor for initialization of 'C4_with_collision'}} } //------------------------------------------------------------------------------ diff --git a/clang/test/SemaCXX/attr-require-constant-initialization.cpp b/clang/test/SemaCXX/attr-require-constant-initialization.cpp index 12bae81fd2a47..5584b4b55614b 100644 --- a/clang/test/SemaCXX/attr-require-constant-initialization.cpp +++ b/clang/test/SemaCXX/attr-require-constant-initialization.cpp @@ -185,7 +185,7 @@ struct TT2 { ATTR static constexpr LitType lit = {}; ATTR static const NonLit non_lit; ATTR static const NonLit non_lit_list_init; - ATTR static const NonLit non_lit_copy_init; // expected-note {{required by 'require_constant_initialization' attribute here}} + ATTR static const NonLit non_lit_copy_init; #endif }; PODType TT2::pod_noinit; // expected-note 0+ {{declared here}} @@ -203,8 +203,9 @@ PODType TT2::pod_copy_init(TT2::pod_noinit); // expected-error {{variable does n #if __cplusplus >= 201402L const NonLit TT2::non_lit(42); const NonLit TT2::non_lit_list_init = {42}; -const NonLit TT2::non_lit_copy_init = 42; // expected-error {{variable does not have a constant initializer}} -// expected-note@-1 {{subexpression not valid in a constant expression}} +// FIXME: This is invalid, but we incorrectly elide the copy. It's OK if we +// start diagnosing this. +const NonLit TT2::non_lit_copy_init = 42; #endif #if __cplusplus >= 201103L diff --git a/clang/test/SemaCXX/builtin-object-size-cxx14.cpp b/clang/test/SemaCXX/builtin-object-size-cxx14.cpp index e0b05883bb24f..a57bb6b53787a 100644 --- a/clang/test/SemaCXX/builtin-object-size-cxx14.cpp +++ b/clang/test/SemaCXX/builtin-object-size-cxx14.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2a %s typedef __SIZE_TYPE__ size_t; @@ -105,4 +106,10 @@ namespace InvalidBase { struct S { const char *name; }; S invalid_base(); constexpr size_t bos_name = __builtin_object_size(invalid_base().name, 1); + static_assert(bos_name == -1, ""); + + struct T { ~T(); }; + T invalid_base_2(); + constexpr size_t bos_dtor = __builtin_object_size(&(T&)(T&&)invalid_base_2(), 0); + static_assert(bos_dtor == -1, ""); } diff --git a/clang/test/SemaCXX/compare-cxx2a.cpp b/clang/test/SemaCXX/compare-cxx2a.cpp index c68a0ae9133f3..b6e7fc806114b 100644 --- a/clang/test/SemaCXX/compare-cxx2a.cpp +++ b/clang/test/SemaCXX/compare-cxx2a.cpp @@ -8,12 +8,18 @@ #define ASSERT_TYPE(...) static_assert(__is_same(__VA_ARGS__)) #define ASSERT_EXPR_TYPE(Expr, Expect) static_assert(__is_same(decltype(Expr), Expect)); +struct S { + static int x[5]; +}; + void self_compare() { int a; int *b = nullptr; + S s; (void)(a <=> a); // expected-warning {{self-comparison always evaluates to 'std::strong_ordering::equal'}} (void)(b <=> b); // expected-warning {{self-comparison always evaluates to 'std::strong_ordering::equal'}} + (void)(s.x[a] <=> S::x[a]); // expected-warning {{self-comparison always evaluates to 'std::strong_ordering::equal'}} } void test0(long a, unsigned long b) { diff --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp index bcfb3ac5e9550..27ad0f654f566 100644 --- a/clang/test/SemaCXX/constant-expression-cxx11.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp @@ -1108,6 +1108,11 @@ namespace MemberPointer { static_assert((int Derived::*)(int Mid<0>::*)&Mid<0>::n != (int Derived::*)(int Mid<1>::*)&Mid<1>::n, ""); static_assert(&Mid<0>::n == (int Mid<0>::*)&Base::n, ""); + + constexpr int apply(const A &a, int (A::*f)() const) { + return (a.*f)(); + } + static_assert(apply(A(2), &A::f) == 5, ""); } namespace ArrayBaseDerived { @@ -1889,9 +1894,10 @@ namespace ConstexprConstructorRecovery { }; constexpr X() noexcept {}; protected: - E val{0}; // expected-error {{cannot initialize a member subobject of type 'ConstexprConstructorRecovery::X::E' with an rvalue of type 'int'}} + E val{0}; // expected-error {{cannot initialize a member subobject of type 'ConstexprConstructorRecovery::X::E' with an rvalue of type 'int'}} expected-note {{here}} }; - constexpr X x{}; + // FIXME: We should avoid issuing this follow-on diagnostic. + constexpr X x{}; // expected-error {{constant expression}} expected-note {{not initialized}} } namespace Lifetime { @@ -2036,7 +2042,7 @@ namespace BadDefaultInit { // here is bogus (we discard the k(k) initializer because the parameter 'k' // has been marked invalid). struct B { // expected-note 2{{candidate}} - constexpr B( // expected-error {{must initialize all members}} expected-note {{candidate}} + constexpr B( // expected-warning {{initialize all members}} expected-note {{candidate}} int k = X::n) : // expected-error {{no matching constructor}} k(k) {} int k; // expected-note {{not initialized}} diff --git a/clang/test/SemaCXX/constant-expression-cxx1y.cpp b/clang/test/SemaCXX/constant-expression-cxx1y.cpp index 1f9cb57492584..2a8304ebda641 100644 --- a/clang/test/SemaCXX/constant-expression-cxx1y.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx1y.cpp @@ -810,9 +810,10 @@ namespace StmtExpr { } static_assert(f(1) == 1, ""); // expected-error {{constant expression}} expected-note {{in call}} - constexpr int g() { // expected-error {{never produces a constant}} - return ({ int n; n; }); // expected-note {{object of type 'int' is not initialized}} + constexpr int g() { + return ({ int n; n; }); // expected-note {{read of uninitialized object}} } + static_assert(g() == 0, ""); // expected-error {{constant expression}} expected-note {{in call}} // FIXME: We should handle the void statement expression case. constexpr int h() { // expected-error {{never produces a constant}} @@ -1222,3 +1223,22 @@ namespace PR39728 { ~Comment1() = default; }; } + +namespace TemporaryWithBadPointer { + constexpr int *get_bad_pointer() { + int n = 0; // expected-note 2{{here}} + return &n; // expected-warning {{stack}} + } + constexpr int *bad_pointer = get_bad_pointer(); // expected-error {{constant expression}} expected-note {{pointer to 'n' is not a constant expression}} + + struct DoBadThings { int *&℘ int n; }; + constexpr DoBadThings dbt = { // expected-error {{constant expression}} + nullptr, // expected-note {{pointer to 'n' is not a constant expression}} + (dbt.wp = get_bad_pointer(), 0) + }; + + constexpr DoBadThings dbt2 = { // ok + get_bad_pointer(), + (dbt2.wp = nullptr, 0) + }; +} diff --git a/clang/test/SemaCXX/constant-expression-cxx2a.cpp b/clang/test/SemaCXX/constant-expression-cxx2a.cpp index dba877ff2111a..cb8f16a8b0f57 100644 --- a/clang/test/SemaCXX/constant-expression-cxx2a.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx2a.cpp @@ -1,11 +1,32 @@ -// RUN: %clang_cc1 -std=c++2a -verify %s -fcxx-exceptions -triple=x86_64-linux-gnu +// RUN: %clang_cc1 -std=c++2a -verify %s -fcxx-exceptions -triple=x86_64-linux-gnu -Wno-mismatched-new-delete #include "Inputs/std-compare.h" namespace std { struct type_info; + struct destroying_delete_t { + explicit destroying_delete_t() = default; + } inline constexpr destroying_delete{}; + struct nothrow_t { + explicit nothrow_t() = default; + } inline constexpr nothrow{}; + using size_t = decltype(sizeof(0)); + enum class align_val_t : size_t {}; }; +[[nodiscard]] void *operator new(std::size_t, const std::nothrow_t&) noexcept; +[[nodiscard]] void *operator new(std::size_t, std::align_val_t, const std::nothrow_t&) noexcept; +[[nodiscard]] void *operator new[](std::size_t, const std::nothrow_t&) noexcept; +[[nodiscard]] void *operator new[](std::size_t, std::align_val_t, const std::nothrow_t&) noexcept; +void operator delete(void*, const std::nothrow_t&) noexcept; +void operator delete(void*, std::align_val_t, const std::nothrow_t&) noexcept; +void operator delete[](void*, const std::nothrow_t&) noexcept; +void operator delete[](void*, std::align_val_t, const std::nothrow_t&) noexcept; + +// Helper to print out values for debugging. +constexpr void not_defined(); +template constexpr void print(T) { not_defined(); } + namespace ThreeWayComparison { struct A { int n; @@ -219,15 +240,19 @@ static_assert(for_range_init()); namespace Virtual { struct NonZeroOffset { int padding = 123; }; + constexpr void assert(bool b) { if (!b) throw 0; } + // Ensure that we pick the right final overrider during construction. struct A { virtual constexpr char f() const { return 'A'; } char a = f(); + constexpr ~A() { assert(f() == 'A'); } }; struct NoOverrideA : A {}; struct B : NonZeroOffset, NoOverrideA { virtual constexpr char f() const { return 'B'; } char b = f(); + constexpr ~B() { assert(f() == 'B'); } }; struct NoOverrideB : B {}; struct C : NonZeroOffset, A { @@ -236,12 +261,15 @@ namespace Virtual { char c = ((A*)this)->f(); char ba = pba->f(); constexpr C(A *pba) : pba(pba) {} + constexpr ~C() { assert(f() == 'C'); } }; struct D : NonZeroOffset, NoOverrideB, C { // expected-warning {{inaccessible}} virtual constexpr char f() const { return 'D'; } char d = f(); constexpr D() : C((B*)this) {} + constexpr ~D() { assert(f() == 'D'); } }; + constexpr int n = (D(), 0); constexpr D d; static_assert(((B&)d).a == 'A'); static_assert(((C&)d).a == 'A'); @@ -416,7 +444,7 @@ namespace TypeId { namespace Union { struct Base { - int y; // expected-note {{here}} + int y; // expected-note 2{{here}} }; struct A : Base { int x; @@ -489,13 +517,15 @@ namespace Union { constexpr A return_uninit_struct() { B b = {.b = 1}; b.a.x = 2; - return b.a; + return b.a; // expected-note {{in call to 'A(b.a)'}} expected-note {{subobject of type 'int' is not initialized}} } - // FIXME: It's unclear that this should be valid. Copying a B involves - // copying the object representation of the union, but copying an A invokes a - // copy constructor that copies the object elementwise, and reading from - // b.a.y is undefined. - static_assert(return_uninit_struct().x == 2); + // Note that this is rejected even though return_uninit() is accepted, and + // return_uninit() copies the same stuff wrapped in a union. + // + // Copying a B involves copying the object representation of the union, but + // copying an A invokes a copy constructor that copies the object + // elementwise, and reading from b.a.y is undefined. + static_assert(return_uninit_struct().x == 2); // expected-error {{constant expression}} expected-note {{in call}} constexpr B return_init_all() { B b = {.b = 1}; b.a.x = 2; @@ -531,6 +561,29 @@ namespace Union { S3 s; s.n = 0; } + + union ref_member_1 { + int a; + int b; + }; + struct ref_member_2 { + ref_member_1 &&r; + }; + union ref_member_3 { + ref_member_2 a, b; + }; + constexpr int ref_member_test_1() { + ref_member_3 r = {.a = {.r = {.a = 1}}}; + r.a.r.b = 2; + return r.a.r.b; + } + static_assert(ref_member_test_1() == 2); + constexpr int ref_member_test_2() { // expected-error {{never produces a constant}} + ref_member_3 r = {.a = {.r = {.a = 1}}}; + // FIXME: This note isn't great. The 'read' here is reading the referent of the reference. + r.b.r.b = 2; // expected-note {{read of member 'b' of union with active member 'a'}} + return r.b.r.b; + } } namespace TwosComplementShifts { @@ -548,3 +601,723 @@ namespace TwosComplementShifts { static_assert(-3 >> 1 == -2); static_assert(-4 >> 1 == -2); } + +namespace Uninit { + constexpr int f(bool init) { + int a; + if (init) + a = 1; + return a; // expected-note {{read of uninitialized object}} + } + static_assert(f(true) == 1); + static_assert(f(false) == 1); // expected-error {{constant expression}} expected-note {{in call}} + + struct X { + int n; // expected-note {{declared here}} + constexpr X(bool init) { + if (init) n = 123; + } + }; + constinit X x1(true); + constinit X x2(false); // expected-error {{constant initializer}} expected-note {{constinit}} expected-note {{subobject of type 'int' is not initialized}} + + struct Y { + struct Z { int n; }; // expected-note {{here}} + Z z1; + Z z2; + Z z3; + // OK: the lifetime of z1 (and its members) start before the initializer of + // z2 runs. + constexpr Y() : z2{ (z1.n = 1, z1.n + 1) } { z3.n = 3; } + // Not OK: z3 is not in its lifetime when the initializer of z2 runs. + constexpr Y(int) : z2{ + (z3.n = 1, // expected-note {{assignment to object outside its lifetime}} + z3.n + 1) // expected-warning {{uninitialized}} + } { z1.n = 3; } + constexpr Y(int, int) : z2{} {} + }; + // FIXME: This is working around clang not implementing DR2026. With that + // fixed, we should be able to test this without the injected copy. + constexpr Y copy(Y y) { return y; } // expected-note {{in call to 'Y(y)'}} expected-note {{subobject of type 'int' is not initialized}} + constexpr Y y1 = copy(Y()); + static_assert(y1.z1.n == 1 && y1.z2.n == 2 && y1.z3.n == 3); + + constexpr Y y2 = copy(Y(0)); // expected-error {{constant expression}} expected-note {{in call}} + + static_assert(Y(0,0).z2.n == 0); + static_assert(Y(0,0).z1.n == 0); // expected-error {{constant expression}} expected-note {{read of uninitialized object}} + static_assert(Y(0,0).z3.n == 0); // expected-error {{constant expression}} expected-note {{read of uninitialized object}} + + static_assert(copy(Y(0,0)).z2.n == 0); // expected-error {{constant expression}} expected-note {{in call}} + + constexpr unsigned char not_even_unsigned_char() { + unsigned char c; + return c; // expected-note {{read of uninitialized object}} + } + constexpr unsigned char x = not_even_unsigned_char(); // expected-error {{constant expression}} expected-note {{in call}} + + constexpr int switch_var(int n) { + switch (n) { + case 1: + int a; + a = n; + return a; + + case 2: + a = n; + return a; + } + } + constexpr int s1 = switch_var(1); + constexpr int s2 = switch_var(2); + static_assert(s1 == 1 && s2 == 2); + + constexpr bool switch_into_init_stmt() { + switch (1) { + if (int n; false) { + for (int m; false;) { + case 1: + n = m = 1; + return n == 1 && m == 1; + } + } + } + } + static_assert(switch_into_init_stmt()); +} + +namespace dtor { + void lifetime_extension() { + struct X { constexpr ~X() {} }; + X &&a = X(); + } + + template constexpr T &&ref(T &&t) { return (T&&)t; } + + struct Buf { + char buf[64]; + int n = 0; + constexpr void operator+=(char c) { buf[n++] = c; } + constexpr bool operator==(const char *str) const { + return str[n] == 0 && __builtin_memcmp(str, buf, n) == 0; + } + constexpr bool operator!=(const char *str) const { return !operator==(str); } + }; + + struct A { + constexpr A(Buf &buf, char c) : buf(buf), c(c) { buf += c; } + constexpr ~A() { buf += c; } + constexpr operator bool() const { return true; } + Buf &buf; + char c; + }; + + constexpr bool dtor_calls_dtor() { + union U { + constexpr U(Buf &buf) : u(buf, 'u') { buf += 'U'; } + constexpr ~U() { u.buf += 'U'; } + A u, v; + }; + + struct B : A { + A c, &&d, e; + union { + A f; + }; + U u; + constexpr B(Buf &buf) + : A(buf, 'a'), c(buf, 'c'), d(ref(A(buf, 'd'))), e(A(buf, 'e')), f(buf, 'f'), u(buf) { + buf += 'b'; + } + constexpr ~B() { + buf += 'b'; + } + }; + + Buf buf; + { + B b(buf); + if (buf != "acddefuUb") + return false; + } + if (buf != "acddefuUbbUeca") + return false; + return true; + } + static_assert(dtor_calls_dtor()); + + constexpr void abnormal_termination(Buf &buf) { + struct Indestructible { + constexpr ~Indestructible(); // not defined + }; + + A a(buf, 'a'); + A(buf, 'b'); + int n = 0; + for (A &&c = A(buf, 'c'); A d = A(buf, 'd'); A(buf, 'e')) { + switch (A f(buf, 'f'); A g = A(buf, 'g')) { // expected-warning {{boolean}} + case false: { + A x(buf, 'x'); + } + + case true: { + A h(buf, 'h'); + switch (n++) { + case 0: + break; + case 1: + continue; + case 2: + return; + } + break; + } + + default: + Indestructible indest; + } + + A j = (A(buf, 'i'), A(buf, 'j')); + } + } + + constexpr bool check_abnormal_termination() { + Buf buf = {}; + abnormal_termination(buf); + return buf == + "abbc" + "dfgh" /*break*/ "hgfijijeed" + "dfgh" /*continue*/ "hgfeed" + "dfgh" /*return*/ "hgfd" + "ca"; + } + static_assert(check_abnormal_termination()); + + constexpr bool run_dtors_on_array_filler() { + struct S { + int times_destroyed = 0; + constexpr ~S() { if (++times_destroyed != 1) throw "oops"; } + }; + S s[3]; + return true; + } + static_assert(run_dtors_on_array_filler()); + + // Ensure that we can handle temporary cleanups for array temporaries. + struct ArrElem { constexpr ~ArrElem() {} }; + using Arr = ArrElem[3]; + static_assert((Arr{}, true)); +} + +namespace dynamic_alloc { + constexpr int *p = // expected-error {{constant}} expected-note {{pointer to heap-allocated object is not a constant expression}} + new int; // expected-note {{heap allocation performed here}} + + constexpr int f(int n) { + int *p = new int[n]; + for (int i = 0; i != n; ++i) { + p[i] = i; + } + int k = 0; + for (int i = 0; i != n; ++i) { + k += p[i]; + } + delete[] p; + return k; + } + static_assert(f(123) == 123 * 122 / 2); + + constexpr bool nvdtor() { // expected-error {{never produces a constant expression}} + struct S { + constexpr ~S() {} + }; + struct T : S {}; + delete (S*)new T; // expected-note {{delete of object with dynamic type 'T' through pointer to base class type 'S' with non-virtual destructor}} + return true; + } + + constexpr int vdtor_1() { + int a; + struct S { + constexpr S(int *p) : p(p) {} + constexpr virtual ~S() { *p = 1; } + int *p; + }; + struct T : S { + // implicit destructor defined eagerly because it is constexpr and virtual + using S::S; + }; + delete (S*)new T(&a); + return a; + } + static_assert(vdtor_1() == 1); + + constexpr int vdtor_2() { + int a = 0; + struct S { constexpr virtual ~S() {} }; + struct T : S { + constexpr T(int *p) : p(p) {} + constexpr ~T() { ++*p; } + int *p; + }; + S *p = new T{&a}; + delete p; + return a; + } + static_assert(vdtor_2() == 1); + + constexpr int vdtor_3(int mode) { + int a = 0; + struct S { constexpr virtual ~S() {} }; + struct T : S { + constexpr T(int *p) : p(p) {} + constexpr ~T() { ++*p; } + int *p; + }; + S *p = new T[3]{&a, &a, &a}; // expected-note 2{{heap allocation}} + switch (mode) { + case 0: + delete p; // expected-note {{non-array delete used to delete pointer to array object of type 'T [3]'}} + break; + case 1: + // FIXME: This diagnosic isn't great; we should mention the cast to S* + // somewhere in here. + delete[] p; // expected-note {{delete of pointer to subobject '&{*new T [3]#0}[0]'}} + break; + case 2: + delete (T*)p; // expected-note {{non-array delete used to delete pointer to array object of type 'T [3]'}} + break; + case 3: + delete[] (T*)p; + break; + } + return a; + } + static_assert(vdtor_3(0) == 3); // expected-error {{}} expected-note {{in call}} + static_assert(vdtor_3(1) == 3); // expected-error {{}} expected-note {{in call}} + static_assert(vdtor_3(2) == 3); // expected-error {{}} expected-note {{in call}} + static_assert(vdtor_3(3) == 3); + + constexpr void delete_mismatch() { // expected-error {{never produces a constant expression}} + delete[] // expected-note {{array delete used to delete pointer to non-array object of type 'int'}} + new int; // expected-note {{allocation}} + } + + template + constexpr T dynarray(int elems, int i) { + T *p; + if constexpr (sizeof(T) == 1) + p = new T[elems]{"fox"}; // expected-note {{evaluated array bound 3 is too small to hold 4 explicitly initialized elements}} + else + p = new T[elems]{1, 2, 3}; // expected-note {{evaluated array bound 2 is too small to hold 3 explicitly initialized elements}} + T n = p[i]; // expected-note 4{{past-the-end}} + delete [] p; + return n; + } + static_assert(dynarray(4, 0) == 1); + static_assert(dynarray(4, 1) == 2); + static_assert(dynarray(4, 2) == 3); + static_assert(dynarray(4, 3) == 0); + static_assert(dynarray(4, 4) == 0); // expected-error {{constant expression}} expected-note {{in call}} + static_assert(dynarray(3, 2) == 3); + static_assert(dynarray(3, 3) == 0); // expected-error {{constant expression}} expected-note {{in call}} + static_assert(dynarray(2, 1) == 0); // expected-error {{constant expression}} expected-note {{in call}} + static_assert(dynarray(5, 0) == 'f'); + static_assert(dynarray(5, 1) == 'o'); + static_assert(dynarray(5, 2) == 'x'); + static_assert(dynarray(5, 3) == 0); // (from string) + static_assert(dynarray(5, 4) == 0); // (from filler) + static_assert(dynarray(5, 5) == 0); // expected-error {{constant expression}} expected-note {{in call}} + static_assert(dynarray(4, 0) == 'f'); + static_assert(dynarray(4, 1) == 'o'); + static_assert(dynarray(4, 2) == 'x'); + static_assert(dynarray(4, 3) == 0); + static_assert(dynarray(4, 4) == 0); // expected-error {{constant expression}} expected-note {{in call}} + static_assert(dynarray(3, 2) == 'x'); // expected-error {{constant expression}} expected-note {{in call}} + + constexpr bool run_dtors_on_array_filler() { + struct S { + int times_destroyed = 0; + constexpr ~S() { if (++times_destroyed != 1) throw "oops"; } + }; + delete[] new S[3]; + return true; + } + static_assert(run_dtors_on_array_filler()); + + constexpr bool erroneous_array_bound(long long n) { + delete[] new int[n]; // expected-note {{array bound -1 is negative}} expected-note {{array bound 4611686018427387904 is too large}} + return true; + } + static_assert(erroneous_array_bound(3)); + static_assert(erroneous_array_bound(0)); + static_assert(erroneous_array_bound(-1)); // expected-error {{constant expression}} expected-note {{in call}} + static_assert(erroneous_array_bound(1LL << 62)); // expected-error {{constant expression}} expected-note {{in call}} + + constexpr bool erroneous_array_bound_nothrow(long long n) { + int *p = new (std::nothrow) int[n]; + bool result = p != 0; + delete[] p; + return result; + } + static_assert(erroneous_array_bound_nothrow(3)); + static_assert(erroneous_array_bound_nothrow(0)); + static_assert(!erroneous_array_bound_nothrow(-1)); + static_assert(!erroneous_array_bound_nothrow(1LL << 62)); + + constexpr bool evaluate_nothrow_arg() { + bool ok = false; + delete new ((ok = true, std::nothrow)) int; + return ok; + } + static_assert(evaluate_nothrow_arg()); + + constexpr void double_delete() { // expected-error {{never produces a constant expression}} + int *p = new int; + delete p; + delete p; // expected-note {{delete of pointer that has already been deleted}} + } + constexpr bool super_secret_double_delete() { + struct A { + constexpr ~A() { delete this; } // expected-note {{destruction of object that is already being destroyed}} expected-note {{in call}} + }; + delete new A; // expected-note {{in call}} + return true; + } + static_assert(super_secret_double_delete()); // expected-error {{constant expression}} expected-note {{in call}} + + constexpr void use_after_free() { // expected-error {{never produces a constant expression}} + int *p = new int; + delete p; + *p = 1; // expected-note {{assignment to heap allocated object that has been deleted}} + } + constexpr void use_after_free_2() { // expected-error {{never produces a constant expression}} + struct X { constexpr void f() {} }; + X *p = new X; + delete p; + p->f(); // expected-note {{member call on heap allocated object that has been deleted}} + } + + template struct X { + std::size_t n; + char *p; + void dependent(); + }; + template void X::dependent() { + char *p; + // Ensure that we don't try to evaluate these for overflow and crash. These + // are all value-dependent expressions. + p = new char[n]; + p = new (n) char[n]; + p = new char(n); + } +} + +struct placement_new_arg {}; +void *operator new(std::size_t, placement_new_arg); +void operator delete(void*, placement_new_arg); + +namespace placement_new_delete { + struct ClassSpecificNew { + void *operator new(std::size_t); + }; + struct ClassSpecificDelete { + void operator delete(void*); + }; + struct DestroyingDelete { + void operator delete(DestroyingDelete*, std::destroying_delete_t); + }; + struct alignas(64) Overaligned {}; + + constexpr bool ok() { + delete new Overaligned; + delete ::new ClassSpecificNew; + ::delete new ClassSpecificDelete; + ::delete new DestroyingDelete; + return true; + } + static_assert(ok()); + + constexpr bool bad(int which) { + switch (which) { + case 0: + delete new (placement_new_arg{}) int; // expected-note {{call to placement 'operator new'}} + break; + + case 1: + delete new ClassSpecificNew; // expected-note {{call to class-specific 'operator new'}} + break; + + case 2: + delete new ClassSpecificDelete; // expected-note {{call to class-specific 'operator delete'}} + break; + + case 3: + delete new DestroyingDelete; // expected-note {{call to class-specific 'operator delete'}} + break; + + case 4: + // FIXME: This technically follows the standard's rules, but it seems + // unreasonable to expect implementations to support this. + delete new (std::align_val_t{64}) Overaligned; // expected-note {{placement new expression is not yet supported}} + break; + } + + return true; + } + static_assert(bad(0)); // expected-error {{constant expression}} expected-note {{in call}} + static_assert(bad(1)); // expected-error {{constant expression}} expected-note {{in call}} + static_assert(bad(2)); // expected-error {{constant expression}} expected-note {{in call}} + static_assert(bad(3)); // expected-error {{constant expression}} expected-note {{in call}} + static_assert(bad(4)); // expected-error {{constant expression}} expected-note {{in call}} +} + +namespace delete_random_things { + static_assert((delete new int, true)); + static_assert((delete (int*)0, true)); + int n; // expected-note {{declared here}} + static_assert((delete &n, true)); // expected-error {{}} expected-note {{delete of pointer '&n' that does not point to a heap-allocated object}} + struct A { int n; }; + static_assert((delete &(new A)->n, true)); // expected-error {{}} expected-note {{delete of pointer to subobject '&{*new delete_random_things::A#0}.n'}} + static_assert((delete (new int + 1), true)); // expected-error {{}} expected-note {{delete of pointer '&{*new int#0} + 1' that does not point to complete object}} + static_assert((delete[] (new int[3] + 1), true)); // expected-error {{}} expected-note {{delete of pointer to subobject '&{*new int [3]#0}[1]'}} + static_assert((delete &(int&)(int&&)0, true)); // expected-error {{}} expected-note {{delete of pointer '&0' that does not point to a heap-allocated object}} expected-note {{temporary created here}} +} + +namespace value_dependent_delete { + template void f(T *p) { + int arr[(delete p, 0)]; + } +} + +namespace memory_leaks { + static_assert(*new bool(true)); // expected-error {{}} expected-note {{allocation performed here was not deallocated}} + + constexpr bool *f() { return new bool(true); } // expected-note {{allocation performed here was not deallocated}} + static_assert(*f()); // expected-error {{}} + + struct UP { + bool *p; + constexpr ~UP() { delete p; } + constexpr bool &operator*() { return *p; } + }; + constexpr UP g() { return {new bool(true)}; } + static_assert(*g()); // ok + + constexpr bool h(UP p) { return *p; } + static_assert(h({new bool(true)})); // ok +} + +namespace dtor_call { + struct A { int n; }; + constexpr void f() { // expected-error {{never produces a constant expression}} + A a; // expected-note {{destroying object 'a' whose lifetime has already ended}} + a.~A(); + } + union U { A a; }; + constexpr void g() { + U u; + u.a.n = 3; + u.a.~A(); + // There's now effectively no active union member, but we model it as if + // 'a' is still the active union member (but its lifetime has ended). + u.a.n = 4; // Start lifetime of 'a' again. + u.a.~A(); + } + static_assert((g(), true)); + + constexpr bool pseudo() { + using T = bool; + bool b = false; + // This does evaluate the store to 'b'... + (b = true).~T(); + // ... but does not end the lifetime of the object. + return b; + } + static_assert(pseudo()); + + constexpr void use_after_destroy() { + A a; + a.~A(); + A b = a; // expected-note {{in call}} expected-note {{read of object outside its lifetime}} + } + static_assert((use_after_destroy(), true)); // expected-error {{}} expected-note {{in call}} + + constexpr void double_destroy() { + A a; + a.~A(); + a.~A(); // expected-note {{destruction of object outside its lifetime}} + } + static_assert((double_destroy(), true)); // expected-error {{}} expected-note {{in call}} + + struct X { char *p; constexpr ~X() { *p++ = 'X'; } }; + struct Y : X { int y; virtual constexpr ~Y() { *p++ = 'Y'; } }; + struct Z : Y { int z; constexpr ~Z() override { *p++ = 'Z'; } }; + union VU { + constexpr VU() : z() {} + constexpr ~VU() {} + Z z; + }; + + constexpr bool virt_dtor(int mode, const char *expected) { + char buff[4] = {}; + VU vu; + vu.z.p = buff; + switch (mode) { + case 0: + vu.z.~Z(); + break; + case 1: + ((Y&)vu.z).~Y(); + break; + case 2: + ((X&)vu.z).~X(); + break; + case 3: + ((Y&)vu.z).Y::~Y(); + vu.z.z = 1; // ok, still have a Z (with no Y base class!) + break; + case 4: + ((X&)vu.z).X::~X(); + vu.z.y = 1; // ok, still have a Z and a Y (with no X base class!) + break; + } + return __builtin_strcmp(expected, buff) == 0; + } + static_assert(virt_dtor(0, "ZYX")); + static_assert(virt_dtor(1, "ZYX")); + static_assert(virt_dtor(2, "X")); + static_assert(virt_dtor(3, "YX")); + static_assert(virt_dtor(4, "X")); + + constexpr void use_after_virt_destroy() { + char buff[4] = {}; + VU vu; + vu.z.p = buff; + ((Y&)vu.z).~Y(); + ((Z&)vu.z).z = 1; // expected-note {{assignment to object outside its lifetime}} + } + static_assert((use_after_virt_destroy(), true)); // expected-error {{}} expected-note {{in call}} + + constexpr void destroy_after_lifetime() { + A *p; + { + A a; + p = &a; + } + p->~A(); // expected-note {{destruction of object outside its lifetime}} + } + static_assert((destroy_after_lifetime(), true)); // expected-error {{}} expected-note {{in call}} + + constexpr void destroy_after_lifetime2() { + A *p = []{ A a; return &a; }(); // expected-warning {{}} expected-note {{declared here}} + p->~A(); // expected-note {{destruction of variable whose lifetime has ended}} + } + static_assert((destroy_after_lifetime2(), true)); // expected-error {{}} expected-note {{in call}} + + constexpr void destroy_after_lifetime3() { + A *p = []{ return &(A&)(A&&)A(); }(); // expected-warning {{}} expected-note {{temporary created here}} + p->~A(); // expected-note {{destruction of temporary whose lifetime has ended}} + } + static_assert((destroy_after_lifetime3(), true)); // expected-error {{}} expected-note {{in call}} + + constexpr void destroy_after_lifetime4() { // expected-error {{never produces a constant expression}} + A *p = new A; + delete p; + p->~A(); // expected-note {{destruction of heap allocated object that has been deleted}} + } + + struct Extern { constexpr ~Extern() {} } extern e; + constexpr void destroy_extern() { // expected-error {{never produces a constant expression}} + e.~Extern(); // expected-note {{cannot modify an object that is visible outside}} + } + + constexpr A &&a_ref = A(); // expected-note {{temporary created here}} + constexpr void destroy_extern_2() { // expected-error {{never produces a constant expression}} + a_ref.~A(); // expected-note {{destruction of temporary is not allowed in a constant expression outside the expression that created the temporary}} + } + + struct S { + constexpr S() { n = 1; } + constexpr ~S() { n = 0; } + int n; + }; + constexpr void destroy_volatile() { + volatile S s; + } + static_assert((destroy_volatile(), true)); // ok, not volatile during construction and destruction + + constexpr void destroy_null() { // expected-error {{never produces a constant expression}} + ((A*)nullptr)->~A(); // expected-note {{destruction of dereferenced null pointer}} + } + + constexpr void destroy_past_end() { // expected-error {{never produces a constant expression}} + A a; + (&a+1)->~A(); // expected-note {{destruction of dereferenced one-past-the-end pointer}} + } + + constexpr void destroy_past_end_array() { // expected-error {{never produces a constant expression}} + A a[2]; + a[2].~A(); // expected-note {{destruction of dereferenced one-past-the-end pointer}} + } + + union As { + A a, b; + }; + + constexpr void destroy_no_active() { // expected-error {{never produces a constant expression}} + As as; + as.b.~A(); // expected-note {{destruction of member 'b' of union with no active member}} + } + + constexpr void destroy_inactive() { // expected-error {{never produces a constant expression}} + As as; + as.a.n = 1; + as.b.~A(); // expected-note {{destruction of member 'b' of union with active member 'a'}} + } + + constexpr void destroy_no_active_2() { // expected-error {{never produces a constant expression}} + As as; + as.a.n = 1; + as.a.~A(); + // FIXME: This diagnostic is wrong; the union has no active member now. + as.b.~A(); // expected-note {{destruction of member 'b' of union with active member 'a'}} + } + + constexpr void destroy_pointer() { + using T = int*; + T p; + // We used to think this was an -> member access because its left-hand side + // is a pointer. Ensure we don't crash. + p.~T(); + } + static_assert((destroy_pointer(), true)); +} + +namespace temp_dtor { + void f(); + struct A { + bool b; + constexpr ~A() { if (b) f(); } + }; + + // We can't accept either of these unless we start actually registering the + // destructors of the A temporaries to run on shutdown. It's unclear what the + // intended standard behavior is so we reject this for now. + constexpr A &&a = A{false}; // expected-error {{constant}} expected-note {{non-trivial destruction of lifetime-extended temporary}} + void f() { a.b = true; } + + constexpr A &&b = A{true}; // expected-error {{constant}} expected-note {{non-trivial destruction of lifetime-extended temporary}} + + // FIXME: We could in prinicple accept this. + constexpr const A &c = A{false}; // expected-error {{constant}} expected-note {{non-trivial destruction of lifetime-extended temporary}} +} + +namespace value_dependent_init { + struct A { + constexpr ~A() {} + }; + template void f() { + A a = T(); + } +} diff --git a/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp b/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp index d48ee03dccebb..0a12e7eebe45e 100644 --- a/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp +++ b/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp @@ -61,13 +61,13 @@ void test_record() { constexpr int_splicer splice{0x0C05FEFE, 0xCAFEBABE}; - static_assert(bit_cast(splice) == LITTLE_END - ? 0xCAFEBABE0C05FEFE - : 0x0C05FEFECAFEBABE); + static_assert(bit_cast(splice) == (LITTLE_END + ? 0xCAFEBABE0C05FEFE + : 0x0C05FEFECAFEBABE)); - static_assert(bit_cast(0xCAFEBABE0C05FEFE).x == LITTLE_END - ? 0x0C05FEFE - : 0xCAFEBABE); + static_assert(bit_cast(0xCAFEBABE0C05FEFE).x == (LITTLE_END + ? 0x0C05FEFE + : 0xCAFEBABE)); static_assert(round_trip(splice)); static_assert(round_trip(splice)); @@ -220,7 +220,7 @@ void backtrace() { void test_array_fill() { constexpr unsigned char a[4] = {1, 2}; constexpr unsigned int i = bit_cast(a); - static_assert(i == LITTLE_END ? 0x00000201 : 0x01020000, ""); + static_assert(i == (LITTLE_END ? 0x00000201 : 0x01020000)); } typedef decltype(nullptr) nullptr_t; diff --git a/clang/test/SemaCXX/constexpr-turing-cxx2a.cpp b/clang/test/SemaCXX/constexpr-turing-cxx2a.cpp new file mode 100644 index 0000000000000..0941cf408f2e3 --- /dev/null +++ b/clang/test/SemaCXX/constexpr-turing-cxx2a.cpp @@ -0,0 +1,66 @@ +// RUN: %clang_cc1 -verify -std=c++2a %s +// expected-no-diagnostics + +const unsigned halt = (unsigned)-1; + +enum Dir { L, R }; +struct Action { + bool tape; + Dir dir; + unsigned next; +}; +using State = Action[2]; + +// An infinite tape! +struct Tape { + constexpr Tape() = default; + constexpr ~Tape() { + if (l) { l->r = nullptr; delete l; } + if (r) { r->l = nullptr; delete r; } + } + constexpr Tape *left() { + if (!l) { l = new Tape; l->r = this; } + return l; + } + constexpr Tape *right() { + if (!r) { r = new Tape; r->l = this; } + return r; + } + Tape *l = nullptr; + bool val = false; + Tape *r = nullptr; +}; + +// Run turing machine 'tm' on tape 'tape' from state 'state'. Return number of +// steps taken until halt. +constexpr unsigned run(const State *tm) { + Tape *tape = new Tape; + unsigned state = 0; + unsigned steps = 0; + + for (state = 0; state != halt; ++steps) { + auto [val, dir, next_state] = tm[state][tape->val]; + tape->val = val; + tape = (dir == L ? tape->left() : tape->right()); + state = next_state; + } + + delete tape; + return steps; +} + +// 3-state busy beaver. S(bb3) = 21. +constexpr State bb3[] = { + { { true, R, 1 }, { true, R, halt } }, + { { true, L, 1 }, { false, R, 2 } }, + { { true, L, 2 }, { true, L, 0 } } +}; +static_assert(run(bb3) == 21, ""); + +// 4-state busy beaver. S(bb4) = 107. +constexpr State bb4[] = { + { { true, R, 1 }, { true, L, 1 } }, + { { true, L, 0 }, { false, L, 2 } }, + { { true, R, halt }, { true, L, 3 } }, + { { true, R, 3 }, { false, R, 0 } } }; +static_assert(run(bb4) == 107, ""); diff --git a/clang/test/SemaCXX/cxx2a-consteval.cpp b/clang/test/SemaCXX/cxx2a-consteval.cpp index c531b92033d30..3b52ed4409ee1 100644 --- a/clang/test/SemaCXX/cxx2a-consteval.cpp +++ b/clang/test/SemaCXX/cxx2a-consteval.cpp @@ -27,7 +27,7 @@ struct A { } consteval A(int i); consteval A() = default; - consteval ~A() = default; // expected-error {{destructor cannot be marked consteval}} + consteval ~A() = default; }; consteval struct B {}; // expected-error {{struct cannot be marked consteval}} @@ -45,11 +45,17 @@ consteval int f1() {} // expected-error {{no return statement in consteval funct struct C { C() {} + ~C() {} }; struct D { C c; consteval D() = default; // expected-error {{cannot be consteval}} + consteval ~D() = default; // expected-error {{cannot be consteval}} +}; + +struct E : C { // expected-note {{here}} + consteval ~E() {} // expected-error {{cannot be declared consteval because base class 'basic_sema::C' does not have a constexpr destructor}} }; } diff --git a/clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp b/clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp new file mode 100644 index 0000000000000..23582f2e3026e --- /dev/null +++ b/clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp @@ -0,0 +1,168 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s -DNEW=__builtin_operator_new -DDELETE=__builtin_operator_delete +// RUN: %clang_cc1 -std=c++2a -verify %s "-DNEW=operator new" "-DDELETE=operator delete" +// RUN: %clang_cc1 -std=c++2a -verify %s "-DNEW=::operator new" "-DDELETE=::operator delete" + +constexpr bool alloc_from_user_code() { + void *p = NEW(sizeof(int)); // expected-note {{cannot allocate untyped memory in a constant expression; use 'std::allocator::allocate'}} + DELETE(p); + return true; +} +static_assert(alloc_from_user_code()); // expected-error {{constant expression}} expected-note {{in call}} + +namespace std { + using size_t = decltype(sizeof(0)); + // FIXME: It would be preferable to point these notes at the location of the call to allocator<...>::[de]allocate instead + template struct allocator { + constexpr T *allocate(size_t N) { + return (T*)NEW(sizeof(T) * N); // expected-note 3{{heap allocation}} expected-note {{not deallocated}} + } + constexpr void deallocate(void *p) { + DELETE(p); // expected-note 2{{'std::allocator<...>::deallocate' used to delete pointer to object allocated with 'new'}} + } + }; +} + +constexpr bool alloc_via_std_allocator() { + std::allocator alloc; + int *p = alloc.allocate(1); + alloc.deallocate(p); + return true; +} +static_assert(alloc_via_std_allocator()); + +template<> struct std::allocator { + constexpr void *allocate() { return NEW(8); } // expected-note {{cannot allocate memory of function type 'void ()'}} +}; +constexpr void *fn = std::allocator().allocate(); // expected-error {{constant expression}} expected-note {{in call}} + +struct Incomplete; +template<> struct std::allocator { + constexpr void *allocate() { return NEW(8); } // expected-note {{cannot allocate memory of incomplete type 'Incomplete'}} +}; +constexpr void *incomplete = std::allocator().allocate(); // expected-error {{constant expression}} expected-note {{in call}} + +struct WrongSize { char x[5]; }; +static_assert(sizeof(WrongSize) == 5); +template<> struct std::allocator { + constexpr void *allocate() { return NEW(7); } // expected-note {{allocated size 7 is not a multiple of size 5 of element type 'WrongSize'}} +}; +constexpr void *wrong_size = std::allocator().allocate(); // expected-error {{constant expression}} expected-note {{in call}} + +constexpr bool mismatched(int alloc_kind, int dealloc_kind) { + int *p; + switch (alloc_kind) { + case 0: + p = new int; // expected-note {{heap allocation}} + break; + case 1: + p = new int[1]; // expected-note {{heap allocation}} + break; + case 2: + p = std::allocator().allocate(1); + break; + } + switch (dealloc_kind) { + case 0: + delete p; // expected-note {{'delete' used to delete pointer to object allocated with 'std::allocator<...>::allocate'}} + break; + case 1: + delete[] p; // expected-note {{'delete' used to delete pointer to object allocated with 'std::allocator<...>::allocate'}} + break; + case 2: + std::allocator().deallocate(p); // expected-note 2{{in call}} + break; + } + return true; +} +static_assert(mismatched(0, 2)); // expected-error {{constant expression}} expected-note {{in call}} +static_assert(mismatched(1, 2)); // expected-error {{constant expression}} expected-note {{in call}} +static_assert(mismatched(2, 0)); // expected-error {{constant expression}} expected-note {{in call}} +static_assert(mismatched(2, 1)); // expected-error {{constant expression}} expected-note {{in call}} +static_assert(mismatched(2, 2)); + +constexpr int *escape = std::allocator().allocate(3); // expected-error {{constant expression}} expected-note {{pointer to subobject of heap-allocated}} +constexpr int leak = (std::allocator().allocate(3), 0); // expected-error {{constant expression}} +constexpr int no_lifetime_start = (*std::allocator().allocate(1) = 1); // expected-error {{constant expression}} expected-note {{assignment to object outside its lifetime}} + +void *operator new(std::size_t, void *p) { return p; } +constexpr bool no_placement_new_in_user_code() { // expected-error {{never produces a constant expression}} + int a; + new (&a) int(42); // expected-note {{call to placement 'operator new'}} + return a == 42; +} + +namespace std { + constexpr bool placement_new_in_stdlib() { + int a; + new (&a) int(42); + return a == 42; + } +} +static_assert(std::placement_new_in_stdlib()); + +namespace std { + template + constexpr void construct_at(void *p, Args &&...args) { + new (p) T((Args&&)args...); // #new + } +} + +constexpr bool call_std_construct_at() { + int *p = std::allocator().allocate(3); + std::construct_at(p, 1); + std::construct_at(p + 1, 2); + std::construct_at(p + 2, 3); + bool good = p[0] + p[1] + p[2] == 6; + std::allocator().deallocate(p); + return good; +} +static_assert(call_std_construct_at()); + +constexpr bool bad_construct_at_type() { + int a; + // expected-note@#new {{placement new would change type of storage from 'int' to 'float'}} + std::construct_at(&a, 1.0f); // expected-note {{in call}} + return true; +} +static_assert(bad_construct_at_type()); // expected-error{{}} expected-note {{in call}} + +constexpr bool bad_construct_at_subobject() { + struct X { int a, b; }; + union A { + int a; + X x; + }; + A a = {1}; + // expected-note@#new {{construction of subobject of member 'x' of union with active member 'a' is not allowed in a constant expression}} + std::construct_at(&a.x.a, 1); // expected-note {{in call}} + return true; +} +static_assert(bad_construct_at_subobject()); // expected-error{{}} expected-note {{in call}} + +constexpr bool change_union_member() { + union U { + int a; + int b; + }; + U u = {.a = 1}; + std::construct_at(&u.b, 2); + return u.b == 2; +} +static_assert(change_union_member()); + +int external; +// expected-note@#new {{visible outside}} +static_assert((std::construct_at(&external, 1), true)); // expected-error{{}} expected-note {{in call}} + +constexpr int &&temporary = 0; // expected-note {{created here}} +// expected-note@#new {{construction of temporary is not allowed in a constant expression outside the expression that created the temporary}} +static_assert((std::construct_at(&temporary, 1), true)); // expected-error{{}} expected-note {{in call}} + +constexpr bool construct_after_lifetime() { + int *p = new int; + delete p; + // expected-note@#new {{construction of heap allocated object that has been deleted}} + std::construct_at(p); // expected-note {{in call}} + return true; +} +static_assert(construct_after_lifetime()); // expected-error {{}} expected-note {{in call}} diff --git a/clang/test/SemaCXX/cxx2a-explicit-bool.cpp b/clang/test/SemaCXX/cxx2a-explicit-bool.cpp index 1c532cfb59766..56fa76f0a8bd5 100644 --- a/clang/test/SemaCXX/cxx2a-explicit-bool.cpp +++ b/clang/test/SemaCXX/cxx2a-explicit-bool.cpp @@ -717,3 +717,21 @@ A d2{0, 0}; A d3 = {0.0, 0.0};// expected-error {{explicit deduction guide}} } + +namespace PR42980 { +using size_t = decltype(sizeof(0)); + +struct Str {// expected-note+ {{candidate constructor}} + template + explicit(N > 7)// expected-note {{resolved to true}} + Str(char const (&str)[N]); +}; + +template +Str::Str(char const(&str)[N]) { } +// expected-note@-1 {{candidate constructor}} + +Str a = "short"; +Str b = "not so short";// expected-error {{no viable conversion}} + +} \ No newline at end of file diff --git a/clang/test/SemaCXX/gnu_inline.cpp b/clang/test/SemaCXX/gnu_inline.cpp new file mode 100644 index 0000000000000..43a4bae48e95c --- /dev/null +++ b/clang/test/SemaCXX/gnu_inline.cpp @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +extern inline +__attribute__((__gnu_inline__)) +void gnu_inline1() {} + +inline +__attribute__((__gnu_inline__)) // expected-warning {{'gnu_inline' attribute without 'extern' in C++ treated as externally available, this changed in Clang 10}} +void gnu_inline2() {} diff --git a/clang/test/SemaCXX/ms-exception-spec.cpp b/clang/test/SemaCXX/ms-exception-spec.cpp index 60bfeba037ad4..cf460356c9789 100644 --- a/clang/test/SemaCXX/ms-exception-spec.cpp +++ b/clang/test/SemaCXX/ms-exception-spec.cpp @@ -1,9 +1,36 @@ -// RUN: %clang_cc1 %s -fsyntax-only -verify -fms-compatibility -fexceptions -fcxx-exceptions +// RUN: %clang_cc1 -std=c++11 %s -fsyntax-only -verify -fms-compatibility -fexceptions -fcxx-exceptions +// RUN: %clang_cc1 -std=c++17 %s -fsyntax-only -verify -fms-compatibility -fexceptions -fcxx-exceptions +// FIXME: Should -fms-compatibility soften these errors into warnings to match +// MSVC? In practice, MSVC never implemented dynamic exception specifiers, so +// there isn't much Windows code in the wild that uses them. +#if __cplusplus >= 201703L +// expected-error@+3 {{ISO C++17 does not allow dynamic exception specifications}} +// expected-note@+2 {{use 'noexcept(false)' instead}} +#endif void f() throw(...) { } namespace PR28080 { struct S; // expected-note {{forward declaration}} +#if __cplusplus >= 201703L +// expected-error@+3 {{ISO C++17 does not allow dynamic exception specifications}} +// expected-note@+2 {{use 'noexcept(false)' instead}} +#endif void fn() throw(S); // expected-warning {{incomplete type}} expected-note{{previous declaration}} void fn() throw(); // expected-warning {{does not match previous declaration}} } + +template struct FooPtr { + template FooPtr(U *p) : m_pT(nullptr) {} + + template <> + // FIXME: It would be better if this note pointed at the primary template + // above. + // expected-note@+1 {{previous declaration is here}} + FooPtr(T *pInterface) throw() // expected-warning {{exception specification in declaration does not match previous declaration}} + : m_pT(pInterface) {} + + T *m_pT; +}; +struct Bar {}; +template struct FooPtr; // expected-note {{requested here}} diff --git a/clang/test/SemaCXX/self-comparison.cpp b/clang/test/SemaCXX/self-comparison.cpp index ac129b68a67a9..e20706a8b6cc9 100644 --- a/clang/test/SemaCXX/self-comparison.cpp +++ b/clang/test/SemaCXX/self-comparison.cpp @@ -40,3 +40,85 @@ bool g() { Y::n == Y::n; } template bool g(); // should not produce any warnings + +namespace member_tests { +struct B { + int field; + static int static_field; + int test(B b) { + return field == field; // expected-warning {{self-comparison always evaluates to true}} + return static_field == static_field; // expected-warning {{self-comparison always evaluates to true}} + return static_field == b.static_field; // expected-warning {{self-comparison always evaluates to true}} + return B::static_field == this->static_field; // expected-warning {{self-comparison always evaluates to true}} + return this == this; // expected-warning {{self-comparison always evaluates to true}} + + return field == b.field; + return this->field == b.field; + } +}; + +enum { + I0, + I1, + I2, +}; + +struct S { + int field; + static int static_field; + int array[4]; +}; + +struct T { + int field; + static int static_field; + int array[4]; + S s; +}; + +int struct_test(S s1, S s2, S *s3, T t) { + return s1.field == s1.field; // expected-warning {{self-comparison always evaluates to true}} + return s2.field == s2.field; // expected-warning {{self-comparison always evaluates to true}} + return s1.static_field == s2.static_field; // expected-warning {{self-comparison always evaluates to true}} + return S::static_field == s1.static_field; // expected-warning {{self-comparison always evaluates to true}} + return s1.array == s1.array; // expected-warning {{self-comparison always evaluates to true}} + return t.s.static_field == S::static_field; // expected-warning {{self-comparison always evaluates to true}} + return s3->field == s3->field; // expected-warning {{self-comparison always evaluates to true}} + return s3->static_field == S::static_field; // expected-warning {{self-comparison always evaluates to true}} + return s1.array[0] == s1.array[0]; // expected-warning {{self-comparison always evaluates to true}} + return s1.array[0] == s1.array[0ull]; // expected-warning {{self-comparison always evaluates to true}} + return s1.array[I1] == s1.array[I1]; // expected-warning {{self-comparison always evaluates to true}} + return s1.array[s2.array[0]] == s1.array[s2.array[0]]; // expected-warning {{self-comparison always evaluates to true}} + return s3->array[t.field] == s3->array[t.field]; // expected-warning {{self-comparison always evaluates to true}} + + // Try all operators + return t.field == t.field; // expected-warning {{self-comparison always evaluates to true}} + return t.field <= t.field; // expected-warning {{self-comparison always evaluates to true}} + return t.field >= t.field; // expected-warning {{self-comparison always evaluates to true}} + + return t.field != t.field; // expected-warning {{self-comparison always evaluates to false}} + return t.field < t.field; // expected-warning {{self-comparison always evaluates to false}} + return t.field > t.field; // expected-warning {{self-comparison always evaluates to false}} + + // no warning + return s1.field == s2.field; + return s2.array == s1.array; + return s2.array[0] == s1.array[0]; + return s1.array[I1] == s1.array[I2]; + + return s1.static_field == t.static_field; +}; + +struct U { + bool operator!=(const U&); +}; + +bool operator==(const U&, const U&); + +// May want to warn on this in the future. +int user_defined(U u) { + return u == u; + return u != u; +} + +} // namespace member_tests diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp index 9bc238e678954..99270f19e68b1 100644 --- a/clang/test/SemaCXX/type-traits.cpp +++ b/clang/test/SemaCXX/type-traits.cpp @@ -12,6 +12,7 @@ typedef NonPOD NonPODArMB[10][2]; // PODs enum Enum { EV }; +enum SignedEnum : signed int { }; struct POD { Enum e; int i; float f; NonPOD* p; }; struct Empty {}; struct IncompleteStruct; @@ -56,14 +57,14 @@ struct HasInheritedCons : HasDefaultCons { using HasDefaultCons::HasDefaultCons; struct HasNoInheritedCons : HasCons {}; struct HasCopyAssign { HasCopyAssign operator =(const HasCopyAssign&); }; struct HasMoveAssign { HasMoveAssign operator =(const HasMoveAssign&&); }; -struct HasNoThrowMoveAssign { +struct HasNoThrowMoveAssign { HasNoThrowMoveAssign& operator=( const HasNoThrowMoveAssign&&) throw(); }; -struct HasNoExceptNoThrowMoveAssign { +struct HasNoExceptNoThrowMoveAssign { HasNoExceptNoThrowMoveAssign& operator=( - const HasNoExceptNoThrowMoveAssign&&) noexcept; + const HasNoExceptNoThrowMoveAssign&&) noexcept; }; -struct HasThrowMoveAssign { +struct HasThrowMoveAssign { HasThrowMoveAssign& operator=(const HasThrowMoveAssign&&) #if __cplusplus <= 201402L throw(POD); @@ -73,7 +74,7 @@ struct HasThrowMoveAssign { }; -struct HasNoExceptFalseMoveAssign { +struct HasNoExceptFalseMoveAssign { HasNoExceptFalseMoveAssign& operator=( const HasNoExceptFalseMoveAssign&&) noexcept(false); }; struct HasMoveCtor { HasMoveCtor(const HasMoveCtor&&); }; @@ -82,17 +83,17 @@ struct HasMemberMoveAssign { HasMoveAssign member; }; struct HasStaticMemberMoveCtor { static HasMoveCtor member; }; struct HasStaticMemberMoveAssign { static HasMoveAssign member; }; struct HasMemberThrowMoveAssign { HasThrowMoveAssign member; }; -struct HasMemberNoExceptFalseMoveAssign { +struct HasMemberNoExceptFalseMoveAssign { HasNoExceptFalseMoveAssign member; }; struct HasMemberNoThrowMoveAssign { HasNoThrowMoveAssign member; }; -struct HasMemberNoExceptNoThrowMoveAssign { +struct HasMemberNoExceptNoThrowMoveAssign { HasNoExceptNoThrowMoveAssign member; }; -struct HasDefaultTrivialCopyAssign { +struct HasDefaultTrivialCopyAssign { HasDefaultTrivialCopyAssign &operator=( - const HasDefaultTrivialCopyAssign&) = default; + const HasDefaultTrivialCopyAssign&) = default; }; -struct TrivialMoveButNotCopy { +struct TrivialMoveButNotCopy { TrivialMoveButNotCopy &operator=(TrivialMoveButNotCopy&&) = default; TrivialMoveButNotCopy &operator=(const TrivialMoveButNotCopy&); }; @@ -361,7 +362,7 @@ void is_enum() struct FinalClass final { }; -template +template struct PotentiallyFinal { }; template @@ -801,6 +802,7 @@ void is_fundamental() int t23[T(__is_fundamental(unsigned long))]; int t24[T(__is_fundamental(void))]; int t25[T(__is_fundamental(cvoid))]; + int t26[T(__is_fundamental(decltype(nullptr)))]; int t30[F(__is_fundamental(Union))]; int t31[F(__is_fundamental(UnionAr))]; @@ -1419,12 +1421,12 @@ void is_signed() int t04[T(__is_signed(short))]; int t05[T(__is_signed(signed char))]; int t06[T(__is_signed(wchar_t))]; + int t07[T(__is_signed(float))]; + int t08[T(__is_signed(double))]; + int t09[T(__is_signed(long double))]; - int t10[F(__is_signed(bool))]; - int t11[F(__is_signed(cvoid))]; - int t12[F(__is_signed(float))]; - int t13[F(__is_signed(double))]; - int t14[F(__is_signed(long double))]; + int t13[F(__is_signed(bool))]; + int t14[F(__is_signed(cvoid))]; int t15[F(__is_signed(unsigned char))]; int t16[F(__is_signed(unsigned int))]; int t17[F(__is_signed(unsigned long long))]; @@ -1434,9 +1436,10 @@ void is_signed() int t21[F(__is_signed(ClassType))]; int t22[F(__is_signed(Derives))]; int t23[F(__is_signed(Enum))]; - int t24[F(__is_signed(IntArNB))]; - int t25[F(__is_signed(Union))]; - int t26[F(__is_signed(UnionAr))]; + int t24[F(__is_signed(SignedEnum))]; + int t25[F(__is_signed(IntArNB))]; + int t26[F(__is_signed(Union))]; + int t27[F(__is_signed(UnionAr))]; } void is_unsigned() @@ -2005,7 +2008,7 @@ class PrivateCopy { }; template -struct X0 { +struct X0 { template X0(const X0&); }; @@ -2766,7 +2769,7 @@ struct CanBeUniqueIfNoPadding : NotUniqueBecauseTailPadding { char b[7]; }; -static_assert(!has_unique_object_representations::value, +static_assert(!has_unique_object_representations::value, "non trivial"); // Can be unique on Itanium, since the is child class' data is 'folded' into the // parent's tail padding. diff --git a/clang/test/SemaCXX/undefined-inline.cpp b/clang/test/SemaCXX/undefined-inline.cpp index feb12f4552cab..21c6b5de79de3 100644 --- a/clang/test/SemaCXX/undefined-inline.cpp +++ b/clang/test/SemaCXX/undefined-inline.cpp @@ -40,20 +40,20 @@ namespace test7 { } namespace test8 { - inline void foo() __attribute__((gnu_inline)); + inline void foo() __attribute__((gnu_inline)); // expected-warning {{'gnu_inline' attribute without 'extern' in C++ treated as externally available, this changed in Clang 10}} void test() { foo(); } } namespace test9 { void foo(); void test() { foo(); } - inline void foo() __attribute__((gnu_inline)); + inline void foo() __attribute__((gnu_inline)); // expected-warning {{'gnu_inline' attribute without 'extern' in C++ treated as externally available, this changed in Clang 10}} } namespace test10 { inline void foo(); void test() { foo(); } - inline void foo() __attribute__((gnu_inline)); + inline void foo() __attribute__((gnu_inline)); // expected-warning {{'gnu_inline' attribute without 'extern' in C++ treated as externally available, this changed in Clang 10}} } namespace test11 { diff --git a/clang/test/SemaCXX/warn-consumed-analysis.cpp b/clang/test/SemaCXX/warn-consumed-analysis.cpp index 0a6aed6186265..b4dddb6763724 100644 --- a/clang/test/SemaCXX/warn-consumed-analysis.cpp +++ b/clang/test/SemaCXX/warn-consumed-analysis.cpp @@ -53,12 +53,18 @@ class CONSUMABLE(unconsumed) DestructorTester { public: DestructorTester(); DestructorTester(int); + DestructorTester(nullptr_t) RETURN_TYPESTATE(unconsumed); + DestructorTester(DestructorTester &&); void operator*() CALLABLE_WHEN("unconsumed"); ~DestructorTester() CALLABLE_WHEN("consumed"); + }; +void dtByVal(DestructorTester); +void dtByValMarkUnconsumed(DestructorTester RETURN_TYPESTATE(unconsumed)); + void baf0(const ConsumableClass var); void baf1(const ConsumableClass &var); void baf2(const ConsumableClass *var); @@ -120,6 +126,19 @@ void testDestruction() { expected-warning {{invalid invocation of method '~DestructorTester' on object 'D1' while it is in the 'unconsumed' state}} } +void testDestructionByVal() { + { + // both the var and the temporary are consumed: + DestructorTester D0(nullptr); + dtByVal((DestructorTester &&)D0); + } + { + // the var is consumed but the temporary isn't: + DestructorTester D1(nullptr); + dtByValMarkUnconsumed((DestructorTester &&)D1); // expected-warning {{invalid invocation of method '~DestructorTester' on a temporary object while it is in the 'unconsumed' state}} + } +} + void testTempValue() { *ConsumableClass(); // expected-warning {{invalid invocation of method 'operator*' on a temporary object while it is in the 'consumed' state}} } @@ -413,10 +432,15 @@ void testParamReturnTypestateCallee(bool cond, ConsumableClass &Param RETUR Param.consume(); } +void testRvalueRefParamReturnTypestateCallee(ConsumableClass &&Param RETURN_TYPESTATE(unconsumed)) { + Param.unconsume(); +} + void testParamReturnTypestateCaller() { ConsumableClass var; testParamReturnTypestateCallee(true, var); + testRvalueRefParamReturnTypestateCallee((ConsumableClass &&)var); *var; } @@ -480,6 +504,9 @@ void testCallingConventions() { baf2(&var); *var; + + baf3(var); + *var; baf4(var); *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'unknown' state}} diff --git a/clang/test/SemaCXX/warn-pessmizing-move.cpp b/clang/test/SemaCXX/warn-pessmizing-move.cpp index deedec41bd766..2c27cd7f95f74 100644 --- a/clang/test/SemaCXX/warn-pessmizing-move.cpp +++ b/clang/test/SemaCXX/warn-pessmizing-move.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -Wpessimizing-move -std=c++11 -verify %s +// RUN: %clang_cc1 -fsyntax-only -Wpessimizing-move -std=c++11 -verify %s -DUSER_DEFINED // RUN: %clang_cc1 -fsyntax-only -Wpessimizing-move -std=c++11 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s // definitions for std::move @@ -12,7 +13,15 @@ template typename remove_reference::type &&move(T &&t); } } -struct A {}; +struct A { +#ifdef USER_DEFINED + A() {} + A(const A &) {} + A(A &&) {} + A &operator=(const A &) { return *this; } + A &operator=(A &&) { return *this; } +#endif +}; struct B { B() {} B(A) {} @@ -47,6 +56,19 @@ B test2(A a1, B b1) { // expected-note@-2{{remove std::move call}} // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:20}:"" // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:22-[[@LINE-4]]:23}:"" + + return A(); + return test1(a2); + return std::move(A()); + // expected-warning@-1{{prevents copy elision}} + // expected-note@-2{{remove std::move call}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:20}:"" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:23-[[@LINE-4]]:24}:"" + return std::move(test1(a2)); + // expected-warning@-1{{prevents copy elision}} + // expected-note@-2{{remove std::move call}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:20}:"" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:29-[[@LINE-4]]:30}:"" } A global_a; @@ -101,11 +123,24 @@ void test6() { // expected-note@-2{{remove std::move call}} // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:20}:"" // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:23-[[@LINE-4]]:24}:"" + + a3 = std::move(A()); + // expected-warning@-1{{prevents copy elision}} + // expected-note@-2{{remove std::move call}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:8-[[@LINE-3]]:18}:"" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:21-[[@LINE-4]]:22}:"" + A a4 = std::move(test3()); // expected-warning@-1{{prevents copy elision}} // expected-note@-2{{remove std::move call}} // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:20}:"" // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:27-[[@LINE-4]]:28}:"" + + a4 = std::move(test3()); + // expected-warning@-1{{prevents copy elision}} + // expected-note@-2{{remove std::move call}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:8-[[@LINE-3]]:18}:"" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:25-[[@LINE-4]]:26}:"" } A test7() { @@ -122,13 +157,13 @@ A test7() { A a3 = (std::move(A())); // expected-warning@-1{{prevents copy elision}} // expected-note@-2{{remove std::move call}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:21}:"" - // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:24-[[@LINE-4]]:26}:"" + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:11-[[@LINE-3]]:21}:"" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:24-[[@LINE-4]]:25}:"" A a4 = (std::move((A()))); // expected-warning@-1{{prevents copy elision}} // expected-note@-2{{remove std::move call}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:21}:"" - // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:26-[[@LINE-4]]:28}:"" + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:11-[[@LINE-3]]:21}:"" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:26-[[@LINE-4]]:27}:"" return std::move(a1); // expected-warning@-1{{prevents copy elision}} @@ -143,13 +178,13 @@ A test7() { return (std::move(a1)); // expected-warning@-1{{prevents copy elision}} // expected-note@-2{{remove std::move call}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:21}:"" - // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:23-[[@LINE-4]]:25}:"" + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:11-[[@LINE-3]]:21}:"" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:23-[[@LINE-4]]:24}:"" return (std::move((a1))); // expected-warning@-1{{prevents copy elision}} // expected-note@-2{{remove std::move call}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:21}:"" - // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:25-[[@LINE-4]]:27}:"" + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:11-[[@LINE-3]]:21}:"" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:25-[[@LINE-4]]:26}:"" } #define wrap1(x) x @@ -227,30 +262,3 @@ namespace templates { test2(); } } - -A init_list() { - A a1; - return { std::move(a1) }; - // expected-warning@-1{{prevents copy elision}} - // expected-note@-2{{remove std::move call}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:22}:"" - // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:24-[[@LINE-4]]:27}:"" - - return { (std::move(a1)) }; - // expected-warning@-1{{prevents copy elision}} - // expected-note@-2{{remove std::move call}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:23}:"" - // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:25-[[@LINE-4]]:29}:"" - - A a2 = { std::move(A()) }; - // expected-warning@-1{{prevents copy elision}} - // expected-note@-2{{remove std::move call}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:22}:"" - // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:25-[[@LINE-4]]:28}:"" - - A a3 = { (std::move(A())) }; - // expected-warning@-1{{prevents copy elision}} - // expected-note@-2{{remove std::move call}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:23}:"" - // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:26-[[@LINE-4]]:30}:"" -} diff --git a/clang/test/SemaCXX/warn-redundant-move.cpp b/clang/test/SemaCXX/warn-redundant-move.cpp index e04a525641738..2bfc8c9312f02 100644 --- a/clang/test/SemaCXX/warn-redundant-move.cpp +++ b/clang/test/SemaCXX/warn-redundant-move.cpp @@ -114,17 +114,3 @@ namespace templates { test2(A()); } } - -A init_list(A a) { - return { std::move(a) }; - // expected-warning@-1{{redundant move in return statement}} - // expected-note@-2{{remove std::move call here}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:22}:"" - // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:23-[[@LINE-4]]:26}:"" - - return { (std::move(a)) }; - // expected-warning@-1{{redundant move in return statement}} - // expected-note@-2{{remove std::move call here}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:23}:"" - // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:24-[[@LINE-4]]:28}:"" -} diff --git a/clang/test/SemaCXX/warn-unreachable.cpp b/clang/test/SemaCXX/warn-unreachable.cpp index a14e4226861c1..c664c1912899d 100644 --- a/clang/test/SemaCXX/warn-unreachable.cpp +++ b/clang/test/SemaCXX/warn-unreachable.cpp @@ -393,6 +393,9 @@ void tautological_compare(bool x, int y) { else calledFun(); // expected-warning {{will never be executed}} + if (y == -1 && y != -1) // expected-note {{silence}} + calledFun(); // expected-warning {{will never be executed}} + // TODO: Extend warning to the following code: if (x < -1) calledFun(); @@ -408,6 +411,4 @@ void tautological_compare(bool x, int y) { else calledFun(); - if (y == -1 && y != -1) - calledFun(); } diff --git a/clang/test/SemaObjC/signed-char-bool-conversion.m b/clang/test/SemaObjC/signed-char-bool-conversion.m new file mode 100644 index 0000000000000..476ecc6a0603c --- /dev/null +++ b/clang/test/SemaObjC/signed-char-bool-conversion.m @@ -0,0 +1,49 @@ +// RUN: %clang_cc1 %s -verify -Wobjc-signed-char-bool +// RUN: %clang_cc1 -xobjective-c++ %s -verify -Wobjc-signed-char-bool + +typedef signed char BOOL; +#define YES __objc_yes +#define NO __objc_no + +typedef unsigned char Boolean; + +BOOL b; +Boolean boolean; +float fl; +int i; +int *ptr; + +void t1() { + b = boolean; + b = fl; // expected-warning {{implicit conversion from floating-point type 'float' to 'BOOL'}} + b = i; // expected-warning {{implicit conversion from integral type 'int' to 'BOOL'}} + + b = 1.0; + b = 0.0; + b = 1.1; // expected-warning {{implicit conversion from 'double' to 'BOOL' (aka 'signed char') changes value from 1.1 to 1}} + b = 2.1; // expected-warning {{implicit conversion from constant value 2.1 to 'BOOL'; the only well defined values for 'BOOL' are YES and NO}} + + b = YES; +#ifndef __cplusplus + b = ptr; // expected-warning {{incompatible pointer to integer conversion assigning to 'BOOL' (aka 'signed char') from 'int *'}} +#endif +} + +@interface BoolProp +@property BOOL p; +@end + +void t2(BoolProp *bp) { + bp.p = YES; + bp.p = NO; + bp.p = boolean; + bp.p = fl; // expected-warning {{implicit conversion from floating-point type 'float' to 'BOOL'}} + bp.p = i; // expected-warning {{implicit conversion from integral type 'int' to 'BOOL'}} + bp.p = b; + bp.p = bp.p; +#ifndef __cplusplus + bp.p = ptr; // expected-warning {{incompatible pointer to integer conversion assigning to 'BOOL' (aka 'signed char') from 'int *'}} +#endif + bp.p = 1; + bp.p = 2; // expected-warning {{implicit conversion from constant value 2 to 'BOOL'; the only well defined values for 'BOOL' are YES and NO}} +} diff --git a/clang/test/SemaOpenCL/fdeclare-opencl-builtins.cl b/clang/test/SemaOpenCL/fdeclare-opencl-builtins.cl index 590f27353dd4c..250a3f008c942 100644 --- a/clang/test/SemaOpenCL/fdeclare-opencl-builtins.cl +++ b/clang/test/SemaOpenCL/fdeclare-opencl-builtins.cl @@ -1,10 +1,20 @@ -// RUN: %clang_cc1 %s -triple spir -verify -pedantic -fsyntax-only -cl-std=CL2.0 -fdeclare-opencl-builtins -DNO_HEADER -// RUN: %clang_cc1 %s -triple spir -verify -pedantic -fsyntax-only -cl-std=CL2.0 -fdeclare-opencl-builtins -finclude-default-header +// RUN: %clang_cc1 %s -triple spir -verify -pedantic -Wconversion -Werror -fsyntax-only -cl-std=CL -fdeclare-opencl-builtins -DNO_HEADER +// RUN: %clang_cc1 %s -triple spir -verify -pedantic -Wconversion -Werror -fsyntax-only -cl-std=CL -fdeclare-opencl-builtins -finclude-default-header +// RUN: %clang_cc1 %s -triple spir -verify -pedantic -Wconversion -Werror -fsyntax-only -cl-std=CL1.2 -fdeclare-opencl-builtins -DNO_HEADER +// RUN: %clang_cc1 %s -triple spir -verify -pedantic -Wconversion -Werror -fsyntax-only -cl-std=CL1.2 -fdeclare-opencl-builtins -finclude-default-header +// RUN: %clang_cc1 %s -triple spir -verify -pedantic -Wconversion -Werror -fsyntax-only -cl-std=CL2.0 -fdeclare-opencl-builtins -DNO_HEADER +// RUN: %clang_cc1 %s -triple spir -verify -pedantic -Wconversion -Werror -fsyntax-only -cl-std=CL2.0 -fdeclare-opencl-builtins -finclude-default-header + +#if __OPENCL_C_VERSION__ >= CL_VERSION_2_0 // expected-no-diagnostics +#endif // Test the -fdeclare-opencl-builtins option. #pragma OPENCL EXTENSION cl_khr_fp16 : enable +#if __OPENCL_C_VERSION__ < CL_VERSION_1_2 +#pragma OPENCL EXTENSION cl_khr_fp64 : enable +#endif // Provide typedefs when invoking clang without -finclude-default-header. #ifdef NO_HEADER @@ -15,7 +25,10 @@ typedef half half4 __attribute__((ext_vector_type(4))); typedef int int2 __attribute__((ext_vector_type(2))); typedef int int4 __attribute__((ext_vector_type(4))); typedef long long2 __attribute__((ext_vector_type(2))); +typedef unsigned char uchar; typedef unsigned int uint; +typedef unsigned long ulong; +typedef unsigned short ushort; typedef __SIZE_TYPE__ size_t; #endif @@ -58,14 +71,20 @@ kernel void basic_image_readonly(read_only image2d_t image_read_only_image2d) { resf = read_imagef(image_read_only_image2d, i2); res = read_imageh(image_read_only_image2d, i2); res = read_imageh(image_read_only_image2d, sampler, i2); + + int imgWidth = get_image_width(image_read_only_image2d); } +#if __OPENCL_C_VERSION__ >= CL_VERSION_2_0 kernel void basic_image_readwrite(read_write image3d_t image_read_write_image3d) { half4 h4; int4 i4; write_imageh(image_read_write_image3d, i4, h4); + + int imgDepth = get_image_depth(image_read_write_image3d); } +#endif // __OPENCL_C_VERSION__ >= CL_VERSION_2_0 kernel void basic_image_writeonly(write_only image1d_buffer_t image_write_only_image1d_buffer) { half4 h4; @@ -78,4 +97,40 @@ kernel void basic_image_writeonly(write_only image1d_buffer_t image_write_only_i kernel void basic_subgroup(global uint *out) { out[0] = get_sub_group_size(); +#if __OPENCL_C_VERSION__ < CL_VERSION_2_0 +// expected-error@-2{{implicit declaration of function 'get_sub_group_size' is invalid in OpenCL}} +// expected-error@-3{{implicit conversion changes signedness: 'int' to 'uint' (aka 'unsigned int')}} +#endif +} + +kernel void basic_vector_data() { +#if __OPENCL_C_VERSION__ >= CL_VERSION_2_0 + generic void *generic_p; +#endif + constant void *constant_p; + local void *local_p; + global void *global_p; + private void *private_p; + size_t s; + + vload4(s, (const __constant ulong *) constant_p); + vload16(s, (const __constant short *) constant_p); + +#if __OPENCL_C_VERSION__ >= CL_VERSION_2_0 + vload3(s, (const __generic ushort *) generic_p); + vload16(s, (const __generic uchar *) generic_p); +#endif + + vload8(s, (const __global long *) global_p); + vload2(s, (const __local uint *) local_p); + vload16(s, (const __private float *) private_p); +} + +kernel void basic_work_item() { + uint ui; + + get_enqueued_local_size(ui); +#if __OPENCL_C_VERSION__ < CL_VERSION_2_0 +// expected-error@-2{{implicit declaration of function 'get_enqueued_local_size' is invalid in OpenCL}} +#endif } diff --git a/clang/test/SemaTemplate/default-arguments-cxx0x.cpp b/clang/test/SemaTemplate/default-arguments-cxx0x.cpp index c24ed12a0248c..2114cc94e6c60 100644 --- a/clang/test/SemaTemplate/default-arguments-cxx0x.cpp +++ b/clang/test/SemaTemplate/default-arguments-cxx0x.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++14 -verify %s // expected-no-diagnostics // Test default template arguments for function templates. @@ -114,3 +115,17 @@ namespace rdar34167492 { S _a{}; }; } + +#if __cplusplus >= 201402L +namespace lambda { + // Verify that a default argument in a lambda can refer to the type of a + // previous `auto` argument without crashing. + template + void bar() { + (void) [](auto c, int x = sizeof(decltype(c))) {}; + } + void foo() { + bar(); + } +} // namespace lambda +#endif diff --git a/clang/test/SemaTemplate/using-decl.cpp b/clang/test/SemaTemplate/using-decl.cpp new file mode 100644 index 0000000000000..1ef2a2dfaa019 --- /dev/null +++ b/clang/test/SemaTemplate/using-decl.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s +// expected-no-diagnostics + +namespace UsingInGenericLambda { + namespace a { + enum { b }; + } + template void c() { + auto d = [](auto) { + using a::b; + (void)b; + }; + d(0); + } + void e() { c(); } +} diff --git a/clang/test/clang-rename/Typedef.cpp b/clang/test/clang-rename/Typedef.cpp new file mode 100644 index 0000000000000..64d337fae22c7 --- /dev/null +++ b/clang/test/clang-rename/Typedef.cpp @@ -0,0 +1,8 @@ +namespace std { +class basic_string {}; +typedef basic_string string; +} // namespace std + +std::string foo(); // // CHECK: std::new_string foo(); + +// RUN: clang-rename -offset=93 -new-name=new_string %s -- | sed 's,//.*,,' | FileCheck %s diff --git a/clang/tools/clang-format/clang-format-diff.py b/clang/tools/clang-format/clang-format-diff.py index a4f22ca029366..122db49ff73c7 100755 --- a/clang/tools/clang-format/clang-format-diff.py +++ b/clang/tools/clang-format/clang-format-diff.py @@ -43,8 +43,8 @@ def main(): help='custom pattern selecting file paths to reformat ' '(case sensitive, overrides -iregex)') parser.add_argument('-iregex', metavar='PATTERN', default= - r'.*\.(cpp|cc|c\+\+|cxx|c|cl|h|hpp|m|mm|inc|js|ts|proto' - r'|protodevel|java)', + r'.*\.(cpp|cc|c\+\+|cxx|c|cl|h|hh|hpp|m|mm|inc|js|ts|proto' + r'|protodevel|java|cs)', help='custom pattern selecting file paths to reformat ' '(case insensitive, overridden by -regex)') parser.add_argument('-sort-includes', action='store_true', default=False, diff --git a/clang/tools/clang-format/git-clang-format b/clang/tools/clang-format/git-clang-format index ef7f22d0f873e..bf05d9143fa18 100755 --- a/clang/tools/clang-format/git-clang-format +++ b/clang/tools/clang-format/git-clang-format @@ -77,13 +77,14 @@ def main(): 'c', 'h', # C 'm', # ObjC 'mm', # ObjC++ - 'cc', 'cp', 'cpp', 'c++', 'cxx', 'hpp', 'hxx', # C++ + 'cc', 'cp', 'cpp', 'c++', 'cxx', 'hh', 'hpp', 'hxx', # C++ 'cu', # CUDA # Other languages that clang-format supports 'proto', 'protodevel', # Protocol Buffers 'java', # Java 'js', # JavaScript 'ts', # TypeScript + 'cs', # C Sharp ]) p = argparse.ArgumentParser( diff --git a/clang/tools/clang-import-test/clang-import-test.cpp b/clang/tools/clang-import-test/clang-import-test.cpp index e8124d624267c..b42c93e8d3819 100644 --- a/clang/tools/clang-import-test/clang-import-test.cpp +++ b/clang/tools/clang-import-test/clang-import-test.cpp @@ -263,8 +263,8 @@ void AddExternalSource(CIAndOrigins &CI, {CI.getASTContext(), CI.getFileManager()}); llvm::SmallVector Sources; for (CIAndOrigins &Import : Imports) - Sources.push_back({Import.getASTContext(), Import.getFileManager(), - Import.getOriginMap()}); + Sources.emplace_back(Import.getASTContext(), Import.getFileManager(), + Import.getOriginMap()); auto ES = std::make_unique(Target, Sources); CI.getASTContext().setExternalSource(ES.release()); CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage(); diff --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp index 4bd61fb9c893d..d44e3b9ff3558 100644 --- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp +++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp @@ -177,6 +177,11 @@ llvm::cl::opt SkipExcludedPPRanges( "until reaching the end directive."), llvm::cl::init(true), llvm::cl::cat(DependencyScannerCategory)); +llvm::cl::opt Verbose("v", llvm::cl::Optional, + llvm::cl::desc("Use verbose output."), + llvm::cl::init(false), + llvm::cl::cat(DependencyScannerCategory)); + } // end anonymous namespace /// \returns object-file path derived from source-file path. @@ -242,6 +247,7 @@ int main(int argc, const char **argv) { AdjustedArgs.push_back("-o"); AdjustedArgs.push_back("/dev/null"); if (!HasMT && !HasMQ) { + AdjustedArgs.push_back("-M"); AdjustedArgs.push_back("-MT"); // We're interested in source dependencies of an object file. if (!HasMD) { @@ -260,6 +266,8 @@ int main(int argc, const char **argv) { AdjustedArgs.push_back("-Wno-error"); return AdjustedArgs; }); + AdjustingCompilations->appendArgumentsAdjuster( + tooling::getClangStripSerializeDiagnosticAdjuster()); SharedStream Errs(llvm::errs()); // Print out the dependency results to STDOUT by default. @@ -283,8 +291,10 @@ int main(int argc, const char **argv) { std::mutex Lock; size_t Index = 0; - llvm::outs() << "Running clang-scan-deps on " << Inputs.size() - << " files using " << NumWorkers << " workers\n"; + if (Verbose) { + llvm::outs() << "Running clang-scan-deps on " << Inputs.size() + << " files using " << NumWorkers << " workers\n"; + } for (unsigned I = 0; I < NumWorkers; ++I) { auto Worker = [I, &Lock, &Index, &Inputs, &HadErrors, &WorkerTools]() { while (true) { diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp index 29767b880b62d..8ab713fd2954a 100644 --- a/clang/tools/driver/cc1_main.cpp +++ b/clang/tools/driver/cc1_main.cpp @@ -253,6 +253,7 @@ int cc1_main(ArrayRef Argv, const char *Argv0, void *MainAddr) { // If any timers were active but haven't been destroyed yet, print their // results now. This happens in -disable-free mode. llvm::TimerGroup::printAll(llvm::errs()); + llvm::TimerGroup::clearAll(); if (llvm::timeTraceProfilerEnabled()) { SmallString<128> Path(Clang->getFrontendOpts().OutputFile); @@ -270,9 +271,6 @@ int cc1_main(ArrayRef Argv, const char *Argv0, void *MainAddr) { llvm::timeTraceProfilerCleanup(); llvm::errs() << "Time trace json-file dumped to " << Path.str() << "\n"; - llvm::errs() - << "Use chrome://tracing or Speedscope App " - "(https://www.speedscope.app) for flamegraph visualization\n"; } // Our error handler depends on the Diagnostics object, which we're diff --git a/clang/tools/driver/cc1as_main.cpp b/clang/tools/driver/cc1as_main.cpp index fbeea0dd3b02a..ae58a95f36f5b 100644 --- a/clang/tools/driver/cc1as_main.cpp +++ b/clang/tools/driver/cc1as_main.cpp @@ -609,6 +609,7 @@ int cc1as_main(ArrayRef Argv, const char *Argv0, void *MainAddr) { // If any timers were active but haven't been destroyed yet, print their // results now. TimerGroup::printAll(errs()); + TimerGroup::clearAll(); return !!Failed; } diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp index eebc8920fe0ab..f1600490017d7 100644 --- a/clang/tools/driver/driver.cpp +++ b/clang/tools/driver/driver.cpp @@ -499,6 +499,7 @@ int main(int argc_, const char **argv_) { // If any timers were active but haven't been destroyed yet, print their // results now. This happens in -disable-free mode. llvm::TimerGroup::printAll(llvm::errs()); + llvm::TimerGroup::clearAll(); #ifdef _WIN32 // Exit status should not be negative on Win32, unless abnormal termination. diff --git a/clang/tools/libclang/FatalErrorHandler.cpp b/clang/tools/libclang/FatalErrorHandler.cpp index e9a0d41bab3f5..eab17f3dfac73 100644 --- a/clang/tools/libclang/FatalErrorHandler.cpp +++ b/clang/tools/libclang/FatalErrorHandler.cpp @@ -18,11 +18,13 @@ static void aborting_fatal_error_handler(void *, const std::string &reason, ::abort(); } -void clang_install_aborting_llvm_fatal_error_handler() { +extern "C" { +void clang_install_aborting_llvm_fatal_error_handler(void) { llvm::remove_fatal_error_handler(); llvm::install_fatal_error_handler(aborting_fatal_error_handler, nullptr); } -void clang_uninstall_llvm_fatal_error_handler() { +void clang_uninstall_llvm_fatal_error_handler(void) { llvm::remove_fatal_error_handler(); } +} \ No newline at end of file diff --git a/clang/unittests/AST/ASTImporterODRStrategiesTest.cpp b/clang/unittests/AST/ASTImporterODRStrategiesTest.cpp new file mode 100644 index 0000000000000..6a2aa2bfc328b --- /dev/null +++ b/clang/unittests/AST/ASTImporterODRStrategiesTest.cpp @@ -0,0 +1,676 @@ +//===- unittest/AST/ASTImporterODRStrategiesTest.cpp -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Type-parameterized tests to verify the import behaviour in case of ODR +// violation. +// +//===----------------------------------------------------------------------===// + +#include "ASTImporterFixtures.h" + +namespace clang { +namespace ast_matchers { + +using internal::BindableMatcher; + +// DeclTy: Type of the Decl to check. +// Prototype: "Prototype" (forward declaration) of the Decl. +// Definition: A definition for the Prototype. +// ConflictingPrototype: A prototype with the same name but different +// declaration. +// ConflictingDefinition: A different definition for Prototype. +// ConflictingProtoDef: A definition for ConflictingPrototype. +// getPattern: Return a matcher that matches any of Prototype, Definition, +// ConflictingPrototype, ConflictingDefinition, ConflictingProtoDef. + +struct Function { + using DeclTy = FunctionDecl; + static constexpr auto *Prototype = "void X(int);"; + static constexpr auto *ConflictingPrototype = "void X(double);"; + static constexpr auto *Definition = "void X(int a) {}"; + static constexpr auto *ConflictingDefinition = "void X(double a) {}"; + BindableMatcher getPattern() { + return functionDecl(hasName("X"), unless(isImplicit())); + } + Language getLang() { return Lang_C; } +}; + +struct Typedef { + using DeclTy = TypedefNameDecl; + static constexpr auto *Definition = "typedef int X;"; + static constexpr auto *ConflictingDefinition = "typedef double X;"; + BindableMatcher getPattern() { return typedefNameDecl(hasName("X")); } + Language getLang() { return Lang_CXX; } +}; + +struct TypedefAlias { + using DeclTy = TypedefNameDecl; + static constexpr auto *Definition = "using X = int;"; + static constexpr auto *ConflictingDefinition = "using X = double;"; + BindableMatcher getPattern() { return typedefNameDecl(hasName("X")); } + Language getLang() { return Lang_CXX11; } +}; + +struct Enum { + using DeclTy = EnumDecl; + static constexpr auto *Definition = "enum X { a, b };"; + static constexpr auto *ConflictingDefinition = "enum X { a, b, c };"; + BindableMatcher getPattern() { return enumDecl(hasName("X")); } + Language getLang() { return Lang_CXX; } +}; + +struct EnumConstant { + using DeclTy = EnumConstantDecl; + static constexpr auto *Definition = "enum E { X = 0 };"; + static constexpr auto *ConflictingDefinition = "enum E { X = 1 };"; + BindableMatcher getPattern() { return enumConstantDecl(hasName("X")); } + Language getLang() { return Lang_CXX; } +}; + +struct Class { + using DeclTy = CXXRecordDecl; + static constexpr auto *Prototype = "class X;"; + static constexpr auto *Definition = "class X {};"; + static constexpr auto *ConflictingDefinition = "class X { int A; };"; + BindableMatcher getPattern() { + return cxxRecordDecl(hasName("X"), unless(isImplicit())); + } + Language getLang() { return Lang_CXX; } +}; + +struct Variable { + using DeclTy = VarDecl; + static constexpr auto *Prototype = "extern int X;"; + static constexpr auto *ConflictingPrototype = "extern float X;"; + static constexpr auto *Definition = "int X;"; + static constexpr auto *ConflictingDefinition = "float X;"; + BindableMatcher getPattern() { return varDecl(hasName("X")); } + Language getLang() { return Lang_CXX; } +}; + +struct ClassTemplate { + using DeclTy = ClassTemplateDecl; + static constexpr auto *Prototype = "template class X;"; + static constexpr auto *ConflictingPrototype = "template class X;"; + static constexpr auto *Definition = "template class X {};"; + static constexpr auto *ConflictingDefinition = + "template class X { int A; };"; + static constexpr auto *ConflictingProtoDef = "template class X { };"; + BindableMatcher getPattern() { + return classTemplateDecl(hasName("X"), unless(isImplicit())); + } + Language getLang() { return Lang_CXX; } +}; + +struct FunctionTemplate { + using DeclTy = FunctionTemplateDecl; + static constexpr auto *Definition0 = + R"( + template + void X(T a) {}; + )"; + // This is actually not a conflicting definition, but another primary template. + static constexpr auto *Definition1 = + R"( + template + void X(T* a) {}; + )"; + BindableMatcher getPattern() { + return functionTemplateDecl(hasName("X"), unless(isImplicit())); + } + static std::string getDef0() { return Definition0; } + static std::string getDef1() { return Definition1; } + Language getLang() { return Lang_CXX; } +}; + +static const internal::VariadicDynCastAllOfMatcher + varTemplateDecl; + +struct VarTemplate { + using DeclTy = VarTemplateDecl; + static constexpr auto *Definition = + R"( + template + constexpr T X = 0; + )"; + static constexpr auto *ConflictingDefinition = + R"( + template + constexpr int X = 0; + )"; + BindableMatcher getPattern() { return varTemplateDecl(hasName("X")); } + Language getLang() { return Lang_CXX14; } +}; + +struct ClassTemplateSpec { + using DeclTy = ClassTemplateSpecializationDecl; + static constexpr auto *Prototype = + R"( + template class X; + template <> class X; + )"; + static constexpr auto *Definition = + R"( + template class X; + template <> class X {}; + )"; + static constexpr auto *ConflictingDefinition = + R"( + template class X; + template <> class X { int A; }; + )"; + BindableMatcher getPattern() { + return classTemplateSpecializationDecl(hasName("X"), unless(isImplicit())); + } + Language getLang() { return Lang_CXX; } +}; + +// Function template specializations are all "full" specializations. +// Structural equivalency does not check the body of functions, so we cannot +// create conflicting function template specializations. +struct FunctionTemplateSpec { + using DeclTy = FunctionDecl; + + static constexpr auto *Definition0 = + R"( + template + void X(T a); + template <> void X(int a) {}; + )"; + + // This is actually not a conflicting definition, but another full + // specialization. + // Thus, during the import we would create a new specialization with a + // different type argument. + static constexpr auto *Definition1 = + R"( + template + void X(T a); + template <> void X(double a) {}; + )"; + + BindableMatcher getPattern() { + return functionDecl(hasName("X"), isExplicitTemplateSpecialization(), + unless(isImplicit())); + } + static std::string getDef0() { return Definition0; } + static std::string getDef1() { return Definition1; } + Language getLang() { return Lang_CXX; } +}; + +static const internal::VariadicDynCastAllOfMatcher< + Decl, VarTemplateSpecializationDecl> + varTemplateSpecializationDecl; + +struct VarTemplateSpec { + using DeclTy = VarTemplateSpecializationDecl; + static constexpr auto *Definition = + R"( + template T X = 0; + template <> int X = 0; + )"; + static constexpr auto *ConflictingDefinition = + R"( + template T X = 0; + template <> float X = 1.0; + )"; + BindableMatcher getPattern() { + return varTemplateSpecializationDecl(hasName("X"), unless(isImplicit())); + } + Language getLang() { return Lang_CXX14; } +}; + +template +struct ODRViolation : ASTImporterOptionSpecificTestBase { + + using DeclTy = typename TypeParam::DeclTy; + + ODRViolation() { ODRHandling = ODRHandlingParam; } + + static std::string getPrototype() { return TypeParam::Prototype; } + static std::string getConflictingPrototype() { + return TypeParam::ConflictingPrototype; + } + static std::string getDefinition() { return TypeParam::Definition; } + static std::string getConflictingDefinition() { + return TypeParam::ConflictingDefinition; + } + static std::string getConflictingProtoDef() { + return TypeParam::ConflictingProtoDef; + } + static BindableMatcher getPattern() { return TypeParam().getPattern(); } + static Language getLang() { return TypeParam().getLang(); } + + template &, Decl *, Decl *)> + void TypedTest_ImportAfter() { + Decl *ToTU = getToTuDecl(ToTUContent(), getLang()); + auto *ToD = FirstDeclMatcher().match(ToTU, getPattern()); + + Decl *FromTU = getTuDecl(FromTUContent(), getLang()); + auto *FromD = FirstDeclMatcher().match(FromTU, getPattern()); + + auto Result = importOrError(FromD, getLang()); + + ResultChecker(Result, ToTU, ToD); + } + + // Check that a Decl has been successfully imported into a standalone redecl + // chain. + static void CheckImportedAsNew(llvm::Expected &Result, Decl *ToTU, + Decl *ToD) { + ASSERT_TRUE(isSuccess(Result)); + Decl *ImportedD = *Result; + ASSERT_TRUE(ImportedD); + EXPECT_NE(ImportedD, ToD); + EXPECT_EQ(DeclCounter().match(ToTU, getPattern()), 2u); + + // There may be a hidden fwd spec decl before a function spec decl. + if (auto *ImportedF = dyn_cast(ImportedD)) + if (ImportedF->getTemplatedKind() == + FunctionDecl::TK_FunctionTemplateSpecialization) + return; + + EXPECT_FALSE(ImportedD->getPreviousDecl()); + } + + // Check that a Decl was not imported because of NameConflict. + static void CheckImportNameConflict(llvm::Expected &Result, + Decl *ToTU, Decl *ToD) { + EXPECT_TRUE(isImportError(Result, ImportError::NameConflict)); + EXPECT_EQ(DeclCounter().match(ToTU, getPattern()), 1u); + } + + // Check that a Decl was not imported because lookup found the same decl. + static void CheckImportFoundExisting(llvm::Expected &Result, + Decl *ToTU, Decl *ToD) { + ASSERT_TRUE(isSuccess(Result)); + EXPECT_EQ(DeclCounter().match(ToTU, getPattern()), 1u); + } + + void TypedTest_ImportConflictingDefAfterDef() { + TypedTest_ImportAfter(); + } + void TypedTest_ImportConflictingProtoAfterProto() { + TypedTest_ImportAfter(); + } + void TypedTest_ImportConflictingProtoAfterDef() { + TypedTest_ImportAfter(); + } + void TypedTest_ImportConflictingDefAfterProto() { + TypedTest_ImportAfter(); + } + void TypedTest_ImportConflictingProtoDefAfterProto() { + TypedTest_ImportAfter(); + } + void TypedTest_ImportConflictingProtoAfterProtoDef() { + TypedTest_ImportAfter(); + } + void TypedTest_ImportConflictingProtoDefAfterDef() { + TypedTest_ImportAfter(); + } + void TypedTest_ImportConflictingDefAfterProtoDef() { + TypedTest_ImportAfter(); + } + + void TypedTest_DontImportConflictingProtoAfterProto() { + TypedTest_ImportAfter(); + } + void TypedTest_DontImportConflictingDefAfterDef() { + TypedTest_ImportAfter(); + } + void TypedTest_DontImportConflictingProtoAfterDef() { + TypedTest_ImportAfter(); + } + void TypedTest_DontImportConflictingDefAfterProto() { + TypedTest_ImportAfter(); + } + void TypedTest_DontImportConflictingProtoDefAfterProto() { + TypedTest_ImportAfter(); + } + void TypedTest_DontImportConflictingProtoAfterProtoDef() { + TypedTest_ImportAfter(); + } + void TypedTest_DontImportConflictingProtoDefAfterDef() { + TypedTest_ImportAfter(); + } + void TypedTest_DontImportConflictingDefAfterProtoDef() { + TypedTest_ImportAfter(); + } + + // Used for function templates and function template specializations. + void TypedTest_ImportDifferentDefAfterDef() { + TypedTest_ImportAfter(); + } + void TypedTest_DontImportSameDefAfterDef() { + TypedTest_ImportAfter(); + } +}; + +// ============================== +// Define the parametrized tests. +// ============================== + +#define ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( \ + TypeParam, ODRHandlingParam, NamePrefix, TestCase) \ + using TypeParam##ODRHandlingParam = \ + ODRViolation; \ + TEST_P(TypeParam##ODRHandlingParam, NamePrefix##TestCase) { \ + TypedTest_##TestCase(); \ + } + +// clang-format off + +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + Function, Liberal, , + ImportConflictingDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + Typedef, Liberal, , + ImportConflictingDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + TypedefAlias, Liberal, , + ImportConflictingDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + Enum, Liberal, , + ImportConflictingDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + EnumConstant, Liberal, , + ImportConflictingDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + Class, Liberal, , + ImportConflictingDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + Variable, Liberal, , + ImportConflictingDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + ClassTemplate, Liberal, , + ImportConflictingDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + VarTemplate, Liberal, , + ImportConflictingDefAfterDef) +// Class and variable template specializations/instantiatons are always +// imported conservatively, because the AST holds the specializations in a set, +// and the key within the set is a hash calculated from the arguments of the +// specialization. +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + ClassTemplateSpec, Liberal, , + DontImportConflictingDefAfterDef) // Don't import !!! +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + VarTemplateSpec, Liberal, , + DontImportConflictingDefAfterDef) // Don't import !!! + +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + Function, Conservative, , + DontImportConflictingDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + Typedef, Conservative, , + DontImportConflictingDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + TypedefAlias, Conservative, , + DontImportConflictingDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + Enum, Conservative, , + DontImportConflictingDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + EnumConstant, Conservative, , + DontImportConflictingDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + Class, Conservative, , + DontImportConflictingDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + Variable, Conservative, , + DontImportConflictingDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + ClassTemplate, Conservative, , + DontImportConflictingDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + VarTemplate, Conservative, , + DontImportConflictingDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + ClassTemplateSpec, Conservative, , + DontImportConflictingDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + VarTemplateSpec, Conservative, , + DontImportConflictingDefAfterDef) + +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + Function, Liberal, , + ImportConflictingProtoAfterProto) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + Variable, Liberal, , + ImportConflictingProtoAfterProto) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + ClassTemplate, Liberal, , + ImportConflictingProtoAfterProto) + +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + Function, Conservative, , + DontImportConflictingProtoAfterProto) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + Variable, Conservative, , + DontImportConflictingProtoAfterProto) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + ClassTemplate, Conservative, , + DontImportConflictingProtoAfterProto) + +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + Variable, Liberal, , + ImportConflictingProtoAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + ClassTemplate, Liberal, , + ImportConflictingProtoAfterDef) + +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + Variable, Conservative, , + DontImportConflictingProtoAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + ClassTemplate, Conservative, , + DontImportConflictingProtoAfterDef) + +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + Function, Liberal, , + ImportConflictingDefAfterProto) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + Variable, Liberal, , + ImportConflictingDefAfterProto) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + ClassTemplate, Liberal, , + ImportConflictingDefAfterProto) + +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + Function, Conservative, , + DontImportConflictingDefAfterProto) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + Variable, Conservative, , + DontImportConflictingDefAfterProto) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + ClassTemplate, Conservative, , + DontImportConflictingDefAfterProto) + +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + ClassTemplate, Liberal, , + ImportConflictingProtoDefAfterProto) + +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + ClassTemplate, Conservative, , + DontImportConflictingProtoDefAfterProto) + +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + ClassTemplate, Liberal, , + ImportConflictingProtoAfterProtoDef) + +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + ClassTemplate, Conservative, , + DontImportConflictingProtoAfterProtoDef) + +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + ClassTemplate, Liberal, , + ImportConflictingProtoDefAfterDef) + +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + ClassTemplate, Conservative, , + DontImportConflictingProtoDefAfterDef) + +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + ClassTemplate, Liberal, , + ImportConflictingDefAfterProtoDef) + +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + ClassTemplate, Conservative, , + DontImportConflictingDefAfterProtoDef) + +// FunctionTemplate decls overload with each other. Thus, they are imported +// always as a new node, independently from any ODRHandling strategy. +// +// Function template specializations are "full" specializations. Structural +// equivalency does not check the body of functions, so we cannot create +// conflicting function template specializations. Thus, ODR handling strategies +// has nothing to do with function template specializations. Fully specialized +// function templates are imported as new nodes if their template arguments are +// different. +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + FunctionTemplate, Liberal, , + ImportDifferentDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + FunctionTemplateSpec, Liberal, , + ImportDifferentDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + FunctionTemplate, Conservative, , + ImportDifferentDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + FunctionTemplateSpec, Conservative, , + ImportDifferentDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + FunctionTemplate, Liberal, , + DontImportSameDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + FunctionTemplateSpec, Liberal, , + DontImportSameDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + FunctionTemplate, Conservative, , + DontImportSameDefAfterDef) +ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( + FunctionTemplateSpec, Conservative, , + DontImportSameDefAfterDef) + +// ====================== +// Instantiate the tests. +// ====================== + +// FIXME: These fail on Windows. +#if !defined(_WIN32) +INSTANTIATE_TEST_CASE_P( + ODRViolationTests, FunctionConservative, + DefaultTestValuesForRunOptions, ); +#endif +INSTANTIATE_TEST_CASE_P( + ODRViolationTests, TypedefConservative, + DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P( + ODRViolationTests, TypedefAliasConservative, + DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P( + ODRViolationTests, EnumConservative, + DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P( + ODRViolationTests, EnumConstantConservative, + DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P( + ODRViolationTests, ClassConservative, + DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P( + ODRViolationTests, VariableConservative, + DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P( + ODRViolationTests, ClassTemplateConservative, + DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P( + ODRViolationTests, FunctionTemplateConservative, + DefaultTestValuesForRunOptions, ); +// FIXME: Make VarTemplate tests work. +//INSTANTIATE_TEST_CASE_P( + //ODRViolationTests, VarTemplateConservative, + //DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P( + ODRViolationTests, FunctionTemplateSpecConservative, + DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P( + ODRViolationTests, ClassTemplateSpecConservative, + DefaultTestValuesForRunOptions, ); +// FIXME: Make VarTemplateSpec tests work. +//INSTANTIATE_TEST_CASE_P( + //ODRViolationTests, VarTemplateSpecConservative, + //DefaultTestValuesForRunOptions, ); + +// FIXME: These fail on Windows. +#if !defined(_WIN32) +INSTANTIATE_TEST_CASE_P( + ODRViolationTests, FunctionLiberal, + DefaultTestValuesForRunOptions, ); +#endif +INSTANTIATE_TEST_CASE_P( + ODRViolationTests, TypedefLiberal, + DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P( + ODRViolationTests, TypedefAliasLiberal, + DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P( + ODRViolationTests, EnumLiberal, + DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P( + ODRViolationTests, EnumConstantLiberal, + DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P( + ODRViolationTests, ClassLiberal, + DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P( + ODRViolationTests, VariableLiberal, + DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P( + ODRViolationTests, ClassTemplateLiberal, + DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P( + ODRViolationTests, FunctionTemplateLiberal, + DefaultTestValuesForRunOptions, ); +// FIXME: Make VarTemplate tests work. +// INSTANTIATE_TEST_CASE_P( +// ODRViolationTests, VarTemplateLiberal, +// DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P( + ODRViolationTests, ClassTemplateSpecLiberal, + DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P( + ODRViolationTests, FunctionTemplateSpecLiberal, + DefaultTestValuesForRunOptions, ); +// FIXME: Make VarTemplateSpec tests work. +//INSTANTIATE_TEST_CASE_P( + //ODRViolationTests, VarTemplateSpecLiberal, + //DefaultTestValuesForRunOptions, ); + +// clang-format on + +} // end namespace ast_matchers +} // end namespace clang diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp index 17696a38e94f6..1514313231bf3 100644 --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -5481,187 +5481,6 @@ TEST_P(ASTImporterOptionSpecificTestBase, EXPECT_EQ(ImportedX->isAggregate(), FromX->isAggregate()); } -struct ConflictingDeclsWithLiberalStrategy : ASTImporterOptionSpecificTestBase { - ConflictingDeclsWithLiberalStrategy() { - this->ODRHandling = ASTImporter::ODRHandlingType::Liberal; - } -}; - -// Check that a Decl has been successfully imported into a standalone redecl -// chain. -template -static void CheckImportedAsNew(llvm::Expected &Result, Decl *ToTU, - PatternTy Pattern) { - ASSERT_TRUE(isSuccess(Result)); - Decl *ImportedD = *Result; - ASSERT_TRUE(ImportedD); - auto *ToD = FirstDeclMatcher().match(ToTU, Pattern); - EXPECT_NE(ImportedD, ToD); - EXPECT_FALSE(ImportedD->getPreviousDecl()); - EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 2u); -} - -TEST_P(ConflictingDeclsWithLiberalStrategy, Typedef) { - Decl *ToTU = getToTuDecl( - R"( - typedef int X; - )", - Lang_CXX11); - Decl *FromTU = getTuDecl( - R"( - typedef double X; - )", - Lang_CXX11); - auto Pattern = typedefNameDecl(hasName("X")); - auto *FromX = FirstDeclMatcher().match(FromTU, Pattern); - - Expected Result = importOrError(FromX, Lang_CXX11); - CheckImportedAsNew(Result, ToTU, Pattern); -} - -TEST_P(ConflictingDeclsWithLiberalStrategy, TypeAlias) { - Decl *ToTU = getToTuDecl( - R"( - using X = int; - )", - Lang_CXX11); - Decl *FromTU = getTuDecl( - R"( - using X = double; - )", - Lang_CXX11); - auto Pattern = typedefNameDecl(hasName("X")); - auto *FromX = FirstDeclMatcher().match(FromTU, Pattern); - Expected Result = importOrError(FromX, Lang_CXX11); - CheckImportedAsNew(Result, ToTU, Pattern); -} - -TEST_P(ConflictingDeclsWithLiberalStrategy, EnumDecl) { - Decl *ToTU = getToTuDecl( - R"( - enum X { a, b }; - )", - Lang_CXX11); - Decl *FromTU = getTuDecl( - R"( - enum X { a, b, c }; - )", - Lang_CXX11); - auto Pattern = enumDecl(hasName("X")); - auto *FromX = FirstDeclMatcher().match(FromTU, Pattern); - Expected Result = importOrError(FromX, Lang_CXX11); - CheckImportedAsNew(Result, ToTU, Pattern); -} - -TEST_P(ConflictingDeclsWithLiberalStrategy, EnumConstantDecl) { - Decl *ToTU = getToTuDecl( - R"( - enum E { X = 0 }; - )", - Lang_CXX11); - Decl *FromTU = getTuDecl( - R"( - enum E { X = 1 }; - )", - Lang_CXX11); - auto Pattern = enumConstantDecl(hasName("X")); - auto *FromX = FirstDeclMatcher().match(FromTU, Pattern); - Expected Result = importOrError(FromX, Lang_CXX11); - CheckImportedAsNew(Result, ToTU, Pattern); -} - -TEST_P(ConflictingDeclsWithLiberalStrategy, RecordDecl) { - Decl *ToTU = getToTuDecl( - R"( - class X { int a; }; - )", - Lang_CXX11); - Decl *FromTU = getTuDecl( - R"( - class X { int b; }; - )", - Lang_CXX11); - auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit())); - auto *FromX = FirstDeclMatcher().match(FromTU, Pattern); - Expected Result = importOrError(FromX, Lang_CXX11); - CheckImportedAsNew(Result, ToTU, Pattern); -} - -TEST_P(ConflictingDeclsWithLiberalStrategy, VarDecl) { - Decl *ToTU = getToTuDecl( - R"( - int X; - )", - Lang_CXX11); - Decl *FromTU = getTuDecl( - R"( - double X; - )", - Lang_CXX11); - auto Pattern = varDecl(hasName("X")); - auto *FromX = FirstDeclMatcher().match(FromTU, Pattern); - Expected Result = importOrError(FromX, Lang_CXX11); - CheckImportedAsNew(Result, ToTU, Pattern); -} - -TEST_P(ConflictingDeclsWithLiberalStrategy, FunctionDecl) { - Decl *ToTU = getToTuDecl( - R"( - void X(int); - )", - Lang_C); // C, no overloading! - Decl *FromTU = getTuDecl( - R"( - void X(double); - )", - Lang_C); - auto Pattern = functionDecl(hasName("X")); - auto *FromX = FirstDeclMatcher().match(FromTU, Pattern); - Expected Result = importOrError(FromX, Lang_CXX11); - CheckImportedAsNew(Result, ToTU, Pattern); -} - -TEST_P(ConflictingDeclsWithLiberalStrategy, ClassTemplateDecl) { - Decl *ToTU = getToTuDecl( - R"( - template - struct X; - )", - Lang_CXX11); - Decl *FromTU = getTuDecl( - R"( - template - struct X; - )", - Lang_CXX11); - auto Pattern = classTemplateDecl(hasName("X")); - auto *FromX = FirstDeclMatcher().match(FromTU, Pattern); - Expected Result = importOrError(FromX, Lang_CXX11); - CheckImportedAsNew(Result, ToTU, Pattern); -} - -TEST_P(ConflictingDeclsWithLiberalStrategy, DISABLED_VarTemplateDecl) { - const internal::VariadicDynCastAllOfMatcher - varTemplateDecl; - Decl *ToTU = getToTuDecl( - R"( - template - constexpr T X; - )", - Lang_CXX14); - Decl *FromTU = getTuDecl( - R"( - template - constexpr int X = 0; - )", - Lang_CXX14); - auto Pattern = varTemplateDecl(hasName("X")); - auto *FromX = FirstDeclMatcher().match( - FromTU, varTemplateDecl(hasName("X"))); - Expected Result = importOrError(FromX, Lang_CXX11); - CheckImportedAsNew(Result, ToTU, Pattern); -} - INSTANTIATE_TEST_CASE_P(ParameterizedTests, SVEBuiltins, ::testing::Values(ArgVector{"-target", "aarch64-linux-gnu"}), ); @@ -5838,8 +5657,5 @@ INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportVariables, INSTANTIATE_TEST_CASE_P(ParameterizedTests, LLDBLookupTest, DefaultTestValuesForRunOptions, ); -INSTANTIATE_TEST_CASE_P(ParameterizedTests, ConflictingDeclsWithLiberalStrategy, - DefaultTestValuesForRunOptions, ); - } // end namespace ast_matchers } // end namespace clang diff --git a/clang/unittests/AST/CMakeLists.txt b/clang/unittests/AST/CMakeLists.txt index c4940e8acea45..4d463d1cc3095 100644 --- a/clang/unittests/AST/CMakeLists.txt +++ b/clang/unittests/AST/CMakeLists.txt @@ -11,6 +11,7 @@ add_clang_unittest(ASTTests ASTImporterFixtures.cpp ASTImporterTest.cpp ASTImporterGenericRedeclTest.cpp + ASTImporterODRStrategiesTest.cpp ASTImporterVisibilityTest.cpp ASTTraverserTest.cpp ASTTypeTraitsTest.cpp diff --git a/clang/unittests/AST/DeclTest.cpp b/clang/unittests/AST/DeclTest.cpp index 6691952b2f2b9..7a662fb4fbae4 100644 --- a/clang/unittests/AST/DeclTest.cpp +++ b/clang/unittests/AST/DeclTest.cpp @@ -10,12 +10,16 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ASTContext.h" +#include "clang/AST/Mangle.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Basic/LLVM.h" #include "clang/Tooling/Tooling.h" #include "gtest/gtest.h" using namespace clang::ast_matchers; using namespace clang::tooling; +using namespace clang; TEST(Decl, CleansUpAPValues) { MatchFinder Finder; @@ -56,3 +60,49 @@ TEST(Decl, CleansUpAPValues) { "constexpr _Complex __uint128_t c = 0xffffffffffffffff;", Args)); } + +TEST(Decl, AsmLabelAttr) { + // Create two method decls: `f` and `g`. + StringRef Code = R"( + struct S { + void f() {} + void g() {} + }; + )"; + auto AST = + tooling::buildASTFromCodeWithArgs(Code, {"-target", "i386-apple-darwin"}); + ASTContext &Ctx = AST->getASTContext(); + assert(Ctx.getTargetInfo().getDataLayout().getGlobalPrefix() && + "Expected target to have a global prefix"); + DiagnosticsEngine &Diags = AST->getDiagnostics(); + SourceManager &SM = AST->getSourceManager(); + FileID MainFileID = SM.getMainFileID(); + + // Find the method decls within the AST. + SmallVector Decls; + AST->findFileRegionDecls(MainFileID, Code.find('{'), 0, Decls); + ASSERT_TRUE(Decls.size() == 1); + CXXRecordDecl *DeclS = cast(Decls[0]); + NamedDecl *DeclF = *DeclS->method_begin(); + NamedDecl *DeclG = *(++DeclS->method_begin()); + + // Attach asm labels to the decls: one literal, and one not. + DeclF->addAttr(::new (Ctx) AsmLabelAttr(Ctx, SourceLocation(), "foo", + /*LiteralLabel=*/true)); + DeclG->addAttr(::new (Ctx) AsmLabelAttr(Ctx, SourceLocation(), "goo", + /*LiteralLabel=*/false)); + + // Mangle the decl names. + std::string MangleF, MangleG; + std::unique_ptr MC( + ItaniumMangleContext::create(Ctx, Diags)); + { + llvm::raw_string_ostream OS_F(MangleF); + llvm::raw_string_ostream OS_G(MangleG); + MC->mangleName(DeclF, OS_F); + MC->mangleName(DeclG, OS_G); + } + + ASSERT_TRUE(0 == MangleF.compare("\x01" "foo")); + ASSERT_TRUE(0 == MangleG.compare("goo")); +} diff --git a/clang/unittests/AST/NamedDeclPrinterTest.cpp b/clang/unittests/AST/NamedDeclPrinterTest.cpp index a50626517f619..a5c3e19055f43 100644 --- a/clang/unittests/AST/NamedDeclPrinterTest.cpp +++ b/clang/unittests/AST/NamedDeclPrinterTest.cpp @@ -16,9 +16,12 @@ //===----------------------------------------------------------------------===// #include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/PrettyPrinter.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" using namespace clang; @@ -30,11 +33,12 @@ namespace { class PrintMatch : public MatchFinder::MatchCallback { SmallString<1024> Printed; unsigned NumFoundDecls; - bool SuppressUnwrittenScope; + std::function Printer; public: - explicit PrintMatch(bool suppressUnwrittenScope) - : NumFoundDecls(0), SuppressUnwrittenScope(suppressUnwrittenScope) {} + explicit PrintMatch( + std::function Printer) + : NumFoundDecls(0), Printer(std::move(Printer)) {} void run(const MatchFinder::MatchResult &Result) override { const NamedDecl *ND = Result.Nodes.getNodeAs("id"); @@ -45,9 +49,7 @@ class PrintMatch : public MatchFinder::MatchCallback { return; llvm::raw_svector_ostream Out(Printed); - PrintingPolicy Policy = Result.Context->getPrintingPolicy(); - Policy.SuppressUnwrittenScope = SuppressUnwrittenScope; - ND->printQualifiedName(Out, Policy); + Printer(Out, ND); } StringRef getPrinted() const { @@ -59,12 +61,12 @@ class PrintMatch : public MatchFinder::MatchCallback { } }; -::testing::AssertionResult -PrintedNamedDeclMatches(StringRef Code, const std::vector &Args, - bool SuppressUnwrittenScope, - const DeclarationMatcher &NodeMatch, - StringRef ExpectedPrinted, StringRef FileName) { - PrintMatch Printer(SuppressUnwrittenScope); +::testing::AssertionResult PrintedDeclMatches( + StringRef Code, const std::vector &Args, + const DeclarationMatcher &NodeMatch, StringRef ExpectedPrinted, + StringRef FileName, + std::function Print) { + PrintMatch Printer(std::move(Print)); MatchFinder Finder; Finder.addMatcher(NodeMatch, &Printer); std::unique_ptr Factory = @@ -91,6 +93,21 @@ PrintedNamedDeclMatches(StringRef Code, const std::vector &Args, return ::testing::AssertionSuccess(); } +::testing::AssertionResult +PrintedNamedDeclMatches(StringRef Code, const std::vector &Args, + bool SuppressUnwrittenScope, + const DeclarationMatcher &NodeMatch, + StringRef ExpectedPrinted, StringRef FileName) { + return PrintedDeclMatches(Code, Args, NodeMatch, ExpectedPrinted, FileName, + [=](llvm::raw_ostream &Out, const NamedDecl *ND) { + auto Policy = + ND->getASTContext().getPrintingPolicy(); + Policy.SuppressUnwrittenScope = + SuppressUnwrittenScope; + ND->printQualifiedName(Out, Policy); + }); +} + ::testing::AssertionResult PrintedNamedDeclCXX98Matches(StringRef Code, StringRef DeclName, StringRef ExpectedPrinted) { @@ -127,6 +144,17 @@ PrintedWrittenPropertyDeclObjCMatches(StringRef Code, StringRef DeclName, "input.m"); } +::testing::AssertionResult +PrintedNestedNameSpecifierMatches(StringRef Code, StringRef DeclName, + StringRef ExpectedPrinted) { + std::vector Args{"-std=c++11"}; + return PrintedDeclMatches(Code, Args, namedDecl(hasName(DeclName)).bind("id"), + ExpectedPrinted, "input.cc", + [](llvm::raw_ostream &Out, const NamedDecl *D) { + D->printNestedNameSpecifier(Out); + }); +} + } // unnamed namespace TEST(NamedDeclPrinter, TestNamespace1) { @@ -223,3 +251,21 @@ R"( "property", "Obj::property")); } + +TEST(NamedDeclPrinter, NestedNameSpecifierSimple) { + const char *Code = + R"( + namespace foo { namespace bar { void func(); } } +)"; + ASSERT_TRUE(PrintedNestedNameSpecifierMatches(Code, "func", "foo::bar::")); +} + +TEST(NamedDeclPrinter, NestedNameSpecifierTemplateArgs) { + const char *Code = + R"( + template struct vector; + template <> struct vector { int method(); }; +)"; + ASSERT_TRUE( + PrintedNestedNameSpecifierMatches(Code, "method", "vector::")); +} diff --git a/clang/unittests/Driver/ToolChainTest.cpp b/clang/unittests/Driver/ToolChainTest.cpp index 80938c83f8247..f84e508b6cbdb 100644 --- a/clang/unittests/Driver/ToolChainTest.cpp +++ b/clang/unittests/Driver/ToolChainTest.cpp @@ -60,7 +60,7 @@ TEST(ToolChainTest, VFSGCCInstallation) { llvm::MemoryBuffer::getMemBuffer("\n")); std::unique_ptr C(TheDriver.BuildCompilation( - {"-fsyntax-only", "--gcc-toolchain=", "foo.cpp"})); + {"-fsyntax-only", "--gcc-toolchain=", "--sysroot=", "foo.cpp"})); EXPECT_TRUE(C); std::string S; diff --git a/clang/unittests/Format/CleanupTest.cpp b/clang/unittests/Format/CleanupTest.cpp index 0628c38a2bd6b..b0c81b509d2ae 100644 --- a/clang/unittests/Format/CleanupTest.cpp +++ b/clang/unittests/Format/CleanupTest.cpp @@ -183,10 +183,15 @@ TEST_F(CleanupTest, TrailingCommaInParens) { std::string Code = "int main() { f(,1,,2,3,f(1,2,),4,,);}"; std::string Expected = "int main() { f(1,2,3,f(1,2),4);}"; EXPECT_EQ(Expected, cleanupAroundOffsets({15, 18, 29, 33}, Code)); + + // Lambda contents are also checked for trailing commas. + Code = "int main() { [](){f(,1,,2,3,f(1,2,),4,,);}();}"; + Expected = "int main() { [](){f(1,2,3,f(1,2),4);}();}"; + EXPECT_EQ(Expected, cleanupAroundOffsets({20, 23, 34, 38}, Code)); } TEST_F(CleanupTest, TrailingCommaInBraces) { - // Trainling comma is allowed in brace list. + // Trailing comma is allowed in brace list. // If there was trailing comma in the original code, then trailing comma is // preserved. In this example, element between the last two commas is deleted // causing the second-last comma to be redundant. @@ -194,7 +199,7 @@ TEST_F(CleanupTest, TrailingCommaInBraces) { std::string Expected = "void f() { std::vector v = {1,2,3,}; }"; EXPECT_EQ(Expected, cleanupAroundOffsets({39}, Code)); - // If there was no trailing comma in the original code, then trainling comma + // If there was no trailing comma in the original code, then trailing comma // introduced by replacements should be cleaned up. In this example, the // element after the last comma is deleted causing the last comma to be // redundant. diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index b1fcd39a49aed..d7759bc6df3b3 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -29,11 +29,7 @@ FormatStyle getGoogleStyle() { return getGoogleStyle(FormatStyle::LK_Cpp); } class FormatTest : public ::testing::Test { protected: - enum StatusCheck { - SC_ExpectComplete, - SC_ExpectIncomplete, - SC_DoNotCheck - }; + enum StatusCheck { SC_ExpectComplete, SC_ExpectIncomplete, SC_DoNotCheck }; std::string format(llvm::StringRef Code, const FormatStyle &Style = getLLVMStyle(), @@ -332,13 +328,15 @@ TEST_F(FormatTest, RemovesEmptyLines) { format("namespace {\n" "int i;\n" "\n" - "}", LLVMWithNoNamespaceFix)); + "}", + LLVMWithNoNamespaceFix)); EXPECT_EQ("namespace {\n" "int i;\n" "}", format("namespace {\n" "int i;\n" - "}", LLVMWithNoNamespaceFix)); + "}", + LLVMWithNoNamespaceFix)); EXPECT_EQ("namespace {\n" "int i;\n" "\n" @@ -346,13 +344,15 @@ TEST_F(FormatTest, RemovesEmptyLines) { format("namespace {\n" "int i;\n" "\n" - "};", LLVMWithNoNamespaceFix)); + "};", + LLVMWithNoNamespaceFix)); EXPECT_EQ("namespace {\n" "int i;\n" "};", format("namespace {\n" "int i;\n" - "};", LLVMWithNoNamespaceFix)); + "};", + LLVMWithNoNamespaceFix)); EXPECT_EQ("namespace {\n" "int i;\n" "\n" @@ -903,7 +903,8 @@ TEST_F(FormatTest, FormatsForLoop) { verifyFormat("for (Foo *x = 0; x != in; x++) {\n}"); verifyFormat("Foo *x;\nfor (x = 0; x != in; x++) {\n}"); verifyFormat("Foo *x;\nfor (x in y) {\n}"); - verifyFormat("for (const Foo &baz = in.value(); !baz.at_end(); ++baz) {\n}"); + verifyFormat( + "for (const Foo &baz = in.value(); !baz.at_end(); ++baz) {\n}"); FormatStyle NoBinPacking = getLLVMStyle(); NoBinPacking.BinPackParameters = false; @@ -1295,20 +1296,26 @@ TEST_F(FormatTest, ShortCaseLabels) { "#endif\n" "}", Style)); - EXPECT_EQ("switch (a) {\n" "case 0:\n" - " return; // long long long long long long long long long long long long comment\n" - " // line\n" "}", + EXPECT_EQ("switch (a) {\n" + "case 0:\n" + " return; // long long long long long long long long long long " + "long long comment\n" + " // line\n" + "}", format("switch (a) {\n" - "case 0: return; // long long long long long long long long long long long long comment line\n" + "case 0: return; // long long long long long long long long " + "long long long long comment line\n" "}", Style)); EXPECT_EQ("switch (a) {\n" "case 0:\n" - " return; /* long long long long long long long long long long long long comment\n" + " return; /* long long long long long long long long long long " + "long long comment\n" " line */\n" "}", format("switch (a) {\n" - "case 0: return; /* long long long long long long long long long long long long comment line */\n" + "case 0: return; /* long long long long long long long long " + "long long long long comment line */\n" "}", Style)); verifyFormat("switch (a) {\n" @@ -1596,7 +1603,7 @@ TEST_F(FormatTest, FormatsClasses) { TEST_F(FormatTest, BreakInheritanceStyle) { FormatStyle StyleWithInheritanceBreakBeforeComma = getLLVMStyle(); StyleWithInheritanceBreakBeforeComma.BreakInheritanceList = - FormatStyle::BILS_BeforeComma; + FormatStyle::BILS_BeforeComma; verifyFormat("class MyClass : public X {};", StyleWithInheritanceBreakBeforeComma); verifyFormat("class MyClass\n" @@ -1614,7 +1621,7 @@ TEST_F(FormatTest, BreakInheritanceStyle) { FormatStyle StyleWithInheritanceBreakAfterColon = getLLVMStyle(); StyleWithInheritanceBreakAfterColon.BreakInheritanceList = - FormatStyle::BILS_AfterColon; + FormatStyle::BILS_AfterColon; verifyFormat("class MyClass : public X {};", StyleWithInheritanceBreakAfterColon); verifyFormat("class MyClass : public X, public Y {};", @@ -1908,8 +1915,7 @@ TEST_F(FormatTest, FormatsNamespaces) { // This code is more common than we thought; if we // layout this correctly the semicolon will go into // its own line, which is undesirable. - verifyFormat("namespace {};", - LLVMWithNoNamespaceFix); + verifyFormat("namespace {};", LLVMWithNoNamespaceFix); verifyFormat("namespace {\n" "class A {};\n" "};", @@ -2086,8 +2092,8 @@ TEST_F(FormatTest, FormatsCompactNamespaces) { Style.NamespaceMacros.push_back("TESTSUITE"); verifyFormat("namespace A { namespace B {\n" - "}} // namespace A::B", - Style); + "}} // namespace A::B", + Style); EXPECT_EQ("namespace out { namespace in {\n" "}} // namespace out::in", @@ -2401,6 +2407,16 @@ TEST_F(FormatTest, FormatTryCatchBraceStyles) { " // something\n" "}", Style); + Style.BreakBeforeBraces = FormatStyle::BS_Whitesmiths; + verifyFormat("try\n" + " {\n" + " // something white\n" + " }\n" + "catch (...)\n" + " {\n" + " // something white\n" + " }", + Style); Style.BreakBeforeBraces = FormatStyle::BS_GNU; verifyFormat("try\n" " {\n" @@ -2931,7 +2947,8 @@ TEST_F(FormatTest, MacroCallsWithoutTrailingSemicolon) { "{\n" "}", format("void f() try {\n" - "}", Style)); + "}", + Style)); EXPECT_EQ("class SomeClass {\n" "public:\n" " SomeClass() EXCLUSIVE_LOCK_FUNCTION(mu_);\n" @@ -3730,16 +3747,19 @@ TEST_F(FormatTest, FormatBeginBlockEndMacros) { Style.MacroBlockEnd = "^[A-Z_]+_END$"; verifyFormat("FOO_BEGIN\n" " FOO_ENTRY\n" - "FOO_END", Style); + "FOO_END", + Style); verifyFormat("FOO_BEGIN\n" " NESTED_FOO_BEGIN\n" " NESTED_FOO_ENTRY\n" " NESTED_FOO_END\n" - "FOO_END", Style); + "FOO_END", + Style); verifyFormat("FOO_BEGIN(Foo, Bar)\n" " int x;\n" " x = 1;\n" - "FOO_END(Baz)", Style); + "FOO_END(Baz)", + Style); } //===----------------------------------------------------------------------===// @@ -4006,7 +4026,9 @@ TEST_F(FormatTest, ExpressionIndentationBreakingBeforeOperators) { EXPECT_EQ("return someVeryVeryLongConditionThatBarelyFitsOnALine\n" "\t&& (someOtherLongishConditionPart1\n" "\t\t|| someOtherEvenLongerNestedConditionPart2);", - format("return someVeryVeryLongConditionThatBarelyFitsOnALine && (someOtherLongishConditionPart1 || someOtherEvenLongerNestedConditionPart2);", + format("return someVeryVeryLongConditionThatBarelyFitsOnALine && " + "(someOtherLongishConditionPart1 || " + "someOtherEvenLongerNestedConditionPart2);", Style)); } @@ -4450,28 +4472,28 @@ TEST_F(FormatTest, BreakConstructorInitializersAfterColon) { "SomeClass::Constructor() :\n" " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa),\n" " aaaaaaaaaaaaaaa(aaaaaaaaaaaa) {}", - Style); + Style); verifyFormat("Constructor(aaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" " aaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) :\n" " aaaaaaaaaa(aaaaaa) {}", - Style); + Style); verifyFormat("Constructor() :\n" " aaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaa),\n" " aaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" " aaaaaaaaaaaaaaaaaaaaaaaaaaa),\n" " aaaaaaaaaaaaaaaaaaaaaaa() {}", - Style); + Style); verifyFormat("Constructor() :\n" " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n" " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) {}", - Style); + Style); verifyFormat("Constructor(int Parameter = 0) :\n" " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaa),\n" " aaaaaaaaaaaa(aaaaaaaaaaaaaaaaa) {}", - Style); + Style); verifyFormat("Constructor() :\n" " aaaaaaaaaaaaaaaaaaaaaa(a), bbbbbbbbbbbbbbbbbbbbbbbb(b) {\n" "}", @@ -4479,7 +4501,7 @@ TEST_F(FormatTest, BreakConstructorInitializersAfterColon) { verifyFormat("Constructor() :\n" " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n" " aaaaaaaaaaaaaaaaaaaaaaaaa(aaaa, aaaa)) {}", - Style); + Style); // Here a line could be saved by splitting the second initializer onto two // lines, but that is not desirable. @@ -4487,7 +4509,7 @@ TEST_F(FormatTest, BreakConstructorInitializersAfterColon) { " aaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaa),\n" " aaaaaaaaaaa(aaaaaaaaaaa),\n" " aaaaaaaaaaaaaaaaaaaaat(aaaaaaaaaaaaaaaaaaaaaaaaaaaa) {}", - Style); + Style); FormatStyle OnePerLine = Style; OnePerLine.ConstructorInitializerAllOnOneLineOrOnePerLine = true; @@ -4519,12 +4541,11 @@ TEST_F(FormatTest, BreakConstructorInitializersAfterColon) { " aaaaaaaaaaaaaaaaaaaaaa) {}", OnePerLine); OnePerLine.BinPackParameters = false; - verifyFormat( - "Constructor() :\n" - " aaaaaaaaaaaaaaaaaaaaaaaa(\n" - " aaaaaaaaaaa().aaa(),\n" - " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) {}", - OnePerLine); + verifyFormat("Constructor() :\n" + " aaaaaaaaaaaaaaaaaaaaaaaa(\n" + " aaaaaaaaaaa().aaa(),\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) {}", + OnePerLine); OnePerLine.ColumnLimit = 60; verifyFormat("Constructor() :\n" " aaaaaaaaaaaaaaaaaaaa(a),\n" @@ -4537,7 +4558,7 @@ TEST_F(FormatTest, BreakConstructorInitializersAfterColon) { format("Constructor() :\n" " // Comment forcing unwanted break.\n" " aaaa(aaaa) {}", - Style)); + Style)); Style.ColumnLimit = 0; verifyFormat("SomeClass::Constructor() :\n" @@ -4547,7 +4568,7 @@ TEST_F(FormatTest, BreakConstructorInitializersAfterColon) { " a(a) {}", Style); verifyFormat("SomeClass::Constructor() :\n" - " a(a), b(b), c(c) {}", + " a(a), b(b), c(c) {}", Style); verifyFormat("SomeClass::Constructor() :\n" " a(a) {\n" @@ -4558,40 +4579,43 @@ TEST_F(FormatTest, BreakConstructorInitializersAfterColon) { Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None; verifyFormat("SomeClass::Constructor() :\n" - " a(a), b(b), c(c) {\n" - "}", + " a(a), b(b), c(c) {\n" + "}", Style); verifyFormat("SomeClass::Constructor() :\n" " a(a) {\n" - "}", + "}", Style); Style.ColumnLimit = 80; Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_All; Style.ConstructorInitializerIndentWidth = 2; - verifyFormat("SomeClass::Constructor() : a(a), b(b), c(c) {}", - Style); + verifyFormat("SomeClass::Constructor() : a(a), b(b), c(c) {}", Style); verifyFormat("SomeClass::Constructor() :\n" " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" " bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb {}", Style); - // `ConstructorInitializerIndentWidth` actually applies to InheritanceList as well + // `ConstructorInitializerIndentWidth` actually applies to InheritanceList as + // well Style.BreakInheritanceList = FormatStyle::BILS_BeforeColon; - verifyFormat("class SomeClass\n" - " : public aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" - " public bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb {};", - Style); + verifyFormat( + "class SomeClass\n" + " : public aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" + " public bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb {};", + Style); Style.BreakInheritanceList = FormatStyle::BILS_BeforeComma; - verifyFormat("class SomeClass\n" - " : public aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" - " , public bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb {};", - Style); + verifyFormat( + "class SomeClass\n" + " : public aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " , public bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb {};", + Style); Style.BreakInheritanceList = FormatStyle::BILS_AfterColon; - verifyFormat("class SomeClass :\n" - " public aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" - " public bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb {};", - Style); + verifyFormat( + "class SomeClass :\n" + " public aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" + " public bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb {};", + Style); } #ifndef EXPENSIVE_CHECKS @@ -4777,11 +4801,10 @@ TEST_F(FormatTest, BreaksFunctionDeclarations) { "typename aaaaaaaaaa::aaaaaaaaaaa\n" "aaaaaaaaaa::aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n" " bool *aaaaaaaaaaaaaaaaaa, bool *aa) {}"); - verifyGoogleFormat( - "template \n" - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" - "aaaaaaaaaaaaaaaaaaaaaaa::aaaaaaaaaaaaa(\n" - " aaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaa);"); + verifyGoogleFormat("template \n" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + "aaaaaaaaaaaaaaaaaaaaaaa::aaaaaaaaaaaaa(\n" + " aaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaa);"); FormatStyle Style = getLLVMStyle(); Style.PointerAlignment = FormatStyle::PAS_Left; @@ -4880,6 +4903,13 @@ TEST_F(FormatTest, BreaksFunctionDeclarationsWithTrailingTokens) { "}", Style); + Style.BreakBeforeBraces = FormatStyle::BS_Whitesmiths; + verifyFormat("void someLongFunction(\n" + " int someLongParameter) const\n" + " {\n" + " }", + Style); + // Unless these are unknown annotations. verifyFormat("void SomeFunction(aaaaaaaaaa aaaaaaaaaaaaaaa,\n" " aaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)\n" @@ -5154,11 +5184,10 @@ TEST_F(FormatTest, FormatsBuilderPattern) { verifyFormat("return aaaaaaaaaaaaaaaaa->aaaaa().aaaaaaaaaaaaa().aaaaaa() <\n" " aaaaaaaaaaaaaaa->aaaaa().aaaaaaaaaaaaa().aaaaaa();"); - verifyFormat( - "aaaaaaa->aaaaaaa\n" - " ->aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n" - " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)\n" - " ->aaaaaaaa(aaaaaaaaaaaaaaa);"); + verifyFormat("aaaaaaa->aaaaaaa\n" + " ->aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)\n" + " ->aaaaaaaa(aaaaaaaaaaaaaaa);"); verifyFormat( "aaaaaaa->aaaaaaa\n" " ->aaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)\n" @@ -5515,10 +5544,9 @@ TEST_F(FormatTest, BreaksConditionalExpressions) { verifyFormat( "aaaa(aaaaaaaaaaaaaaaaaaaa, aaaaaaa ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" " : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);"); - verifyFormat( - "aaaa(aaaaaaaaa, aaaaaaaaa,\n" - " aaaaaaa ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" - " : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);"); + verifyFormat("aaaa(aaaaaaaaa, aaaaaaaaa,\n" + " aaaaaaa ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);"); verifyFormat( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaa ? aaaa(aaaaaa)\n" " : aaaaaaaaaaaaa);"); @@ -5660,11 +5688,10 @@ TEST_F(FormatTest, BreaksConditionalExpressionsAfterOperator) { "aaaa(aaaaaaaaaaaaaaaaaaaa, aaaaaaa ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa :\n" " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);", Style); - verifyFormat( - "aaaa(aaaaaaaa, aaaaaaaaaa,\n" - " aaaaaaa ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa :\n" - " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);", - Style); + verifyFormat("aaaa(aaaaaaaa, aaaaaaaaaa,\n" + " aaaaaaa ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa :\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);", + Style); verifyFormat( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaa ? aaaa(aaaaaa) :\n" " aaaaaaaaaaaaa);", @@ -6467,7 +6494,8 @@ TEST_F(FormatTest, WrapsTemplateDeclarations) { verifyFormat("template class C {};", NeverBreak); verifyFormat("template void f();", NeverBreak); verifyFormat("template void f() {}", NeverBreak); - verifyFormat("template \nvoid foo(aaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbb) {}", + verifyFormat("template \nvoid foo(aaaaaaaaaaaaaaaaaaaaaaaaaa " + "bbbbbbbbbbbbbbbbbbbb) {}", NeverBreak); verifyFormat("void aaaaaaaaaaaaaaaaaaa(\n" @@ -6483,9 +6511,11 @@ TEST_F(FormatTest, WrapsTemplateDeclarations) { verifyFormat("template class A {\n" "public:\n" " E *f();\n" - "};", NeverBreak); + "};", + NeverBreak); NeverBreak.PenaltyBreakTemplateDeclaration = 100; - verifyFormat("template void\nfoo(aaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbb) {}", + verifyFormat("template void\nfoo(aaaaaaaaaaaaaaaaaaaaaaaaaa " + "bbbbbbbbbbbbbbbbbbbb) {}", NeverBreak); } @@ -6560,22 +6590,24 @@ TEST_F(FormatTest, WrapsTemplateParameters) { Style); Style.AlignAfterOpenBracket = FormatStyle::BAS_AlwaysBreak; Style.BreakBeforeBinaryOperators = FormatStyle::BOS_None; - verifyFormat( - "template struct s {};\n" - "extern s<\n" - " aaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaa,\n" - " aaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaa>\n" - " y;", - Style); + verifyFormat("template struct s {};\n" + "extern s<\n" + " aaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaa, " + "aaaaaaaaaaaaaaaaaaaaaa,\n" + " aaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaa, " + "aaaaaaaaaaaaaaaaaaaaaa>\n" + " y;", + Style); Style.AlignAfterOpenBracket = FormatStyle::BAS_AlwaysBreak; Style.BreakBeforeBinaryOperators = FormatStyle::BOS_All; - verifyFormat( - "template struct t {};\n" - "extern t<\n" - " aaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaa,\n" - " aaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaa>\n" - " y;", - Style); + verifyFormat("template struct t {};\n" + "extern t<\n" + " aaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaa, " + "aaaaaaaaaaaaaaaaaaaaaa,\n" + " aaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaa, " + "aaaaaaaaaaaaaaaaaaaaaa>\n" + " y;", + Style); } TEST_F(FormatTest, WrapsAtNestedNameSpecifiers) { @@ -6602,12 +6634,11 @@ TEST_F(FormatTest, WrapsAtNestedNameSpecifiers) { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa::aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n" " aaaaaaaaaaaaaaaaaaaaaaa);"); - verifyFormat( - "aaaaaaaaaaaaaaaaaa(aaaaaaaa,\n" - " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa::\n" - " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" - " aaaaaaaaaaaaaaaaaaaaa);", - getLLVMStyleWithColumns(74)); + verifyFormat("aaaaaaaaaaaaaaaaaa(aaaaaaaa,\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa::\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" + " aaaaaaaaaaaaaaaaaaaaa);", + getLLVMStyleWithColumns(74)); verifyFormat("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa::\n" " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" @@ -6866,9 +6897,78 @@ TEST_F(FormatTest, UnderstandsFunctionRefQualification) { Spaces.SpacesInCStyleCastParentheses = false; Spaces.SpacesInParentheses = true; verifyFormat("Deleted &operator=( const Deleted & ) & = default;", Spaces); - verifyFormat("SomeType MemberFunction( const Deleted & ) & = delete;", Spaces); + verifyFormat("SomeType MemberFunction( const Deleted & ) & = delete;", + Spaces); verifyFormat("Deleted &operator=( const Deleted & ) &;", Spaces); verifyFormat("SomeType MemberFunction( const Deleted & ) &;", Spaces); + + FormatStyle BreakTemplate = getLLVMStyle(); + BreakTemplate.AlwaysBreakTemplateDeclarations = FormatStyle::BTDS_Yes; + + verifyFormat("struct f {\n" + " template \n" + " int &foo(const std::string &str) & noexcept {}\n" + "};", + BreakTemplate); + + verifyFormat("struct f {\n" + " template \n" + " int &foo(const std::string &str) && noexcept {}\n" + "};", + BreakTemplate); + + verifyFormat("struct f {\n" + " template \n" + " int &foo(const std::string &str) const & noexcept {}\n" + "};", + BreakTemplate); + + verifyFormat("struct f {\n" + " template \n" + " int &foo(const std::string &str) const & noexcept {}\n" + "};", + BreakTemplate); + + verifyFormat("struct f {\n" + " template \n" + " auto foo(const std::string &str) && noexcept -> int & {}\n" + "};", + BreakTemplate); + + FormatStyle AlignLeftBreakTemplate = getLLVMStyle(); + AlignLeftBreakTemplate.AlwaysBreakTemplateDeclarations = + FormatStyle::BTDS_Yes; + AlignLeftBreakTemplate.PointerAlignment = FormatStyle::PAS_Left; + + verifyFormat("struct f {\n" + " template \n" + " int& foo(const std::string& str) & noexcept {}\n" + "};", + AlignLeftBreakTemplate); + + verifyFormat("struct f {\n" + " template \n" + " int& foo(const std::string& str) && noexcept {}\n" + "};", + AlignLeftBreakTemplate); + + verifyFormat("struct f {\n" + " template \n" + " int& foo(const std::string& str) const & noexcept {}\n" + "};", + AlignLeftBreakTemplate); + + verifyFormat("struct f {\n" + " template \n" + " int& foo(const std::string& str) const & noexcept {}\n" + "};", + AlignLeftBreakTemplate); + + verifyFormat("struct f {\n" + " template \n" + " auto foo(const std::string& str) && noexcept -> int& {}\n" + "};", + AlignLeftBreakTemplate); } TEST_F(FormatTest, UnderstandsNewAndDelete) { @@ -7075,14 +7175,13 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { " (sizeof(T) > 1 || sizeof(T) < 8)>::type>\n" "void F();", getLLVMStyleWithColumns(70)); - verifyFormat( - "template ::value &&\n" - " (sizeof(T) > 1 || sizeof(T) < 8)>::type,\n" - " class U>\n" - "void F();", - getLLVMStyleWithColumns(70)); + verifyFormat("template ::value &&\n" + " (sizeof(T) > 1 || sizeof(T) < 8)>::type,\n" + " class U>\n" + "void F();", + getLLVMStyleWithColumns(70)); verifyFormat( "template v{12} GUARDED_BY(mutex);"); @@ -8475,7 +8575,7 @@ TEST_F(FormatTest, SplitEmptyStruct) { "{\n" "} Foo_t;", Style); - //typedef struct Bar {} Bar_t; + // typedef struct Bar {} Bar_t; } TEST_F(FormatTest, SplitEmptyUnion) { @@ -8866,7 +8966,6 @@ TEST_F(FormatTest, FormatForObjectiveCMethodDecls) { verifyGoogleFormat("- foo:(int)foo;"); } - TEST_F(FormatTest, BreaksStringLiterals) { EXPECT_EQ("\"some text \"\n" "\"other\";", @@ -8989,14 +9088,13 @@ TEST_F(FormatTest, BreaksStringLiterals) { // Verify that splitting the strings understands // Style::AlwaysBreakBeforeMultilineStrings. - EXPECT_EQ( - "aaaaaaaaaaaa(\n" - " \"aaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaa \"\n" - " \"aaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaa\");", - format("aaaaaaaaaaaa(\"aaaaaaaaaaaaaaaaaaaaaaaaaa " - "aaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaa " - "aaaaaaaaaaaaaaaaaaaaaa\");", - getGoogleStyle())); + EXPECT_EQ("aaaaaaaaaaaa(\n" + " \"aaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaa \"\n" + " \"aaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaa\");", + format("aaaaaaaaaaaa(\"aaaaaaaaaaaaaaaaaaaaaaaaaa " + "aaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaa " + "aaaaaaaaaaaaaaaaaaaaaa\");", + getGoogleStyle())); EXPECT_EQ("return \"aaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaa \"\n" " \"aaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaa\";", format("return \"aaaaaaaaaaaaaaaaaaaaaa " @@ -9133,10 +9231,10 @@ TEST_F(FormatTest, BreaksStringLiteralsWithin_TMacro) { TEST_F(FormatTest, BreaksStringLiteralOperands) { // In a function call with two operands, the second can be broken with no line // break before it. - EXPECT_EQ("func(a, \"long long \"\n" - " \"long long\");", - format("func(a, \"long long long long\");", - getLLVMStyleWithColumns(24))); + EXPECT_EQ( + "func(a, \"long long \"\n" + " \"long long\");", + format("func(a, \"long long long long\");", getLLVMStyleWithColumns(24))); // In a function call with three operands, the second must be broken with a // line break before it. EXPECT_EQ("func(a,\n" @@ -9165,22 +9263,21 @@ TEST_F(FormatTest, BreaksStringLiteralOperands) { // break before it. EXPECT_EQ("a << \"line line \"\n" " \"line\";", - format("a << \"line line line\";", - getLLVMStyleWithColumns(20))); + format("a << \"line line line\";", getLLVMStyleWithColumns(20))); // In a chain of << with three operands, the second can be broken with no line // break before it. - EXPECT_EQ("abcde << \"line \"\n" - " \"line line\"\n" - " << c;", - format("abcde << \"line line line\" << c;", - getLLVMStyleWithColumns(20))); + EXPECT_EQ( + "abcde << \"line \"\n" + " \"line line\"\n" + " << c;", + format("abcde << \"line line line\" << c;", getLLVMStyleWithColumns(20))); // In a chain of << with three operands, the third must be broken with a line // break before it. - EXPECT_EQ("a << b\n" - " << \"line line \"\n" - " \"line\";", - format("a << b << \"line line line\";", - getLLVMStyleWithColumns(20))); + EXPECT_EQ( + "a << b\n" + " << \"line line \"\n" + " \"line\";", + format("a << b << \"line line line\";", getLLVMStyleWithColumns(20))); // In a chain of << with three operands, the second can be broken with no line // break before it and the third must be broken with a line break before it. EXPECT_EQ("abcd << \"line line \"\n" @@ -9191,10 +9288,10 @@ TEST_F(FormatTest, BreaksStringLiteralOperands) { getLLVMStyleWithColumns(20))); // In a chain of binary operators with two operands, the second can be broken // with no line break before it. - EXPECT_EQ("abcd + \"line line \"\n" - " \"line line\";", - format("abcd + \"line line line line\";", - getLLVMStyleWithColumns(20))); + EXPECT_EQ( + "abcd + \"line line \"\n" + " \"line line\";", + format("abcd + \"line line line line\";", getLLVMStyleWithColumns(20))); // In a chain of binary operators with three operands, the second must be // broken with a line break before it. EXPECT_EQ("abcd +\n" @@ -9291,6 +9388,19 @@ TEST_F(FormatTest, DoesNotTryToParseUDLiteralsInPreCpp11Code) { format("#define x(_a) printf(\"foo\"_a);", Style)); } +TEST_F(FormatTest, CppLexVersion) { + FormatStyle Style = getLLVMStyle(); + // Formatting of x * y differs if x is a type. + verifyFormat("void foo() { MACRO(a * b); }", Style); + verifyFormat("void foo() { MACRO(int *b); }", Style); + + // LLVM style uses latest lexer. + verifyFormat("void foo() { MACRO(char8_t *b); }", Style); + Style.Standard = FormatStyle::LS_Cpp17; + // But in c++17, char8_t isn't a keyword. + verifyFormat("void foo() { MACRO(char8_t * b); }", Style); +} + TEST_F(FormatTest, UnderstandsCpp1y) { verifyFormat("int bi{1'000'000};"); } TEST_F(FormatTest, BreakStringLiteralsBeforeUnbreakableTokenSequence) { @@ -9877,6 +9987,79 @@ TEST_F(FormatTest, ConfigurableUseOfTab) { Tab); } +TEST_F(FormatTest, ZeroTabWidth) { + FormatStyle Tab = getLLVMStyleWithColumns(42); + Tab.IndentWidth = 8; + Tab.UseTab = FormatStyle::UT_Never; + Tab.TabWidth = 0; + EXPECT_EQ("void a(){\n" + " // line starts with '\t'\n" + "};", + format("void a(){\n" + "\t// line starts with '\t'\n" + "};", + Tab)); + + EXPECT_EQ("void a(){\n" + " // line starts with '\t'\n" + "};", + format("void a(){\n" + "\t\t// line starts with '\t'\n" + "};", + Tab)); + + Tab.UseTab = FormatStyle::UT_ForIndentation; + EXPECT_EQ("void a(){\n" + " // line starts with '\t'\n" + "};", + format("void a(){\n" + "\t// line starts with '\t'\n" + "};", + Tab)); + + EXPECT_EQ("void a(){\n" + " // line starts with '\t'\n" + "};", + format("void a(){\n" + "\t\t// line starts with '\t'\n" + "};", + Tab)); + + Tab.UseTab = FormatStyle::UT_ForContinuationAndIndentation; + EXPECT_EQ("void a(){\n" + " // line starts with '\t'\n" + "};", + format("void a(){\n" + "\t// line starts with '\t'\n" + "};", + Tab)); + + EXPECT_EQ("void a(){\n" + " // line starts with '\t'\n" + "};", + format("void a(){\n" + "\t\t// line starts with '\t'\n" + "};", + Tab)); + + Tab.UseTab = FormatStyle::UT_Always; + EXPECT_EQ("void a(){\n" + "// line starts with '\t'\n" + "};", + format("void a(){\n" + "\t// line starts with '\t'\n" + "};", + Tab)); + + EXPECT_EQ("void a(){\n" + "// line starts with '\t'\n" + "};", + format("void a(){\n" + "\t\t// line starts with '\t'\n" + "};", + Tab)); +} + TEST_F(FormatTest, CalculatesOriginalColumn) { EXPECT_EQ("\"qqqqqqqqqqqqqqqqqqqqqqqqqq\\\n" "q\"; /* some\n" @@ -11347,6 +11530,253 @@ TEST_F(FormatTest, AllmanBraceBreaking) { BreakBeforeBraceShortIfs); } +TEST_F(FormatTest, WhitesmithsBraceBreaking) { + FormatStyle WhitesmithsBraceStyle = getLLVMStyle(); + WhitesmithsBraceStyle.BreakBeforeBraces = FormatStyle::BS_Whitesmiths; + + // Make a few changes to the style for testing purposes + WhitesmithsBraceStyle.AllowShortFunctionsOnASingleLine = + FormatStyle::SFS_Empty; + WhitesmithsBraceStyle.AllowShortLambdasOnASingleLine = FormatStyle::SLS_None; + WhitesmithsBraceStyle.ColumnLimit = 0; + + // FIXME: this test case can't decide whether there should be a blank line + // after the ~D() line or not. It adds one if one doesn't exist in the test + // and it removes the line if one exists. + /* + verifyFormat("class A;\n" + "namespace B\n" + " {\n" + "class C;\n" + "// Comment\n" + "class D\n" + " {\n" + "public:\n" + " D();\n" + " ~D() {}\n" + "private:\n" + " enum E\n" + " {\n" + " F\n" + " }\n" + " };\n" + " } // namespace B\n", + WhitesmithsBraceStyle); + */ + + verifyFormat("namespace a\n" + " {\n" + "class A\n" + " {\n" + " void f()\n" + " {\n" + " if (true)\n" + " {\n" + " a();\n" + " b();\n" + " }\n" + " }\n" + " void g()\n" + " {\n" + " return;\n" + " }\n" + " };\n" + "struct B\n" + " {\n" + " int x;\n" + " };\n" + " } // namespace a", + WhitesmithsBraceStyle); + + verifyFormat("void f()\n" + " {\n" + " if (true)\n" + " {\n" + " a();\n" + " }\n" + " else if (false)\n" + " {\n" + " b();\n" + " }\n" + " else\n" + " {\n" + " c();\n" + " }\n" + " }\n", + WhitesmithsBraceStyle); + + verifyFormat("void f()\n" + " {\n" + " for (int i = 0; i < 10; ++i)\n" + " {\n" + " a();\n" + " }\n" + " while (false)\n" + " {\n" + " b();\n" + " }\n" + " do\n" + " {\n" + " c();\n" + " } while (false)\n" + " }\n", + WhitesmithsBraceStyle); + + // FIXME: the block and the break under case 2 in this test don't get indented + // correctly + /* + verifyFormat("void switchTest1(int a)\n" + " {\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " {\n" + " }\n" + " break;\n" + " }\n" + " }\n", + WhitesmithsBraceStyle); + */ + + // FIXME: the block and the break under case 2 in this test don't get indented + // correctly + /* + verifyFormat("void switchTest2(int a)\n" + " {\n" + " switch (a)\n" + " {\n" + " case 0:\n" + " break;\n" + " case 1:\n" + " {\n" + " break;\n" + " }\n" + " case 2:\n" + " {\n" + " }\n" + " break;\n" + " default:\n" + " break;\n" + " }\n" + " }\n", + WhitesmithsBraceStyle); + */ + + verifyFormat("enum X\n" + " {\n" + " Y = 0, // testing\n" + " }\n", + WhitesmithsBraceStyle); + + verifyFormat("enum X\n" + " {\n" + " Y = 0\n" + " }\n", + WhitesmithsBraceStyle); + verifyFormat("enum X\n" + " {\n" + " Y = 0,\n" + " Z = 1\n" + " };\n", + WhitesmithsBraceStyle); + + verifyFormat("@interface BSApplicationController ()\n" + " {\n" + "@private\n" + " id _extraIvar;\n" + " }\n" + "@end\n", + WhitesmithsBraceStyle); + + verifyFormat("#ifdef _DEBUG\n" + "int foo(int i = 0)\n" + "#else\n" + "int foo(int i = 5)\n" + "#endif\n" + " {\n" + " return i;\n" + " }", + WhitesmithsBraceStyle); + + verifyFormat("void foo() {}\n" + "void bar()\n" + "#ifdef _DEBUG\n" + " {\n" + " foo();\n" + " }\n" + "#else\n" + " {\n" + " }\n" + "#endif", + WhitesmithsBraceStyle); + + verifyFormat("void foobar()\n" + " {\n" + " int i = 5;\n" + " }\n" + "#ifdef _DEBUG\n" + "void bar()\n" + " {\n" + " }\n" + "#else\n" + "void bar()\n" + " {\n" + " foobar();\n" + " }\n" + "#endif", + WhitesmithsBraceStyle); + + // This shouldn't affect ObjC blocks.. + verifyFormat("[self doSomeThingWithACompletionHandler:^{\n" + " // ...\n" + " int i;\n" + "}];", + WhitesmithsBraceStyle); + verifyFormat("void (^block)(void) = ^{\n" + " // ...\n" + " int i;\n" + "};", + WhitesmithsBraceStyle); + // .. or dict literals. + verifyFormat("void f()\n" + " {\n" + " [object someMethod:@{@\"a\" : @\"b\"}];\n" + " }", + WhitesmithsBraceStyle); + + verifyFormat("int f()\n" + " { // comment\n" + " return 42;\n" + " }", + WhitesmithsBraceStyle); + + FormatStyle BreakBeforeBraceShortIfs = WhitesmithsBraceStyle; + BreakBeforeBraceShortIfs.AllowShortIfStatementsOnASingleLine = + FormatStyle::SIS_Always; + BreakBeforeBraceShortIfs.AllowShortLoopsOnASingleLine = true; + verifyFormat("void f(bool b)\n" + " {\n" + " if (b)\n" + " {\n" + " return;\n" + " }\n" + " }\n", + BreakBeforeBraceShortIfs); + verifyFormat("void f(bool b)\n" + " {\n" + " if (b) return;\n" + " }\n", + BreakBeforeBraceShortIfs); + verifyFormat("void f(bool b)\n" + " {\n" + " while (b)\n" + " {\n" + " return;\n" + " }\n" + " }\n", + BreakBeforeBraceShortIfs); +} + TEST_F(FormatTest, GNUBraceBreaking) { FormatStyle GNUBraceStyle = getLLVMStyle(); GNUBraceStyle.BreakBeforeBraces = FormatStyle::BS_GNU; @@ -11558,7 +11988,8 @@ TEST_F(FormatTest, OptimizeBreakPenaltyVsExcess) { Style)); verifyFormat("int a; // the\n" - " // comment", Style); + " // comment", + Style); EXPECT_EQ("int a; /* first line\n" " * second\n" " * line third\n" @@ -11648,11 +12079,11 @@ TEST_F(FormatTest, OptimizeBreakPenaltyVsExcess) { // Make sure we do not keep protruding characters if strict mode reflow is // cheaper than keeping protruding characters. Style.ColumnLimit = 21; - EXPECT_EQ("// foo foo foo foo\n" - "// foo foo foo foo\n" - "// foo foo foo foo\n", - format("// foo foo foo foo foo foo foo foo foo foo foo foo\n", - Style)); + EXPECT_EQ( + "// foo foo foo foo\n" + "// foo foo foo foo\n" + "// foo foo foo foo\n", + format("// foo foo foo foo foo foo foo foo foo foo foo foo\n", Style)); EXPECT_EQ("int a = /* long block\n" " comment */\n" @@ -11662,8 +12093,8 @@ TEST_F(FormatTest, OptimizeBreakPenaltyVsExcess) { #define EXPECT_ALL_STYLES_EQUAL(Styles) \ for (size_t i = 1; i < Styles.size(); ++i) \ - EXPECT_EQ(Styles[0], Styles[i]) << "Style #" << i << " of " << Styles.size() \ - << " differs from Style #0" + EXPECT_EQ(Styles[0], Styles[i]) \ + << "Style #" << i << " of " << Styles.size() << " differs from Style #0" TEST_F(FormatTest, GetsPredefinedStyleByName) { SmallVector Styles; @@ -11857,8 +12288,7 @@ TEST_F(FormatTest, ParsesConfiguration) { CHECK_PARSE("ObjCBlockIndentWidth: 1234", ObjCBlockIndentWidth, 1234u); CHECK_PARSE("ColumnLimit: 1234", ColumnLimit, 1234u); CHECK_PARSE("MaxEmptyLinesToKeep: 1234", MaxEmptyLinesToKeep, 1234u); - CHECK_PARSE("PenaltyBreakAssignment: 1234", - PenaltyBreakAssignment, 1234u); + CHECK_PARSE("PenaltyBreakAssignment: 1234", PenaltyBreakAssignment, 1234u); CHECK_PARSE("PenaltyBreakBeforeFirstCallParameter: 1234", PenaltyBreakBeforeFirstCallParameter, 1234u); CHECK_PARSE("PenaltyBreakTemplateDeclaration: 1234", @@ -11888,11 +12318,18 @@ TEST_F(FormatTest, ParsesConfiguration) { FormatStyle::PAS_Middle); Style.Standard = FormatStyle::LS_Auto; + CHECK_PARSE("Standard: c++03", Standard, FormatStyle::LS_Cpp03); + CHECK_PARSE("Standard: c++11", Standard, FormatStyle::LS_Cpp11); + CHECK_PARSE("Standard: c++14", Standard, FormatStyle::LS_Cpp14); + CHECK_PARSE("Standard: c++17", Standard, FormatStyle::LS_Cpp17); + CHECK_PARSE("Standard: c++20", Standard, FormatStyle::LS_Cpp20); + CHECK_PARSE("Standard: Auto", Standard, FormatStyle::LS_Auto); + CHECK_PARSE("Standard: Latest", Standard, FormatStyle::LS_Latest); + // Legacy aliases: CHECK_PARSE("Standard: Cpp03", Standard, FormatStyle::LS_Cpp03); - CHECK_PARSE("Standard: Cpp11", Standard, FormatStyle::LS_Cpp11); + CHECK_PARSE("Standard: Cpp11", Standard, FormatStyle::LS_Latest); CHECK_PARSE("Standard: C++03", Standard, FormatStyle::LS_Cpp03); CHECK_PARSE("Standard: C++11", Standard, FormatStyle::LS_Cpp11); - CHECK_PARSE("Standard: Auto", Standard, FormatStyle::LS_Auto); Style.BreakBeforeBinaryOperators = FormatStyle::BOS_All; CHECK_PARSE("BreakBeforeBinaryOperators: NonAssignment", @@ -11919,15 +12356,15 @@ TEST_F(FormatTest, ParsesConfiguration) { BreakConstructorInitializers, FormatStyle::BCIS_BeforeComma); Style.BreakInheritanceList = FormatStyle::BILS_BeforeColon; - CHECK_PARSE("BreakInheritanceList: BeforeComma", - BreakInheritanceList, FormatStyle::BILS_BeforeComma); - CHECK_PARSE("BreakInheritanceList: AfterColon", - BreakInheritanceList, FormatStyle::BILS_AfterColon); - CHECK_PARSE("BreakInheritanceList: BeforeColon", - BreakInheritanceList, FormatStyle::BILS_BeforeColon); + CHECK_PARSE("BreakInheritanceList: BeforeComma", BreakInheritanceList, + FormatStyle::BILS_BeforeComma); + CHECK_PARSE("BreakInheritanceList: AfterColon", BreakInheritanceList, + FormatStyle::BILS_AfterColon); + CHECK_PARSE("BreakInheritanceList: BeforeColon", BreakInheritanceList, + FormatStyle::BILS_BeforeColon); // For backward compatibility: - CHECK_PARSE("BreakBeforeInheritanceComma: true", - BreakInheritanceList, FormatStyle::BILS_BeforeComma); + CHECK_PARSE("BreakBeforeInheritanceComma: true", BreakInheritanceList, + FormatStyle::BILS_BeforeComma); Style.AlignAfterOpenBracket = FormatStyle::BAS_AlwaysBreak; CHECK_PARSE("AlignAfterOpenBracket: Align", AlignAfterOpenBracket, @@ -12024,6 +12461,8 @@ TEST_F(FormatTest, ParsesConfiguration) { FormatStyle::BS_Stroustrup); CHECK_PARSE("BreakBeforeBraces: Allman", BreakBeforeBraces, FormatStyle::BS_Allman); + CHECK_PARSE("BreakBeforeBraces: Whitesmiths", BreakBeforeBraces, + FormatStyle::BS_Whitesmiths); CHECK_PARSE("BreakBeforeBraces: GNU", BreakBeforeBraces, FormatStyle::BS_GNU); CHECK_PARSE("BreakBeforeBraces: WebKit", BreakBeforeBraces, FormatStyle::BS_WebKit); @@ -12044,16 +12483,16 @@ TEST_F(FormatTest, ParsesConfiguration) { FormatStyle::RTBS_TopLevelDefinitions); Style.AlwaysBreakTemplateDeclarations = FormatStyle::BTDS_Yes; - CHECK_PARSE("AlwaysBreakTemplateDeclarations: No", AlwaysBreakTemplateDeclarations, - FormatStyle::BTDS_No); - CHECK_PARSE("AlwaysBreakTemplateDeclarations: MultiLine", AlwaysBreakTemplateDeclarations, - FormatStyle::BTDS_MultiLine); - CHECK_PARSE("AlwaysBreakTemplateDeclarations: Yes", AlwaysBreakTemplateDeclarations, - FormatStyle::BTDS_Yes); - CHECK_PARSE("AlwaysBreakTemplateDeclarations: false", AlwaysBreakTemplateDeclarations, - FormatStyle::BTDS_MultiLine); - CHECK_PARSE("AlwaysBreakTemplateDeclarations: true", AlwaysBreakTemplateDeclarations, - FormatStyle::BTDS_Yes); + CHECK_PARSE("AlwaysBreakTemplateDeclarations: No", + AlwaysBreakTemplateDeclarations, FormatStyle::BTDS_No); + CHECK_PARSE("AlwaysBreakTemplateDeclarations: MultiLine", + AlwaysBreakTemplateDeclarations, FormatStyle::BTDS_MultiLine); + CHECK_PARSE("AlwaysBreakTemplateDeclarations: Yes", + AlwaysBreakTemplateDeclarations, FormatStyle::BTDS_Yes); + CHECK_PARSE("AlwaysBreakTemplateDeclarations: false", + AlwaysBreakTemplateDeclarations, FormatStyle::BTDS_MultiLine); + CHECK_PARSE("AlwaysBreakTemplateDeclarations: true", + AlwaysBreakTemplateDeclarations, FormatStyle::BTDS_Yes); Style.AlwaysBreakAfterDefinitionReturnType = FormatStyle::DRTBS_All; CHECK_PARSE("AlwaysBreakAfterDefinitionReturnType: None", @@ -12112,7 +12551,7 @@ TEST_F(FormatTest, ParsesConfiguration) { Style.IncludeStyle.IncludeCategories.clear(); std::vector ExpectedCategories = { - {"abc/.*", 2}, {".*", 1}}; + {"abc/.*", 2, 0}, {".*", 1, 0}}; CHECK_PARSE("IncludeCategories:\n" " - Regex: abc/.*\n" " Priority: 2\n" @@ -12445,17 +12884,17 @@ TEST_F(FormatTest, ConstructorInitializerIndentWidth) { "SomeLongTemplateVariableName<\n" " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa>", Style); - verifyFormat( - "bool smaller = 1 < bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb(\n" - " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);", - Style); + verifyFormat("bool smaller = 1 < " + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb(\n" + " " + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);", + Style); Style.BreakConstructorInitializers = FormatStyle::BCIS_AfterColon; - verifyFormat( - "SomeClass::Constructor() :\n" - "aaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaa),\n" - "aaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaa) {}", - Style); + verifyFormat("SomeClass::Constructor() :\n" + "aaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaa),\n" + "aaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaa) {}", + Style); } TEST_F(FormatTest, BreakConstructorInitializersBeforeComma) { @@ -12591,7 +13030,8 @@ TEST_F(FormatTest, FormatsWithWebKitStyle) { // However, don't merge non-empty short loops. EXPECT_EQ("while (true) {\n" " continue;\n" - "}", format("while (true) { continue; }", Style)); + "}", + format("while (true) { continue; }", Style)); // Constructor initializers are formatted one per line with the "," on the // new line. @@ -12729,15 +13169,17 @@ TEST_F(FormatTest, FormatsLambdas) { verifyFormat("void f() {\n" " other.other.other.other.other(\n" " x.begin(), x.end(),\n" - " [something, rather](int, int, int, int, int, int, int) { return 1; });\n" - "}\n"); - verifyFormat("void f() {\n" - " other.other.other.other.other(\n" - " x.begin(), x.end(),\n" - " [something, rather](int, int, int, int, int, int, int) {\n" - " //\n" - " });\n" + " [something, rather](int, int, int, int, int, int, int) { " + "return 1; });\n" "}\n"); + verifyFormat( + "void f() {\n" + " other.other.other.other.other(\n" + " x.begin(), x.end(),\n" + " [something, rather](int, int, int, int, int, int, int) {\n" + " //\n" + " });\n" + "}\n"); verifyFormat("SomeFunction([]() { // A cool function...\n" " return 43;\n" "});"); @@ -12789,9 +13231,10 @@ TEST_F(FormatTest, FormatsLambdas) { verifyFormat("SomeFunction({[&] {\n" " // comment\n" "}});"); - verifyFormat("virtual aaaaaaaaaaaaaaaa(\n" - " std::function bbbbbbbbbbbb = [&]() { return true; },\n" - " aaaaa aaaaaaaaa);"); + verifyFormat( + "virtual aaaaaaaaaaaaaaaa(\n" + " std::function bbbbbbbbbbbb = [&]() { return true; },\n" + " aaaaa aaaaaaaaa);"); // Lambdas with return types. verifyFormat("int c = []() -> int { return 2; }();\n"); @@ -12822,77 +13265,77 @@ TEST_F(FormatTest, FormatsLambdas) { verifyFormat("[]() -> foo<5 < 2> { return {}; };"); verifyFormat("[]() -> foo<2 ? 1 : 0> { return {}; };"); verifyFormat("namespace bar {\n" - "// broken:\n" - "auto foo{[]() -> foo<5 + 2> { return {}; }};\n" - "} // namespace bar"); + "// broken:\n" + "auto foo{[]() -> foo<5 + 2> { return {}; }};\n" + "} // namespace bar"); verifyFormat("namespace bar {\n" - "// broken:\n" - "auto foo{[]() -> foo<5 - 2> { return {}; }};\n" - "} // namespace bar"); + "// broken:\n" + "auto foo{[]() -> foo<5 - 2> { return {}; }};\n" + "} // namespace bar"); verifyFormat("namespace bar {\n" - "// broken:\n" - "auto foo{[]() -> foo<5 / 2> { return {}; }};\n" - "} // namespace bar"); + "// broken:\n" + "auto foo{[]() -> foo<5 / 2> { return {}; }};\n" + "} // namespace bar"); verifyFormat("namespace bar {\n" - "// broken:\n" - "auto foo{[]() -> foo<5 * 2> { return {}; }};\n" - "} // namespace bar"); + "// broken:\n" + "auto foo{[]() -> foo<5 * 2> { return {}; }};\n" + "} // namespace bar"); verifyFormat("namespace bar {\n" - "// broken:\n" - "auto foo{[]() -> foo<5 % 2> { return {}; }};\n" - "} // namespace bar"); + "// broken:\n" + "auto foo{[]() -> foo<5 % 2> { return {}; }};\n" + "} // namespace bar"); verifyFormat("namespace bar {\n" - "// broken:\n" - "auto foo{[]() -> foo<5 << 2> { return {}; }};\n" - "} // namespace bar"); + "// broken:\n" + "auto foo{[]() -> foo<5 << 2> { return {}; }};\n" + "} // namespace bar"); verifyFormat("namespace bar {\n" - "// broken:\n" - "auto foo{[]() -> foo { return {}; }};\n" - "} // namespace bar"); + "// broken:\n" + "auto foo{[]() -> foo { return {}; }};\n" + "} // namespace bar"); verifyFormat("namespace bar {\n" - "// broken:\n" - "auto foo{[]() -> foo<~5> { return {}; }};\n" - "} // namespace bar"); + "// broken:\n" + "auto foo{[]() -> foo<~5> { return {}; }};\n" + "} // namespace bar"); verifyFormat("namespace bar {\n" - "// broken:\n" - "auto foo{[]() -> foo<5 | 2> { return {}; }};\n" - "} // namespace bar"); + "// broken:\n" + "auto foo{[]() -> foo<5 | 2> { return {}; }};\n" + "} // namespace bar"); verifyFormat("namespace bar {\n" - "// broken:\n" - "auto foo{[]() -> foo<5 || 2> { return {}; }};\n" - "} // namespace bar"); + "// broken:\n" + "auto foo{[]() -> foo<5 || 2> { return {}; }};\n" + "} // namespace bar"); verifyFormat("namespace bar {\n" - "// broken:\n" - "auto foo{[]() -> foo<5 & 2> { return {}; }};\n" - "} // namespace bar"); + "// broken:\n" + "auto foo{[]() -> foo<5 & 2> { return {}; }};\n" + "} // namespace bar"); verifyFormat("namespace bar {\n" - "// broken:\n" - "auto foo{[]() -> foo<5 && 2> { return {}; }};\n" - "} // namespace bar"); + "// broken:\n" + "auto foo{[]() -> foo<5 && 2> { return {}; }};\n" + "} // namespace bar"); verifyFormat("namespace bar {\n" - "// broken:\n" - "auto foo{[]() -> foo<5 == 2> { return {}; }};\n" - "} // namespace bar"); + "// broken:\n" + "auto foo{[]() -> foo<5 == 2> { return {}; }};\n" + "} // namespace bar"); verifyFormat("namespace bar {\n" - "// broken:\n" - "auto foo{[]() -> foo<5 != 2> { return {}; }};\n" - "} // namespace bar"); + "// broken:\n" + "auto foo{[]() -> foo<5 != 2> { return {}; }};\n" + "} // namespace bar"); verifyFormat("namespace bar {\n" - "// broken:\n" - "auto foo{[]() -> foo<5 >= 2> { return {}; }};\n" - "} // namespace bar"); + "// broken:\n" + "auto foo{[]() -> foo<5 >= 2> { return {}; }};\n" + "} // namespace bar"); verifyFormat("namespace bar {\n" - "// broken:\n" - "auto foo{[]() -> foo<5 <= 2> { return {}; }};\n" - "} // namespace bar"); + "// broken:\n" + "auto foo{[]() -> foo<5 <= 2> { return {}; }};\n" + "} // namespace bar"); verifyFormat("namespace bar {\n" - "// broken:\n" - "auto foo{[]() -> foo<5 < 2> { return {}; }};\n" - "} // namespace bar"); + "// broken:\n" + "auto foo{[]() -> foo<5 < 2> { return {}; }};\n" + "} // namespace bar"); verifyFormat("namespace bar {\n" - "// broken:\n" - "auto foo{[]() -> foo<2 ? 1 : 0> { return {}; }};\n" - "} // namespace bar"); + "// broken:\n" + "auto foo{[]() -> foo<2 ? 1 : 0> { return {}; }};\n" + "} // namespace bar"); verifyFormat("[]() -> a<1> {};"); verifyFormat("[]() -> a<1> { ; };"); verifyFormat("[]() -> a<1> { ; }();"); @@ -12964,23 +13407,25 @@ TEST_F(FormatTest, FormatsLambdas) { // A lambda with a very long line forces arg0 to be pushed out irrespective of // the BinPackArguments value (as long as the code is wide enough). - verifyFormat("something->SomeFunction(\n" - " a,\n" - " [this] {\n" - " D0000000000000000000000000000000000000000000000000000000000001();\n" - " },\n" - " b);\n"); + verifyFormat( + "something->SomeFunction(\n" + " a,\n" + " [this] {\n" + " " + "D0000000000000000000000000000000000000000000000000000000000001();\n" + " },\n" + " b);\n"); - // A multi-line lambda is pulled up as long as the introducer fits on the previous - // line and there are no further args. + // A multi-line lambda is pulled up as long as the introducer fits on the + // previous line and there are no further args. verifyFormat("function(1, [this, that] {\n" " //\n" "});\n"); verifyFormat("function([this, that] {\n" " //\n" "});\n"); - // FIXME: this format is not ideal and we should consider forcing the first arg - // onto its own line. + // FIXME: this format is not ideal and we should consider forcing the first + // arg onto its own line. verifyFormat("function(a, b, c, //\n" " d, [this, that] {\n" " //\n" @@ -13550,24 +13995,23 @@ TEST_F(FormatTest, DisableRegions) { " int k;")); // Don't reflow comments within disabled regions. - EXPECT_EQ( - "// clang-format off\n" - "// long long long long long long line\n" - "/* clang-format on */\n" - "/* long long long\n" - " * long long long\n" - " * line */\n" - "int i;\n" - "/* clang-format off */\n" - "/* long long long long long long line */\n", - format("// clang-format off\n" - "// long long long long long long line\n" - "/* clang-format on */\n" - "/* long long long long long long line */\n" - "int i;\n" - "/* clang-format off */\n" - "/* long long long long long long line */\n", - getLLVMStyleWithColumns(20))); + EXPECT_EQ("// clang-format off\n" + "// long long long long long long line\n" + "/* clang-format on */\n" + "/* long long long\n" + " * long long long\n" + " * line */\n" + "int i;\n" + "/* clang-format off */\n" + "/* long long long long long long line */\n", + format("// clang-format off\n" + "// long long long long long long line\n" + "/* clang-format on */\n" + "/* long long long long long long line */\n" + "int i;\n" + "/* clang-format off */\n" + "/* long long long long long long line */\n", + getLLVMStyleWithColumns(20))); } TEST_F(FormatTest, DoNotCrashOnInvalidInput) { @@ -13601,9 +14045,7 @@ TEST_F(FormatTest, ArrayAsTemplateType) { format("auto a = unique_ptr < Foo < Bar>[10]> ;", Spaces)); } -TEST_F(FormatTest, NoSpaceAfterSuper) { - verifyFormat("__super::FooBar();"); -} +TEST_F(FormatTest, NoSpaceAfterSuper) { verifyFormat("__super::FooBar();"); } TEST(FormatStyle, GetStyleWithEmptyFileName) { llvm::vfs::InMemoryFileSystem FS; @@ -13758,7 +14200,8 @@ TEST_F(FormatTest, FormatSortsUsingDeclarations) { EXPECT_EQ("using std::cin;\n" "using std::cout;", format("using std::cout;\n" - "using std::cin;", getGoogleStyle())); + "using std::cin;", + getGoogleStyle())); } TEST_F(FormatTest, UTF8CharacterLiteralCpp03) { @@ -13775,10 +14218,8 @@ TEST_F(FormatTest, UTF8CharacterLiteralCpp11) { } TEST_F(FormatTest, DoNotFormatLikelyXml) { - EXPECT_EQ("", - format("", getGoogleStyle())); - EXPECT_EQ(" ", - format(" ", getGoogleStyle())); + EXPECT_EQ("", format("", getGoogleStyle())); + EXPECT_EQ(" ", format(" ", getGoogleStyle())); } TEST_F(FormatTest, StructuredBindings) { @@ -13798,13 +14239,16 @@ TEST_F(FormatTest, StructuredBindings) { EXPECT_EQ("auto const &[a, b] = f();", format("auto const&[a, b] = f();")); EXPECT_EQ("auto const volatile &&[a, b] = f();", format("auto const volatile &&[a, b] = f();")); - EXPECT_EQ("auto const &&[a, b] = f();", format("auto const && [a, b] = f();")); - EXPECT_EQ("const auto &[a, b] = f();", format("const auto & [a, b] = f();")); + EXPECT_EQ("auto const &&[a, b] = f();", + format("auto const && [a, b] = f();")); + EXPECT_EQ("const auto &[a, b] = f();", + format("const auto & [a, b] = f();")); EXPECT_EQ("const auto volatile &&[a, b] = f();", format("const auto volatile &&[a, b] = f();")); EXPECT_EQ("volatile const auto &&[a, b] = f();", format("volatile const auto &&[a, b] = f();")); - EXPECT_EQ("const auto &&[a, b] = f();", format("const auto && [a, b] = f();")); + EXPECT_EQ("const auto &&[a, b] = f();", + format("const auto && [a, b] = f();")); // Make sure we don't mistake structured bindings for lambdas. FormatStyle PointerMiddle = getLLVMStyle(); @@ -13831,11 +14275,15 @@ TEST_F(FormatTest, StructuredBindings) { EXPECT_EQ("auto [x, y](expr);", format("auto[x,y] (expr);")); EXPECT_EQ("auto &[x, y](expr);", format("auto & [x,y] (expr);")); EXPECT_EQ("auto &&[x, y](expr);", format("auto && [x,y] (expr);")); - EXPECT_EQ("auto const &[x, y](expr);", format("auto const & [x,y] (expr);")); - EXPECT_EQ("auto const &&[x, y](expr);", format("auto const && [x,y] (expr);")); + EXPECT_EQ("auto const &[x, y](expr);", + format("auto const & [x,y] (expr);")); + EXPECT_EQ("auto const &&[x, y](expr);", + format("auto const && [x,y] (expr);")); EXPECT_EQ("auto [x, y]{expr};", format("auto[x,y] {expr};")); - EXPECT_EQ("auto const &[x, y]{expr};", format("auto const & [x,y] {expr};")); - EXPECT_EQ("auto const &&[x, y]{expr};", format("auto const && [x,y] {expr};")); + EXPECT_EQ("auto const &[x, y]{expr};", + format("auto const & [x,y] {expr};")); + EXPECT_EQ("auto const &&[x, y]{expr};", + format("auto const && [x,y] {expr};")); format::FormatStyle Spaces = format::getLLVMStyle(); Spaces.SpacesInSquareBrackets = true; @@ -13851,7 +14299,8 @@ TEST_F(FormatTest, FileAndCode) { EXPECT_EQ(FormatStyle::LK_ObjC, guessLanguage("foo.m", "")); EXPECT_EQ(FormatStyle::LK_ObjC, guessLanguage("foo.mm", "")); EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", "")); - EXPECT_EQ(FormatStyle::LK_ObjC, guessLanguage("foo.h", "@interface Foo\n@end\n")); + EXPECT_EQ(FormatStyle::LK_ObjC, + guessLanguage("foo.h", "@interface Foo\n@end\n")); EXPECT_EQ( FormatStyle::LK_ObjC, guessLanguage("foo.h", "#define TRY(x, y) @try { x; } @finally { y; }")); @@ -13859,7 +14308,8 @@ TEST_F(FormatTest, FileAndCode) { guessLanguage("foo.h", "#define AVAIL(x) @available(x, *))")); EXPECT_EQ(FormatStyle::LK_ObjC, guessLanguage("foo.h", "@class Foo;")); EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo", "")); - EXPECT_EQ(FormatStyle::LK_ObjC, guessLanguage("foo", "@interface Foo\n@end\n")); + EXPECT_EQ(FormatStyle::LK_ObjC, + guessLanguage("foo", "@interface Foo\n@end\n")); EXPECT_EQ(FormatStyle::LK_ObjC, guessLanguage("foo.h", "int DoStuff(CGRect rect);\n")); EXPECT_EQ( @@ -13921,36 +14371,36 @@ TEST_F(FormatTest, GuessLanguageWithCaret) { } TEST_F(FormatTest, GuessedLanguageWithInlineAsmClobbers) { - EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", - "void f() {\n" - " asm (\"mov %[e], %[d]\"\n" - " : [d] \"=rm\" (d)\n" - " [e] \"rm\" (*e));\n" - "}")); - EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", - "void f() {\n" - " _asm (\"mov %[e], %[d]\"\n" - " : [d] \"=rm\" (d)\n" - " [e] \"rm\" (*e));\n" - "}")); - EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", - "void f() {\n" - " __asm (\"mov %[e], %[d]\"\n" - " : [d] \"=rm\" (d)\n" - " [e] \"rm\" (*e));\n" - "}")); - EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", - "void f() {\n" - " __asm__ (\"mov %[e], %[d]\"\n" - " : [d] \"=rm\" (d)\n" - " [e] \"rm\" (*e));\n" - "}")); - EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", - "void f() {\n" - " asm (\"mov %[e], %[d]\"\n" - " : [d] \"=rm\" (d),\n" - " [e] \"rm\" (*e));\n" - "}")); + EXPECT_EQ(FormatStyle::LK_Cpp, + guessLanguage("foo.h", "void f() {\n" + " asm (\"mov %[e], %[d]\"\n" + " : [d] \"=rm\" (d)\n" + " [e] \"rm\" (*e));\n" + "}")); + EXPECT_EQ(FormatStyle::LK_Cpp, + guessLanguage("foo.h", "void f() {\n" + " _asm (\"mov %[e], %[d]\"\n" + " : [d] \"=rm\" (d)\n" + " [e] \"rm\" (*e));\n" + "}")); + EXPECT_EQ(FormatStyle::LK_Cpp, + guessLanguage("foo.h", "void f() {\n" + " __asm (\"mov %[e], %[d]\"\n" + " : [d] \"=rm\" (d)\n" + " [e] \"rm\" (*e));\n" + "}")); + EXPECT_EQ(FormatStyle::LK_Cpp, + guessLanguage("foo.h", "void f() {\n" + " __asm__ (\"mov %[e], %[d]\"\n" + " : [d] \"=rm\" (d)\n" + " [e] \"rm\" (*e));\n" + "}")); + EXPECT_EQ(FormatStyle::LK_Cpp, + guessLanguage("foo.h", "void f() {\n" + " asm (\"mov %[e], %[d]\"\n" + " : [d] \"=rm\" (d),\n" + " [e] \"rm\" (*e));\n" + "}")); EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", "void f() {\n" " asm volatile (\"mov %[e], %[d]\"\n" @@ -13981,7 +14431,8 @@ TEST_F(FormatTest, TypenameMacros) { verifyFormat("struct foo {\n" " int bar;\n" " TAILQ_ENTRY(a) bleh;\n" - "};", Google); + "};", + Google); FormatStyle Macros = getLLVMStyle(); Macros.TypenameMacros = TypenameMacros; @@ -14001,6 +14452,15 @@ TEST_F(FormatTest, TypenameMacros) { verifyFormat("STACK_OF(int*)* a;", Macros); } +TEST_F(FormatTest, AmbersandInLamda) { + // Test case reported in https://bugs.llvm.org/show_bug.cgi?id=41899 + FormatStyle AlignStyle = getLLVMStyle(); + AlignStyle.PointerAlignment = FormatStyle::PAS_Left; + verifyFormat("auto lambda = [&a = a]() { a = 2; };", AlignStyle); + AlignStyle.PointerAlignment = FormatStyle::PAS_Right; + verifyFormat("auto lambda = [&a = a]() { a = 2; };", AlignStyle); +} + } // end namespace } // end namespace format } // end namespace clang diff --git a/clang/unittests/Format/SortImportsTestJava.cpp b/clang/unittests/Format/SortImportsTestJava.cpp index d2826a2107cda..12869eeb54eff 100644 --- a/clang/unittests/Format/SortImportsTestJava.cpp +++ b/clang/unittests/Format/SortImportsTestJava.cpp @@ -285,6 +285,13 @@ TEST_F(SortImportsTestJava, NoReplacementsForValidImports) { sortIncludes(FmtStyle, Code, GetCodeRange(Code), "input.java").empty()); } +TEST_F(SortImportsTestJava, NoReplacementsForValidImportsWindows) { + std::string Code = "import org.a;\r\n" + "import org.b;\r\n"; + EXPECT_TRUE( + sortIncludes(FmtStyle, Code, GetCodeRange(Code), "input.java").empty()); +} + } // end namespace } // end namespace format } // end namespace clang diff --git a/clang/unittests/Format/SortIncludesTest.cpp b/clang/unittests/Format/SortIncludesTest.cpp index c00d3cb747d65..0c3a95a10630e 100644 --- a/clang/unittests/Format/SortIncludesTest.cpp +++ b/clang/unittests/Format/SortIncludesTest.cpp @@ -70,6 +70,77 @@ TEST_F(SortIncludesTest, BasicSorting) { {tooling::Range(25, 1)})); } +TEST_F(SortIncludesTest, SortedIncludesUsingSortPriorityAttribute) { + FmtStyle.IncludeStyle.IncludeBlocks = tooling::IncludeStyle::IBS_Regroup; + FmtStyle.IncludeStyle.IncludeCategories = { + {"^", 1, 0}, + {"^", 1, 1}, + {"^", 8, 10}, + {"^\".*\\.h\"", 10, 12}}; + EXPECT_EQ("#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "\n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "\n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "\n" + "#include \n" + "\n" + "#include \"pathnames.h\"\n", + sort("#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \"pathnames.h\"\n" + "#include \n" + "#include \n" + "#include \n" + "#include \n")); +} +TEST_F(SortIncludesTest, SortPriorityNotDefined) { + FmtStyle = getLLVMStyle(); + EXPECT_EQ("#include \"FormatTestUtils.h\"\n" + "#include \"clang/Format/Format.h\"\n" + "#include \"llvm/ADT/None.h\"\n" + "#include \"llvm/Support/Debug.h\"\n" + "#include \"gtest/gtest.h\"\n", + sort("#include \"clang/Format/Format.h\"\n" + "#include \"llvm/ADT/None.h\"\n" + "#include \"FormatTestUtils.h\"\n" + "#include \"gtest/gtest.h\"\n" + "#include \"llvm/Support/Debug.h\"\n")); +} + TEST_F(SortIncludesTest, NoReplacementsForValidIncludes) { // Identical #includes have led to a failure with an unstable sort. std::string Code = "#include \n" @@ -448,7 +519,7 @@ TEST_F(SortIncludesTest, SupportCaseInsensitiveMatching) { } TEST_F(SortIncludesTest, NegativePriorities) { - Style.IncludeCategories = {{".*important_os_header.*", -1}, {".*", 1}}; + Style.IncludeCategories = {{".*important_os_header.*", -1, 0}, {".*", 1, 0}}; EXPECT_EQ("#include \"important_os_header.h\"\n" "#include \"c_main.h\"\n" "#include \"a_other.h\"\n", @@ -468,7 +539,7 @@ TEST_F(SortIncludesTest, NegativePriorities) { } TEST_F(SortIncludesTest, PriorityGroupsAreSeparatedWhenRegroupping) { - Style.IncludeCategories = {{".*important_os_header.*", -1}, {".*", 1}}; + Style.IncludeCategories = {{".*important_os_header.*", -1, 0}, {".*", 1, 0}}; Style.IncludeBlocks = tooling::IncludeStyle::IBS_Regroup; EXPECT_EQ("#include \"important_os_header.h\"\n" @@ -653,6 +724,14 @@ TEST_F(SortIncludesTest, DoNotOutputReplacementsForSortedBlocksWithRegrouping) { EXPECT_EQ(Code, sort(Code, "input.h", 0)); } +TEST_F(SortIncludesTest, + DoNotOutputReplacementsForSortedBlocksWithRegroupingWindows) { + Style.IncludeBlocks = Style.IBS_Regroup; + std::string Code = "#include \"b.h\"\r\n" + "\r\n" + "#include \r\n"; + EXPECT_EQ(Code, sort(Code, "input.h", 0)); +} TEST_F(SortIncludesTest, DoNotRegroupGroupsInGoogleObjCStyle) { FmtStyle = getGoogleStyle(FormatStyle::LK_ObjC); diff --git a/clang/unittests/Lex/DependencyDirectivesSourceMinimizerTest.cpp b/clang/unittests/Lex/DependencyDirectivesSourceMinimizerTest.cpp index 530519573a012..5eb7d256a367a 100644 --- a/clang/unittests/Lex/DependencyDirectivesSourceMinimizerTest.cpp +++ b/clang/unittests/Lex/DependencyDirectivesSourceMinimizerTest.cpp @@ -187,7 +187,7 @@ TEST(MinimizeSourceToDependencyDirectivesTest, DefineMultilineArgs) { " call((a), \\\n" " (b))", Out)); - EXPECT_STREQ("#define MACRO(a,b) call((a),(b))\n", Out.data()); + EXPECT_STREQ("#define MACRO(a,b) call((a), (b))\n", Out.data()); } TEST(MinimizeSourceToDependencyDirectivesTest, @@ -200,7 +200,17 @@ TEST(MinimizeSourceToDependencyDirectivesTest, " call((a), \\\r" " (b))", Out)); - EXPECT_STREQ("#define MACRO(a,b) call((a),(b))\n", Out.data()); + EXPECT_STREQ("#define MACRO(a,b) call((a), (b))\n", Out.data()); +} + +TEST(MinimizeSourceToDependencyDirectivesTest, DefineMultilineArgsStringize) { + SmallVector Out; + + ASSERT_FALSE(minimizeSourceToDependencyDirectives("#define MACRO(a,b) \\\n" + " #a \\\n" + " #b", + Out)); + EXPECT_STREQ("#define MACRO(a,b) #a #b\n", Out.data()); } TEST(MinimizeSourceToDependencyDirectivesTest, @@ -213,7 +223,7 @@ TEST(MinimizeSourceToDependencyDirectivesTest, " call((a), \\\r\n" " (b))", Out)); - EXPECT_STREQ("#define MACRO(a,b) call((a),(b))\n", Out.data()); + EXPECT_STREQ("#define MACRO(a,b) call((a), (b))\n", Out.data()); } TEST(MinimizeSourceToDependencyDirectivesTest, @@ -226,7 +236,7 @@ TEST(MinimizeSourceToDependencyDirectivesTest, " call((a), \\\n\r" " (b))", Out)); - EXPECT_STREQ("#define MACRO(a,b) call((a),(b))\n", Out.data()); + EXPECT_STREQ("#define MACRO(a,b) call((a), (b))\n", Out.data()); } TEST(MinimizeSourceToDependencyDirectivesTest, DefineNumber) { @@ -466,6 +476,17 @@ TEST(MinimizeSourceToDependencyDirectivesTest, SplitIdentifier) { EXPECT_STREQ("#define GUA RD\n", Out.data()); } +TEST(MinimizeSourceToDependencyDirectivesTest, + WhitespaceAfterLineContinuationSlash) { + SmallVector Out; + + ASSERT_FALSE(minimizeSourceToDependencyDirectives("#define A 1 + \\ \n" + "2 + \\\t\n" + "3\n", + Out)); + EXPECT_STREQ("#define A 1 + 2 + 3\n", Out.data()); +} + TEST(MinimizeSourceToDependencyDirectivesTest, PoundWarningAndError) { SmallVector Out; diff --git a/clang/unittests/Tooling/CMakeLists.txt b/clang/unittests/Tooling/CMakeLists.txt index 3a4094a8b4051..2b35302c7b1fa 100644 --- a/clang/unittests/Tooling/CMakeLists.txt +++ b/clang/unittests/Tooling/CMakeLists.txt @@ -54,6 +54,7 @@ add_clang_unittest(ToolingTests RefactoringTest.cpp ReplacementsYamlTest.cpp RewriterTest.cpp + SourceCodeBuildersTest.cpp SourceCodeTest.cpp StencilTest.cpp ToolingTest.cpp diff --git a/clang/unittests/Tooling/RangeSelectorTest.cpp b/clang/unittests/Tooling/RangeSelectorTest.cpp index 38c15be00cd06..58ce63cbd750f 100644 --- a/clang/unittests/Tooling/RangeSelectorTest.cpp +++ b/clang/unittests/Tooling/RangeSelectorTest.cpp @@ -520,6 +520,35 @@ TEST(RangeSelectorTest, ElementsOpErrors) { Failed(withTypeErrorMessage("stmt"))); } +TEST(RangeSelectorTest, ElseBranchOpSingleStatement) { + StringRef Code = R"cc( + int f() { + int x = 0; + if (true) x = 3; + else x = 4; + return x + 5; + } + )cc"; + StringRef ID = "id"; + TestMatch Match = matchCode(Code, ifStmt().bind(ID)); + EXPECT_THAT_EXPECTED(select(elseBranch(ID), Match), HasValue("else x = 4;")); +} + +TEST(RangeSelectorTest, ElseBranchOpCompoundStatement) { + StringRef Code = R"cc( + int f() { + int x = 0; + if (true) x = 3; + else { x = 4; } + return x + 5; + } + )cc"; + StringRef ID = "id"; + TestMatch Match = matchCode(Code, ifStmt().bind(ID)); + EXPECT_THAT_EXPECTED(select(elseBranch(ID), Match), + HasValue("else { x = 4; }")); +} + // Tests case where the matched node is the complete expanded text. TEST(RangeSelectorTest, ExpansionOp) { StringRef Code = R"cc( @@ -546,4 +575,29 @@ TEST(RangeSelectorTest, ExpansionOpPartial) { HasValue("BADDECL(x * x)")); } +TEST(RangeSelectorTest, IfBoundOpBound) { + StringRef Code = R"cc( + int f() { + return 3 + 5; + } + )cc"; + StringRef ID = "id", Op = "op"; + TestMatch Match = + matchCode(Code, binaryOperator(hasLHS(expr().bind(ID))).bind(Op)); + EXPECT_THAT_EXPECTED(select(ifBound(ID, node(ID), node(Op)), Match), + HasValue("3")); +} + +TEST(RangeSelectorTest, IfBoundOpUnbound) { + StringRef Code = R"cc( + int f() { + return 3 + 5; + } + )cc"; + StringRef ID = "id", Op = "op"; + TestMatch Match = matchCode(Code, binaryOperator().bind(Op)); + EXPECT_THAT_EXPECTED(select(ifBound(ID, node(ID), node(Op)), Match), + HasValue("3 + 5")); +} + } // namespace diff --git a/clang/unittests/Tooling/SourceCodeBuildersTest.cpp b/clang/unittests/Tooling/SourceCodeBuildersTest.cpp new file mode 100644 index 0000000000000..2bf50ffad5850 --- /dev/null +++ b/clang/unittests/Tooling/SourceCodeBuildersTest.cpp @@ -0,0 +1,230 @@ +//===- unittest/Tooling/SourceCodeBuildersTest.cpp ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactoring/SourceCodeBuilders.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/Testing/Support/SupportHelpers.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace clang; +using namespace tooling; +using namespace ast_matchers; + +namespace { +using MatchResult = MatchFinder::MatchResult; +using llvm::ValueIs; + +// Create a valid translation unit from a statement. +static std::string wrapSnippet(StringRef StatementCode) { + return ("struct S { S(); S(int); int field; };\n" + "S operator+(const S &a, const S &b);\n" + "auto test_snippet = []{" + + StatementCode + "};") + .str(); +} + +static DeclarationMatcher wrapMatcher(const StatementMatcher &Matcher) { + return varDecl(hasName("test_snippet"), + hasDescendant(compoundStmt(hasAnySubstatement(Matcher)))); +} + +struct TestMatch { + // The AST unit from which `result` is built. We bundle it because it backs + // the result. Users are not expected to access it. + std::unique_ptr AstUnit; + // The result to use in the test. References `ast_unit`. + MatchResult Result; +}; + +// Matches `Matcher` against the statement `StatementCode` and returns the +// result. Handles putting the statement inside a function and modifying the +// matcher correspondingly. `Matcher` should match one of the statements in +// `StatementCode` exactly -- that is, produce exactly one match. However, +// `StatementCode` may contain other statements not described by `Matcher`. +static llvm::Optional matchStmt(StringRef StatementCode, + StatementMatcher Matcher) { + auto AstUnit = buildASTFromCode(wrapSnippet(StatementCode)); + if (AstUnit == nullptr) { + ADD_FAILURE() << "AST construction failed"; + return llvm::None; + } + ASTContext &Context = AstUnit->getASTContext(); + auto Matches = ast_matchers::match(wrapMatcher(Matcher), Context); + // We expect a single, exact match for the statement. + if (Matches.size() != 1) { + ADD_FAILURE() << "Wrong number of matches: " << Matches.size(); + return llvm::None; + } + return TestMatch{std::move(AstUnit), MatchResult(Matches[0], &Context)}; +} + +static void testPredicate(bool (*Pred)(const Expr &), StringRef Snippet, + bool Expected) { + auto StmtMatch = matchStmt(Snippet, expr().bind("expr")); + ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet; + EXPECT_EQ(Expected, Pred(*StmtMatch->Result.Nodes.getNodeAs("expr"))) + << "Snippet: " << Snippet; +} + +// Tests the predicate on the call argument, assuming `Snippet` is a function +// call. +static void testPredicateOnArg(bool (*Pred)(const Expr &), StringRef Snippet, + bool Expected) { + auto StmtMatch = matchStmt( + Snippet, expr(ignoringImplicit(callExpr(hasArgument( + 0, ignoringElidableConstructorCall(expr().bind("arg"))))))); + ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet; + EXPECT_EQ(Expected, Pred(*StmtMatch->Result.Nodes.getNodeAs("arg"))) + << "Snippet: " << Snippet; +} + +TEST(SourceCodeBuildersTest, needParensAfterUnaryOperator) { + testPredicate(needParensAfterUnaryOperator, "3 + 5;", true); + testPredicate(needParensAfterUnaryOperator, "true ? 3 : 5;", true); + testPredicate(needParensAfterUnaryOperator, "S(3) + S(5);", true); + + testPredicate(needParensAfterUnaryOperator, "int x; x;", false); + testPredicate(needParensAfterUnaryOperator, "int(3.0);", false); + testPredicate(needParensAfterUnaryOperator, "void f(); f();", false); + testPredicate(needParensAfterUnaryOperator, "int a[3]; a[0];", false); + testPredicate(needParensAfterUnaryOperator, "S x; x.field;", false); + testPredicate(needParensAfterUnaryOperator, "int x = 1; --x;", false); + testPredicate(needParensAfterUnaryOperator, "int x = 1; -x;", false); +} + +TEST(SourceCodeBuildersTest, needParensAfterUnaryOperatorInImplicitConversion) { + // The binary operation will be embedded in various implicit + // expressions. Verify they are ignored. + testPredicateOnArg(needParensAfterUnaryOperator, "void f(S); f(3 + 5);", + true); +} + +TEST(SourceCodeBuildersTest, mayEverNeedParens) { + testPredicate(mayEverNeedParens, "3 + 5;", true); + testPredicate(mayEverNeedParens, "true ? 3 : 5;", true); + testPredicate(mayEverNeedParens, "int x = 1; --x;", true); + testPredicate(mayEverNeedParens, "int x = 1; -x;", true); + + testPredicate(mayEverNeedParens, "int x; x;", false); + testPredicate(mayEverNeedParens, "int(3.0);", false); + testPredicate(mayEverNeedParens, "void f(); f();", false); + testPredicate(mayEverNeedParens, "int a[3]; a[0];", false); + testPredicate(mayEverNeedParens, "S x; x.field;", false); +} + +TEST(SourceCodeBuildersTest, mayEverNeedParensInImplictConversion) { + // The binary operation will be embedded in various implicit + // expressions. Verify they are ignored. + testPredicateOnArg(mayEverNeedParens, "void f(S); f(3 + 5);", true); +} + +static void testBuilder( + llvm::Optional (*Builder)(const Expr &, const ASTContext &), + StringRef Snippet, StringRef Expected) { + auto StmtMatch = matchStmt(Snippet, expr().bind("expr")); + ASSERT_TRUE(StmtMatch); + EXPECT_THAT(Builder(*StmtMatch->Result.Nodes.getNodeAs("expr"), + *StmtMatch->Result.Context), + ValueIs(Expected)); +} + +TEST(SourceCodeBuildersTest, BuildParensUnaryOp) { + testBuilder(buildParens, "-4;", "(-4)"); +} + +TEST(SourceCodeBuildersTest, BuildParensBinOp) { + testBuilder(buildParens, "4 + 4;", "(4 + 4)"); +} + +TEST(SourceCodeBuildersTest, BuildParensValue) { + testBuilder(buildParens, "4;", "4"); +} + +TEST(SourceCodeBuildersTest, BuildParensSubscript) { + testBuilder(buildParens, "int a[3]; a[0];", "a[0]"); +} + +TEST(SourceCodeBuildersTest, BuildParensCall) { + testBuilder(buildParens, "int f(int); f(4);", "f(4)"); +} + +TEST(SourceCodeBuildersTest, BuildAddressOfValue) { + testBuilder(buildAddressOf, "S x; x;", "&x"); +} + +TEST(SourceCodeBuildersTest, BuildAddressOfPointerDereference) { + testBuilder(buildAddressOf, "S *x; *x;", "x"); +} + +TEST(SourceCodeBuildersTest, BuildAddressOfPointerDereferenceIgnoresParens) { + testBuilder(buildAddressOf, "S *x; *(x);", "x"); +} + +TEST(SourceCodeBuildersTest, BuildAddressOfBinaryOperation) { + testBuilder(buildAddressOf, "S x; x + x;", "&(x + x)"); +} + +TEST(SourceCodeBuildersTest, BuildDereferencePointer) { + testBuilder(buildDereference, "S *x; x;", "*x"); +} + +TEST(SourceCodeBuildersTest, BuildDereferenceValueAddress) { + testBuilder(buildDereference, "S x; &x;", "x"); +} + +TEST(SourceCodeBuildersTest, BuildDereferenceValueAddressIgnoresParens) { + testBuilder(buildDereference, "S x; &(x);", "x"); +} + +TEST(SourceCodeBuildersTest, BuildDereferenceBinaryOperation) { + testBuilder(buildDereference, "S *x; x + 1;", "*(x + 1)"); +} + +TEST(SourceCodeBuildersTest, BuildDotValue) { + testBuilder(buildDot, "S x; x;", "x."); +} + +TEST(SourceCodeBuildersTest, BuildDotPointerDereference) { + testBuilder(buildDot, "S *x; *x;", "x->"); +} + +TEST(SourceCodeBuildersTest, BuildDotPointerDereferenceIgnoresParens) { + testBuilder(buildDot, "S *x; *(x);", "x->"); +} + +TEST(SourceCodeBuildersTest, BuildDotBinaryOperation) { + testBuilder(buildDot, "S x; x + x;", "(x + x)."); +} + +TEST(SourceCodeBuildersTest, BuildDotPointerDereferenceExprWithParens) { + testBuilder(buildDot, "S *x; *(x + 1);", "(x + 1)->"); +} + +TEST(SourceCodeBuildersTest, BuildArrowPointer) { + testBuilder(buildArrow, "S *x; x;", "x->"); +} + +TEST(SourceCodeBuildersTest, BuildArrowValueAddress) { + testBuilder(buildArrow, "S x; &x;", "x."); +} + +TEST(SourceCodeBuildersTest, BuildArrowValueAddressIgnoresParens) { + testBuilder(buildArrow, "S x; &(x);", "x."); +} + +TEST(SourceCodeBuildersTest, BuildArrowBinaryOperation) { + testBuilder(buildArrow, "S *x; x + 1;", "(x + 1)->"); +} + +TEST(SourceCodeBuildersTest, BuildArrowValueAddressWithParens) { + testBuilder(buildArrow, "S x; &(true ? x : x);", "(true ? x : x)."); +} +} // namespace diff --git a/clang/unittests/Tooling/StencilTest.cpp b/clang/unittests/Tooling/StencilTest.cpp index e5063e15af3e2..2202693f023db 100644 --- a/clang/unittests/Tooling/StencilTest.cpp +++ b/clang/unittests/Tooling/StencilTest.cpp @@ -10,6 +10,8 @@ #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Tooling/FixIt.h" #include "clang/Tooling/Tooling.h" +#include "llvm/Support/Error.h" +#include "llvm/Testing/Support/Error.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -18,34 +20,23 @@ using namespace tooling; using namespace ast_matchers; namespace { +using ::llvm::HasValue; using ::testing::AllOf; using ::testing::Eq; using ::testing::HasSubstr; using MatchResult = MatchFinder::MatchResult; +using stencil::access; using stencil::cat; using stencil::dPrint; +using stencil::ifBound; +using stencil::run; using stencil::text; -// In tests, we can't directly match on llvm::Expected since its accessors -// mutate the object. So, we collapse it to an Optional. -static llvm::Optional toOptional(llvm::Expected V) { - if (V) - return *V; - ADD_FAILURE() << "Losing error in conversion to IsSomething: " - << llvm::toString(V.takeError()); - return llvm::None; -} - -// A very simple matcher for llvm::Optional values. -MATCHER_P(IsSomething, ValueMatcher, "") { - if (!arg) - return false; - return ::testing::ExplainMatchResult(ValueMatcher, *arg, result_listener); -} - // Create a valid translation-unit from a statement. -static std::string wrapSnippet(llvm::Twine StatementCode) { - return ("auto stencil_test_snippet = []{" + StatementCode + "};").str(); +static std::string wrapSnippet(StringRef StatementCode) { + return ("struct S { int field; }; auto stencil_test_snippet = []{" + + StatementCode + "};") + .str(); } static DeclarationMatcher wrapMatcher(const StatementMatcher &Matcher) { @@ -63,9 +54,10 @@ struct TestMatch { // Matches `Matcher` against the statement `StatementCode` and returns the // result. Handles putting the statement inside a function and modifying the -// matcher correspondingly. `Matcher` should match `StatementCode` exactly -- -// that is, produce exactly one match. -static llvm::Optional matchStmt(llvm::Twine StatementCode, +// matcher correspondingly. `Matcher` should match one of the statements in +// `StatementCode` exactly -- that is, produce exactly one match. However, +// `StatementCode` may contain other statements not described by `Matcher`. +static llvm::Optional matchStmt(StringRef StatementCode, StatementMatcher Matcher) { auto AstUnit = buildASTFromCode(wrapSnippet(StatementCode)); if (AstUnit == nullptr) { @@ -123,7 +115,7 @@ class StencilTest : public ::testing::Test { // Tests failures caused by references to unbound nodes. `unbound_id` is the // id that will cause the failure. - void testUnboundNodeError(const Stencil &Stencil, llvm::StringRef UnboundId) { + void testUnboundNodeError(const Stencil &Stencil, StringRef UnboundId) { testError(Stencil, AllOf(HasSubstr(UnboundId), HasSubstr("not bound"))); } }; @@ -143,8 +135,8 @@ TEST_F(StencilTest, SingleStatement) { // Invert the if-then-else. auto Stencil = cat("if (!", node(Condition), ") ", statement(Else), " else ", statement(Then)); - EXPECT_THAT(toOptional(Stencil.eval(StmtMatch->Result)), - IsSomething(Eq("if (!true) return 0; else return 1;"))); + EXPECT_THAT_EXPECTED(Stencil.eval(StmtMatch->Result), + HasValue("if (!true) return 0; else return 1;")); } TEST_F(StencilTest, SingleStatementCallOperator) { @@ -162,8 +154,8 @@ TEST_F(StencilTest, SingleStatementCallOperator) { // Invert the if-then-else. Stencil S = cat("if (!", node(Condition), ") ", statement(Else), " else ", statement(Then)); - EXPECT_THAT(toOptional(S(StmtMatch->Result)), - IsSomething(Eq("if (!true) return 0; else return 1;"))); + EXPECT_THAT_EXPECTED(S(StmtMatch->Result), + HasValue("if (!true) return 0; else return 1;")); } TEST_F(StencilTest, UnboundNode) { @@ -188,8 +180,7 @@ void testExpr(StringRef Id, StringRef Snippet, const Stencil &Stencil, StringRef Expected) { auto StmtMatch = matchStmt(Snippet, expr().bind(Id)); ASSERT_TRUE(StmtMatch); - EXPECT_THAT(toOptional(Stencil.eval(StmtMatch->Result)), - IsSomething(Expected)); + EXPECT_THAT_EXPECTED(Stencil.eval(StmtMatch->Result), HasValue(Expected)); } TEST_F(StencilTest, SelectionOp) { @@ -197,6 +188,111 @@ TEST_F(StencilTest, SelectionOp) { testExpr(Id, "3;", cat(node(Id)), "3"); } +TEST_F(StencilTest, IfBoundOpBound) { + StringRef Id = "id"; + testExpr(Id, "3;", cat(ifBound(Id, text("5"), text("7"))), "5"); +} + +TEST_F(StencilTest, IfBoundOpUnbound) { + StringRef Id = "id"; + testExpr(Id, "3;", cat(ifBound("other", text("5"), text("7"))), "7"); +} + +TEST_F(StencilTest, AccessOpValue) { + StringRef Snippet = R"cc( + S x; + x; + )cc"; + StringRef Id = "id"; + testExpr(Id, Snippet, cat(access(Id, "field")), "x.field"); +} + +TEST_F(StencilTest, AccessOpValueExplicitText) { + StringRef Snippet = R"cc( + S x; + x; + )cc"; + StringRef Id = "id"; + testExpr(Id, Snippet, cat(access(Id, text("field"))), "x.field"); +} + +TEST_F(StencilTest, AccessOpValueAddress) { + StringRef Snippet = R"cc( + S x; + &x; + )cc"; + StringRef Id = "id"; + testExpr(Id, Snippet, cat(access(Id, "field")), "x.field"); +} + +TEST_F(StencilTest, AccessOpPointer) { + StringRef Snippet = R"cc( + S *x; + x; + )cc"; + StringRef Id = "id"; + testExpr(Id, Snippet, cat(access(Id, "field")), "x->field"); +} + +TEST_F(StencilTest, AccessOpPointerDereference) { + StringRef Snippet = R"cc( + S *x; + *x; + )cc"; + StringRef Id = "id"; + testExpr(Id, Snippet, cat(access(Id, "field")), "x->field"); +} + +TEST_F(StencilTest, AccessOpExplicitThis) { + using clang::ast_matchers::hasObjectExpression; + using clang::ast_matchers::memberExpr; + + // Set up the code so we can bind to a use of this. + StringRef Snippet = R"cc( + class C { + public: + int x; + int foo() { return this->x; } + }; + )cc"; + auto StmtMatch = + matchStmt(Snippet, returnStmt(hasReturnValue(ignoringImplicit(memberExpr( + hasObjectExpression(expr().bind("obj"))))))); + ASSERT_TRUE(StmtMatch); + const Stencil Stencil = cat(access("obj", "field")); + EXPECT_THAT_EXPECTED(Stencil.eval(StmtMatch->Result), + HasValue("this->field")); +} + +TEST_F(StencilTest, AccessOpImplicitThis) { + using clang::ast_matchers::hasObjectExpression; + using clang::ast_matchers::memberExpr; + + // Set up the code so we can bind to a use of (implicit) this. + StringRef Snippet = R"cc( + class C { + public: + int x; + int foo() { return x; } + }; + )cc"; + auto StmtMatch = + matchStmt(Snippet, returnStmt(hasReturnValue(ignoringImplicit(memberExpr( + hasObjectExpression(expr().bind("obj"))))))); + ASSERT_TRUE(StmtMatch); + const Stencil Stencil = cat(access("obj", "field")); + EXPECT_THAT_EXPECTED(Stencil.eval(StmtMatch->Result), HasValue("field")); +} + +TEST_F(StencilTest, RunOp) { + StringRef Id = "id"; + auto SimpleFn = [Id](const MatchResult &R) { + return std::string(R.Nodes.getNodeAs(Id) != nullptr ? "Bound" + : "Unbound"); + }; + testExpr(Id, "3;", cat(run(SimpleFn)), "Bound"); +} + TEST(StencilEqualityTest, Equality) { auto Lhs = cat("foo", dPrint("dprint_id")); auto Rhs = cat("foo", dPrint("dprint_id")); @@ -221,4 +317,12 @@ TEST(StencilEqualityTest, InEqualitySelection) { auto S2 = cat(node("node")); EXPECT_NE(S1, S2); } + +// `run` is opaque. +TEST(StencilEqualityTest, InEqualityRun) { + auto F = [](const MatchResult &R) { return "foo"; }; + auto S1 = cat(run(F)); + auto S2 = cat(run(F)); + EXPECT_NE(S1, S2); +} } // namespace diff --git a/clang/unittests/Tooling/TransformerTest.cpp b/clang/unittests/Tooling/TransformerTest.cpp index 554e586c3c484..5d55182f8273b 100644 --- a/clang/unittests/Tooling/TransformerTest.cpp +++ b/clang/unittests/Tooling/TransformerTest.cpp @@ -710,6 +710,57 @@ TEST_F(TransformerTest, IdentityMacro) { testRule(ruleStrlenSize(), Input, Expected); } +// Tests that two changes in a single macro expansion do not lead to conflicts +// in applying the changes. +TEST_F(TransformerTest, TwoChangesInOneMacroExpansion) { + std::string Input = R"cc( +#define PLUS(a,b) (a) + (b) + int f() { return PLUS(3, 4); } + )cc"; + std::string Expected = R"cc( +#define PLUS(a,b) (a) + (b) + int f() { return PLUS(LIT, LIT); } + )cc"; + + testRule(makeRule(integerLiteral(), change(text("LIT"))), Input, Expected); +} + +// Tests case where the rule's match spans both source from the macro and its +// arg, with the begin location (the "anchor") being the arg. +TEST_F(TransformerTest, MatchSpansMacroTextButChangeDoesNot) { + std::string Input = R"cc( +#define PLUS_ONE(a) a + 1 + int f() { return PLUS_ONE(3); } + )cc"; + std::string Expected = R"cc( +#define PLUS_ONE(a) a + 1 + int f() { return PLUS_ONE(LIT); } + )cc"; + + StringRef E = "expr"; + testRule(makeRule(binaryOperator(hasLHS(expr().bind(E))), + change(node(E), text("LIT"))), + Input, Expected); +} + +// Tests case where the rule's match spans both source from the macro and its +// arg, with the begin location (the "anchor") being inside the macro. +TEST_F(TransformerTest, MatchSpansMacroTextButChangeDoesNotAnchoredInMacro) { + std::string Input = R"cc( +#define PLUS_ONE(a) 1 + a + int f() { return PLUS_ONE(3); } + )cc"; + std::string Expected = R"cc( +#define PLUS_ONE(a) 1 + a + int f() { return PLUS_ONE(LIT); } + )cc"; + + StringRef E = "expr"; + testRule(makeRule(binaryOperator(hasRHS(expr().bind(E))), + change(node(E), text("LIT"))), + Input, Expected); +} + // No rewrite is applied when the changed text does not encompass the entirety // of the expanded text. That is, the edit would have to be applied to the // macro's definition to succeed and editing the expansion point would not diff --git a/clang/utils/TableGen/CMakeLists.txt b/clang/utils/TableGen/CMakeLists.txt index d33ede2a75a29..96b97a7483833 100644 --- a/clang/utils/TableGen/CMakeLists.txt +++ b/clang/utils/TableGen/CMakeLists.txt @@ -12,6 +12,7 @@ add_tablegen(clang-tblgen CLANG ClangOpenCLBuiltinEmitter.cpp ClangOptionDocEmitter.cpp ClangSACheckersEmitter.cpp + ClangTypeNodesEmitter.cpp NeonEmitter.cpp TableGen.cpp ) diff --git a/clang/utils/TableGen/ClangASTNodesEmitter.cpp b/clang/utils/TableGen/ClangASTNodesEmitter.cpp index a0bbdbab33542..3ece9be6a5c77 100644 --- a/clang/utils/TableGen/ClangASTNodesEmitter.cpp +++ b/clang/utils/TableGen/ClangASTNodesEmitter.cpp @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +#include "TableGenBackends.h" + #include "llvm/TableGen/Record.h" #include "llvm/TableGen/TableGenBackend.h" #include @@ -173,15 +175,14 @@ void ClangASTNodesEmitter::run(raw_ostream &OS) { OS << "#undef ABSTRACT_" << macroName(Root.getName()) << "\n"; } -namespace clang { -void EmitClangASTNodes(RecordKeeper &RK, raw_ostream &OS, - const std::string &N, const std::string &S) { +void clang::EmitClangASTNodes(RecordKeeper &RK, raw_ostream &OS, + const std::string &N, const std::string &S) { ClangASTNodesEmitter(RK, N, S).run(OS); } // Emits and addendum to a .inc file to enumerate the clang declaration // contexts. -void EmitClangDeclContext(RecordKeeper &Records, raw_ostream &OS) { +void clang::EmitClangDeclContext(RecordKeeper &Records, raw_ostream &OS) { // FIXME: Find a .td file format to allow for this to be represented better. emitSourceFileHeader("List of AST Decl nodes", OS); @@ -225,4 +226,3 @@ void EmitClangDeclContext(RecordKeeper &Records, raw_ostream &OS) { OS << "#undef DECL_CONTEXT\n"; OS << "#undef DECL_CONTEXT_BASE\n"; } -} // end namespace clang diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index 1020492bc2a89..0d92a321c7475 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +#include "TableGenBackends.h" + #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" @@ -1596,6 +1598,13 @@ CreateSemanticSpellings(const std::vector &Spellings, std::string Ret(" enum Spelling {\n"); std::set Uniques; unsigned Idx = 0; + + // If we have a need to have this many spellings we likely need to add an + // extra bit to the SpellingIndex in AttributeCommonInfo, then increase the + // value of SpellingNotCalculated there and here. + assert(Spellings.size() < 15 && + "Too many spellings, would step on SpellingNotCalculated in " + "AttributeCommonInfo"); for (auto I = Spellings.begin(), E = Spellings.end(); I != E; ++I, ++Idx) { const FlattenedSpelling &S = *I; const std::string &Variety = S.variety(); @@ -1629,6 +1638,7 @@ CreateSemanticSpellings(const std::vector &Spellings, // enumerator. Ret += " " + EnumName + " = " + llvm::utostr(Idx); } + Ret += ",\n SpellingNotCalculated = 15\n"; Ret += "\n };\n\n"; return Ret; } @@ -2211,10 +2221,8 @@ static void emitClangAttrThisIsaIdentifierArgList(RecordKeeper &Records, OS << "#endif // CLANG_ATTR_THIS_ISA_IDENTIFIER_ARG_LIST\n\n"; } -namespace clang { - // Emits the class definitions for attributes. -void EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { +void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { emitSourceFileHeader("Attribute classes' definitions", OS); OS << "#ifndef LLVM_CLANG_ATTR_CLASSES_INC\n"; @@ -2341,7 +2349,7 @@ void EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { OS << ", SourceRange Range, AttributeCommonInfo::Syntax Syntax"; if (!ElideSpelling) OS << ", " << R.getName() - << "Attr::Spelling Spelling = " + << "Attr::Spelling S = " "static_cast(SpellingNotCalculated)"; OS << ") {\n"; OS << " AttributeCommonInfo I(Range, "; @@ -2353,7 +2361,7 @@ void EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { OS << ", Syntax"; if (!ElideSpelling) - OS << ", Spelling"; + OS << ", S"; OS << ");\n"; OS << " return Create"; if (Implicit) @@ -2483,7 +2491,7 @@ void EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { } // Emits the class method definitions for attributes. -void EmitClangAttrImpl(RecordKeeper &Records, raw_ostream &OS) { +void clang::EmitClangAttrImpl(RecordKeeper &Records, raw_ostream &OS) { emitSourceFileHeader("Attribute classes' member function definitions", OS); std::vector Attrs = Records.getAllDerivedDefinitions("Attr"); @@ -2548,8 +2556,6 @@ void EmitClangAttrImpl(RecordKeeper &Records, raw_ostream &OS) { EmitFunc("printPretty(OS, Policy)"); } -} // end namespace clang - static void emitAttrList(raw_ostream &OS, StringRef Class, const std::vector &AttrList) { for (auto Cur : AttrList) { diff --git a/clang/utils/TableGen/ClangCommentCommandInfoEmitter.cpp b/clang/utils/TableGen/ClangCommentCommandInfoEmitter.cpp index c0dd70281a4d9..fc79d59713d69 100644 --- a/clang/utils/TableGen/ClangCommentCommandInfoEmitter.cpp +++ b/clang/utils/TableGen/ClangCommentCommandInfoEmitter.cpp @@ -11,6 +11,8 @@ // //===----------------------------------------------------------------------===// +#include "TableGenBackends.h" + #include "llvm/TableGen/Record.h" #include "llvm/TableGen/StringMatcher.h" #include "llvm/TableGen/TableGenBackend.h" @@ -18,8 +20,7 @@ using namespace llvm; -namespace clang { -void EmitClangCommentCommandInfo(RecordKeeper &Records, raw_ostream &OS) { +void clang::EmitClangCommentCommandInfo(RecordKeeper &Records, raw_ostream &OS) { emitSourceFileHeader("A list of commands useable in documentation " "comments", OS); @@ -105,7 +106,7 @@ static std::string MangleName(StringRef Str) { return Mangled; } -void EmitClangCommentCommandList(RecordKeeper &Records, raw_ostream &OS) { +void clang::EmitClangCommentCommandList(RecordKeeper &Records, raw_ostream &OS) { emitSourceFileHeader("A list of commands useable in documentation " "comments", OS); @@ -121,4 +122,3 @@ void EmitClangCommentCommandList(RecordKeeper &Records, raw_ostream &OS) { OS << "COMMENT_COMMAND(" << MangledName << ")\n"; } } -} // end namespace clang diff --git a/clang/utils/TableGen/ClangCommentHTMLNamedCharacterReferenceEmitter.cpp b/clang/utils/TableGen/ClangCommentHTMLNamedCharacterReferenceEmitter.cpp index 81af5b4b95f19..ed3f4bd6ef6c1 100644 --- a/clang/utils/TableGen/ClangCommentHTMLNamedCharacterReferenceEmitter.cpp +++ b/clang/utils/TableGen/ClangCommentHTMLNamedCharacterReferenceEmitter.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "TableGenBackends.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/TableGen/Error.h" @@ -45,9 +46,8 @@ static bool translateCodePointToUTF8(unsigned CodePoint, return true; } -namespace clang { -void EmitClangCommentHTMLNamedCharacterReferences(RecordKeeper &Records, - raw_ostream &OS) { +void clang::EmitClangCommentHTMLNamedCharacterReferences(RecordKeeper &Records, + raw_ostream &OS) { std::vector Tags = Records.getAllDerivedDefinitions("NCR"); std::vector NameToUTF8; SmallString<32> CLiteral; @@ -79,6 +79,3 @@ void EmitClangCommentHTMLNamedCharacterReferences(RecordKeeper &Records, OS << " return StringRef();\n" << "}\n\n"; } - -} // end namespace clang - diff --git a/clang/utils/TableGen/ClangDataCollectorsEmitter.cpp b/clang/utils/TableGen/ClangDataCollectorsEmitter.cpp index 4079efc808231..45082935c1f79 100644 --- a/clang/utils/TableGen/ClangDataCollectorsEmitter.cpp +++ b/clang/utils/TableGen/ClangDataCollectorsEmitter.cpp @@ -1,10 +1,10 @@ +#include "TableGenBackends.h" #include "llvm/TableGen/Record.h" #include "llvm/TableGen/TableGenBackend.h" using namespace llvm; -namespace clang { -void EmitClangDataCollectors(RecordKeeper &RK, raw_ostream &OS) { +void clang::EmitClangDataCollectors(RecordKeeper &RK, raw_ostream &OS) { const auto &Defs = RK.getClasses(); for (const auto &Entry : Defs) { Record &R = *Entry.second; @@ -15,4 +15,3 @@ void EmitClangDataCollectors(RecordKeeper &RK, raw_ostream &OS) { } OS << "#undef DEF_ADD_DATA\n"; } -} // end namespace clang diff --git a/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp b/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp index 13e564e130ce4..778375010041d 100644 --- a/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp +++ b/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "TableGenBackends.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/PointerUnion.h" @@ -1187,9 +1188,8 @@ static bool isRemark(const Record &Diag) { /// ClangDiagsDefsEmitter - The top-level class emits .def files containing /// declarations of Clang diagnostics. -namespace clang { -void EmitClangDiagsDefs(RecordKeeper &Records, raw_ostream &OS, - const std::string &Component) { +void clang::EmitClangDiagsDefs(RecordKeeper &Records, raw_ostream &OS, + const std::string &Component) { // Write the #if guard if (!Component.empty()) { std::string ComponentName = StringRef(Component).upper(); @@ -1288,7 +1288,6 @@ void EmitClangDiagsDefs(RecordKeeper &Records, raw_ostream &OS, OS << ")\n"; } } -} // end namespace clang //===----------------------------------------------------------------------===// // Warning Group Tables generation @@ -1528,8 +1527,7 @@ static void emitCategoryTable(RecordKeeper &Records, raw_ostream &OS) { OS << "#endif // GET_CATEGORY_TABLE\n\n"; } -namespace clang { -void EmitClangDiagGroups(RecordKeeper &Records, raw_ostream &OS) { +void clang::EmitClangDiagGroups(RecordKeeper &Records, raw_ostream &OS) { // Compute a mapping from a DiagGroup to all of its parents. DiagGroupParentMap DGParentMap(Records); @@ -1565,7 +1563,6 @@ void EmitClangDiagGroups(RecordKeeper &Records, raw_ostream &OS) { OS); emitCategoryTable(Records, OS); } -} // end namespace clang //===----------------------------------------------------------------------===// // Diagnostic name index generation @@ -1582,8 +1579,7 @@ struct RecordIndexElement }; } // end anonymous namespace. -namespace clang { -void EmitClangDiagsIndexName(RecordKeeper &Records, raw_ostream &OS) { +void clang::EmitClangDiagsIndexName(RecordKeeper &Records, raw_ostream &OS) { const std::vector &Diags = Records.getAllDerivedDefinitions("Diagnostic"); @@ -1673,7 +1669,7 @@ void writeDiagnosticText(DiagnosticTextBuilder &Builder, const Record *R, } // namespace } // namespace docs -void EmitClangDiagDocs(RecordKeeper &Records, raw_ostream &OS) { +void clang::EmitClangDiagDocs(RecordKeeper &Records, raw_ostream &OS) { using namespace docs; // Get the documentation introduction paragraph. @@ -1792,5 +1788,3 @@ void EmitClangDiagDocs(RecordKeeper &Records, raw_ostream &OS) { OS << "\n"; } } - -} // end namespace clang diff --git a/clang/utils/TableGen/ClangOpcodesEmitter.cpp b/clang/utils/TableGen/ClangOpcodesEmitter.cpp index 1ff20f0f44943..e5bfac5ba1b6f 100644 --- a/clang/utils/TableGen/ClangOpcodesEmitter.cpp +++ b/clang/utils/TableGen/ClangOpcodesEmitter.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "TableGenBackends.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" #include "llvm/TableGen/StringMatcher.h" @@ -351,10 +352,6 @@ void ClangOpcodesEmitter::PrintTypes(raw_ostream &OS, ArrayRef Types) OS << ">"; } -namespace clang { - -void EmitClangOpcodes(RecordKeeper &Records, raw_ostream &OS) { +void clang::EmitClangOpcodes(RecordKeeper &Records, raw_ostream &OS) { ClangOpcodesEmitter(Records).run(OS); } - -} // end namespace clang diff --git a/clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp b/clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp index b93a68fb30946..c8975d7bf615d 100644 --- a/clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp +++ b/clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp @@ -50,6 +50,7 @@ // //===----------------------------------------------------------------------===// +#include "TableGenBackends.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" @@ -121,11 +122,13 @@ class BuiltinNameEmitter { // Emit the BuiltinTable table. This table contains all the overloads of // each function, and is a struct OpenCLBuiltinDecl. // E.g.: - // // convert_float2_rtn - // { 58, 2 }, + // // 891 convert_float2_rtn + // { 58, 2, 100, 0 }, // This means that the signature of this convert_float2_rtn overload has // 1 argument (+1 for the return type), stored at index 58 in - // the SignatureTable. + // the SignatureTable. The last two values represent the minimum (1.0) and + // maximum (0, meaning no max version) OpenCL version in which this overload + // is supported. void EmitBuiltinTable(); // Emit a StringMatcher function to check whether a function name is an @@ -268,6 +271,10 @@ struct OpenCLBuiltinStruct { // the SignatureTable represent the complete signature. The first type at // index SigTableIndex is the return type. const unsigned NumTypes; + // First OpenCL version in which this overload was introduced (e.g. CL20). + const unsigned short MinVersion; + // First OpenCL version in which this overload was removed (e.g. CL20). + const unsigned short MaxVersion; }; )"; @@ -400,11 +407,13 @@ void BuiltinNameEmitter::EmitBuiltinTable() { OS << " // " << (Index + 1) << ": " << FOM.first << "\n"; for (const auto &Overload : FOM.second) { - OS << " { " - << Overload.second << ", " - << Overload.first->getValueAsListOfDefs("Signature").size() + OS << " { " << Overload.second << ", " + << Overload.first->getValueAsListOfDefs("Signature").size() << ", " + << Overload.first->getValueAsDef("MinVersion")->getValueAsInt("ID") + << ", " + << Overload.first->getValueAsDef("MaxVersion")->getValueAsInt("ID") << " },\n"; - Index++; + Index++; } } OS << "};\n\n"; @@ -631,11 +640,7 @@ static void OCL2Qual(ASTContext &Context, const OpenCLTypeStruct &Ty, OS << "\n} // OCL2Qual\n"; } -namespace clang { - -void EmitClangOpenCLBuiltins(RecordKeeper &Records, raw_ostream &OS) { +void clang::EmitClangOpenCLBuiltins(RecordKeeper &Records, raw_ostream &OS) { BuiltinNameEmitter NameChecker(Records, OS); NameChecker.Emit(); } - -} // end namespace clang diff --git a/clang/utils/TableGen/ClangOptionDocEmitter.cpp b/clang/utils/TableGen/ClangOptionDocEmitter.cpp index 7027113c4fa81..b944ad9608f5e 100644 --- a/clang/utils/TableGen/ClangOptionDocEmitter.cpp +++ b/clang/utils/TableGen/ClangOptionDocEmitter.cpp @@ -8,6 +8,7 @@ // //===----------------------------------------------------------------------===// +#include "TableGenBackends.h" #include "llvm/TableGen/Error.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" @@ -21,8 +22,6 @@ using namespace llvm; -namespace clang { -namespace docs { namespace { struct DocumentedOption { Record *Option; @@ -380,11 +379,8 @@ void emitDocumentation(int Depth, const Documentation &Doc, } } // namespace -} // namespace docs - -void EmitClangOptDocs(RecordKeeper &Records, raw_ostream &OS) { - using namespace docs; +void clang::EmitClangOptDocs(RecordKeeper &Records, raw_ostream &OS) { const Record *DocInfo = Records.getDef("GlobalDocumentation"); if (!DocInfo) { PrintFatalError("The GlobalDocumentation top-level definition is missing, " @@ -396,4 +392,3 @@ void EmitClangOptDocs(RecordKeeper &Records, raw_ostream &OS) { emitDocumentation(0, extractDocumentation(Records), DocInfo, OS); } -} // end namespace clang diff --git a/clang/utils/TableGen/ClangSACheckersEmitter.cpp b/clang/utils/TableGen/ClangSACheckersEmitter.cpp index 7dd0895b76d45..feefbeb411387 100644 --- a/clang/utils/TableGen/ClangSACheckersEmitter.cpp +++ b/clang/utils/TableGen/ClangSACheckersEmitter.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "TableGenBackends.h" #include "llvm/ADT/StringMap.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" @@ -174,8 +175,7 @@ static void printOption(llvm::raw_ostream &OS, StringRef FullName, OS << "true"; } -namespace clang { -void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) { +void clang::EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) { std::vector checkers = Records.getAllDerivedDefinitions("Checker"); std::vector packages = Records.getAllDerivedDefinitions("Package"); @@ -315,4 +315,3 @@ void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) { OS << "#endif // GET_CHECKER_OPTIONS\n" "\n"; } -} // end namespace clang diff --git a/clang/utils/TableGen/ClangTypeNodesEmitter.cpp b/clang/utils/TableGen/ClangTypeNodesEmitter.cpp new file mode 100644 index 0000000000000..c9986c8fa496b --- /dev/null +++ b/clang/utils/TableGen/ClangTypeNodesEmitter.cpp @@ -0,0 +1,220 @@ +//=== ClangTypeNodesEmitter.cpp - Generate type node tables -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This tblgen backend emits the node table (the .def file) for Clang +// type nodes. +// +// This file defines the AST type info database. Each type node is +// enumerated by providing its name (e.g., "Builtin" or "Enum") and +// base class (e.g., "Type" or "TagType"). Depending on where in the +// abstract syntax tree the type will show up, the enumeration uses +// one of five different macros: +// +// TYPE(Class, Base) - A type that can show up anywhere in the AST, +// and might be dependent, canonical, or non-canonical. All clients +// will need to understand these types. +// +// ABSTRACT_TYPE(Class, Base) - An abstract class that shows up in +// the type hierarchy but has no concrete instances. +// +// NON_CANONICAL_TYPE(Class, Base) - A type that can show up +// anywhere in the AST but will never be a part of a canonical +// type. Clients that only need to deal with canonical types +// (ignoring, e.g., typedefs and other type aliases used for +// pretty-printing) can ignore these types. +// +// DEPENDENT_TYPE(Class, Base) - A type that will only show up +// within a C++ template that has not been instantiated, e.g., a +// type that is always dependent. Clients that do not need to deal +// with uninstantiated C++ templates can ignore these types. +// +// NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) - A type that +// is non-canonical unless it is dependent. Defaults to TYPE because +// it is neither reliably dependent nor reliably non-canonical. +// +// There is a sixth macro, independent of the others. Most clients +// will not need to use it. +// +// LEAF_TYPE(Class) - A type that never has inner types. Clients +// which can operate on such types more efficiently may wish to do so. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringRef.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/TableGenBackend.h" +#include +#include +#include +#include "TableGenBackends.h" + +using namespace llvm; + +// These are spellings in the generated output. +#define TypeMacroName "TYPE" +#define AbstractTypeMacroName "ABSTRACT_TYPE" +#define DependentTypeMacroName "DEPENDENT_TYPE" +#define NonCanonicalTypeMacroName "NON_CANONICAL_TYPE" +#define NonCanonicalUnlessDependentTypeMacroName "NON_CANONICAL_UNLESS_DEPENDENT_TYPE" +#define TypeMacroArgs "(Class, Base)" +#define LastTypeMacroName "LAST_TYPE" +#define LeafTypeMacroName "LEAF_TYPE" + +// These are spellings in the tblgen file. +// (Type is also used for the spelling of the AST class.) +#define TypeClassName "Type" +#define DerivedTypeClassName "DerivedType" +#define AlwaysDependentClassName "AlwaysDependent" +#define NeverCanonicalClassName "NeverCanonical" +#define NeverCanonicalUnlessDependentClassName "NeverCanonicalUnlessDependent" +#define LeafTypeClassName "LeafType" +#define AbstractFieldName "Abstract" +#define BaseFieldName "Base" + +static StringRef getIdForType(Record *type) { + // The record name is expected to be the full C++ class name, + // including "Type". Check for that and strip it off. + auto fullName = type->getName(); + if (!fullName.endswith("Type")) + PrintFatalError(type->getLoc(), "name of Type node doesn't end in Type"); + return fullName.drop_back(4); +} + +namespace { +class TypeNodeEmitter { + RecordKeeper &Records; + raw_ostream &Out; + const std::vector Types; + std::vector MacrosToUndef; + +public: + TypeNodeEmitter(RecordKeeper &records, raw_ostream &out) + : Records(records), Out(out), + Types(Records.getAllDerivedDefinitions("Type")) { + } + + void emit(); + +private: + void emitFallbackDefine(StringRef macroName, StringRef fallbackMacroName, + StringRef args); + + void emitNodeInvocations(); + void emitLastNodeInvocation(); + void emitLeafNodeInvocations(); + + void addMacroToUndef(StringRef macroName); + void emitUndefs(); +}; +} + +void TypeNodeEmitter::emit() { + if (Types.empty()) + PrintFatalError("no Type records in input!"); + + emitSourceFileHeader("An x-macro database of Clang type nodes", Out); + + // Preamble + addMacroToUndef(TypeMacroName); + addMacroToUndef(AbstractTypeMacroName); + emitFallbackDefine(AbstractTypeMacroName, TypeMacroName, TypeMacroArgs); + emitFallbackDefine(NonCanonicalTypeMacroName, TypeMacroName, TypeMacroArgs); + emitFallbackDefine(DependentTypeMacroName, TypeMacroName, TypeMacroArgs); + emitFallbackDefine(NonCanonicalUnlessDependentTypeMacroName, TypeMacroName, + TypeMacroArgs); + + // Invocations. + emitNodeInvocations(); + emitLastNodeInvocation(); + emitLeafNodeInvocations(); + + // Postmatter + emitUndefs(); +} + +void TypeNodeEmitter::emitFallbackDefine(StringRef macroName, + StringRef fallbackMacroName, + StringRef args) { + Out << "#ifndef " << macroName << "\n"; + Out << "# define " << macroName << args + << " " << fallbackMacroName << args << "\n"; + Out << "#endif\n"; + + addMacroToUndef(macroName); +} + +void TypeNodeEmitter::emitNodeInvocations() { + for (auto type : Types) { + // The name with the Type suffix. + StringRef id = getIdForType(type); + + // Figure out which macro to use. + StringRef macroName; + auto setMacroName = [&](StringRef newName) { + if (!macroName.empty()) + PrintFatalError(type->getLoc(), + Twine("conflict when computing macro name for " + "Type node: trying to use both \"") + + macroName + "\" and \"" + newName + "\""); + macroName = newName; + }; + if (type->isSubClassOf(AlwaysDependentClassName)) + setMacroName(DependentTypeMacroName); + if (type->isSubClassOf(NeverCanonicalClassName)) + setMacroName(NonCanonicalTypeMacroName); + if (type->isSubClassOf(NeverCanonicalUnlessDependentClassName)) + setMacroName(NonCanonicalUnlessDependentTypeMacroName); + if (type->getValueAsBit(AbstractFieldName)) + setMacroName(AbstractTypeMacroName); + if (macroName.empty()) + macroName = TypeMacroName; + + // Compute the base class. + StringRef baseName = TypeClassName; + if (type->isSubClassOf(DerivedTypeClassName)) + baseName = type->getValueAsDef(BaseFieldName)->getName(); + + // Generate the invocation line. + Out << macroName << "(" << id << ", " << baseName << ")\n"; + } +} + +void TypeNodeEmitter::emitLastNodeInvocation() { + // We check that this is non-empty earlier. + Out << "#ifdef " LastTypeMacroName "\n" + LastTypeMacroName "(" << getIdForType(Types.back()) << ")\n" + "#undef " LastTypeMacroName "\n" + "#endif\n"; +} + +void TypeNodeEmitter::emitLeafNodeInvocations() { + Out << "#ifdef " LeafTypeMacroName "\n"; + + for (auto type : Types) { + if (!type->isSubClassOf(LeafTypeClassName)) continue; + Out << LeafTypeMacroName "(" << getIdForType(type) << ")\n"; + } + + Out << "#undef " LeafTypeMacroName "\n" + "#endif\n"; +} + +void TypeNodeEmitter::addMacroToUndef(StringRef macroName) { + MacrosToUndef.push_back(macroName); +} + +void TypeNodeEmitter::emitUndefs() { + for (auto ¯oName : MacrosToUndef) { + Out << "#undef " << macroName << "\n"; + } +} + +void clang::EmitClangTypeNodes(RecordKeeper &records, raw_ostream &out) { + TypeNodeEmitter(records, out).emit(); +} diff --git a/clang/utils/TableGen/NeonEmitter.cpp b/clang/utils/TableGen/NeonEmitter.cpp index d5a9744b7d40d..f420fa6a8c893 100644 --- a/clang/utils/TableGen/NeonEmitter.cpp +++ b/clang/utils/TableGen/NeonEmitter.cpp @@ -23,6 +23,7 @@ // //===----------------------------------------------------------------------===// +#include "TableGenBackends.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/None.h" @@ -2629,22 +2630,18 @@ void NeonEmitter::runFP16(raw_ostream &OS) { OS << "#endif /* __ARM_FP16_H */\n"; } -namespace clang { - -void EmitNeon(RecordKeeper &Records, raw_ostream &OS) { +void clang::EmitNeon(RecordKeeper &Records, raw_ostream &OS) { NeonEmitter(Records).run(OS); } -void EmitFP16(RecordKeeper &Records, raw_ostream &OS) { +void clang::EmitFP16(RecordKeeper &Records, raw_ostream &OS) { NeonEmitter(Records).runFP16(OS); } -void EmitNeonSema(RecordKeeper &Records, raw_ostream &OS) { +void clang::EmitNeonSema(RecordKeeper &Records, raw_ostream &OS) { NeonEmitter(Records).runHeader(OS); } -void EmitNeonTest(RecordKeeper &Records, raw_ostream &OS) { +void clang::EmitNeonTest(RecordKeeper &Records, raw_ostream &OS) { llvm_unreachable("Neon test generation no longer implemented!"); } - -} // end namespace clang diff --git a/clang/utils/TableGen/TableGen.cpp b/clang/utils/TableGen/TableGen.cpp index a92370019338a..d0f8a7564496b 100644 --- a/clang/utils/TableGen/TableGen.cpp +++ b/clang/utils/TableGen/TableGen.cpp @@ -47,6 +47,7 @@ enum ActionType { GenClangCommentNodes, GenClangDeclNodes, GenClangStmtNodes, + GenClangTypeNodes, GenClangOpcodes, GenClangSACheckers, GenClangCommentHTMLTags, @@ -130,6 +131,8 @@ cl::opt Action( "Generate Clang AST declaration nodes"), clEnumValN(GenClangStmtNodes, "gen-clang-stmt-nodes", "Generate Clang AST statement nodes"), + clEnumValN(GenClangTypeNodes, "gen-clang-type-nodes", + "Generate Clang AST type nodes"), clEnumValN(GenClangOpcodes, "gen-clang-opcodes", "Generate Clang constexpr interpreter opcodes"), clEnumValN(GenClangSACheckers, "gen-clang-sa-checkers", @@ -254,6 +257,9 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) { case GenClangStmtNodes: EmitClangASTNodes(Records, OS, "Stmt", ""); break; + case GenClangTypeNodes: + EmitClangTypeNodes(Records, OS); + break; case GenClangOpcodes: EmitClangOpcodes(Records, OS); break; diff --git a/clang/utils/TableGen/TableGenBackends.h b/clang/utils/TableGen/TableGenBackends.h index f9ee477efde09..cdd492b4e558d 100644 --- a/clang/utils/TableGen/TableGenBackends.h +++ b/clang/utils/TableGen/TableGenBackends.h @@ -27,6 +27,7 @@ namespace clang { void EmitClangDeclContext(llvm::RecordKeeper &RK, llvm::raw_ostream &OS); void EmitClangASTNodes(llvm::RecordKeeper &RK, llvm::raw_ostream &OS, const std::string &N, const std::string &S); +void EmitClangTypeNodes(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitClangAttrParserStringSwitches(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index 487d6fb2d93e5..40af4732ef600 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -9955,19 +9955,19 @@

C++ defect report implementation status

1690 C++14 Associated namespace for local type - Clang 9 + Clang 9 1691 C++14 Argument-dependent lookup and opaque enumerations - Clang 9 + Clang 9 1692 C++14 Associated namespaces of doubly-nested classes - Clang 9 + Clang 9 1693 @@ -10147,7 +10147,7 @@

C++ defect report implementation status

1722 CD4 Should lambda to function pointer conversion function be noexcept? - Clang 9 + Clang 9 1723 @@ -10483,7 +10483,7 @@

C++ defect report implementation status

1778 C++14 exception-specification in explicitly-defaulted functions - Clang 9 + Clang 9 1779 @@ -11047,7 +11047,7 @@

C++ defect report implementation status

1872 CD4 Instantiations of constexpr templates that cannot appear in constant expressions - Clang 9 + Clang 9 1873 @@ -12655,7 +12655,7 @@

C++ defect report implementation status

2140 CD4 Lvalue-to-rvalue conversion of std::nullptr_t - Clang 9 + Clang 9 2141 @@ -12835,7 +12835,7 @@

C++ defect report implementation status

2170 DR Unclear definition of odr-use for arrays - Clang 9 + Clang 9 2171 @@ -13567,7 +13567,7 @@

C++ defect report implementation status

2292 DRWP simple-template-id is ambiguous between class-name and type-name - Clang 9 + Clang 9 2293 @@ -13933,7 +13933,7 @@

C++ defect report implementation status

2353 DR Potential results of a member access expression for a static data member - Clang 9 + Clang 9 2354 @@ -14131,13 +14131,13 @@

C++ defect report implementation status

2386 DR tuple_size requirements for structured binding - Clang 9 + Clang 9 2387 DR Linkage of const-qualified variable template - Clang 9 + Clang 9 2388 diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 151192b2ff28c..041d6aa242c7b 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -114,7 +114,7 @@

C++11 implementation status

P1009R2 (DR) - Clang 9 + Clang 9 Static assertions @@ -289,7 +289,7 @@

C++11 implementation status

P1286R2 (DR) - Clang 9 + Clang 9 Deleted functions @@ -664,7 +664,7 @@

C++17 implementation status

P1771R1 (DR) - Clang 9 + Clang 9 [[maybe_unused]] attribute @@ -866,7 +866,7 @@

C++2a implementation status

P1042R1 - Clang 9 + Clang 9 Designated initializers @@ -876,7 +876,7 @@

C++2a implementation status

template-parameter-list for generic lambdas P0428R2 - Clang 9 + Clang 9 Concepts @@ -910,7 +910,7 @@

C++2a implementation status

ADL and function templates that are not visible P0846R0 - Clang 9 + Clang 9 const mismatch with defaulted copy constructor @@ -957,7 +957,7 @@

C++2a implementation status

[[no_unique_address]] attribute P0840R2 - Clang 9 + Clang 9 [[likely]] and [[unlikely]] attributes @@ -972,7 +972,7 @@

C++2a implementation status

Pack expansion in lambda init-capture P0780R2 - Clang 9 + Clang 9 @@ -988,7 +988,7 @@

C++2a implementation status

Relaxations of constexpr restrictions P1064R0 - Clang 9 + Clang 9 P1002R1 @@ -996,22 +996,20 @@

C++2a implementation status

P1327R1 - Clang 9 + Clang 9 P1330R0 P1331R2 - No + SVN P1668R1 - Clang 9 P0784R7 - No Prohibit aggregates with user-declared constructors @@ -1026,13 +1024,13 @@

C++2a implementation status

explicit(bool) P0892R2 - Clang 9 + Clang 9 Signed integers are two's complement P1236R1 - Clang 9 + Clang 9 char8_t @@ -1047,7 +1045,7 @@

C++2a implementation status

std::is_constant_evaluated P0595R2 - Clang 9 + Clang 9 Nested inline namespaces @@ -1100,7 +1098,7 @@

C++2a implementation status

Deprecate a[b,c] P1161R3 - Clang 9 + Clang 9 Deprecate some problematic uses of volatile @@ -1110,7 +1108,7 @@

C++2a implementation status

[[nodiscard("with reason")]] P1301R4 - Clang 9 + Clang 9 using enum diff --git a/clang/www/make_cxx_dr_status b/clang/www/make_cxx_dr_status index ff4a3f5c1b471..4351d659e41ae 100755 --- a/clang/www/make_cxx_dr_status +++ b/clang/www/make_cxx_dr_status @@ -111,9 +111,6 @@ def availability(issue): elif status == '10': avail = 'SVN' avail_style = ' class="svn"' - elif status == '9': - avail = 'Clang 9' - avail_style = ' class="svn"' elif re.match('^[0-9]+\.?[0-9]*', status): avail = 'Clang %s' % status avail_style = ' class="full"' diff --git a/compiler-rt/cmake/Modules/BuiltinTests.cmake b/compiler-rt/cmake/Modules/BuiltinTests.cmake index eee77ad020091..4a123638c8bcf 100644 --- a/compiler-rt/cmake/Modules/BuiltinTests.cmake +++ b/compiler-rt/cmake/Modules/BuiltinTests.cmake @@ -72,6 +72,12 @@ function(try_compile_only output) endif() endforeach() + # Strip quotes from the compile command, as the compiler is not expecting + # quoted arguments (see discussion on D62063 for when this can come up). If + # the quotes were there for arugments with spaces in them, the quotes were + # not going to help since the string gets split on spaces below. + string(REPLACE "\"" "" test_compile_command "${test_compile_command}") + string(REPLACE " " ";" test_compile_command "${test_compile_command}") execute_process( diff --git a/compiler-rt/cmake/Modules/CompilerRTDarwinUtils.cmake b/compiler-rt/cmake/Modules/CompilerRTDarwinUtils.cmake index 042c8a08dec41..15d34afe5368a 100644 --- a/compiler-rt/cmake/Modules/CompilerRTDarwinUtils.cmake +++ b/compiler-rt/cmake/Modules/CompilerRTDarwinUtils.cmake @@ -1,6 +1,8 @@ include(CMakeParseArguments) include(CompilerRTUtils) +set(CMAKE_LIPO "lipo" CACHE PATH "path to the lipo tool") + # On OS X SDKs can be installed anywhere on the base system and xcode-select can # set the default Xcode to use. This function finds the SDKs that are present in # the current Xcode. @@ -244,7 +246,7 @@ function(darwin_lipo_libs name) if(LIB_DEPENDS AND LIB_LIPO_FLAGS) add_custom_command(OUTPUT ${LIB_OUTPUT_DIR}/lib${name}.a COMMAND ${CMAKE_COMMAND} -E make_directory ${LIB_OUTPUT_DIR} - COMMAND lipo -output + COMMAND ${CMAKE_LIPO} -output ${LIB_OUTPUT_DIR}/lib${name}.a -create ${LIB_LIPO_FLAGS} DEPENDS ${LIB_DEPENDS} diff --git a/compiler-rt/cmake/config-ix.cmake b/compiler-rt/cmake/config-ix.cmake index d2da0496b97fc..875acc83be4e3 100644 --- a/compiler-rt/cmake/config-ix.cmake +++ b/compiler-rt/cmake/config-ix.cmake @@ -208,6 +208,32 @@ macro(get_test_cc_for_arch arch cc_out cflags_out) endif() endmacro() +# Returns CFLAGS that should be used to run tests for the +# specific apple platform and architecture. +function(get_test_cflags_for_apple_platform platform arch cflags_out) + is_valid_apple_platform("${platform}" is_valid_platform) + if (NOT is_valid_platform) + message(FATAL_ERROR "\"${platform}\" is not a valid apple platform") + endif() + set(test_cflags "") + get_target_flags_for_arch(${arch} test_cflags) + list(APPEND test_cflags ${DARWIN_${platform}_CFLAGS}) + string(REPLACE ";" " " test_cflags_str "${test_cflags}") + string(APPEND test_cflags_str "${COMPILER_RT_TEST_COMPILER_CFLAGS}") + set(${cflags_out} "${test_cflags_str}" PARENT_SCOPE) +endfunction() + +function(is_valid_apple_platform platform is_valid_out) + set(is_valid FALSE) + if ("${platform}" STREQUAL "") + message(FATAL_ERROR "platform cannot be empty") + endif() + if ("${platform}" MATCHES "^(osx|((ios|watchos|tvos)(sim)?))$") + set(is_valid TRUE) + endif() + set(${is_valid_out} ${is_valid} PARENT_SCOPE) +endfunction() + set(ARM64 aarch64) set(ARM32 arm armhf) set(HEXAGON hexagon) diff --git a/compiler-rt/lib/asan/asan_errors.cpp b/compiler-rt/lib/asan/asan_errors.cpp index 0ef97794be3cb..541c6e0353b57 100644 --- a/compiler-rt/lib/asan/asan_errors.cpp +++ b/compiler-rt/lib/asan/asan_errors.cpp @@ -35,7 +35,8 @@ static void OnStackUnwind(const SignalContext &sig, // corresponding code in the sanitizer_common and we use this callback to // print it. static_cast(callback_context)->Print(); - stack->Unwind(sig.pc, sig.bp, sig.context, fast); + stack->Unwind(StackTrace::GetNextInstructionPc(sig.pc), sig.bp, sig.context, + fast); } void ErrorDeadlySignal::Print() { diff --git a/compiler-rt/lib/asan/asan_interceptors.cpp b/compiler-rt/lib/asan/asan_interceptors.cpp index cea4d63e64d55..b19cf25c7cd00 100644 --- a/compiler-rt/lib/asan/asan_interceptors.cpp +++ b/compiler-rt/lib/asan/asan_interceptors.cpp @@ -164,6 +164,11 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *) ASAN_MEMSET_IMPL(ctx, block, c, size); \ } while (false) +#if CAN_SANITIZE_LEAKS +#define COMMON_INTERCEPTOR_STRERROR() \ + __lsan::ScopedInterceptorDisabler disabler +#endif + #include "sanitizer_common/sanitizer_common_interceptors.inc" #include "sanitizer_common/sanitizer_signal_interceptors.inc" @@ -560,24 +565,59 @@ INTERCEPTOR(long long, atoll, const char *nptr) { } #endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL -#if ASAN_INTERCEPT___CXA_ATEXIT +#if ASAN_INTERCEPT___CXA_ATEXIT || ASAN_INTERCEPT_ATEXIT static void AtCxaAtexit(void *unused) { (void)unused; StopInitOrderChecking(); } +#endif +#if ASAN_INTERCEPT___CXA_ATEXIT INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, void *dso_handle) { #if SANITIZER_MAC if (UNLIKELY(!asan_inited)) return REAL(__cxa_atexit)(func, arg, dso_handle); #endif ENSURE_ASAN_INITED(); +#if CAN_SANITIZE_LEAKS + __lsan::ScopedInterceptorDisabler disabler; +#endif int res = REAL(__cxa_atexit)(func, arg, dso_handle); REAL(__cxa_atexit)(AtCxaAtexit, nullptr, nullptr); return res; } #endif // ASAN_INTERCEPT___CXA_ATEXIT +#if ASAN_INTERCEPT_ATEXIT +INTERCEPTOR(int, atexit, void (*func)()) { + ENSURE_ASAN_INITED(); +#if CAN_SANITIZE_LEAKS + __lsan::ScopedInterceptorDisabler disabler; +#endif + // Avoid calling real atexit as it is unrechable on at least on Linux. + int res = REAL(__cxa_atexit)((void (*)(void *a))func, nullptr, nullptr); + REAL(__cxa_atexit)(AtCxaAtexit, nullptr, nullptr); + return res; +} +#endif + +#if ASAN_INTERCEPT_PTHREAD_ATFORK +extern "C" { +extern int _pthread_atfork(void (*prepare)(), void (*parent)(), + void (*child)()); +}; + +INTERCEPTOR(int, pthread_atfork, void (*prepare)(), void (*parent)(), + void (*child)()) { +#if CAN_SANITIZE_LEAKS + __lsan::ScopedInterceptorDisabler disabler; +#endif + // REAL(pthread_atfork) cannot be called due to symbol indirections at least + // on NetBSD + return _pthread_atfork(prepare, parent, child); +} +#endif + #if ASAN_INTERCEPT_VFORK DEFINE_REAL(int, vfork) DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork) @@ -660,6 +700,14 @@ void InitializeAsanInterceptors() { ASAN_INTERCEPT_FUNC(__cxa_atexit); #endif +#if ASAN_INTERCEPT_ATEXIT + ASAN_INTERCEPT_FUNC(atexit); +#endif + +#if ASAN_INTERCEPT_PTHREAD_ATFORK + ASAN_INTERCEPT_FUNC(pthread_atfork); +#endif + #if ASAN_INTERCEPT_VFORK ASAN_INTERCEPT_FUNC(vfork); #endif diff --git a/compiler-rt/lib/asan/asan_interceptors.h b/compiler-rt/lib/asan/asan_interceptors.h index 155ea4156abbe..344a64bd83d33 100644 --- a/compiler-rt/lib/asan/asan_interceptors.h +++ b/compiler-rt/lib/asan/asan_interceptors.h @@ -99,6 +99,12 @@ void InitializePlatformInterceptors(); # define ASAN_INTERCEPT___CXA_ATEXIT 0 #endif +#if SANITIZER_NETBSD +# define ASAN_INTERCEPT_ATEXIT 1 +#else +# define ASAN_INTERCEPT_ATEXIT 0 +#endif + #if SANITIZER_LINUX && !SANITIZER_ANDROID # define ASAN_INTERCEPT___STRDUP 1 #else @@ -112,6 +118,12 @@ void InitializePlatformInterceptors(); # define ASAN_INTERCEPT_VFORK 0 #endif +#if SANITIZER_NETBSD +# define ASAN_INTERCEPT_PTHREAD_ATFORK 1 +#else +# define ASAN_INTERCEPT_PTHREAD_ATFORK 0 +#endif + DECLARE_REAL(int, memcmp, const void *a1, const void *a2, uptr size) DECLARE_REAL(char*, strchr, const char *str, int c) DECLARE_REAL(SIZE_T, strlen, const char *s) diff --git a/compiler-rt/lib/asan/tests/asan_str_test.cpp b/compiler-rt/lib/asan/tests/asan_str_test.cpp index cc5bd2b4446e3..33a38a81567fc 100644 --- a/compiler-rt/lib/asan/tests/asan_str_test.cpp +++ b/compiler-rt/lib/asan/tests/asan_str_test.cpp @@ -458,8 +458,6 @@ TEST(AddressSanitizer, StrNCatOOBTest) { strncat(to, from, from_size); from[from_size - 1] = '\0'; strncat(to, from, 2 * from_size); - // Catenating empty string with an invalid string is still an error. - EXPECT_DEATH(strncat(to - 1, from, 0), LeftOOBAccessMessage(1)); strncat(to, from + from_size - 1, 10); // One of arguments points to not allocated memory. EXPECT_DEATH(strncat(to - 1, from, 2), LeftOOBAccessMessage(1)); diff --git a/compiler-rt/lib/builtins/fp_add_impl.inc b/compiler-rt/lib/builtins/fp_add_impl.inc index 582324746fe52..ab63213490324 100644 --- a/compiler-rt/lib/builtins/fp_add_impl.inc +++ b/compiler-rt/lib/builtins/fp_add_impl.inc @@ -94,7 +94,7 @@ static __inline fp_t __addXf3__(fp_t a, fp_t b) { const unsigned int align = aExponent - bExponent; if (align) { if (align < typeWidth) { - const bool sticky = bSignificand << (typeWidth - align); + const bool sticky = (bSignificand << (typeWidth - align)) != 0; bSignificand = bSignificand >> align | sticky; } else { bSignificand = 1; // Set the sticky bit. b is known to be non-zero. @@ -133,7 +133,7 @@ static __inline fp_t __addXf3__(fp_t a, fp_t b) { // The result is denormal before rounding. The exponent is zero and we // need to shift the significand. const int shift = 1 - aExponent; - const bool sticky = aSignificand << (typeWidth - shift); + const bool sticky = (aSignificand << (typeWidth - shift)) != 0; aSignificand = aSignificand >> shift | sticky; aExponent = 0; } diff --git a/compiler-rt/lib/builtins/fp_lib.h b/compiler-rt/lib/builtins/fp_lib.h index d1a988ea47133..e2a906681c46d 100644 --- a/compiler-rt/lib/builtins/fp_lib.h +++ b/compiler-rt/lib/builtins/fp_lib.h @@ -245,7 +245,7 @@ static __inline void wideLeftShift(rep_t *hi, rep_t *lo, int count) { static __inline void wideRightShiftWithSticky(rep_t *hi, rep_t *lo, unsigned int count) { if (count < typeWidth) { - const bool sticky = *lo << (typeWidth - count); + const bool sticky = (*lo << (typeWidth - count)) != 0; *lo = *hi << (typeWidth - count) | *lo >> count | sticky; *hi = *hi >> count; } else if (count < 2 * typeWidth) { diff --git a/compiler-rt/lib/builtins/fp_trunc_impl.inc b/compiler-rt/lib/builtins/fp_trunc_impl.inc index 133c8bbe5c2fe..6662be7607e70 100644 --- a/compiler-rt/lib/builtins/fp_trunc_impl.inc +++ b/compiler-rt/lib/builtins/fp_trunc_impl.inc @@ -113,7 +113,7 @@ static __inline dst_t __truncXfYf2__(src_t a) { if (shift > srcSigBits) { absResult = 0; } else { - const bool sticky = significand << (srcBits - shift); + const bool sticky = (significand << (srcBits - shift)) != 0; src_rep_t denormalizedSignificand = significand >> shift | sticky; absResult = denormalizedSignificand >> (srcSigBits - dstSigBits); const src_rep_t roundBits = denormalizedSignificand & roundMask; diff --git a/compiler-rt/lib/crt/CMakeLists.txt b/compiler-rt/lib/crt/CMakeLists.txt index 34c368f7ca444..90e94b93db477 100644 --- a/compiler-rt/lib/crt/CMakeLists.txt +++ b/compiler-rt/lib/crt/CMakeLists.txt @@ -70,7 +70,7 @@ function(check_cxx_section_exists section output) endfunction() check_cxx_section_exists(".init_array" COMPILER_RT_HAS_INITFINI_ARRAY - SOURCE "__attribute__((constructor)) void f() {}\nint main() { return 0; }\n") + SOURCE "volatile int x;\n__attribute__((constructor)) void f() {x = 0;}\nint main() { return 0; }\n") append_list_if(COMPILER_RT_HAS_STD_C11_FLAG -std=c11 CRT_CFLAGS) append_list_if(COMPILER_RT_HAS_INITFINI_ARRAY -DCRT_HAS_INITFINI_ARRAY CRT_CFLAGS) diff --git a/compiler-rt/lib/fuzzer/FuzzerDriver.cpp b/compiler-rt/lib/fuzzer/FuzzerDriver.cpp index 54c7ff0795857..44c90655b9328 100644 --- a/compiler-rt/lib/fuzzer/FuzzerDriver.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerDriver.cpp @@ -708,7 +708,6 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { Options.FeaturesDir = Flags.features_dir; if (Flags.collect_data_flow) Options.CollectDataFlow = Flags.collect_data_flow; - Options.LazyCounters = Flags.lazy_counters; if (Flags.stop_file) Options.StopFile = Flags.stop_file; diff --git a/compiler-rt/lib/fuzzer/FuzzerExtFunctions.def b/compiler-rt/lib/fuzzer/FuzzerExtFunctions.def index 7b53b085560b7..51edf8444e94d 100644 --- a/compiler-rt/lib/fuzzer/FuzzerExtFunctions.def +++ b/compiler-rt/lib/fuzzer/FuzzerExtFunctions.def @@ -33,6 +33,7 @@ EXT_FUNC(__sanitizer_install_malloc_and_free_hooks, int, (void (*malloc_hook)(const volatile void *, size_t), void (*free_hook)(const volatile void *)), false); +EXT_FUNC(__sanitizer_log_write, void, (const char *buf, size_t len), false); EXT_FUNC(__sanitizer_purge_allocator, void, (), false); EXT_FUNC(__sanitizer_print_memory_profile, void, (size_t, size_t), false); EXT_FUNC(__sanitizer_print_stack_trace, void, (), true); diff --git a/compiler-rt/lib/fuzzer/FuzzerFlags.def b/compiler-rt/lib/fuzzer/FuzzerFlags.def index a11cfe4405f31..0e19a9cde6ca3 100644 --- a/compiler-rt/lib/fuzzer/FuzzerFlags.def +++ b/compiler-rt/lib/fuzzer/FuzzerFlags.def @@ -123,9 +123,6 @@ FUZZER_FLAG_INT(handle_term, 1, "If 1, try to intercept SIGTERM.") FUZZER_FLAG_INT(handle_xfsz, 1, "If 1, try to intercept SIGXFSZ.") FUZZER_FLAG_INT(handle_usr1, 1, "If 1, try to intercept SIGUSR1.") FUZZER_FLAG_INT(handle_usr2, 1, "If 1, try to intercept SIGUSR2.") -FUZZER_FLAG_INT(lazy_counters, 0, "If 1, a performance optimization is" - "enabled for the 8bit inline counters. " - "Requires that libFuzzer successfully installs its SEGV handler") FUZZER_FLAG_INT(close_fd_mask, 0, "If 1, close stdout at startup; " "if 2, close stderr; if 3, close both. " "Be careful, this will also close e.g. stderr of asan.") diff --git a/compiler-rt/lib/fuzzer/FuzzerLoop.cpp b/compiler-rt/lib/fuzzer/FuzzerLoop.cpp index 9c266739306b0..df533e877d37a 100644 --- a/compiler-rt/lib/fuzzer/FuzzerLoop.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerLoop.cpp @@ -513,10 +513,12 @@ size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const { } void Fuzzer::CrashOnOverwrittenData() { - Printf("==%d== ERROR: libFuzzer: fuzz target overwrites it's const input\n", + Printf("==%d== ERROR: libFuzzer: fuzz target overwrites its const input\n", GetPid()); + PrintStackTrace(); + Printf("SUMMARY: libFuzzer: overwrites-const-input\n"); DumpCurrentUnit("crash-"); - Printf("SUMMARY: libFuzzer: out-of-memory\n"); + PrintFinalStats(); _Exit(Options.ErrorExitCode); // Stop right now. } @@ -740,10 +742,6 @@ void Fuzzer::ReadAndExecuteSeedCorpora(Vector &CorporaFiles) { uint8_t dummy = 0; ExecuteCallback(&dummy, 0); - // Protect lazy counters here, after the once-init code has been executed. - if (Options.LazyCounters) - TPC.ProtectLazyCounters(); - if (CorporaFiles.empty()) { Printf("INFO: A corpus is not provided, starting from an empty corpus\n"); Unit U({'\n'}); // Valid ASCII input. diff --git a/compiler-rt/lib/fuzzer/FuzzerOptions.h b/compiler-rt/lib/fuzzer/FuzzerOptions.h index ad3df015bc778..beecc980380b4 100644 --- a/compiler-rt/lib/fuzzer/FuzzerOptions.h +++ b/compiler-rt/lib/fuzzer/FuzzerOptions.h @@ -75,7 +75,6 @@ struct FuzzingOptions { bool HandleXfsz = false; bool HandleUsr1 = false; bool HandleUsr2 = false; - bool LazyCounters = false; }; } // namespace fuzzer diff --git a/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp b/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp index c47357703b953..f03be7a39502f 100644 --- a/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp @@ -67,45 +67,6 @@ void TracePC::HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop) { NumInline8bitCounters += M.Size(); } -// Mark all full page counter regions as PROT_NONE and set Enabled=false. -// The first time the instrumented code hits such a protected/disabled -// counter region we should catch a SEGV and call UnprotectLazyCounters, -// which will mark the page as PROT_READ|PROT_WRITE and set Enabled=true. -// -// Whenever other functions iterate over the counters they should ignore -// regions with Enabled=false. -void TracePC::ProtectLazyCounters() { - size_t NumPagesProtected = 0; - IterateCounterRegions([&](Module::Region &R) { - if (!R.OneFullPage) return; - if (Mprotect(R.Start, R.Stop - R.Start, false)) { - R.Enabled = false; - NumPagesProtected++; - } - }); - if (NumPagesProtected) - Printf("INFO: %zd pages of counters where protected;" - " libFuzzer's SEGV handler must be installed\n", - NumPagesProtected); -} - -bool TracePC::UnprotectLazyCounters(void *CounterPtr) { - // Printf("UnprotectLazyCounters: %p\n", CounterPtr); - if (!CounterPtr) - return false; - bool Done = false; - uint8_t *Addr = reinterpret_cast(CounterPtr); - IterateCounterRegions([&](Module::Region &R) { - if (!R.OneFullPage || R.Enabled || Done) return; - if (Addr >= R.Start && Addr < R.Stop) - if (Mprotect(R.Start, R.Stop - R.Start, true)) { - R.Enabled = true; - Done = true; - } - }); - return Done; -} - void TracePC::HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop) { const PCTableEntry *B = reinterpret_cast(Start); const PCTableEntry *E = reinterpret_cast(Stop); diff --git a/compiler-rt/lib/fuzzer/FuzzerTracePC.h b/compiler-rt/lib/fuzzer/FuzzerTracePC.h index 4f5ebeb047a16..501f3b544971f 100644 --- a/compiler-rt/lib/fuzzer/FuzzerTracePC.h +++ b/compiler-rt/lib/fuzzer/FuzzerTracePC.h @@ -119,9 +119,6 @@ class TracePC { void SetFocusFunction(const std::string &FuncName); bool ObservedFocusFunction(); - void ProtectLazyCounters(); - bool UnprotectLazyCounters(void *CounterPtr); - struct PCTableEntry { uintptr_t PC, PCFlags; }; diff --git a/compiler-rt/lib/fuzzer/FuzzerUtil.h b/compiler-rt/lib/fuzzer/FuzzerUtil.h index 0a127911df3cc..85c5571d684f5 100644 --- a/compiler-rt/lib/fuzzer/FuzzerUtil.h +++ b/compiler-rt/lib/fuzzer/FuzzerUtil.h @@ -52,8 +52,6 @@ void SetSignalHandler(const FuzzingOptions& Options); void SleepSeconds(int Seconds); -bool Mprotect(void *Ptr, size_t Size, bool AllowReadWrite); - unsigned long GetPid(); size_t GetPeakRSSMb(); diff --git a/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp b/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp index 1f04b33c154e4..50071a7e5c8f3 100644 --- a/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp @@ -305,12 +305,19 @@ void CrashHandler(zx_handle_t *Event) { } // namespace -bool Mprotect(void *Ptr, size_t Size, bool AllowReadWrite) { - return false; // UNIMPLEMENTED -} - // Platform specific functions. void SetSignalHandler(const FuzzingOptions &Options) { + // Make sure information from libFuzzer and the sanitizers are easy to + // reassemble. `__sanitizer_log_write` has the added benefit of ensuring the + // DSO map is always available for the symbolizer. + // A uint64_t fits in 20 chars, so 64 is plenty. + char Buf[64]; + memset(Buf, 0, sizeof(Buf)); + snprintf(Buf, sizeof(Buf), "==%lu== INFO: libFuzzer starting.\n", GetPid()); + if (EF->__sanitizer_log_write) + __sanitizer_log_write(Buf, sizeof(Buf)); + Printf("%s", Buf); + // Set up alarm handler if needed. if (Options.UnitTimeoutSec > 0) { std::thread T(AlarmHandler, Options.UnitTimeoutSec / 2 + 1); diff --git a/compiler-rt/lib/fuzzer/FuzzerUtilPosix.cpp b/compiler-rt/lib/fuzzer/FuzzerUtilPosix.cpp index 110785d874137..cefe7ae181e72 100644 --- a/compiler-rt/lib/fuzzer/FuzzerUtilPosix.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerUtilPosix.cpp @@ -37,7 +37,6 @@ static void (*upstream_segv_handler)(int, siginfo_t *, void *); static void SegvHandler(int sig, siginfo_t *si, void *ucontext) { assert(si->si_signo == SIGSEGV); - if (TPC.UnprotectLazyCounters(si->si_addr)) return; if (upstream_segv_handler) return upstream_segv_handler(sig, si, ucontext); Fuzzer::StaticCrashSignalCallback(); @@ -98,11 +97,6 @@ void SetTimer(int Seconds) { SetSigaction(SIGALRM, AlarmHandler); } -bool Mprotect(void *Ptr, size_t Size, bool AllowReadWrite) { - return 0 == mprotect(Ptr, Size, - AllowReadWrite ? (PROT_READ | PROT_WRITE) : PROT_NONE); -} - void SetSignalHandler(const FuzzingOptions& Options) { if (Options.UnitTimeoutSec > 0) SetTimer(Options.UnitTimeoutSec / 2 + 1); diff --git a/compiler-rt/lib/fuzzer/FuzzerUtilWindows.cpp b/compiler-rt/lib/fuzzer/FuzzerUtilWindows.cpp index 074e1eb423094..ed90044c3f835 100644 --- a/compiler-rt/lib/fuzzer/FuzzerUtilWindows.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerUtilWindows.cpp @@ -111,10 +111,6 @@ static TimerQ Timer; static void CrashHandler(int) { Fuzzer::StaticCrashSignalCallback(); } -bool Mprotect(void *Ptr, size_t Size, bool AllowReadWrite) { - return false; // UNIMPLEMENTED -} - void SetSignalHandler(const FuzzingOptions& Options) { HandlerOpt = &Options; diff --git a/compiler-rt/lib/fuzzer/utils/FuzzedDataProvider.h b/compiler-rt/lib/fuzzer/utils/FuzzedDataProvider.h deleted file mode 100644 index 5692060c7f7eb..0000000000000 --- a/compiler-rt/lib/fuzzer/utils/FuzzedDataProvider.h +++ /dev/null @@ -1,247 +0,0 @@ -//===- FuzzedDataProvider.h - Utility header for fuzz targets ---*- C++ -* ===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// This a temporary copy of compiler-rt/include/fuzzer/FuzzedDataProvider.h. -// TODO(mmoroz@chromium.org): delete this copy. -// A single header library providing an utility class to break up an array of -// bytes. Whenever run on the same input, provides the same output, as long as -// its methods are called in the same order, with the same arguments. -//===----------------------------------------------------------------------===// - -#ifndef LLVM_FUZZER_FUZZED_DATA_PROVIDER_H_ -#define LLVM_FUZZER_FUZZED_DATA_PROVIDER_H_ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -class FuzzedDataProvider { - public: - // |data| is an array of length |size| that the FuzzedDataProvider wraps to - // provide more granular access. |data| must outlive the FuzzedDataProvider. - FuzzedDataProvider(const uint8_t *data, size_t size) - : data_ptr_(data), remaining_bytes_(size) {} - ~FuzzedDataProvider() = default; - - // Returns a std::vector containing |num_bytes| of input data. If fewer than - // |num_bytes| of data remain, returns a shorter std::vector containing all - // of the data that's left. Can be used with any byte sized type, such as - // char, unsigned char, uint8_t, etc. - template std::vector ConsumeBytes(size_t num_bytes) { - num_bytes = std::min(num_bytes, remaining_bytes_); - return ConsumeBytes(num_bytes, num_bytes); - } - - // Similar to |ConsumeBytes|, but also appends the terminator value at the end - // of the resulting vector. Useful, when a mutable null-terminated C-string is - // needed, for example. But that is a rare case. Better avoid it, if possible, - // and prefer using |ConsumeBytes| or |ConsumeBytesAsString| methods. - template - std::vector ConsumeBytesWithTerminator(size_t num_bytes, - T terminator = 0) { - num_bytes = std::min(num_bytes, remaining_bytes_); - std::vector result = ConsumeBytes(num_bytes + 1, num_bytes); - result.back() = terminator; - return result; - } - - // Returns a std::string containing |num_bytes| of input data. Using this and - // |.c_str()| on the resulting string is the best way to get an immutable - // null-terminated C string. If fewer than |num_bytes| of data remain, returns - // a shorter std::string containing all of the data that's left. - std::string ConsumeBytesAsString(size_t num_bytes) { - static_assert(sizeof(std::string::value_type) == sizeof(uint8_t), - "ConsumeBytesAsString cannot convert the data to a string."); - - num_bytes = std::min(num_bytes, remaining_bytes_); - std::string result( - reinterpret_cast(data_ptr_), - num_bytes); - Advance(num_bytes); - return result; - } - - // Returns a number in the range [min, max] by consuming bytes from the - // input data. The value might not be uniformly distributed in the given - // range. If there's no input data left, always returns |min|. |min| must - // be less than or equal to |max|. - template T ConsumeIntegralInRange(T min, T max) { - static_assert(std::is_integral::value, "An integral type is required."); - static_assert(sizeof(T) <= sizeof(uint64_t), "Unsupported integral type."); - - if (min > max) - abort(); - - // Use the biggest type possible to hold the range and the result. - uint64_t range = static_cast(max) - min; - uint64_t result = 0; - size_t offset = 0; - - while (offset < sizeof(T) * CHAR_BIT && (range >> offset) > 0 && - remaining_bytes_ != 0) { - // Pull bytes off the end of the seed data. Experimentally, this seems to - // allow the fuzzer to more easily explore the input space. This makes - // sense, since it works by modifying inputs that caused new code to run, - // and this data is often used to encode length of data read by - // |ConsumeBytes|. Separating out read lengths makes it easier modify the - // contents of the data that is actually read. - --remaining_bytes_; - result = (result << CHAR_BIT) | data_ptr_[remaining_bytes_]; - offset += CHAR_BIT; - } - - // Avoid division by 0, in case |range + 1| results in overflow. - if (range != std::numeric_limits::max()) - result = result % (range + 1); - - return static_cast(min + result); - } - - // Returns a std::string of length from 0 to |max_length|. When it runs out of - // input data, returns what remains of the input. Designed to be more stable - // with respect to a fuzzer inserting characters than just picking a random - // length and then consuming that many bytes with |ConsumeBytes|. - std::string ConsumeRandomLengthString(size_t max_length) { - // Reads bytes from the start of |data_ptr_|. Maps "\\" to "\", and maps "\" - // followed by anything else to the end of the string. As a result of this - // logic, a fuzzer can insert characters into the string, and the string - // will be lengthened to include those new characters, resulting in a more - // stable fuzzer than picking the length of a string independently from - // picking its contents. - std::string result; - - // Reserve the anticipated capaticity to prevent several reallocations. - result.reserve(std::min(max_length, remaining_bytes_)); - for (size_t i = 0; i < max_length && remaining_bytes_ != 0; ++i) { - char next = ConvertUnsignedToSigned(data_ptr_[0]); - Advance(1); - if (next == '\\' && remaining_bytes_ != 0) { - next = ConvertUnsignedToSigned(data_ptr_[0]); - Advance(1); - if (next != '\\') - break; - } - result += next; - } - - result.shrink_to_fit(); - return result; - } - - // Returns a std::vector containing all remaining bytes of the input data. - template std::vector ConsumeRemainingBytes() { - return ConsumeBytes(remaining_bytes_); - } - - // Prefer using |ConsumeRemainingBytes| unless you actually need a std::string - // object. - // Returns a std::vector containing all remaining bytes of the input data. - std::string ConsumeRemainingBytesAsString() { - return ConsumeBytesAsString(remaining_bytes_); - } - - // Returns a number in the range [Type's min, Type's max]. The value might - // not be uniformly distributed in the given range. If there's no input data - // left, always returns |min|. - template T ConsumeIntegral() { - return ConsumeIntegralInRange(std::numeric_limits::min(), - std::numeric_limits::max()); - } - - // Reads one byte and returns a bool, or false when no data remains. - bool ConsumeBool() { return 1 & ConsumeIntegral(); } - - // Returns a copy of a value selected from a fixed-size |array|. - template - T PickValueInArray(const T (&array)[size]) { - static_assert(size > 0, "The array must be non empty."); - return array[ConsumeIntegralInRange(0, size - 1)]; - } - - template - T PickValueInArray(std::initializer_list list) { - // static_assert(list.size() > 0, "The array must be non empty."); - return *(list.begin() + ConsumeIntegralInRange(0, list.size() - 1)); - } - - // Return an enum value. The enum must start at 0 and be contiguous. It must - // also contain |kMaxValue| aliased to its largest (inclusive) value. Such as: - // enum class Foo { SomeValue, OtherValue, kMaxValue = OtherValue }; - template T ConsumeEnum() { - static_assert(std::is_enum::value, "|T| must be an enum type."); - return static_cast(ConsumeIntegralInRange( - 0, static_cast(T::kMaxValue))); - } - - // Reports the remaining bytes available for fuzzed input. - size_t remaining_bytes() { return remaining_bytes_; } - - private: - FuzzedDataProvider(const FuzzedDataProvider &) = delete; - FuzzedDataProvider &operator=(const FuzzedDataProvider &) = delete; - - void Advance(size_t num_bytes) { - if (num_bytes > remaining_bytes_) - abort(); - - data_ptr_ += num_bytes; - remaining_bytes_ -= num_bytes; - } - - template - std::vector ConsumeBytes(size_t size, size_t num_bytes_to_consume) { - static_assert(sizeof(T) == sizeof(uint8_t), "Incompatible data type."); - - // The point of using the size-based constructor below is to increase the - // odds of having a vector object with capacity being equal to the length. - // That part is always implementation specific, but at least both libc++ and - // libstdc++ allocate the requested number of bytes in that constructor, - // which seems to be a natural choice for other implementations as well. - // To increase the odds even more, we also call |shrink_to_fit| below. - std::vector result(size); - std::memcpy(result.data(), data_ptr_, num_bytes_to_consume); - Advance(num_bytes_to_consume); - - // Even though |shrink_to_fit| is also implementation specific, we expect it - // to provide an additional assurance in case vector's constructor allocated - // a buffer which is larger than the actual amount of data we put inside it. - result.shrink_to_fit(); - return result; - } - - template TS ConvertUnsignedToSigned(TU value) { - static_assert(sizeof(TS) == sizeof(TU), "Incompatible data types."); - static_assert(!std::numeric_limits::is_signed, - "Source type must be unsigned."); - - // TODO(Dor1s): change to `if constexpr` once C++17 becomes mainstream. - if (std::numeric_limits::is_modulo) - return static_cast(value); - - // Avoid using implementation-defined unsigned to signer conversions. - // To learn more, see https://stackoverflow.com/questions/13150449. - if (value <= std::numeric_limits::max()) - return static_cast(value); - else { - constexpr auto TS_min = std::numeric_limits::min(); - return TS_min + static_cast(value - TS_min); - } - } - - const uint8_t *data_ptr_; - size_t remaining_bytes_; -}; - -#endif // LLVM_FUZZER_FUZZED_DATA_PROVIDER_H_ diff --git a/compiler-rt/lib/hwasan/hwasan_tag_mismatch_aarch64.S b/compiler-rt/lib/hwasan/hwasan_tag_mismatch_aarch64.S index 92f6274804868..4c060a61e98ed 100644 --- a/compiler-rt/lib/hwasan/hwasan_tag_mismatch_aarch64.S +++ b/compiler-rt/lib/hwasan/hwasan_tag_mismatch_aarch64.S @@ -51,14 +51,60 @@ // +---------------------------------+ <-- [x30 / SP] // This function takes two arguments: -// * x0: The address of read/write instruction that caused HWASan check fail. -// * x1: The tag size. +// * x0: The data address. +// * x1: The encoded access info for the failing access. +// This function has two entry points. The first, __hwasan_tag_mismatch, is used +// by clients that were compiled without short tag checks (i.e. binaries built +// by older compilers and binaries targeting older runtimes). In this case the +// outlined tag check will be missing the code handling short tags (which won't +// be used in the binary's own stack variables but may be used on the heap +// or stack variables in other binaries), so the check needs to be done here. +// +// The second, __hwasan_tag_mismatch_v2, is used by binaries targeting newer +// runtimes. This entry point bypasses the short tag check since it will have +// already been done as part of the outlined tag check. Since tag mismatches are +// uncommon, there isn't a significant performance benefit to being able to +// bypass the check; the main benefits are that we can sometimes avoid +// clobbering the x17 register in error reports, and that the program will have +// a runtime dependency on the __hwasan_tag_mismatch_v2 symbol therefore it will +// fail to start up given an older (i.e. incompatible) runtime. .section .text .file "hwasan_tag_mismatch_aarch64.S" .global __hwasan_tag_mismatch .type __hwasan_tag_mismatch, %function __hwasan_tag_mismatch: + // Compute the granule position one past the end of the access. + mov x16, #1 + and x17, x1, #0xf + lsl x16, x16, x17 + and x17, x0, #0xf + add x17, x16, x17 + + // Load the shadow byte again and check whether it is a short tag within the + // range of the granule position computed above. + ubfx x16, x0, #4, #52 + ldrb w16, [x9, x16] + cmp w16, #0xf + b.hi __hwasan_tag_mismatch_v2 + cmp w16, w17 + b.lo __hwasan_tag_mismatch_v2 + + // Load the real tag from the last byte of the granule and compare against + // the pointer tag. + orr x16, x0, #0xf + ldrb w16, [x16] + cmp x16, x0, lsr #56 + b.ne __hwasan_tag_mismatch_v2 + + // Restore x0, x1 and sp to their values from before the __hwasan_tag_mismatch + // call and resume execution. + ldp x0, x1, [sp], #256 + ret + +.global __hwasan_tag_mismatch_v2 +.type __hwasan_tag_mismatch_v2, %function +__hwasan_tag_mismatch_v2: CFI_STARTPROC // Set the CFA to be the return address for caller of __hwasan_check_*. Note diff --git a/compiler-rt/lib/lsan/lsan.cpp b/compiler-rt/lib/lsan/lsan.cpp index deabf044afc4d..4ce03046ffb7f 100644 --- a/compiler-rt/lib/lsan/lsan.cpp +++ b/compiler-rt/lib/lsan/lsan.cpp @@ -89,7 +89,7 @@ static void InitializeFlags() { static void OnStackUnwind(const SignalContext &sig, const void *, BufferedStackTrace *stack) { - stack->Unwind(sig.pc, sig.bp, sig.context, + stack->Unwind(StackTrace::GetNextInstructionPc(sig.pc), sig.bp, sig.context, common_flags()->fast_unwind_on_fatal); } diff --git a/compiler-rt/lib/lsan/lsan_common.cpp b/compiler-rt/lib/lsan/lsan_common.cpp index 86bd120e776a6..9ff9f4c5d1c97 100644 --- a/compiler-rt/lib/lsan/lsan_common.cpp +++ b/compiler-rt/lib/lsan/lsan_common.cpp @@ -570,11 +570,7 @@ static bool CheckForLeaks() { EnsureMainThreadIDIsCorrect(); CheckForLeaksParam param; param.success = false; - LockThreadRegistry(); - LockAllocator(); - DoStopTheWorld(CheckForLeaksCallback, ¶m); - UnlockAllocator(); - UnlockThreadRegistry(); + LockStuffAndStopTheWorld(CheckForLeaksCallback, ¶m); if (!param.success) { Report("LeakSanitizer has encountered a fatal error.\n"); diff --git a/compiler-rt/lib/lsan/lsan_common.h b/compiler-rt/lib/lsan/lsan_common.h index 58dc00faaee5c..d24abe31b71b5 100644 --- a/compiler-rt/lib/lsan/lsan_common.h +++ b/compiler-rt/lib/lsan/lsan_common.h @@ -129,8 +129,9 @@ struct RootRegion { InternalMmapVector const *GetRootRegions(); void ScanRootRegion(Frontier *frontier, RootRegion const ®ion, uptr region_begin, uptr region_end, bool is_readable); -// Run stoptheworld while holding any platform-specific locks. -void DoStopTheWorld(StopTheWorldCallback callback, void* argument); +// Run stoptheworld while holding any platform-specific locks, as well as the +// allocator and thread registry locks. +void LockStuffAndStopTheWorld(StopTheWorldCallback callback, void* argument); void ScanRangeForPointers(uptr begin, uptr end, Frontier *frontier, diff --git a/compiler-rt/lib/lsan/lsan_common_linux.cpp b/compiler-rt/lib/lsan/lsan_common_linux.cpp index 9ce27a983b502..ea1a4a2f569d0 100644 --- a/compiler-rt/lib/lsan/lsan_common_linux.cpp +++ b/compiler-rt/lib/lsan/lsan_common_linux.cpp @@ -115,10 +115,14 @@ void HandleLeaks() { if (common_flags()->exitcode) Die(); } -static int DoStopTheWorldCallback(struct dl_phdr_info *info, size_t size, - void *data) { +static int LockStuffAndStopTheWorldCallback(struct dl_phdr_info *info, + size_t size, void *data) { + LockThreadRegistry(); + LockAllocator(); DoStopTheWorldParam *param = reinterpret_cast(data); StopTheWorld(param->callback, param->argument); + UnlockAllocator(); + UnlockThreadRegistry(); return 1; } @@ -130,9 +134,9 @@ static int DoStopTheWorldCallback(struct dl_phdr_info *info, size_t size, // while holding the libdl lock in the parent thread, we can safely reenter it // in the tracer. The solution is to run stoptheworld from a dl_iterate_phdr() // callback in the parent thread. -void DoStopTheWorld(StopTheWorldCallback callback, void *argument) { +void LockStuffAndStopTheWorld(StopTheWorldCallback callback, void *argument) { DoStopTheWorldParam param = {callback, argument}; - dl_iterate_phdr(DoStopTheWorldCallback, ¶m); + dl_iterate_phdr(LockStuffAndStopTheWorldCallback, ¶m); } } // namespace __lsan diff --git a/compiler-rt/lib/lsan/lsan_common_mac.cpp b/compiler-rt/lib/lsan/lsan_common_mac.cpp index 5204a6624edcc..c1804e93c11db 100644 --- a/compiler-rt/lib/lsan/lsan_common_mac.cpp +++ b/compiler-rt/lib/lsan/lsan_common_mac.cpp @@ -193,8 +193,12 @@ void ProcessPlatformSpecificAllocations(Frontier *frontier) { // causes rare race conditions. void HandleLeaks() {} -void DoStopTheWorld(StopTheWorldCallback callback, void *argument) { +void LockStuffAndStopTheWorld(StopTheWorldCallback callback, void *argument) { + LockThreadRegistry(); + LockAllocator(); StopTheWorld(callback, argument); + UnlockAllocator(); + UnlockThreadRegistry(); } } // namespace __lsan diff --git a/compiler-rt/lib/lsan/lsan_interceptors.cpp b/compiler-rt/lib/lsan/lsan_interceptors.cpp index f06d5fff70635..f642bb807bc88 100644 --- a/compiler-rt/lib/lsan/lsan_interceptors.cpp +++ b/compiler-rt/lib/lsan/lsan_interceptors.cpp @@ -345,6 +345,55 @@ INTERCEPTOR(void, thr_exit, tid_t *state) { #define LSAN_MAYBE_INTERCEPT_THR_EXIT #endif +#if SANITIZER_INTERCEPT___CXA_ATEXIT +INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, + void *dso_handle) { + __lsan::ScopedInterceptorDisabler disabler; + return REAL(__cxa_atexit)(func, arg, dso_handle); +} +#define LSAN_MAYBE_INTERCEPT___CXA_ATEXIT INTERCEPT_FUNCTION(__cxa_atexit) +#else +#define LSAN_MAYBE_INTERCEPT___CXA_ATEXIT +#endif + +#if SANITIZER_INTERCEPT_ATEXIT +INTERCEPTOR(int, atexit, void (*f)()) { + __lsan::ScopedInterceptorDisabler disabler; + return REAL(__cxa_atexit)((void (*)(void *a))f, 0, 0); +} +#define LSAN_MAYBE_INTERCEPT_ATEXIT INTERCEPT_FUNCTION(atexit) +#else +#define LSAN_MAYBE_INTERCEPT_ATEXIT +#endif + +#if SANITIZER_INTERCEPT_PTHREAD_ATFORK +extern "C" { +extern int _pthread_atfork(void (*prepare)(), void (*parent)(), + void (*child)()); +}; + +INTERCEPTOR(int, pthread_atfork, void (*prepare)(), void (*parent)(), + void (*child)()) { + __lsan::ScopedInterceptorDisabler disabler; + // REAL(pthread_atfork) cannot be called due to symbol indirections at least + // on NetBSD + return _pthread_atfork(prepare, parent, child); +} +#define LSAN_MAYBE_INTERCEPT_PTHREAD_ATFORK INTERCEPT_FUNCTION(pthread_atfork) +#else +#define LSAN_MAYBE_INTERCEPT_PTHREAD_ATFORK +#endif + +#if SANITIZER_INTERCEPT_STRERROR +INTERCEPTOR(char *, strerror, int errnum) { + __lsan::ScopedInterceptorDisabler disabler; + return REAL(strerror)(errnum); +} +#define LSAN_MAYBE_INTERCEPT_STRERROR INTERCEPT_FUNCTION(strerror) +#else +#define LSAN_MAYBE_INTERCEPT_STRERROR +#endif + struct ThreadParam { void *(*callback)(void *arg); void *param; @@ -454,6 +503,12 @@ void InitializeInterceptors() { LSAN_MAYBE_INTERCEPT__LWP_EXIT; LSAN_MAYBE_INTERCEPT_THR_EXIT; + LSAN_MAYBE_INTERCEPT___CXA_ATEXIT; + LSAN_MAYBE_INTERCEPT_ATEXIT; + LSAN_MAYBE_INTERCEPT_PTHREAD_ATFORK; + + LSAN_MAYBE_INTERCEPT_STRERROR; + #if !SANITIZER_NETBSD && !SANITIZER_FREEBSD if (pthread_key_create(&g_thread_finalize_key, &thread_finalize)) { Report("LeakSanitizer: failed to create thread key.\n"); diff --git a/compiler-rt/lib/msan/msan.cpp b/compiler-rt/lib/msan/msan.cpp index da3534bc53f71..6ea63cb2c48ff 100644 --- a/compiler-rt/lib/msan/msan.cpp +++ b/compiler-rt/lib/msan/msan.cpp @@ -378,7 +378,7 @@ void __msan_warning_noreturn() { static void OnStackUnwind(const SignalContext &sig, const void *, BufferedStackTrace *stack) { - stack->Unwind(sig.pc, sig.bp, sig.context, + stack->Unwind(StackTrace::GetNextInstructionPc(sig.pc), sig.bp, sig.context, common_flags()->fast_unwind_on_fatal); } diff --git a/compiler-rt/lib/msan/msan_interceptors.cpp b/compiler-rt/lib/msan/msan_interceptors.cpp index ff760e43d6343..1d9d9f7986d76 100644 --- a/compiler-rt/lib/msan/msan_interceptors.cpp +++ b/compiler-rt/lib/msan/msan_interceptors.cpp @@ -765,17 +765,24 @@ INTERCEPTOR(char *, fgets_unlocked, char *s, int size, void *stream) { #define MSAN_MAYBE_INTERCEPT_FGETS_UNLOCKED #endif +#define INTERCEPTOR_GETRLIMIT_BODY(func, resource, rlim) \ + if (msan_init_is_running) \ + return REAL(getrlimit)(resource, rlim); \ + ENSURE_MSAN_INITED(); \ + int res = REAL(func)(resource, rlim); \ + if (!res) \ + __msan_unpoison(rlim, __sanitizer::struct_rlimit_sz); \ + return res + INTERCEPTOR(int, getrlimit, int resource, void *rlim) { - if (msan_init_is_running) - return REAL(getrlimit)(resource, rlim); - ENSURE_MSAN_INITED(); - int res = REAL(getrlimit)(resource, rlim); - if (!res) - __msan_unpoison(rlim, __sanitizer::struct_rlimit_sz); - return res; + INTERCEPTOR_GETRLIMIT_BODY(getrlimit, resource, rlim); } #if !SANITIZER_FREEBSD && !SANITIZER_NETBSD +INTERCEPTOR(int, __getrlimit, int resource, void *rlim) { + INTERCEPTOR_GETRLIMIT_BODY(__getrlimit, resource, rlim); +} + INTERCEPTOR(int, getrlimit64, int resource, void *rlim) { if (msan_init_is_running) return REAL(getrlimit64)(resource, rlim); ENSURE_MSAN_INITED(); @@ -806,10 +813,12 @@ INTERCEPTOR(int, prlimit64, int pid, int resource, void *new_rlimit, return res; } +#define MSAN_MAYBE_INTERCEPT___GETRLIMIT INTERCEPT_FUNCTION(__getrlimit) #define MSAN_MAYBE_INTERCEPT_GETRLIMIT64 INTERCEPT_FUNCTION(getrlimit64) #define MSAN_MAYBE_INTERCEPT_PRLIMIT INTERCEPT_FUNCTION(prlimit) #define MSAN_MAYBE_INTERCEPT_PRLIMIT64 INTERCEPT_FUNCTION(prlimit64) #else +#define MSAN_MAYBE_INTERCEPT___GETRLIMIT #define MSAN_MAYBE_INTERCEPT_GETRLIMIT64 #define MSAN_MAYBE_INTERCEPT_PRLIMIT #define MSAN_MAYBE_INTERCEPT_PRLIMIT64 @@ -1678,6 +1687,7 @@ void InitializeInterceptors() { INTERCEPT_FUNCTION(socketpair); MSAN_MAYBE_INTERCEPT_FGETS_UNLOCKED; INTERCEPT_FUNCTION(getrlimit); + MSAN_MAYBE_INTERCEPT___GETRLIMIT; MSAN_MAYBE_INTERCEPT_GETRLIMIT64; MSAN_MAYBE_INTERCEPT_PRLIMIT; MSAN_MAYBE_INTERCEPT_PRLIMIT64; diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc index 7ea896ef2d441..587b1ea227226 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -36,6 +36,7 @@ // COMMON_INTERCEPTOR_MMAP_IMPL // COMMON_INTERCEPTOR_COPY_STRING // COMMON_INTERCEPTOR_STRNDUP_IMPL +// COMMON_INTERCEPTOR_STRERROR //===----------------------------------------------------------------------===// #include "interception/interception.h" @@ -301,6 +302,10 @@ bool PlatformHasDifferentMemcpyAndMemmove(); return new_mem; #endif +#ifndef COMMON_INTERCEPTOR_STRERROR +#define COMMON_INTERCEPTOR_STRERROR() {} +#endif + struct FileMetadata { // For open_memstream(). char **addr; @@ -3677,6 +3682,7 @@ INTERCEPTOR(int, sched_getparam, int pid, void *param) { INTERCEPTOR(char *, strerror, int errnum) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strerror, errnum); + COMMON_INTERCEPTOR_STRERROR(); char *res = REAL(strerror)(errnum); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1); return res; diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc index c72554973b05e..c78b6e10b6895 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc @@ -14,6 +14,7 @@ INTERFACE_FUNCTION(__sanitizer_set_death_callback) INTERFACE_FUNCTION(__sanitizer_set_report_path) INTERFACE_FUNCTION(__sanitizer_set_report_fd) INTERFACE_FUNCTION(__sanitizer_verify_contiguous_container) +INTERFACE_WEAK_FUNCTION(__sanitizer_on_print) INTERFACE_WEAK_FUNCTION(__sanitizer_report_error_summary) INTERFACE_WEAK_FUNCTION(__sanitizer_sandbox_on_notify) // Sanitizer weak hooks diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h b/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h index cf079c4355455..c3b6f285569a8 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h @@ -567,5 +567,9 @@ #define SANITIZER_INTERCEPT_SL_INIT (SI_FREEBSD || SI_NETBSD) #define SANITIZER_INTERCEPT_GETRANDOM SI_LINUX +#define SANITIZER_INTERCEPT___CXA_ATEXIT SI_NETBSD +#define SANITIZER_INTERCEPT_ATEXIT SI_NETBSD +#define SANITIZER_INTERCEPT_PTHREAD_ATFORK SI_NETBSD + #endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp index 69919ae74fab1..304b3a01a08b6 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp @@ -328,7 +328,6 @@ bool MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) { bool MmapFixedSuperNoReserve(uptr fixed_addr, uptr size, const char *name) { #if SANITIZER_FREEBSD - int flags = 0; if (common_flags()->no_huge_pages_for_shadow) return MmapFixedNoReserve(fixed_addr, size, name); // MAP_NORESERVE is implicit with FreeBSD diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_printf.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_printf.cpp index 9d1c544786d34..7063de257a9f0 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_printf.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_printf.cpp @@ -229,15 +229,21 @@ void SetPrintfAndReportCallback(void (*callback)(const char *)) { // Can be overriden in frontend. #if SANITIZER_GO && defined(TSAN_EXTERNAL_HOOKS) // Implementation must be defined in frontend. +// TODO(morehouse): Remove OnPrint after migrating Go to __sanitizer_on_print. extern "C" void OnPrint(const char *str); +extern "C" void __sanitizer_on_print(const char *str); #else -SANITIZER_INTERFACE_WEAK_DEF(void, OnPrint, const char *str) { +SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_on_print, const char *str) { (void)str; } #endif static void CallPrintfAndReportCallback(const char *str) { +#if SANITIZER_GO && defined(TSAN_EXTERNAL_HOOKS) + // TODO(morehouse): Remove OnPrint after migrating Go to __sanitizer_on_print. OnPrint(str); +#endif + __sanitizer_on_print(str); if (PrintfAndReportCallback) PrintfAndReportCallback(str); } diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cpp index 1a43759e38ae1..b2628dcc4dc1f 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cpp @@ -27,6 +27,8 @@ namespace __sanitizer { +namespace { + //---------------------------- UnwindSlow -------------------------------------- typedef struct { @@ -46,38 +48,6 @@ release_my_map_info_list_func release_my_map_info_list; unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch; } // extern "C" -#if SANITIZER_ANDROID -void SanitizerInitializeUnwinder() { - if (AndroidGetApiLevel() >= ANDROID_LOLLIPOP_MR1) return; - - // Pre-lollipop Android can not unwind through signal handler frames with - // libgcc unwinder, but it has a libcorkscrew.so library with the necessary - // workarounds. - void *p = dlopen("libcorkscrew.so", RTLD_LAZY); - if (!p) { - VReport(1, - "Failed to open libcorkscrew.so. You may see broken stack traces " - "in SEGV reports."); - return; - } - acquire_my_map_info_list = - (acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list"); - release_my_map_info_list = - (release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list"); - unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym( - p, "unwind_backtrace_signal_arch"); - if (!acquire_my_map_info_list || !release_my_map_info_list || - !unwind_backtrace_signal_arch) { - VReport(1, - "Failed to find one of the required symbols in libcorkscrew.so. " - "You may see broken stack traces in SEGV reports."); - acquire_my_map_info_list = 0; - unwind_backtrace_signal_arch = 0; - release_my_map_info_list = 0; - } -} -#endif - #if defined(__arm__) && !SANITIZER_NETBSD // NetBSD uses dwarf EH #define UNWIND_STOP _URC_END_OF_STACK @@ -119,6 +89,40 @@ _Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) { return UNWIND_CONTINUE; } +} // namespace + +#if SANITIZER_ANDROID +void SanitizerInitializeUnwinder() { + if (AndroidGetApiLevel() >= ANDROID_LOLLIPOP_MR1) return; + + // Pre-lollipop Android can not unwind through signal handler frames with + // libgcc unwinder, but it has a libcorkscrew.so library with the necessary + // workarounds. + void *p = dlopen("libcorkscrew.so", RTLD_LAZY); + if (!p) { + VReport(1, + "Failed to open libcorkscrew.so. You may see broken stack traces " + "in SEGV reports."); + return; + } + acquire_my_map_info_list = + (acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list"); + release_my_map_info_list = + (release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list"); + unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym( + p, "unwind_backtrace_signal_arch"); + if (!acquire_my_map_info_list || !release_my_map_info_list || + !unwind_backtrace_signal_arch) { + VReport(1, + "Failed to find one of the required symbols in libcorkscrew.so. " + "You may see broken stack traces in SEGV reports."); + acquire_my_map_info_list = 0; + unwind_backtrace_signal_arch = 0; + release_my_map_info_list = 0; + } +} +#endif + void BufferedStackTrace::UnwindSlow(uptr pc, u32 max_depth) { CHECK_GE(max_depth, 2); size = 0; diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_win_weak_interception.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_win_weak_interception.cpp index a6081a8931ab9..b14bbf76d9a76 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_win_weak_interception.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_win_weak_interception.cpp @@ -38,6 +38,7 @@ int interceptWhenPossible(uptr dll_function, const char *real_function) { // Declare weak hooks. extern "C" { +void __sanitizer_on_print(const char *str); void __sanitizer_weak_hook_memcmp(uptr called_pc, const void *s1, const void *s2, uptr n, int result); void __sanitizer_weak_hook_strcmp(uptr called_pc, const char *s1, diff --git a/compiler-rt/lib/tsan/CMakeLists.txt b/compiler-rt/lib/tsan/CMakeLists.txt index fcadacfe1bc56..9fd3e2d779249 100644 --- a/compiler-rt/lib/tsan/CMakeLists.txt +++ b/compiler-rt/lib/tsan/CMakeLists.txt @@ -32,7 +32,7 @@ set(TSAN_SOURCES rtl/tsan_fd.cpp rtl/tsan_flags.cpp rtl/tsan_ignoreset.cpp - rtl/tsan_interceptors.cpp + rtl/tsan_interceptors_posix.cpp rtl/tsan_interface.cpp rtl/tsan_interface_ann.cpp rtl/tsan_interface_atomic.cpp diff --git a/compiler-rt/lib/tsan/go/tsan_go.cpp b/compiler-rt/lib/tsan/go/tsan_go.cpp index 271f6bc00cd08..f5998c0c78166 100644 --- a/compiler-rt/lib/tsan/go/tsan_go.cpp +++ b/compiler-rt/lib/tsan/go/tsan_go.cpp @@ -54,20 +54,31 @@ struct SymbolizeCodeContext { }; SymbolizedStack *SymbolizeCode(uptr addr) { - SymbolizedStack *s = SymbolizedStack::New(addr); - SymbolizeCodeContext cbctx; - internal_memset(&cbctx, 0, sizeof(cbctx)); - cbctx.pc = addr; - go_runtime_cb(CallbackSymbolizeCode, &cbctx); - if (cbctx.res) { + SymbolizedStack *first = SymbolizedStack::New(addr); + SymbolizedStack *s = first; + for (;;) { + SymbolizeCodeContext cbctx; + internal_memset(&cbctx, 0, sizeof(cbctx)); + cbctx.pc = addr; + go_runtime_cb(CallbackSymbolizeCode, &cbctx); + if (cbctx.res == 0) + break; AddressInfo &info = s->info; info.module_offset = cbctx.off; info.function = internal_strdup(cbctx.func ? cbctx.func : "??"); info.file = internal_strdup(cbctx.file ? cbctx.file : "-"); info.line = cbctx.line; info.column = 0; + + if (cbctx.pc == addr) // outermost (non-inlined) function + break; + addr = cbctx.pc; + // Allocate a stack entry for the parent of the inlined function. + SymbolizedStack *s2 = SymbolizedStack::New(addr); + s->next = s2; + s = s2; } - return s; + return first; } struct SymbolizeDataContext { diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors.cpp b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp similarity index 99% rename from compiler-rt/lib/tsan/rtl/tsan_interceptors.cpp rename to compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp index 4aa9086a2156f..d1d83e23d5585 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_interceptors.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp @@ -1,4 +1,4 @@ -//===-- tsan_interceptors.cpp ---------------------------------------------===// +//===-- tsan_interceptors_posix.cpp ---------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface.h b/compiler-rt/lib/tsan/rtl/tsan_interface.h index b2f0f30c47ea6..6d7286ca5b8af 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_interface.h +++ b/compiler-rt/lib/tsan/rtl/tsan_interface.h @@ -94,6 +94,11 @@ void __tsan_read_range(void *addr, unsigned long size); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write_range(void *addr, unsigned long size); +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_read_range_pc(void *addr, unsigned long size, void *pc); // NOLINT +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_write_range_pc(void *addr, unsigned long size, void *pc); // NOLINT + // User may provide function that would be called right when TSan detects // an error. The argument 'report' is an opaque pointer that can be used to // gather additional information using other TSan report API functions. diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface_inl.h b/compiler-rt/lib/tsan/rtl/tsan_interface_inl.h index 193b91b2ec2aa..f955ddf99247c 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_interface_inl.h +++ b/compiler-rt/lib/tsan/rtl/tsan_interface_inl.h @@ -122,3 +122,11 @@ void __tsan_read_range(void *addr, uptr size) { void __tsan_write_range(void *addr, uptr size) { MemoryAccessRange(cur_thread(), CALLERPC, (uptr)addr, size, true); } + +void __tsan_read_range_pc(void *addr, uptr size, void *pc) { + MemoryAccessRange(cur_thread(), (uptr)pc, (uptr)addr, size, false); +} + +void __tsan_write_range_pc(void *addr, uptr size, void *pc) { + MemoryAccessRange(cur_thread(), (uptr)pc, (uptr)addr, size, true); +} diff --git a/compiler-rt/lib/tsan/rtl/tsan_report.cpp b/compiler-rt/lib/tsan/rtl/tsan_report.cpp index 655aa5f9123b2..368f1ca8adf2c 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_report.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_report.cpp @@ -298,7 +298,7 @@ static bool FrameIsInternal(const SymbolizedStack *frame) { const char *file = frame->info.file; const char *module = frame->info.module; if (file != 0 && - (internal_strstr(file, "tsan_interceptors.cpp") || + (internal_strstr(file, "tsan_interceptors_posix.cpp") || internal_strstr(file, "sanitizer_common_interceptors.inc") || internal_strstr(file, "tsan_interface_"))) return true; diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp index 39b9cea52f744..3f3c0cce119c1 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp @@ -338,7 +338,7 @@ static void CheckShadowMapping() { #if !SANITIZER_GO static void OnStackUnwind(const SignalContext &sig, const void *, BufferedStackTrace *stack) { - stack->Unwind(sig.pc, sig.bp, sig.context, + stack->Unwind(StackTrace::GetNextInstructionPc(sig.pc), sig.bp, sig.context, common_flags()->fast_unwind_on_fatal); } diff --git a/compiler-rt/lib/ubsan/ubsan_signals_standalone.cpp b/compiler-rt/lib/ubsan/ubsan_signals_standalone.cpp index 627b3c4d89bb0..2c91db8ca3974 100644 --- a/compiler-rt/lib/ubsan/ubsan_signals_standalone.cpp +++ b/compiler-rt/lib/ubsan/ubsan_signals_standalone.cpp @@ -45,8 +45,9 @@ namespace __ubsan { static void OnStackUnwind(const SignalContext &sig, const void *, BufferedStackTrace *stack) { - ubsan_GetStackTrace(stack, kStackTraceMax, sig.pc, sig.bp, sig.context, - common_flags()->fast_unwind_on_fatal); + ubsan_GetStackTrace(stack, kStackTraceMax, + StackTrace::GetNextInstructionPc(sig.pc), sig.bp, + sig.context, common_flags()->fast_unwind_on_fatal); } static void UBsanOnDeadlySignal(int signo, void *siginfo, void *context) { diff --git a/compiler-rt/test/asan/CMakeLists.txt b/compiler-rt/test/asan/CMakeLists.txt index 1892c881849a1..79e34a01619a9 100644 --- a/compiler-rt/test/asan/CMakeLists.txt +++ b/compiler-rt/test/asan/CMakeLists.txt @@ -82,15 +82,22 @@ endforeach() # variable to select which iOS device or simulator to use, e.g.: # SANITIZER_IOSSIM_TEST_DEVICE_IDENTIFIER="iPhone 6" if(APPLE) + # FIXME(dliew): This logic should be refactored to the way UBSan Darwin + # testing is done. set(EXCLUDE_FROM_ALL ON) set(ASAN_TEST_TARGET_CC ${COMPILER_RT_TEST_COMPILER}) set(ASAN_TEST_DYNAMIC True) - foreach(arch ${DARWIN_iossim_ARCHS}) + list_intersect(ASAN_TEST_IOSSIM_ARCHS ASAN_SUPPORTED_ARCH DARWIN_iossim_ARCHS) + foreach(arch ${ASAN_TEST_IOSSIM_ARCHS}) set(ASAN_TEST_APPLE_PLATFORM "iossim") set(ASAN_TEST_TARGET_ARCH ${arch}) - set(ASAN_TEST_TARGET_CFLAGS "-arch ${arch} -isysroot ${DARWIN_iossim_SYSROOT} ${COMPILER_RT_TEST_COMPILER_CFLAGS}") + get_test_cflags_for_apple_platform( + "${ASAN_TEST_APPLE_PLATFORM}" + "${ASAN_TEST_TARGET_ARCH}" + ASAN_TEST_TARGET_CFLAGS + ) set(ASAN_TEST_CONFIG_SUFFIX "-${arch}-${ASAN_TEST_APPLE_PLATFORM}") get_bits_for_arch(${arch} ASAN_TEST_BITS) string(TOUPPER ${arch} ARCH_UPPER_CASE) @@ -104,10 +111,14 @@ if(APPLE) DEPENDS ${ASAN_TEST_DEPS}) endforeach() - foreach (arch ${DARWIN_ios_ARCHS}) + list_intersect(ASAN_TEST_IOS_ARCHS ASAN_SUPPORTED_ARCH DARWIN_ios_ARCHS) + foreach (arch ${ASAN_TEST_IOS_ARCHS}) set(ASAN_TEST_APPLE_PLATFORM "ios") set(ASAN_TEST_TARGET_ARCH ${arch}) - set(ASAN_TEST_TARGET_CFLAGS "-arch ${arch} -isysroot ${DARWIN_ios_SYSROOT} ${COMPILER_RT_TEST_COMPILER_CFLAGS}") + get_test_cflags_for_apple_platform( + "${ASAN_TEST_APPLE_PLATFORM}" + "${arch}" + ASAN_TEST_TARGET_CFLAGS) set(ASAN_TEST_CONFIG_SUFFIX "-${arch}-${ASAN_TEST_APPLE_PLATFORM}") get_bits_for_arch(${arch} ASAN_TEST_BITS) string(TOUPPER ${arch} ARCH_UPPER_CASE) diff --git a/compiler-rt/test/asan/TestCases/inline.cpp b/compiler-rt/test/asan/TestCases/inline.cpp index daeb7b49eb22a..12bd27e675844 100644 --- a/compiler-rt/test/asan/TestCases/inline.cpp +++ b/compiler-rt/test/asan/TestCases/inline.cpp @@ -13,6 +13,7 @@ int f(int *p) { int main(int argc, char **argv) { int * volatile x = (int*)malloc(2*sizeof(int) + 2); int res = f(x + 2); + free(x); if (res) exit(0); return 0; diff --git a/compiler-rt/test/builtins/CMakeLists.txt b/compiler-rt/test/builtins/CMakeLists.txt index 43b7e295acbd0..b80b8a112b5b7 100644 --- a/compiler-rt/test/builtins/CMakeLists.txt +++ b/compiler-rt/test/builtins/CMakeLists.txt @@ -25,9 +25,12 @@ if (MSVC AND NOT "${CMAKE_C_COMPILER_ID}" MATCHES "Clang") endif() pythonize_bool(BUILTINS_IS_MSVC) -#TODO: Add support for Apple. -if (NOT APPLE) -foreach(arch ${BUILTIN_SUPPORTED_ARCH}) +set(BUILTIN_TEST_ARCH ${BUILTIN_SUPPORTED_ARCH}) +if(APPLE) + darwin_filter_host_archs(BUILTIN_SUPPORTED_ARCH BUILTIN_TEST_ARCH) +endif() + +foreach(arch ${BUILTIN_TEST_ARCH}) set(BUILTINS_TEST_TARGET_ARCH ${arch}) string(TOLOWER "-${arch}-${OS_NAME}" BUILTINS_TEST_CONFIG_SUFFIX) get_test_cc_for_arch(${arch} BUILTINS_TEST_TARGET_CC BUILTINS_TEST_TARGET_CFLAGS) @@ -49,7 +52,8 @@ foreach(arch ${BUILTIN_SUPPORTED_ARCH}) ) list(APPEND BUILTINS_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/Unit/${CONFIG_NAME}) endforeach() -endif() + +# TODO: Add support for running tests on iOS and iOS simulator. add_lit_testsuite(check-builtins "Running the Builtins tests" ${BUILTINS_TESTSUITES} diff --git a/compiler-rt/test/builtins/Unit/clear_cache_test.c b/compiler-rt/test/builtins/Unit/clear_cache_test.c index e85cc30271373..4220ba65190f9 100644 --- a/compiler-rt/test/builtins/Unit/clear_cache_test.c +++ b/compiler-rt/test/builtins/Unit/clear_cache_test.c @@ -13,25 +13,16 @@ #include #include #include + #if defined(_WIN32) #include -static uintptr_t get_page_size() { - SYSTEM_INFO si; - GetSystemInfo(&si); - return si.dwPageSize; -} #else #include #include - -static uintptr_t get_page_size() { - return sysconf(_SC_PAGE_SIZE); -} #endif extern void __clear_cache(void* start, void* end); - typedef int (*pfunc)(void); // Make these static to avoid ILT jumps for incremental linking on Windows. @@ -51,34 +42,36 @@ memcpy_f(void *dst, const void *src, size_t n) { #endif } -unsigned char execution_buffer[128] __attribute__((aligned(8))); - int main() { - // make executable the page containing execution_buffer - uintptr_t page_size = get_page_size(); - char* start = (char*)((uintptr_t)execution_buffer & (-page_size)); - char* end = (char*)((uintptr_t)(&execution_buffer[128+page_size]) & (-page_size)); -#if defined(_WIN32) - DWORD dummy_oldProt; - MEMORY_BASIC_INFORMATION b; - if (!VirtualQuery(start, &b, sizeof(b))) - return 1; - if (!VirtualProtect(b.BaseAddress, b.RegionSize, PAGE_EXECUTE_READWRITE, &b.Protect)) + const int kSize = 128; +#if !defined(_WIN32) + uint8_t *execution_buffer = mmap(0, kSize, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_ANON | MAP_PRIVATE, 0, 0); + if (execution_buffer == MAP_FAILED) + return 1; #else - if (mprotect(start, end-start, PROT_READ|PROT_WRITE|PROT_EXEC) != 0) -#endif + HANDLE mapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, + PAGE_EXECUTE_READWRITE, 0, kSize, NULL); + if (mapping == NULL) return 1; + uint8_t* execution_buffer = MapViewOfFile( + mapping, FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE, 0, 0, 0); + if (execution_buffer == NULL) + return 1; +#endif + // verify you can copy and execute a function - pfunc f1 = (pfunc)memcpy_f(execution_buffer, func1, 128); - __clear_cache(execution_buffer, &execution_buffer[128]); + pfunc f1 = (pfunc)memcpy_f(execution_buffer, func1, kSize); + __clear_cache(execution_buffer, execution_buffer + kSize); if ((*f1)() != 1) return 1; // verify you can overwrite a function with another - pfunc f2 = (pfunc)memcpy_f(execution_buffer, func2, 128); - __clear_cache(execution_buffer, &execution_buffer[128]); + pfunc f2 = (pfunc)memcpy_f(execution_buffer, func2, kSize); + __clear_cache(execution_buffer, execution_buffer + kSize); if ((*f2)() != 2) return 1; diff --git a/compiler-rt/test/builtins/Unit/lit.cfg.py b/compiler-rt/test/builtins/Unit/lit.cfg.py index e290ce107169b..44b86f0d63995 100644 --- a/compiler-rt/test/builtins/Unit/lit.cfg.py +++ b/compiler-rt/test/builtins/Unit/lit.cfg.py @@ -29,6 +29,9 @@ def get_required_attr(config, attr_name): base_lib = os.path.join(config.compiler_rt_libdir, "clang_rt.builtins%s.lib " % config.target_suffix) config.substitutions.append( ("%librt ", base_lib) ) +elif config.host_os == 'Darwin': + base_lib = os.path.join(config.compiler_rt_libdir, "libclang_rt.osx.a ") + config.substitutions.append( ("%librt ", base_lib + ' -lSystem ') ) else: base_lib = os.path.join(config.compiler_rt_libdir, "libclang_rt.builtins%s.a" % config.target_suffix) diff --git a/compiler-rt/test/fuzzer/CMakeLists.txt b/compiler-rt/test/fuzzer/CMakeLists.txt index 62161d0019bbe..25bdcd2f0d97f 100644 --- a/compiler-rt/test/fuzzer/CMakeLists.txt +++ b/compiler-rt/test/fuzzer/CMakeLists.txt @@ -89,12 +89,19 @@ if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") endif() if (APPLE) + # FIXME(dliew): This logic should be refactored to the way UBSan Darwin + # testing is done. set(EXCLUDE_FROM_ALL ON) - foreach(arch ${DARWIN_ios_ARCHS}) + list_intersect(FUZZER_TEST_IOS_ARCHS FUZZER_SUPPORTED_ARCH DARWIN_ios_ARCHS) + foreach(arch ${FUZZER_TEST_IOS_ARCHS}) set(LIBFUZZER_TEST_APPLE_PLATFORM "ios") set(LIBFUZZER_TEST_TARGET_ARCH ${arch}) - set(LIBFUZZER_TEST_FLAGS "-arch ${arch} -isysroot ${DARWIN_ios_SYSROOT} ${COMPILER_RT_TEST_COMPILER_CFLAGS}") + get_test_cflags_for_apple_platform( + "${LIBFUZZER_TEST_APPLE_PLATFORM}" + "${LIBFUZZER_TEST_TARGET_ARCH}" + LIBFUZZER_TEST_FLAGS + ) set(LIBFUZZER_TEST_CONFIG_SUFFIX "-${arch}-${LIBFUZZER_TEST_APPLE_PLATFORM}") string(TOUPPER ${arch} ARCH_UPPER_CASE) set(CONFIG_NAME "IOS${ARCH_UPPER_CASE}Config") diff --git a/compiler-rt/test/fuzzer/large.test b/compiler-rt/test/fuzzer/large.test index 99ebbbe4e7da0..b03b60fdb6503 100644 --- a/compiler-rt/test/fuzzer/large.test +++ b/compiler-rt/test/fuzzer/large.test @@ -1,7 +1,4 @@ -REQUIRES: linux RUN: %cpp_compiler %S/LargeTest.cpp -o %t-LargeTest RUN: %run %t-LargeTest -runs=10000 -RUN: %env_asan_opts=handle_segv=0 %run %t-LargeTest -runs=10000 -lazy_counters=1 2>&1 | FileCheck %s -RUN: %run %t-LargeTest -runs=10000 -lazy_counters=1 2>&1 | FileCheck %s CHECK: pages of counters where protected; libFuzzer's SEGV handler must be installed diff --git a/compiler-rt/test/fuzzer/overwrite-input.test b/compiler-rt/test/fuzzer/overwrite-input.test index e5ddd62cbdc70..eef1b181ad759 100644 --- a/compiler-rt/test/fuzzer/overwrite-input.test +++ b/compiler-rt/test/fuzzer/overwrite-input.test @@ -1,3 +1,3 @@ RUN: %cpp_compiler %S/OverwriteInputTest.cpp -o %t-OverwriteInputTest RUN: not %run %t-OverwriteInputTest 2>&1 | FileCheck %s -CHECK: ERROR: libFuzzer: fuzz target overwrites it's const input +CHECK: ERROR: libFuzzer: fuzz target overwrites its const input diff --git a/compiler-rt/test/hwasan/TestCases/stack-oob.c b/compiler-rt/test/hwasan/TestCases/stack-oob.c index ba74930921ab5..8c8c110554928 100644 --- a/compiler-rt/test/hwasan/TestCases/stack-oob.c +++ b/compiler-rt/test/hwasan/TestCases/stack-oob.c @@ -1,4 +1,8 @@ +// RUN: %clang_hwasan_oldrt -DSIZE=2 -O0 %s -o %t && %run %t +// RUN: %clang_hwasan -DSIZE=2 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clang_hwasan_oldrt -DSIZE=15 -O0 %s -o %t && %run %t // RUN: %clang_hwasan -DSIZE=15 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clang_hwasan_oldrt -DSIZE=16 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s // RUN: %clang_hwasan -DSIZE=16 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s // RUN: %clang_hwasan -DSIZE=64 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s // RUN: %clang_hwasan -DSIZE=0x1000 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s @@ -16,9 +20,9 @@ int f() { } int main() { - return f(); + f(); // CHECK: READ of size 1 at - // CHECK: #0 {{.*}} in f{{.*}}stack-oob.c:15 + // CHECK: #0 {{.*}} in f{{.*}}stack-oob.c:19 // CHECK: is located in stack of threa diff --git a/compiler-rt/test/hwasan/lit.cfg.py b/compiler-rt/test/hwasan/lit.cfg.py index eead832197259..ee67a261d9199 100644 --- a/compiler-rt/test/hwasan/lit.cfg.py +++ b/compiler-rt/test/hwasan/lit.cfg.py @@ -18,9 +18,11 @@ # equivalent target feature implemented on x86_64. clang_hwasan_common_cflags += ["-mcmodel=large"] clang_hwasan_cflags = clang_hwasan_common_cflags + ["-mllvm", "-hwasan-globals", + "-mllvm", "-hwasan-use-short-granules", "-mllvm", "-hwasan-instrument-landing-pads=0", "-mllvm", "-hwasan-instrument-personality-functions"] -clang_hwasan_oldrt_cflags = clang_hwasan_common_cflags + ["-mllvm", "-hwasan-instrument-landing-pads=1", +clang_hwasan_oldrt_cflags = clang_hwasan_common_cflags + ["-mllvm", "-hwasan-use-short-granules=0", + "-mllvm", "-hwasan-instrument-landing-pads=1", "-mllvm", "-hwasan-instrument-personality-functions=0"] clang_hwasan_cxxflags = config.cxx_mode_flags + clang_hwasan_cflags @@ -31,6 +33,7 @@ def build_invocation(compile_flags): config.substitutions.append( ("%clangxx ", build_invocation(clang_cxxflags)) ) config.substitutions.append( ("%clang_hwasan ", build_invocation(clang_hwasan_cflags)) ) +config.substitutions.append( ("%clang_hwasan_oldrt ", build_invocation(clang_hwasan_oldrt_cflags)) ) config.substitutions.append( ("%clangxx_hwasan ", build_invocation(clang_hwasan_cxxflags)) ) config.substitutions.append( ("%clangxx_hwasan_oldrt ", build_invocation(clang_hwasan_oldrt_cxxflags)) ) config.substitutions.append( ("%compiler_rt_libdir", config.compiler_rt_libdir) ) diff --git a/compiler-rt/test/lsan/TestCases/Linux/libdl_deadlock.cpp b/compiler-rt/test/lsan/TestCases/Linux/libdl_deadlock.cpp new file mode 100644 index 0000000000000..7c1f82f10beef --- /dev/null +++ b/compiler-rt/test/lsan/TestCases/Linux/libdl_deadlock.cpp @@ -0,0 +1,52 @@ +// Regression test for a deadlock in leak detection, +// where lsan would call dl_iterate_phdr while holding the allocator lock. +// RUN: %clangxx_lsan %s -o %t && %run %t + +#include +#include +#include +#include +#include + +std::mutex in, out; + +int Callback(struct dl_phdr_info *info, size_t size, void *data) { + for (int step = 0; step < 50; ++step) { + void *p[1000]; + for (int i = 0; i < 1000; ++i) + p[i] = malloc(10 * i); + + if (step == 0) + in.unlock(); + + for (int i = 0; i < 1000; ++i) + free(p[i]); + } + out.unlock(); + return 1; // just once +} + +void Watchdog() { + // This is just a fail-safe to turn a deadlock (in case the bug reappears) + // into a (slow) test failure. + usleep(10000000); + if (!out.try_lock()) { + write(2, "DEADLOCK\n", 9); + exit(1); + } +} + +int main() { + in.lock(); + out.lock(); + + std::thread t([] { dl_iterate_phdr(Callback, nullptr); }); + t.detach(); + + std::thread w(Watchdog); + w.detach(); + + // Wait for the malloc thread to preheat, then start leak detection (on exit) + in.lock(); + return 0; +} diff --git a/compiler-rt/test/lsan/TestCases/many_tls_keys_pthread.cpp b/compiler-rt/test/lsan/TestCases/many_tls_keys_pthread.cpp index 5b5d692a59010..ecc577ac4c684 100644 --- a/compiler-rt/test/lsan/TestCases/many_tls_keys_pthread.cpp +++ b/compiler-rt/test/lsan/TestCases/many_tls_keys_pthread.cpp @@ -1,17 +1,16 @@ // Test that lsan handles tls correctly for many threads // RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" -// RUN: %clangxx_lsan %s -DUSE_THREAD -o %t-thread -// RUN: %clangxx_lsan %s -DUSE_PTHREAD -o %t-pthread -// RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=0" not %run %t-thread 2>&1 | FileCheck %s -// RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=1" %run %t-thread 2>&1 -// RUN: %env_lsan_opts="" %run %t-thread 2>&1 -// RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=0" not %run %t-pthread 2>&1 | FileCheck %s -// RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=1" %run %t-pthread 2>&1 -// RUN: %env_lsan_opts="" %run %t-pthread 2>&1 +// RUN: %clangxx_lsan %s -o %t +// RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=1" %run %t 2>&1 +// RUN: %env_lsan_opts="" %run %t 2>&1 // Patch r303906 did not fix all the problems. // UNSUPPORTED: arm-linux,armhf-linux +// TSD on NetBSD does not use TLS +// UNSUPPORTED: netbsd + #include #include #include @@ -24,22 +23,6 @@ pthread_cond_t cond = PTHREAD_COND_INITIALIZER; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int finished = 0; -#if USE_THREAD -__thread void *ptr1; -__thread void *ptr2; -__thread void *ptr3; -__thread void *ptr4; -__thread void *ptr5; - -void alloc() { - ptr1 = malloc(1111); - ptr2 = malloc(2222); - ptr3 = malloc(3333); - ptr4 = malloc(4444); - ptr5 = malloc(5555); -} - -#elif USE_PTHREAD // We won't be able to create the maximum number of keys, due to other users // of the tls, but we'll use as many keys as we can before failing to create // a new key. @@ -59,7 +42,6 @@ void alloc() { void pthread_destructor(void *arg) { assert(0 && "pthread destructors shouldn't be called"); } -#endif void *thread_start(void *arg) { alloc(); @@ -74,14 +56,12 @@ void *thread_start(void *arg) { } int main() { -#if USE_PTHREAD for (int i = 0; i < PTHREAD_KEYS_MAX; ++i) { if (pthread_key_create(&keys[i], pthread_destructor)) { keys[i] = PTHREAD_KEY_INVALID; break; } } -#endif pthread_t thread[NUM_THREADS]; for (int i = 0; i < NUM_THREADS; ++i) { diff --git a/compiler-rt/test/lsan/TestCases/many_tls_keys_thread.cpp b/compiler-rt/test/lsan/TestCases/many_tls_keys_thread.cpp index 7e7196fc1e924..cdba0d1781d9a 100644 --- a/compiler-rt/test/lsan/TestCases/many_tls_keys_thread.cpp +++ b/compiler-rt/test/lsan/TestCases/many_tls_keys_thread.cpp @@ -1,6 +1,6 @@ // Test that lsan handles tls correctly for many threads // RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" -// RUN: %clangxx_lsan %s -DUSE_THREAD -o %t +// RUN: %clangxx_lsan %s -o %t // RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s // RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=1" %run %t 2>&1 // RUN: %env_lsan_opts="" %run %t 2>&1 diff --git a/compiler-rt/test/msan/preinit_array.cpp b/compiler-rt/test/msan/preinit_array.cpp new file mode 100644 index 0000000000000..6f877bac0b1c3 --- /dev/null +++ b/compiler-rt/test/msan/preinit_array.cpp @@ -0,0 +1,16 @@ +// RUN: %clangxx_msan -O0 %s -o %t && %run %t + +#include + +volatile int global; +static void pre_ctor() { + volatile int local; + global = 42; + local = 42; +} + +__attribute__((section(".preinit_array"), used)) void(*__local_pre_ctor)(void) = pre_ctor; + +int main(void) { + return 0; +} diff --git a/compiler-rt/test/profile/Posix/instrprof-gcov-fork.test b/compiler-rt/test/profile/Posix/instrprof-gcov-fork.test index 5a406fda4b671..31b6a23be49cd 100644 --- a/compiler-rt/test/profile/Posix/instrprof-gcov-fork.test +++ b/compiler-rt/test/profile/Posix/instrprof-gcov-fork.test @@ -1,4 +1,5 @@ UNSUPPORTED: linux +UNSUPPORTED: darwin RUN: mkdir -p %t.d RUN: cd %t.d diff --git a/compiler-rt/test/profile/instrprof-merging.cpp b/compiler-rt/test/profile/instrprof-merging.cpp index 06f05ce612aa8..692b049ec45c3 100644 --- a/compiler-rt/test/profile/instrprof-merging.cpp +++ b/compiler-rt/test/profile/instrprof-merging.cpp @@ -1,8 +1,11 @@ // 1) Compile shared code into different object files and into an executable. -// RUN: %clangxx_profgen -fcoverage-mapping %s -c -o %t.v1.o -D_VERSION_1 -// RUN: %clangxx_profgen -fcoverage-mapping %s -c -o %t.v2.o -D_VERSION_2 -// RUN: %clangxx_profgen -fcoverage-mapping %t.v1.o %t.v2.o -o %t.exe +// RUN: %clangxx_profgen -std=c++14 -fcoverage-mapping %s -c -o %t.v1.o \ +// RUN: -D_VERSION_1 +// RUN: %clangxx_profgen -std=c++14 -fcoverage-mapping %s -c -o %t.v2.o \ +// RUN: -D_VERSION_2 +// RUN: %clangxx_profgen -std=c++14 -fcoverage-mapping %t.v1.o %t.v2.o \ +// RUN: -o %t.exe // 2) Collect profile data. diff --git a/compiler-rt/test/profile/instrprof-set-file-object-merging.c b/compiler-rt/test/profile/instrprof-set-file-object-merging.c index 3f71a8103561a..0ca5f6ff9ed95 100644 --- a/compiler-rt/test/profile/instrprof-set-file-object-merging.c +++ b/compiler-rt/test/profile/instrprof-set-file-object-merging.c @@ -1,12 +1,12 @@ // Test that the specified output merges the profiling data. // Run the program twice so that the counters accumulate. // RUN: %clang -fprofile-instr-generate -fcoverage-mapping -o %t %s +// RUN: rm -f %t.merging.profraw %t.merging.profdata // RUN: %run %t %t.merging.profraw // RUN: %run %t %t.merging.profraw // RUN: test -f %t.merging.profraw // RUN: llvm-profdata merge -o %t.merging.profdata %t.merging.profraw // RUN: llvm-cov show -instr-profile %t.merging.profdata %t | FileCheck %s --match-full-lines -// RUN: rm %t.merging.profdata %t.merging.profraw #include extern void __llvm_profile_set_file_object(FILE *, int); diff --git a/compiler-rt/test/sanitizer_common/TestCases/Linux/signal_line.cpp b/compiler-rt/test/sanitizer_common/TestCases/Linux/signal_line.cpp new file mode 100644 index 0000000000000..57175effe55d9 --- /dev/null +++ b/compiler-rt/test/sanitizer_common/TestCases/Linux/signal_line.cpp @@ -0,0 +1,36 @@ +// Test line numbers in signal handlers + +// RUN: %clangxx %s -o %t -O0 +// RUN: %env_tool_opts=handle_segv=1:print_stacktrace=1 not %run %t 1 2>&1 | FileCheck --check-prefixes=CHECK1,CHECK %s +// RUN: %env_tool_opts=handle_segv=1:print_stacktrace=1 not %run %t 2 2>&1 | FileCheck --check-prefixes=CHECK2,CHECK %s +// RUN: %env_tool_opts=handle_segv=1:print_stacktrace=1 not %run %t 3 2>&1 | FileCheck --check-prefixes=CHECK3,CHECK %s +// RUN: %env_tool_opts=handle_segv=1:print_stacktrace=1 not %run %t 4 2>&1 | FileCheck --check-prefixes=CHECK4,CHECK %s + +#include +#include + +// CHECK: [[SAN:.*Sanitizer]]:DEADLYSIGNAL +// CHECK: ERROR: [[SAN]]: SEGV on unknown address {{0x[^ ]*}} (pc +int main(int argc, char **argv) { + int n = atoi(argv[1]); + + if (n == 1) + *((volatile int *)0x0) = __LINE__; + // CHECK1: #{{[0-9]+ .*}}main {{.*}}signal_line.cpp:[[@LINE-1]]:[[TAB:[0-9]+]] + // CHECK1: SUMMARY: [[SAN]]: SEGV {{.*}}signal_line.cpp:[[@LINE-2]]:[[TAB]] in main + + if (n == 2) + *((volatile int *)0x0) = __LINE__; + // CHECK2: #{{[0-9]+ .*}}main {{.*}}signal_line.cpp:[[@LINE-1]]:[[TAB:[0-9]+]] + // CHECK2: SUMMARY: [[SAN]]: SEGV {{.*}}signal_line.cpp:[[@LINE-2]]:[[TAB]] in main + + if (n == 3) + *((volatile int *)0x0) = __LINE__; + // CHECK3: #{{[0-9]+ .*}}main {{.*}}signal_line.cpp:[[@LINE-1]]:[[TAB:[0-9]+]] + // CHECK3: SUMMARY: [[SAN]]: SEGV {{.*}}signal_line.cpp:[[@LINE-2]]:[[TAB]] in main + + if (n == 4) + *((volatile int *)0x0) = __LINE__; + // CHECK4: #{{[0-9]+ .*}}main {{.*}}signal_line.cpp:[[@LINE-1]]:[[TAB:[0-9]+]] + // CHECK4: SUMMARY: [[SAN]]: SEGV {{.*}}signal_line.cpp:[[@LINE-2]]:[[TAB]] in main +} diff --git a/compiler-rt/test/sanitizer_common/TestCases/onprint.cpp b/compiler-rt/test/sanitizer_common/TestCases/onprint.cpp new file mode 100644 index 0000000000000..c5afb824714b8 --- /dev/null +++ b/compiler-rt/test/sanitizer_common/TestCases/onprint.cpp @@ -0,0 +1,33 @@ +// Checks that the __sanitizer_on_print hook gets the exact same sanitizer +// report as what is printed to stderr. +// +// RUN: %clangxx %s -o %t +// RUN: %run %t %t-onprint.txt 2>%t-stderr.txt || true +// RUN: diff %t-onprint.txt %t-stderr.txt +// +// UNSUPPORTED: android + +#include +#include +#include + +FILE *f; +volatile void *buf; +volatile char sink; + +extern "C" void __sanitizer_on_print(const char *str) { + fprintf(f, "%s", str); + fflush(f); +} + +int main(int argc, char *argv[]) { + assert(argc >= 2); + f = fopen(argv[1], "w"); + + // Use-after-free to trigger ASan/TSan reports. + void *ptr = malloc(1); + buf = ptr; + free(ptr); + sink = *static_cast(ptr); + return 0; +} diff --git a/compiler-rt/test/tsan/CMakeLists.txt b/compiler-rt/test/tsan/CMakeLists.txt index f21fc2a3beec8..9159e75129b09 100644 --- a/compiler-rt/test/tsan/CMakeLists.txt +++ b/compiler-rt/test/tsan/CMakeLists.txt @@ -49,39 +49,52 @@ endforeach() # variable to select which iOS device or simulator to use, e.g.: # SANITIZER_IOSSIM_TEST_DEVICE_IDENTIFIER="iPhone 6" if(APPLE) + # FIXME(dliew): This logic should be refactored to the way UBSan Darwin + # testing is done. set(EXCLUDE_FROM_ALL ON) - set(TSAN_TEST_TARGET_CC ${COMPILER_RT_TEST_COMPILER}) - set(TSAN_TEST_APPLE_PLATFORM "iossim") - set(arch "x86_64") - set(TSAN_TEST_TARGET_ARCH ${arch}) - set(TSAN_TEST_TARGET_CFLAGS "-arch ${arch} -isysroot ${DARWIN_iossim_SYSROOT} ${COMPILER_RT_TEST_COMPILER_CFLAGS}") - set(TSAN_TEST_CONFIG_SUFFIX "-${arch}-${TSAN_TEST_APPLE_PLATFORM}") - string(TOUPPER ${arch} ARCH_UPPER_CASE) - set(CONFIG_NAME "IOSSim${ARCH_UPPER_CASE}Config") - configure_lit_site_cfg( - ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in - ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg.py - ) - add_lit_testsuite(check-tsan-iossim-${arch} "ThreadSanitizer iOS Simulator ${arch} tests" - ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/ - DEPENDS ${TSAN_TEST_DEPS}) + list_intersect(TSAN_TEST_IOSSIM_ARCHS TSAN_SUPPORTED_ARCH DARWIN_iossim_ARCHS) + foreach(arch ${TSAN_TEST_IOSSIM_ARCHS}) + set(TSAN_TEST_APPLE_PLATFORM "iossim") + set(TSAN_TEST_TARGET_ARCH ${arch}) + get_test_cflags_for_apple_platform( + "${TSAN_TEST_APPLE_PLATFORM}" + "${TSAN_TEST_TARGET_ARCH}" + TSAN_TEST_TARGET_CFLAGS + ) + set(TSAN_TEST_CONFIG_SUFFIX "-${arch}-${TSAN_TEST_APPLE_PLATFORM}") + string(TOUPPER ${arch} ARCH_UPPER_CASE) + set(CONFIG_NAME "IOSSim${ARCH_UPPER_CASE}Config") + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in + ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg.py + ) + add_lit_testsuite(check-tsan-iossim-${arch} "ThreadSanitizer iOS Simulator ${arch} tests" + ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/ + DEPENDS ${TSAN_TEST_DEPS}) + endforeach() - set(TSAN_TEST_APPLE_PLATFORM "ios") - set(arch "arm64") - set(TSAN_TEST_TARGET_ARCH ${arch}) - set(TSAN_TEST_TARGET_CFLAGS "-arch ${arch} -isysroot ${DARWIN_ios_SYSROOT} ${COMPILER_RT_TEST_COMPILER_CFLAGS}") - set(TSAN_TEST_CONFIG_SUFFIX "-${arch}-${TSAN_TEST_APPLE_PLATFORM}") - string(TOUPPER ${arch} ARCH_UPPER_CASE) - set(CONFIG_NAME "IOS${ARCH_UPPER_CASE}Config") - configure_lit_site_cfg( - ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in - ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg.py - ) - add_lit_testsuite(check-tsan-ios-${arch} "ThreadSanitizer iOS Simulator ${arch} tests" - ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/ - DEPENDS ${TSAN_TEST_DEPS}) + list_intersect(TSAN_TEST_IOS_ARCHS TSAN_SUPPORTED_ARCH DARWIN_ios_ARCHS) + foreach(arch ${TSAN_TEST_IOS_ARCHS}) + set(TSAN_TEST_APPLE_PLATFORM "ios") + set(TSAN_TEST_TARGET_ARCH ${arch}) + get_test_cflags_for_apple_platform( + "${TSAN_TEST_APPLE_PLATFORM}" + "${TSAN_TEST_TARGET_ARCH}" + TSAN_TEST_TARGET_CFLAGS + ) + set(TSAN_TEST_CONFIG_SUFFIX "-${arch}-${TSAN_TEST_APPLE_PLATFORM}") + string(TOUPPER ${arch} ARCH_UPPER_CASE) + set(CONFIG_NAME "IOS${ARCH_UPPER_CASE}Config") + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in + ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg.py + ) + add_lit_testsuite(check-tsan-ios-${arch} "ThreadSanitizer iOS ${arch} tests" + ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/ + DEPENDS ${TSAN_TEST_DEPS}) + endforeach() set(EXCLUDE_FROM_ALL OFF) endif() diff --git a/compiler-rt/test/tsan/race_range_pc.cc b/compiler-rt/test/tsan/race_range_pc.cc new file mode 100644 index 0000000000000..a4689bd93ff49 --- /dev/null +++ b/compiler-rt/test/tsan/race_range_pc.cc @@ -0,0 +1,40 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +// This test fails on powerpc64 big endian. +// The Tsan report is returning wrong information about +// the location of the race. +// XFAIL: powerpc64-unknown-linux-gnu + +#include "test.h" + +typedef unsigned long uptr; +extern "C" void __tsan_read_range_pc(uptr addr, uptr size, uptr pc); +extern "C" void __tsan_write_range_pc(uptr addr, uptr size, uptr pc); + +void foobar() { +} + +void barbaz() { +} + +void *Thread(void *p) { + barrier_wait(&barrier); + __tsan_read_range_pc((uptr)p, 32, (uptr)foobar + kPCInc); + return 0; +} + +int main() { + barrier_init(&barrier, 2); + int a[128]; + pthread_t th; + pthread_create(&th, 0, Thread, (void*)a); + __tsan_write_range_pc((uptr)(a+2), 32, (uptr)barbaz + kPCInc); + barrier_wait(&barrier); + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: #0 foobar +// CHECK: #0 barbaz +// CHECK: DONE diff --git a/compiler-rt/test/ubsan/CMakeLists.txt b/compiler-rt/test/ubsan/CMakeLists.txt index ee7032a915e97..9f30718dd1cdf 100644 --- a/compiler-rt/test/ubsan/CMakeLists.txt +++ b/compiler-rt/test/ubsan/CMakeLists.txt @@ -101,22 +101,20 @@ if(APPLE) # variable to select which iOS device or simulator to use, e.g.: # SANITIZER_IOSSIM_TEST_DEVICE_IDENTIFIER="iPhone 6" set(EXCLUDE_FROM_ALL ON) - set(UBSAN_APPLE_PLATFORMS "") - if (COMPILER_RT_ENABLE_IOS) - list(APPEND UBSAN_APPLE_PLATFORMS ios iossim) - endif() - if (COMPILER_RT_ENABLE_WATCHOS) - list(APPEND UBSAN_APPLE_PLATFORMS watchos watchossim) - endif() - if (COMPILER_RT_ENABLE_TVOS) - list(APPEND UBSAN_APPLE_PLATFORMS tvos tvossim) - endif() + set(UBSAN_APPLE_PLATFORMS ${SANITIZER_COMMON_SUPPORTED_OS}) foreach(platform ${UBSAN_APPLE_PLATFORMS}) - foreach(arch ${DARWIN_${platform}_ARCHS}) - set(UBSAN_TEST_TARGET_CFLAGS "-arch ${arch} -isysroot ${DARWIN_${platform}_SYSROOT}") - if (";${UBSAN_SUPPORTED_ARCH};" MATCHES ";${arch};") - add_ubsan_device_testsuite("Standalone" ubsan ${platform} ${arch}) - endif() + list_intersect( + UBSAN_TEST_${platform}_ARCHS + UBSAN_SUPPORTED_ARCH + DARWIN_${platform}_ARCHS + ) + foreach(arch ${UBSAN_TEST_${platform}_ARCHS}) + get_test_cflags_for_apple_platform( + "${platform}" + "${arch}" + UBSAN_TEST_TARGET_CFLAGS + ) + add_ubsan_device_testsuite("Standalone" ubsan ${platform} ${arch}) if(COMPILER_RT_HAS_ASAN AND ";${ASAN_SUPPORTED_ARCH};" MATCHES ";${arch};") add_ubsan_device_testsuite("AddressSanitizer" asan ${platform} ${arch}) diff --git a/libc/LICENSE.txt b/libc/LICENSE.txt new file mode 100644 index 0000000000000..24806ab4c9eb2 --- /dev/null +++ b/libc/LICENSE.txt @@ -0,0 +1,278 @@ +============================================================================== +The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: +============================================================================== + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + +============================================================================== +Software from third parties included in the LLVM Project: +============================================================================== +The LLVM Project contains third party software which is under different license +terms. All such code will be identified clearly using at least one of two +mechanisms: +1) It will be in a separate directory tree with its own `LICENSE.txt` or + `LICENSE` file at the top containing the specific license and restrictions + which apply to that software, or +2) It will contain specific license and restriction terms at the top of every + file. + +============================================================================== +Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): +============================================================================== +University of Illinois/NCSA +Open Source License + +Copyright (c) 2007-2019 University of Illinois at Urbana-Champaign. +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. diff --git a/libc/README.txt b/libc/README.txt new file mode 100644 index 0000000000000..2eb8591cdacb4 --- /dev/null +++ b/libc/README.txt @@ -0,0 +1,8 @@ +LLVM libc +========= + +This directory and its subdirectories contain source code for llvm-libc, +a retargetable implementation of the C standard library. + +LLVM is open source software. You may freely distribute it under the terms of +the license agreement found in LICENSE.txt. diff --git a/libclc/.travis.yml b/libclc/.travis.yml index 0c9992067bf06..e8a41d98d83a2 100644 --- a/libclc/.travis.yml +++ b/libclc/.travis.yml @@ -1,11 +1,6 @@ language: cpp -sudo: false -dist: trusty - -cache: - apt: true - +dist: xenial matrix: include: @@ -15,12 +10,7 @@ matrix: - CHECK_FILES="barts-r600--.bc cayman-r600--.bc cedar-r600--.bc cypress-r600--.bc tahiti-amdgcn--.bc amdgcn--amdhsa.bc nvptx--nvidiacl.bc nvptx64--nvidiacl.bc" addons: apt: - sources: - - llvm-toolchain-trusty-3.9 packages: - - libedit-dev - - g++-4.8 - # From sources above - llvm-3.9-dev - clang-3.9 - env: @@ -29,12 +19,7 @@ matrix: - CHECK_FILES="barts-r600--.bc cayman-r600--.bc cedar-r600--.bc cypress-r600--.bc tahiti-amdgcn--.bc amdgcn--amdhsa.bc tahiti-amdgcn-mesa-mesa3d.bc nvptx--nvidiacl.bc nvptx64--nvidiacl.bc" addons: apt: - sources: - - llvm-toolchain-trusty-4.0 packages: - - libedit-dev - - g++-4.8 - # From sources above - llvm-4.0-dev - clang-4.0 - env: @@ -43,41 +28,28 @@ matrix: - CHECK_FILES="barts-r600--.bc cayman-r600--.bc cedar-r600--.bc cypress-r600--.bc tahiti-amdgcn--.bc amdgcn--amdhsa.bc tahiti-amdgcn-mesa-mesa3d.bc nvptx--nvidiacl.bc nvptx64--nvidiacl.bc" addons: apt: - sources: - - llvm-toolchain-trusty-5.0 packages: - - libedit-dev - - g++-4.8 - # From sources above - llvm-5.0-dev - clang-5.0 - env: - LABEL="make gcc LLVM-6.0" - LLVM_VERSION=6.0 - CHECK_FILES="barts-r600--.bc cayman-r600--.bc cedar-r600--.bc cypress-r600--.bc tahiti-amdgcn--.bc amdgcn--amdhsa.bc tahiti-amdgcn-mesa-mesa3d.bc nvptx--nvidiacl.bc nvptx64--nvidiacl.bc" - # llvm passes -Werror=date-time which is only supported in gcc-4.9+ - - MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9" addons: apt: - sources: - - llvm-toolchain-trusty-6.0 - - ubuntu-toolchain-r-test packages: - - libedit-dev - # LLVM-6 needs libstdc++4.9 - - g++-4.9 - # From sources above - llvm-6.0-dev - clang-6.0 - env: - LABEL="make gcc LLVM-7" - LLVM_VERSION=7 - CHECK_FILES="barts-r600--.bc cayman-r600--.bc cedar-r600--.bc cypress-r600--.bc tahiti-amdgcn--.bc amdgcn--amdhsa.bc tahiti-amdgcn-mesa-mesa3d.bc nvptx--nvidiacl.bc nvptx64--nvidiacl.bc" + # llvm passes -Werror=date-time which is only supported in gcc-4.9+ - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" addons: apt: sources: - - sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-7 main' + - sourceline: 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-7 main' key_url: https://apt.llvm.org/llvm-snapshot.gpg.key - ubuntu-toolchain-r-test packages: @@ -90,11 +62,12 @@ matrix: - LABEL="make gcc LLVM-8" - LLVM_VERSION=8 - CHECK_FILES="barts-r600--.bc cayman-r600--.bc cedar-r600--.bc cypress-r600--.bc tahiti-amdgcn--.bc amdgcn--amdhsa.bc tahiti-amdgcn-mesa-mesa3d.bc nvptx--nvidiacl.bc nvptx64--nvidiacl.bc" + # llvm passes -Werror=date-time which is only supported in gcc-4.9+ - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" addons: apt: sources: - - sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-8 main' + - sourceline: 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-8 main' key_url: https://apt.llvm.org/llvm-snapshot.gpg.key - ubuntu-toolchain-r-test packages: @@ -104,17 +77,29 @@ matrix: - llvm-8-dev - clang-8 - env: - - LABEL="cmake gcc LLVM-3.9" - - LLVM_VERSION=3.9 - - CHECK_FILES="barts-r600--.bc cayman-r600--.bc cedar-r600--.bc cypress-r600--.bc tahiti-amdgcn--.bc amdgcn--amdhsa.bc nvptx--nvidiacl.bc nvptx64--nvidiacl.bc" + - LABEL="make gcc LLVM-9" + - LLVM_VERSION=9 + - CHECK_FILES="barts-r600--.bc cayman-r600--.bc cedar-r600--.bc cypress-r600--.bc tahiti-amdgcn--.bc amdgcn--amdhsa.bc tahiti-amdgcn-mesa-mesa3d.bc nvptx--nvidiacl.bc nvptx64--nvidiacl.bc" + - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" addons: apt: sources: - - llvm-toolchain-trusty-3.9 + - sourceline: 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main' + key_url: https://apt.llvm.org/llvm-snapshot.gpg.key + - ubuntu-toolchain-r-test packages: - libedit-dev - - g++-4.8 + - g++-6 # From sources above + - llvm-9-dev + - clang-9 + - env: + - LABEL="cmake gcc LLVM-3.9" + - LLVM_VERSION=3.9 + - CHECK_FILES="barts-r600--.bc cayman-r600--.bc cedar-r600--.bc cypress-r600--.bc tahiti-amdgcn--.bc amdgcn--amdhsa.bc nvptx--nvidiacl.bc nvptx64--nvidiacl.bc" + addons: + apt: + packages: - llvm-3.9-dev - clang-3.9 - env: @@ -123,12 +108,7 @@ matrix: - CHECK_FILES="barts-r600--.bc cayman-r600--.bc cedar-r600--.bc cypress-r600--.bc tahiti-amdgcn--.bc amdgcn--amdhsa.bc tahiti-amdgcn-mesa-mesa3d.bc nvptx--nvidiacl.bc nvptx64--nvidiacl.bc" addons: apt: - sources: - - llvm-toolchain-trusty-4.0 packages: - - libedit-dev - - g++-4.8 - # From sources above - llvm-4.0-dev - clang-4.0 - env: @@ -137,41 +117,28 @@ matrix: - CHECK_FILES="barts-r600--.bc cayman-r600--.bc cedar-r600--.bc cypress-r600--.bc tahiti-amdgcn--.bc amdgcn--amdhsa.bc tahiti-amdgcn-mesa-mesa3d.bc nvptx--nvidiacl.bc nvptx64--nvidiacl.bc" addons: apt: - sources: - - llvm-toolchain-trusty-5.0 packages: - - libedit-dev - - g++-4.8 - # From sources above - llvm-5.0-dev - clang-5.0 - env: - LABEL="cmake gcc LLVM-6.0" - LLVM_VERSION=6.0 - CHECK_FILES="barts-r600--.bc cayman-r600--.bc cedar-r600--.bc cypress-r600--.bc tahiti-amdgcn--.bc amdgcn--amdhsa.bc tahiti-amdgcn-mesa-mesa3d.bc nvptx--nvidiacl.bc nvptx64--nvidiacl.bc" - # llvm passes -Werror=date-time which is only supported in gcc-4.9+ - - MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9" addons: apt: - sources: - - llvm-toolchain-trusty-6.0 - - ubuntu-toolchain-r-test packages: - - libedit-dev - # LLVM-6 needs libstdc++4.9 - - g++-4.9 - # From sources above - llvm-6.0-dev - clang-6.0 - env: - LABEL="cmake gcc LLVM-7" - LLVM_VERSION=7 - CHECK_FILES="barts-r600--.bc cayman-r600--.bc cedar-r600--.bc cypress-r600--.bc tahiti-amdgcn--.bc amdgcn--amdhsa.bc tahiti-amdgcn-mesa-mesa3d.bc nvptx--nvidiacl.bc nvptx64--nvidiacl.bc" + # llvm passes -Werror=date-time which is only supported in gcc-4.9+ - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" addons: apt: sources: - - sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-7 main' + - sourceline: 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-7 main' key_url: https://apt.llvm.org/llvm-snapshot.gpg.key - ubuntu-toolchain-r-test packages: @@ -184,11 +151,12 @@ matrix: - LABEL="cmake gcc LLVM-8" - LLVM_VERSION=8 - CHECK_FILES="barts-r600--.bc cayman-r600--.bc cedar-r600--.bc cypress-r600--.bc tahiti-amdgcn--.bc amdgcn--amdhsa.bc tahiti-amdgcn-mesa-mesa3d.bc nvptx--nvidiacl.bc nvptx64--nvidiacl.bc" + # llvm passes -Werror=date-time which is only supported in gcc-4.9+ - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" addons: apt: sources: - - sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-8 main' + - sourceline: 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-8 main' key_url: https://apt.llvm.org/llvm-snapshot.gpg.key - ubuntu-toolchain-r-test packages: @@ -197,6 +165,23 @@ matrix: # From sources above - llvm-8-dev - clang-8 + - env: + - LABEL="cmake gcc LLVM-9" + - LLVM_VERSION=9 + - CHECK_FILES="barts-r600--.bc cayman-r600--.bc cedar-r600--.bc cypress-r600--.bc tahiti-amdgcn--.bc amdgcn--amdhsa.bc tahiti-amdgcn-mesa-mesa3d.bc nvptx--nvidiacl.bc nvptx64--nvidiacl.bc" + - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" + addons: + apt: + sources: + - sourceline: 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main' + key_url: https://apt.llvm.org/llvm-snapshot.gpg.key + - ubuntu-toolchain-r-test + packages: + - libedit-dev + - g++-6 + # From sources above + - llvm-9-dev + - clang-9 before_install: - eval "${MATRIX_EVAL}" diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt index 37a1e3fc0821e..54265633ff91e 100644 --- a/libcxx/CMakeLists.txt +++ b/libcxx/CMakeLists.txt @@ -522,65 +522,75 @@ remove_flags("/D_DEBUG" "/MTd" "/MDd" "/MT" "/Md") remove_flags(-Wno-pedantic -pedantic-errors -pedantic) # Required flags ============================================================== -if (LIBCXX_HAS_MUSL_LIBC OR LIBCXX_TARGETING_CLANG_CL) - # musl's pthread implementations uses volatile types in their structs which is - # not a constexpr in C++11 but is in C++14, so we use C++14 with musl. - set(LIBCXX_STANDARD_VER c++14 CACHE STRING "internal option to change build dialect") -else() - set(LIBCXX_STANDARD_VER c++11 CACHE STRING "internal option to change build dialect") -endif() -add_compile_flags_if_supported(-std=${LIBCXX_STANDARD_VER}) -add_compile_flags_if_supported("/std:${LIBCXX_STANDARD_VER}") -mangle_name("LIBCXX_SUPPORTS_STD_EQ_${LIBCXX_STANDARD_VER}_FLAG" SUPPORTS_DIALECT_NAME) -mangle_name("LIBCXX_SUPPORTS_STD_COLON_${LIBCXX_STANDARD_VER}_FLAG" SUPPORTS_DIALECT_NAME_MSVC) -if(NOT ${SUPPORTS_DIALECT_NAME} AND NOT ${SUPPORTS_DIALECT_NAME_MSVC}) - if(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" AND NOT "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC") - message(FATAL_ERROR "C++11 or greater is required but the compiler does not support ${LIBCXX_STANDARD_VER}") +function(cxx_add_basic_build_flags target) + if (LIBCXX_HAS_MUSL_LIBC OR LIBCXX_TARGETING_CLANG_CL) + # musl's pthread implementations uses volatile types in their structs which is + # not a constexpr in C++11 but is in C++14, so we use C++14 with musl. + set(LIBCXX_STANDARD_VER c++14 CACHE STRING "internal option to change build dialect") + else() + set(LIBCXX_STANDARD_VER c++11 CACHE STRING "internal option to change build dialect") + endif() + target_add_compile_flags_if_supported(${target} PRIVATE -std=${LIBCXX_STANDARD_VER}) + target_add_compile_flags_if_supported(${target} PRIVATE "/std:${LIBCXX_STANDARD_VER}") + mangle_name("LIBCXX_SUPPORTS_STD_EQ_${LIBCXX_STANDARD_VER}_FLAG" SUPPORTS_DIALECT_NAME) + mangle_name("LIBCXX_SUPPORTS_STD_COLON_${LIBCXX_STANDARD_VER}_FLAG" SUPPORTS_DIALECT_NAME_MSVC) + if(NOT ${SUPPORTS_DIALECT_NAME} AND NOT ${SUPPORTS_DIALECT_NAME_MSVC}) + if(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" AND NOT "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC") + message(FATAL_ERROR "C++11 or greater is required but the compiler does not support ${LIBCXX_STANDARD_VER}") + endif() endif() -endif() - -# On all systems the system c++ standard library headers need to be excluded. -# MSVC only has -X, which disables all default includes; including the crt. -# Thus, we do nothing and hope we don't accidentally include any of the C++ -# headers -add_compile_flags_if_supported(-nostdinc++) -# Hide all inline function definitions which have not explicitly been marked -# visible. This prevents new definitions for inline functions from appearing in -# the dylib when get ODR used by another function. -add_compile_flags_if_supported(-fvisibility-inlines-hidden) + # On all systems the system c++ standard library headers need to be excluded. + # MSVC only has -X, which disables all default includes; including the crt. + # Thus, we do nothing and hope we don't accidentally include any of the C++ + # headers + target_add_compile_flags_if_supported(${target} PUBLIC -nostdinc++) + + # Hide all inline function definitions which have not explicitly been marked + # visible. This prevents new definitions for inline functions from appearing in + # the dylib when get ODR used by another function. + target_add_compile_flags_if_supported(${target} PRIVATE -fvisibility-inlines-hidden) + + # Our visibility annotations are not quite right for non-Clang compilers, + # so we end up not exporting all the symbols we should. In the future, we + # can improve the situation by providing an explicit list of exported + # symbols on all compilers. + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_add_compile_flags_if_supported(${target} PRIVATE -fvisibility=hidden) + endif() -if (LIBCXX_CONFIGURE_IDE) - # This simply allows IDE to process - add_compile_flags_if_supported(-fcoroutines-ts) -endif() + if (LIBCXX_CONFIGURE_IDE) + # This simply allows IDE to process + target_add_compile_flags_if_supported(${target} PRIVATE -fcoroutines-ts) + endif() -# Let the library headers know they are currently being used to build the -# library. -add_definitions(-D_LIBCPP_BUILDING_LIBRARY) + # Let the library headers know they are currently being used to build the + # library. + target_compile_definitions(${target} PRIVATE -D_LIBCPP_BUILDING_LIBRARY) -if (NOT LIBCXX_ENABLE_NEW_DELETE_DEFINITIONS) - add_definitions(-D_LIBCPP_DISABLE_NEW_DELETE_DEFINITIONS) -endif() + if (NOT LIBCXX_ENABLE_NEW_DELETE_DEFINITIONS) + target_compile_definitions(${target} PRIVATE -D_LIBCPP_DISABLE_NEW_DELETE_DEFINITIONS) + endif() -if (LIBCXX_HAS_COMMENT_LIB_PRAGMA) - add_definitions(-D_LIBCPP_HAS_COMMENT_LIB_PRAGMA) -endif() + if (LIBCXX_HAS_COMMENT_LIB_PRAGMA) + target_compile_definitions(${target} PRIVATE -D_LIBCPP_HAS_COMMENT_LIB_PRAGMA) + endif() +endfunction() # Warning flags =============================================================== -add_definitions(-D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) -add_compile_flags_if_supported( - -Wall -Wextra -W -Wwrite-strings - -Wno-unused-parameter -Wno-long-long - -Werror=return-type -Wextra-semi) -if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - add_compile_flags_if_supported( - -Wno-user-defined-literals - -Wno-covered-switch-default - -Wno-ignored-attributes # FIXME: Caused by _LIBCPP_NODEBUG_TYPE not being supported on older clangs - ) +function(cxx_add_warning_flags target) + target_compile_definitions(${target} PUBLIC -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) + target_add_compile_flags_if_supported(${target} PRIVATE -Wall -Wextra -W -Wwrite-strings + -Wno-unused-parameter -Wno-long-long + -Werror=return-type -Wextra-semi) + if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + target_add_compile_flags_if_supported(${target} PRIVATE + -Wno-user-defined-literals + -Wno-covered-switch-default + -Wno-ignored-attributes # FIXME: Caused by _LIBCPP_NODEBUG_TYPE not being supported on older clangs + ) if (LIBCXX_TARGETING_CLANG_CL) - add_compile_flags_if_supported( + target_add_compile_flags_if_supported(${target} PRIVATE -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-c++11-compat @@ -597,26 +607,27 @@ if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") -Wno-double-promotion # FIXME: remove me ) endif() -elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") - add_compile_flags_if_supported( - -Wno-literal-suffix - -Wno-c++14-compat - -Wno-noexcept-type) -endif() -if (LIBCXX_ENABLE_WERROR) - add_compile_flags_if_supported(-Werror) - add_compile_flags_if_supported(-WX) -else() - # TODO(EricWF) Remove this. We shouldn't be suppressing errors when -Werror is - # added elsewhere. - add_compile_flags_if_supported(-Wno-error) -endif() -if (LIBCXX_ENABLE_PEDANTIC) - add_compile_flags_if_supported(-pedantic) -endif() -if (LIBCXX_DISABLE_MACRO_CONFLICT_WARNINGS) - add_definitions(-D_LIBCPP_DISABLE_MACRO_CONFLICT_WARNINGS) -endif() + elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") + target_add_compile_flags_if_supported(${target} PRIVATE + -Wno-literal-suffix + -Wno-c++14-compat + -Wno-noexcept-type) + endif() + if (LIBCXX_ENABLE_WERROR) + target_add_compile_flags_if_supported(${target} PRIVATE -Werror) + target_add_compile_flags_if_supported(${target} PRIVATE -WX) + else() + # TODO(EricWF) Remove this. We shouldn't be suppressing errors when -Werror is + # added elsewhere. + target_add_compile_flags_if_supported(${target} PRIVATE -Wno-error) + endif() + if (LIBCXX_ENABLE_PEDANTIC) + target_add_compile_flags_if_supported(${target} PRIVATE -pedantic) + endif() + if (LIBCXX_DISABLE_MACRO_CONFLICT_WARNINGS) + target_compile_definitions(${target} PRIVATE -D_LIBCPP_DISABLE_MACRO_CONFLICT_WARNINGS) + endif() +endfunction() # Exception flags ============================================================= if (LIBCXX_ENABLE_EXCEPTIONS) @@ -713,6 +724,31 @@ if (LIBCXX_STANDALONE_BUILD AND SANITIZER_FLAGS) add_flags(${SANITIZER_FLAGS}) endif() +# Windows-related flags ======================================================= +function(cxx_add_windows_flags target) + if(WIN32 AND NOT MINGW) + target_compile_definitions(${target} PRIVATE + # Ignore the -MSC_VER mismatch, as we may build + # with a different compatibility version. + _ALLOW_MSC_VER_MISMATCH + # Don't check the msvcprt iterator debug levels + # as we will define the iterator types; libc++ + # uses a different macro to identify the debug + # level. + _ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH + # We are building the c++ runtime, don't pull in + # msvcprt. + _CRTBLD + # Don't warn on the use of "deprecated" + # "insecure" functions which are standards + # specified. + _CRT_SECURE_NO_WARNINGS + # Use the ISO conforming behaviour for conversion + # in printf, scanf. + _CRT_STDIO_ISO_WIDE_SPECIFIERS) + endif() +endfunction() + # Configuration file flags ===================================================== if (NOT LIBCXX_ABI_VERSION EQUAL 1) config_define(${LIBCXX_ABI_VERSION} _LIBCPP_ABI_VERSION) diff --git a/libcxx/docs/BuildingLibcxx.rst b/libcxx/docs/BuildingLibcxx.rst index 76aa18a047e51..e6a21a930f014 100644 --- a/libcxx/docs/BuildingLibcxx.rst +++ b/libcxx/docs/BuildingLibcxx.rst @@ -317,7 +317,7 @@ libc++ Feature Options **Values**:: ``libc++``, ``libstdc++`` Build the libc++ benchmark tests and Google Benchmark library against the - specified standard library on the platform. On linux this can be used to + specified standard library on the platform. On Linux this can be used to compare libc++ to libstdc++ by building the benchmark tests against both standard libraries. @@ -404,7 +404,7 @@ LLVM-specific options .. option:: LLVM_BUILD_32_BITS:BOOL Build 32-bits executables and libraries on 64-bits systems. This option is - available only on some 64-bits unix systems. Defaults to OFF. + available only on some 64-bits Unix systems. Defaults to OFF. .. option:: LLVM_LIT_ARGS:STRING @@ -527,7 +527,7 @@ These instructions should only be used when you can't install your ABI library. Normally you must link libc++ against a ABI shared library that the linker can find. If you want to build and test libc++ against an ABI -library not in the linker's path you needq to set +library not in the linker's path you need to set ``-DLIBCXX_CXX_ABI_LIBRARY_PATH=/path/to/abi/lib`` when configuring CMake. An example build using libc++abi would look like: diff --git a/libcxx/include/__config b/libcxx/include/__config index f631faa00345e..ee7351e9313d5 100644 --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -1087,6 +1087,16 @@ _LIBCPP_FUNC_VIS extern "C" void __sanitizer_annotate_contiguous_container( # endif // _LIBCPP_HAS_THREAD_API #endif // _LIBCPP_HAS_NO_THREADS +#if defined(_LIBCPP_HAS_THREAD_API_PTHREAD) +#if defined(__ANDROID__) && __ANDROID_API__ >= 30 +#define _LIBCPP_HAS_COND_CLOCKWAIT +#elif defined(_LIBCPP_GLIBC_PREREQ) +#if _LIBCPP_GLIBC_PREREQ(2, 30) +#define _LIBCPP_HAS_COND_CLOCKWAIT +#endif +#endif +#endif + #if defined(_LIBCPP_HAS_NO_THREADS) && defined(_LIBCPP_HAS_THREAD_API_PTHREAD) #error _LIBCPP_HAS_THREAD_API_PTHREAD may only be defined when \ _LIBCPP_HAS_NO_THREADS is not defined. @@ -1441,6 +1451,17 @@ _LIBCPP_FUNC_VIS extern "C" void __sanitizer_annotate_contiguous_container( #define _LIBCPP_UNUSED_VAR(x) ((void)(x)) +// Configures the fopen close-on-exec mode character, if any. This string will +// be appended to any mode string used by fstream for fopen/fdopen. +// +// Not all platforms support this, but it helps avoid fd-leaks on platforms that +// do. +#if defined(__BIONIC__) +# define _LIBCPP_FOPEN_CLOEXEC_MODE "e" +#else +# define _LIBCPP_FOPEN_CLOEXEC_MODE +#endif + #endif // __cplusplus #endif // _LIBCPP_CONFIG diff --git a/libcxx/include/__functional_base b/libcxx/include/__functional_base index 9587d7ab15a1d..ca761c409b627 100644 --- a/libcxx/include/__functional_base +++ b/libcxx/include/__functional_base @@ -558,7 +558,7 @@ struct __is_transparent<_Tp, _Up, // allocator_arg_t -struct _LIBCPP_TEMPLATE_VIS allocator_arg_t { }; +struct _LIBCPP_TEMPLATE_VIS allocator_arg_t { explicit allocator_arg_t() = default; }; #if defined(_LIBCPP_CXX03_LANG) || defined(_LIBCPP_BUILDING_LIBRARY) extern _LIBCPP_EXPORTED_FROM_ABI const allocator_arg_t allocator_arg; diff --git a/libcxx/include/__mutex_base b/libcxx/include/__mutex_base index a85ded8341e00..ed75c82380a6c 100644 --- a/libcxx/include/__mutex_base +++ b/libcxx/include/__mutex_base @@ -15,6 +15,7 @@ #include #include <__threading_support> +#include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) #pragma GCC system_header @@ -65,9 +66,9 @@ public: static_assert(is_nothrow_default_constructible::value, "the default constructor for std::mutex must be nothrow"); -struct _LIBCPP_TYPE_VIS defer_lock_t {}; -struct _LIBCPP_TYPE_VIS try_to_lock_t {}; -struct _LIBCPP_TYPE_VIS adopt_lock_t {}; +struct _LIBCPP_TYPE_VIS defer_lock_t { explicit defer_lock_t() = default; }; +struct _LIBCPP_TYPE_VIS try_to_lock_t { explicit try_to_lock_t() = default; }; +struct _LIBCPP_TYPE_VIS adopt_lock_t { explicit adopt_lock_t() = default; }; #if defined(_LIBCPP_CXX03_LANG) || defined(_LIBCPP_BUILDING_LIBRARY) @@ -337,23 +338,75 @@ public: private: void __do_timed_wait(unique_lock& __lk, chrono::time_point) _NOEXCEPT; +#if defined(_LIBCPP_HAS_COND_CLOCKWAIT) + void __do_timed_wait(unique_lock& __lk, + chrono::time_point) _NOEXCEPT; +#endif + template + void __do_timed_wait(unique_lock& __lk, + chrono::time_point<_Clock, chrono::nanoseconds>) _NOEXCEPT; }; #endif // !_LIBCPP_HAS_NO_THREADS -template +template inline _LIBCPP_INLINE_VISIBILITY typename enable_if < - chrono::__is_duration<_To>::value, - _To + is_floating_point<_Rep>::value, + chrono::nanoseconds >::type -__ceil(chrono::duration<_Rep, _Period> __d) +__safe_nanosecond_cast(chrono::duration<_Rep, _Period> __d) { using namespace chrono; - _To __r = duration_cast<_To>(__d); - if (__r < __d) - ++__r; - return __r; + using __ratio = ratio_divide<_Period, nano>; + using __ns_rep = nanoseconds::rep; + _Rep __result_float = __d.count() * __ratio::num / __ratio::den; + + _Rep __result_max = numeric_limits<__ns_rep>::max(); + if (__result_float >= __result_max) { + return nanoseconds::max(); + } + + _Rep __result_min = numeric_limits<__ns_rep>::min(); + if (__result_float <= __result_min) { + return nanoseconds::min(); + } + + return nanoseconds(static_cast<__ns_rep>(__result_float)); +} + +template +inline _LIBCPP_INLINE_VISIBILITY +typename enable_if +< + !is_floating_point<_Rep>::value, + chrono::nanoseconds +>::type +__safe_nanosecond_cast(chrono::duration<_Rep, _Period> __d) +{ + using namespace chrono; + if (__d.count() == 0) { + return nanoseconds(0); + } + + using __ratio = ratio_divide<_Period, nano>; + using __ns_rep = nanoseconds::rep; + __ns_rep __result_max = std::numeric_limits<__ns_rep>::max(); + if (__d.count() > 0 && __d.count() > __result_max / __ratio::num) { + return nanoseconds::max(); + } + + __ns_rep __result_min = std::numeric_limits<__ns_rep>::min(); + if (__d.count() < 0 && __d.count() < __result_min / __ratio::num) { + return nanoseconds::min(); + } + + __ns_rep __result = __d.count() * __ratio::num / __ratio::den; + if (__result == 0) { + return nanoseconds(1); + } + + return nanoseconds(__result); } #ifndef _LIBCPP_HAS_NO_THREADS @@ -371,7 +424,15 @@ condition_variable::wait_until(unique_lock& __lk, const chrono::time_point<_Clock, _Duration>& __t) { using namespace chrono; - wait_for(__lk, __t - _Clock::now()); + using __clock_tp_ns = time_point<_Clock, nanoseconds>; + + typename _Clock::time_point __now = _Clock::now(); + if (__t <= __now) + return cv_status::timeout; + + __clock_tp_ns __t_ns = __clock_tp_ns(__safe_nanosecond_cast(__t.time_since_epoch())); + + __do_timed_wait(__lk, __t_ns); return _Clock::now() < __t ? cv_status::no_timeout : cv_status::timeout; } @@ -397,15 +458,25 @@ condition_variable::wait_for(unique_lock& __lk, using namespace chrono; if (__d <= __d.zero()) return cv_status::timeout; - typedef time_point > __sys_tpf; - typedef time_point __sys_tpi; - __sys_tpf _Max = __sys_tpi::max(); + using __ns_rep = nanoseconds::rep; steady_clock::time_point __c_now = steady_clock::now(); - system_clock::time_point __s_now = system_clock::now(); - if (_Max - __d > __s_now) - __do_timed_wait(__lk, __s_now + __ceil(__d)); - else - __do_timed_wait(__lk, __sys_tpi::max()); + +#if defined(_LIBCPP_HAS_COND_CLOCKWAIT) + using __clock_tp_ns = time_point; + __ns_rep __now_count_ns = __safe_nanosecond_cast(__c_now.time_since_epoch()).count(); +#else + using __clock_tp_ns = time_point; + __ns_rep __now_count_ns = __safe_nanosecond_cast(system_clock::now().time_since_epoch()).count(); +#endif + + __ns_rep __d_ns_count = __safe_nanosecond_cast(__d).count(); + + if (__now_count_ns > numeric_limits<__ns_rep>::max() - __d_ns_count) { + __do_timed_wait(__lk, __clock_tp_ns::max()); + } else { + __do_timed_wait(__lk, __clock_tp_ns(nanoseconds(__now_count_ns + __d_ns_count))); + } + return steady_clock::now() - __c_now < __d ? cv_status::no_timeout : cv_status::timeout; } @@ -421,6 +492,46 @@ condition_variable::wait_for(unique_lock& __lk, _VSTD::move(__pred)); } +#if defined(_LIBCPP_HAS_COND_CLOCKWAIT) +inline +void +condition_variable::__do_timed_wait(unique_lock& __lk, + chrono::time_point __tp) _NOEXCEPT +{ + using namespace chrono; + if (!__lk.owns_lock()) + __throw_system_error(EPERM, + "condition_variable::timed wait: mutex not locked"); + nanoseconds __d = __tp.time_since_epoch(); + timespec __ts; + seconds __s = duration_cast(__d); + using __ts_sec = decltype(__ts.tv_sec); + const __ts_sec __ts_sec_max = numeric_limits<__ts_sec>::max(); + if (__s.count() < __ts_sec_max) + { + __ts.tv_sec = static_cast<__ts_sec>(__s.count()); + __ts.tv_nsec = (__d - __s).count(); + } + else + { + __ts.tv_sec = __ts_sec_max; + __ts.tv_nsec = giga::num - 1; + } + int __ec = pthread_cond_clockwait(&__cv_, __lk.mutex()->native_handle(), CLOCK_MONOTONIC, &__ts); + if (__ec != 0 && __ec != ETIMEDOUT) + __throw_system_error(__ec, "condition_variable timed_wait failed"); +} +#endif // _LIBCPP_HAS_COND_CLOCKWAIT + +template +inline +void +condition_variable::__do_timed_wait(unique_lock& __lk, + chrono::time_point<_Clock, chrono::nanoseconds> __tp) _NOEXCEPT +{ + wait_for(__lk, __tp - _Clock::now()); +} + #endif // !_LIBCPP_HAS_NO_THREADS _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__string b/libcxx/include/__string index a88b976be3c9b..b4c8815f72d21 100644 --- a/libcxx/include/__string +++ b/libcxx/include/__string @@ -351,6 +351,18 @@ char_traits::compare(const char_type* __s1, const char_type* __s2, size #endif } + +template +_LIBCPP_INLINE_VISIBILITY +_LIBCPP_CONSTEXPR +inline size_t __char_traits_length_checked(const typename _Traits::char_type* __s) _NOEXCEPT { +#if _LIBCPP_DEBUG_LEVEL >= 1 + return __s ? _Traits::length(__s) : (_VSTD::__libcpp_debug_function(_VSTD::__libcpp_debug_info(__FILE__, __LINE__, "p == nullptr", "null pointer pass to non-null argument of char_traits<...>::length")), 0); +#else + return _Traits::length(__s); +#endif +} + inline _LIBCPP_CONSTEXPR_AFTER_CXX14 size_t char_traits::length(const char_type* __s) _NOEXCEPT diff --git a/libcxx/include/__tuple b/libcxx/include/__tuple index 196f3c2b5aa52..4da9ec55f3548 100644 --- a/libcxx/include/__tuple +++ b/libcxx/include/__tuple @@ -477,8 +477,9 @@ using __tuple_like_with_size _LIBCPP_NODEBUG_TYPE = __tuple_like_with_size_imp< >; struct _LIBCPP_TYPE_VIS __check_tuple_constructor_fail { - template - static constexpr bool __enable_default() { return false; } + + static constexpr bool __enable_explicit_default() { return false; } + static constexpr bool __enable_implicit_default() { return false; } template static constexpr bool __enable_explicit() { return false; } template diff --git a/libcxx/include/cmath b/libcxx/include/cmath index 36d26b38760f5..0f06486fb34f3 100644 --- a/libcxx/include/cmath +++ b/libcxx/include/cmath @@ -644,8 +644,8 @@ _LIBCPP_CONSTEXPR _IntT __max_representable_int_for_float() _NOEXCEPT { static_assert(is_floating_point<_FloatT>::value, "must be a floating point type"); static_assert(is_integral<_IntT>::value, "must be an integral type"); static_assert(numeric_limits<_FloatT>::radix == 2, "FloatT has incorrect radix"); - static_assert(_IsSame<_FloatT, float>::value || _IsSame<_FloatT, double>::value - || _IsSame<_FloatT,long double>::value, "unsupported floating point type"); + static_assert((_IsSame<_FloatT, float>::value || _IsSame<_FloatT, double>::value + || _IsSame<_FloatT,long double>::value), "unsupported floating point type"); return _FloatBigger ? numeric_limits<_IntT>::max() : (numeric_limits<_IntT>::max() >> _Bits << _Bits); } diff --git a/libcxx/include/experimental/coroutine b/libcxx/include/experimental/coroutine index e2f0a25f77edc..54ec74b9f9844 100644 --- a/libcxx/include/experimental/coroutine +++ b/libcxx/include/experimental/coroutine @@ -51,7 +51,6 @@ template struct hash>; #include #include // for hash #include -#include #include <__debug> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) diff --git a/libcxx/include/fstream b/libcxx/include/fstream index 7db9017aca9e7..e9138998bf11b 100644 --- a/libcxx/include/fstream +++ b/libcxx/include/fstream @@ -508,34 +508,34 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring( switch (__mode & ~ios_base::ate) { case ios_base::out: case ios_base::out | ios_base::trunc: - return "w"; + return "w" _LIBCPP_FOPEN_CLOEXEC_MODE; case ios_base::out | ios_base::app: case ios_base::app: - return "a"; + return "a" _LIBCPP_FOPEN_CLOEXEC_MODE; case ios_base::in: - return "r"; + return "r" _LIBCPP_FOPEN_CLOEXEC_MODE; case ios_base::in | ios_base::out: - return "r+"; + return "r+" _LIBCPP_FOPEN_CLOEXEC_MODE; case ios_base::in | ios_base::out | ios_base::trunc: - return "w+"; + return "w+" _LIBCPP_FOPEN_CLOEXEC_MODE; case ios_base::in | ios_base::out | ios_base::app: case ios_base::in | ios_base::app: - return "a+"; + return "a+" _LIBCPP_FOPEN_CLOEXEC_MODE; case ios_base::out | ios_base::binary: case ios_base::out | ios_base::trunc | ios_base::binary: - return "wb"; + return "wb" _LIBCPP_FOPEN_CLOEXEC_MODE; case ios_base::out | ios_base::app | ios_base::binary: case ios_base::app | ios_base::binary: - return "ab"; + return "ab" _LIBCPP_FOPEN_CLOEXEC_MODE; case ios_base::in | ios_base::binary: - return "rb"; + return "rb" _LIBCPP_FOPEN_CLOEXEC_MODE; case ios_base::in | ios_base::out | ios_base::binary: - return "r+b"; + return "r+b" _LIBCPP_FOPEN_CLOEXEC_MODE; case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary: - return "w+b"; + return "w+b" _LIBCPP_FOPEN_CLOEXEC_MODE; case ios_base::in | ios_base::out | ios_base::app | ios_base::binary: case ios_base::in | ios_base::app | ios_base::binary: - return "a+b"; + return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE; default: return nullptr; } diff --git a/libcxx/include/memory b/libcxx/include/memory index 12573ca801161..0336a4d5d02d1 100644 --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -662,7 +662,6 @@ void* align(size_t alignment, size_t size, void*& ptr, size_t& space); #include #include #include -#include #if !defined(_LIBCPP_HAS_NO_ATOMIC_HEADER) # include #endif @@ -3872,8 +3871,6 @@ public: : nullptr);} #endif // _LIBCPP_NO_RTTI -#ifndef _LIBCPP_HAS_NO_VARIADICS - template static shared_ptr<_Tp> @@ -3884,37 +3881,6 @@ public: shared_ptr<_Tp> allocate_shared(const _Alloc& __a, _Args&& ...__args); -#else // _LIBCPP_HAS_NO_VARIADICS - - static shared_ptr<_Tp> make_shared(); - - template - static shared_ptr<_Tp> make_shared(_A0&); - - template - static shared_ptr<_Tp> make_shared(_A0&, _A1&); - - template - static shared_ptr<_Tp> make_shared(_A0&, _A1&, _A2&); - - template - static shared_ptr<_Tp> - allocate_shared(const _Alloc& __a); - - template - static shared_ptr<_Tp> - allocate_shared(const _Alloc& __a, _A0& __a0); - - template - static shared_ptr<_Tp> - allocate_shared(const _Alloc& __a, _A0& __a0, _A1& __a1); - - template - static shared_ptr<_Tp> - allocate_shared(const _Alloc& __a, _A0& __a0, _A1& __a1, _A2& __a2); - -#endif // _LIBCPP_HAS_NO_VARIADICS - private: template ::value> struct __shared_ptr_default_allocator @@ -4227,8 +4193,6 @@ shared_ptr<_Tp>::shared_ptr(unique_ptr<_Yp, _Dp> __r, __r.release(); } -#ifndef _LIBCPP_HAS_NO_VARIADICS - template template shared_ptr<_Tp> @@ -4268,165 +4232,6 @@ shared_ptr<_Tp>::allocate_shared(const _Alloc& __a, _Args&& ...__args) return __r; } -#else // _LIBCPP_HAS_NO_VARIADICS - -template -shared_ptr<_Tp> -shared_ptr<_Tp>::make_shared() -{ - static_assert((is_constructible<_Tp>::value), "Can't construct object in make_shared" ); - typedef __shared_ptr_emplace<_Tp, allocator<_Tp> > _CntrlBlk; - typedef allocator<_CntrlBlk> _Alloc2; - typedef __allocator_destructor<_Alloc2> _D2; - _Alloc2 __alloc2; - unique_ptr<_CntrlBlk, _D2> __hold2(__alloc2.allocate(1), _D2(__alloc2, 1)); - ::new(__hold2.get()) _CntrlBlk(__alloc2); - shared_ptr<_Tp> __r; - __r.__ptr_ = __hold2.get()->get(); - __r.__cntrl_ = __hold2.release(); - __r.__enable_weak_this(__r.__ptr_, __r.__ptr_); - return __r; -} - -template -template -shared_ptr<_Tp> -shared_ptr<_Tp>::make_shared(_A0& __a0) -{ - static_assert((is_constructible<_Tp, _A0>::value), "Can't construct object in make_shared" ); - typedef __shared_ptr_emplace<_Tp, allocator<_Tp> > _CntrlBlk; - typedef allocator<_CntrlBlk> _Alloc2; - typedef __allocator_destructor<_Alloc2> _D2; - _Alloc2 __alloc2; - unique_ptr<_CntrlBlk, _D2> __hold2(__alloc2.allocate(1), _D2(__alloc2, 1)); - ::new(__hold2.get()) _CntrlBlk(__alloc2, __a0); - shared_ptr<_Tp> __r; - __r.__ptr_ = __hold2.get()->get(); - __r.__cntrl_ = __hold2.release(); - __r.__enable_weak_this(__r.__ptr_, __r.__ptr_); - return __r; -} - -template -template -shared_ptr<_Tp> -shared_ptr<_Tp>::make_shared(_A0& __a0, _A1& __a1) -{ - static_assert((is_constructible<_Tp, _A0, _A1>::value), "Can't construct object in make_shared" ); - typedef __shared_ptr_emplace<_Tp, allocator<_Tp> > _CntrlBlk; - typedef allocator<_CntrlBlk> _Alloc2; - typedef __allocator_destructor<_Alloc2> _D2; - _Alloc2 __alloc2; - unique_ptr<_CntrlBlk, _D2> __hold2(__alloc2.allocate(1), _D2(__alloc2, 1)); - ::new(__hold2.get()) _CntrlBlk(__alloc2, __a0, __a1); - shared_ptr<_Tp> __r; - __r.__ptr_ = __hold2.get()->get(); - __r.__cntrl_ = __hold2.release(); - __r.__enable_weak_this(__r.__ptr_, __r.__ptr_); - return __r; -} - -template -template -shared_ptr<_Tp> -shared_ptr<_Tp>::make_shared(_A0& __a0, _A1& __a1, _A2& __a2) -{ - static_assert((is_constructible<_Tp, _A0, _A1, _A2>::value), "Can't construct object in make_shared" ); - typedef __shared_ptr_emplace<_Tp, allocator<_Tp> > _CntrlBlk; - typedef allocator<_CntrlBlk> _Alloc2; - typedef __allocator_destructor<_Alloc2> _D2; - _Alloc2 __alloc2; - unique_ptr<_CntrlBlk, _D2> __hold2(__alloc2.allocate(1), _D2(__alloc2, 1)); - ::new(__hold2.get()) _CntrlBlk(__alloc2, __a0, __a1, __a2); - shared_ptr<_Tp> __r; - __r.__ptr_ = __hold2.get()->get(); - __r.__cntrl_ = __hold2.release(); - __r.__enable_weak_this(__r.__ptr_, __r.__ptr_); - return __r; -} - -template -template -shared_ptr<_Tp> -shared_ptr<_Tp>::allocate_shared(const _Alloc& __a) -{ - static_assert((is_constructible<_Tp>::value), "Can't construct object in allocate_shared" ); - typedef __shared_ptr_emplace<_Tp, _Alloc> _CntrlBlk; - typedef typename __allocator_traits_rebind<_Alloc, _CntrlBlk>::type _Alloc2; - typedef __allocator_destructor<_Alloc2> _D2; - _Alloc2 __alloc2(__a); - unique_ptr<_CntrlBlk, _D2> __hold2(__alloc2.allocate(1), _D2(__alloc2, 1)); - ::new(static_cast(_VSTD::addressof(*__hold2.get()))) - _CntrlBlk(__a); - shared_ptr<_Tp> __r; - __r.__ptr_ = __hold2.get()->get(); - __r.__cntrl_ = _VSTD::addressof(*__hold2.release()); - __r.__enable_weak_this(__r.__ptr_, __r.__ptr_); - return __r; -} - -template -template -shared_ptr<_Tp> -shared_ptr<_Tp>::allocate_shared(const _Alloc& __a, _A0& __a0) -{ - static_assert((is_constructible<_Tp, _A0>::value), "Can't construct object in allocate_shared" ); - typedef __shared_ptr_emplace<_Tp, _Alloc> _CntrlBlk; - typedef typename __allocator_traits_rebind<_Alloc, _CntrlBlk>::type _Alloc2; - typedef __allocator_destructor<_Alloc2> _D2; - _Alloc2 __alloc2(__a); - unique_ptr<_CntrlBlk, _D2> __hold2(__alloc2.allocate(1), _D2(__alloc2, 1)); - ::new(static_cast(_VSTD::addressof(*__hold2.get()))) - _CntrlBlk(__a, __a0); - shared_ptr<_Tp> __r; - __r.__ptr_ = __hold2.get()->get(); - __r.__cntrl_ = _VSTD::addressof(*__hold2.release()); - __r.__enable_weak_this(__r.__ptr_, __r.__ptr_); - return __r; -} - -template -template -shared_ptr<_Tp> -shared_ptr<_Tp>::allocate_shared(const _Alloc& __a, _A0& __a0, _A1& __a1) -{ - static_assert((is_constructible<_Tp, _A0, _A1>::value), "Can't construct object in allocate_shared" ); - typedef __shared_ptr_emplace<_Tp, _Alloc> _CntrlBlk; - typedef typename __allocator_traits_rebind<_Alloc, _CntrlBlk>::type _Alloc2; - typedef __allocator_destructor<_Alloc2> _D2; - _Alloc2 __alloc2(__a); - unique_ptr<_CntrlBlk, _D2> __hold2(__alloc2.allocate(1), _D2(__alloc2, 1)); - ::new(static_cast(_VSTD::addressof(*__hold2.get()))) - _CntrlBlk(__a, __a0, __a1); - shared_ptr<_Tp> __r; - __r.__ptr_ = __hold2.get()->get(); - __r.__cntrl_ = _VSTD::addressof(*__hold2.release()); - __r.__enable_weak_this(__r.__ptr_, __r.__ptr_); - return __r; -} - -template -template -shared_ptr<_Tp> -shared_ptr<_Tp>::allocate_shared(const _Alloc& __a, _A0& __a0, _A1& __a1, _A2& __a2) -{ - static_assert((is_constructible<_Tp, _A0, _A1, _A2>::value), "Can't construct object in allocate_shared" ); - typedef __shared_ptr_emplace<_Tp, _Alloc> _CntrlBlk; - typedef typename __allocator_traits_rebind<_Alloc, _CntrlBlk>::type _Alloc2; - typedef __allocator_destructor<_Alloc2> _D2; - _Alloc2 __alloc2(__a); - unique_ptr<_CntrlBlk, _D2> __hold2(__alloc2.allocate(1), _D2(__alloc2, 1)); - ::new(static_cast(_VSTD::addressof(*__hold2.get()))) - _CntrlBlk(__a, __a0, __a1, __a2); - shared_ptr<_Tp> __r; - __r.__ptr_ = __hold2.get()->get(); - __r.__cntrl_ = _VSTD::addressof(*__hold2.release()); - __r.__enable_weak_this(__r.__ptr_, __r.__ptr_); - return __r; -} - -#endif // _LIBCPP_HAS_NO_VARIADICS - template shared_ptr<_Tp>::~shared_ptr() { diff --git a/libcxx/include/mutex b/libcxx/include/mutex index dca62202db114..8fc3c61221b57 100644 --- a/libcxx/include/mutex +++ b/libcxx/include/mutex @@ -86,9 +86,9 @@ public: void unlock(); }; -struct defer_lock_t {}; -struct try_to_lock_t {}; -struct adopt_lock_t {}; +struct defer_lock_t { explicit defer_lock_t() = default; }; +struct try_to_lock_t { explicit try_to_lock_t() = default; }; +struct adopt_lock_t { explicit adopt_lock_t() = default; }; inline constexpr defer_lock_t defer_lock{}; inline constexpr try_to_lock_t try_to_lock{}; diff --git a/libcxx/include/new b/libcxx/include/new index 85e4c4b3fcf2a..40d351e9b7705 100644 --- a/libcxx/include/new +++ b/libcxx/include/new @@ -39,7 +39,7 @@ struct destroying_delete_t { // C++20 }; inline constexpr destroying_delete_t destroying_delete{}; // C++20 -struct nothrow_t {}; +struct nothrow_t { explicit nothrow_t() = default; }; extern const nothrow_t nothrow; typedef void (*new_handler)(); new_handler set_new_handler(new_handler new_p) noexcept; @@ -126,7 +126,7 @@ namespace std // purposefully not using versioning namespace { #if !defined(_LIBCPP_ABI_VCRUNTIME) -struct _LIBCPP_TYPE_VIS nothrow_t {}; +struct _LIBCPP_TYPE_VIS nothrow_t { explicit nothrow_t() = default; }; extern _LIBCPP_FUNC_VIS const nothrow_t nothrow; class _LIBCPP_EXCEPTION_ABI bad_alloc diff --git a/libcxx/include/ostream b/libcxx/include/ostream index e6cf9c970f722..ea3870532f329 100644 --- a/libcxx/include/ostream +++ b/libcxx/include/ostream @@ -1055,7 +1055,7 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, template basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, - const basic_string_view<_CharT, _Traits> __sv) + basic_string_view<_CharT, _Traits> __sv) { return _VSTD::__put_character_sequence(__os, __sv.data(), __sv.size()); } diff --git a/libcxx/include/regex b/libcxx/include/regex index 26efac1c620dd..d13f9addb70a7 100644 --- a/libcxx/include/regex +++ b/libcxx/include/regex @@ -169,15 +169,15 @@ public: // assign: basic_regex& assign(const basic_regex& that); basic_regex& assign(basic_regex&& that) noexcept; - basic_regex& assign(const charT* ptr, flag_type f = regex_constants::ECMAScript); - basic_regex& assign(const charT* p, size_t len, flag_type f); + basic_regex& assign(const charT* ptr, flag_type f = regex_constants::ECMAScript); + basic_regex& assign(const charT* p, size_t len, flag_type f = regex_constants::ECMAScript); template basic_regex& assign(const basic_string& s, - flag_type f = regex_constants::ECMAScript); + flag_type f = regex_constants::ECMAScript); template basic_regex& assign(InputIterator first, InputIterator last, - flag_type f = regex_constants::ECMAScript); - basic_regex& assign(initializer_list, flag_type = regex_constants::ECMAScript); + flag_type f = regex_constants::ECMAScript); + basic_regex& assign(initializer_list, flag_type f = regex_constants::ECMAScript); // const operations: unsigned mark_count() const; @@ -2617,7 +2617,7 @@ public: basic_regex& assign(const value_type* __p, flag_type __f = regex_constants::ECMAScript) {return assign(__p, __p + __traits_.length(__p), __f);} _LIBCPP_INLINE_VISIBILITY - basic_regex& assign(const value_type* __p, size_t __len, flag_type __f) + basic_regex& assign(const value_type* __p, size_t __len, flag_type __f = regex_constants::ECMAScript) {return assign(__p, __p + __len, __f);} template _LIBCPP_INLINE_VISIBILITY diff --git a/libcxx/include/string_view b/libcxx/include/string_view index 04448312ff338..8a684a8f966ce 100644 --- a/libcxx/include/string_view +++ b/libcxx/include/string_view @@ -173,6 +173,7 @@ namespace std { #include <__config> #include <__string> +#include #include #include #include @@ -235,7 +236,7 @@ public: _LIBCPP_CONSTEXPR _LIBCPP_INLINE_VISIBILITY basic_string_view(const _CharT* __s) - : __data(__s), __size(_Traits::length(__s)) {} + : __data(__s), __size(std::__char_traits_length_checked<_Traits>(__s)) {} // [string.view.iterators], iterators _LIBCPP_CONSTEXPR _LIBCPP_INLINE_VISIBILITY @@ -767,6 +768,12 @@ bool operator>=(typename common_type >::type return __lhs.compare(__rhs) >= 0; } + +template +basic_ostream<_CharT, _Traits>& +operator<<(basic_ostream<_CharT, _Traits>& __os, + basic_string_view<_CharT, _Traits> __str); + typedef basic_string_view string_view; #ifndef _LIBCPP_NO_HAS_CHAR8_T typedef basic_string_view u8string_view; diff --git a/libcxx/include/tuple b/libcxx/include/tuple index 32bc86913aaa8..e93824f0aa7f4 100644 --- a/libcxx/include/tuple +++ b/libcxx/include/tuple @@ -19,7 +19,7 @@ namespace std template class tuple { public: - constexpr tuple(); + explicit(see-below) constexpr tuple(); explicit(see-below) tuple(const T&...); // constexpr in C++14 template explicit(see-below) tuple(U&&...); // constexpr in C++14 @@ -499,11 +499,19 @@ class _LIBCPP_TEMPLATE_VIS tuple template struct _CheckArgsConstructor { - template - static constexpr bool __enable_default() { - return __all::value...>::value; + template + static constexpr bool __enable_implicit_default() { + return __all<__is_implicitly_default_constructible<_Tp>::value... >::value; + } + + template + static constexpr bool __enable_explicit_default() { + return + __all::value...>::value && + !__enable_implicit_default< >(); } + template static constexpr bool __enable_explicit() { return @@ -641,22 +649,26 @@ class _LIBCPP_TEMPLATE_VIS tuple const typename tuple_element<_Jp, tuple<_Up...> >::type&& get(const tuple<_Up...>&&) _NOEXCEPT; public: - template ::template __enable_default<_Tp...>() - >::type> - _LIBCPP_INLINE_VISIBILITY - _LIBCPP_CONSTEXPR tuple() + template ::__enable_implicit_default() + , void*> = nullptr> + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR + tuple() + _NOEXCEPT_(__all::value...>::value) {} + + template ::__enable_explicit_default() + , void*> = nullptr> + explicit _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR + tuple() _NOEXCEPT_(__all::value...>::value) {} tuple(tuple const&) = default; tuple(tuple&&) = default; - template , - __dependent_type, _Dummy>... - >::value - > + template ::value >::__enable_implicit_default() + , void*> = nullptr > _LIBCPP_INLINE_VISIBILITY tuple(_AllocArgT, _Alloc const& __a) @@ -665,6 +677,17 @@ public: typename __make_tuple_indices::type(), __tuple_types<_Tp...>()) {} + template ::value>::__enable_explicit_default() + , void*> = nullptr + > + explicit _LIBCPP_INLINE_VISIBILITY + tuple(_AllocArgT, _Alloc const& __a) + : __base_(allocator_arg_t(), __a, + __tuple_indices<>(), __tuple_types<>(), + typename __make_tuple_indices::type(), + __tuple_types<_Tp...>()) {} + template {}; // Member detector base -template