diff --git a/.gitignore b/.gitignore index 20c4f52cd3786..0e7c6c7900133 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,7 @@ autoconf/autom4te.cache /CMakeSettings.json # CLion project configuration /.idea +/cmake-build* #==============================================================================# # Directories to ignore (do not add trailing '/'s, they skip symlinks). diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp index 1cd7cdd10bc25..62f9d19b2a362 100644 --- a/clang-tools-extra/clang-tidy/ClangTidy.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp @@ -672,6 +672,18 @@ getAllChecksAndOptions(bool AllowEnablingAnalyzerAlphaCheckers) { Buffer.append(AnalyzerCheck); Result.Names.insert(Buffer); } + for (std::string OptionName : { +#define GET_CHECKER_OPTIONS +#define CHECKER_OPTION(TYPE, CHECKER, OPTION_NAME, DESCRIPTION, DEFAULT, \ + RELEASE, HIDDEN) \ + Twine(AnalyzerCheckNamePrefix).concat(CHECKER ":" OPTION_NAME).str(), + +#include "clang/StaticAnalyzer/Checkers/Checkers.inc" +#undef CHECKER_OPTION +#undef GET_CHECKER_OPTIONS + }) { + Result.Options.insert(OptionName); + } #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER Context.setOptionsCollector(&Result.Options); diff --git a/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp b/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp index c0bf4903ec391..90c16bfddd84f 100644 --- a/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp +++ b/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp @@ -777,7 +777,7 @@ bool ForLoopIndexUseVisitor::TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C, Expr *Init) { if (C->capturesVariable()) { - const ValueDecl *VDecl = C->getCapturedVar(); + ValueDecl *VDecl = C->getCapturedVar(); if (areSameVariable(IndexVar, VDecl)) { // FIXME: if the index is captured, it will count as an usage and the // alias (if any) won't work, because it is only used in case of having @@ -787,6 +787,8 @@ bool ForLoopIndexUseVisitor::TraverseLambdaCapture(LambdaExpr *LE, : Usage::UK_CaptureByRef, C->getLocation())); } + if (VDecl->isInitCapture()) + TraverseStmtImpl(cast(VDecl)->getInit()); } return VisitorBase::TraverseLambdaCapture(LE, C, Init); } @@ -816,6 +818,17 @@ bool ForLoopIndexUseVisitor::VisitDeclStmt(DeclStmt *S) { return true; } +bool ForLoopIndexUseVisitor::TraverseStmtImpl(Stmt *S) { + // All this pointer swapping is a mechanism for tracking immediate parentage + // of Stmts. + const Stmt *OldNextParent = NextStmtParent; + CurrStmtParent = NextStmtParent; + NextStmtParent = S; + bool Result = VisitorBase::TraverseStmt(S); + NextStmtParent = OldNextParent; + return Result; +} + bool ForLoopIndexUseVisitor::TraverseStmt(Stmt *S) { // If this is an initialization expression for a lambda capture, prune the // traversal so that we don't end up diagnosing the contained DeclRefExpr as @@ -828,15 +841,7 @@ bool ForLoopIndexUseVisitor::TraverseStmt(Stmt *S) { return true; } } - - // All this pointer swapping is a mechanism for tracking immediate parentage - // of Stmts. - const Stmt *OldNextParent = NextStmtParent; - CurrStmtParent = NextStmtParent; - NextStmtParent = S; - bool Result = VisitorBase::TraverseStmt(S); - NextStmtParent = OldNextParent; - return Result; + return TraverseStmtImpl(S); } std::string VariableNamer::createIndexName() { diff --git a/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.h b/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.h index cfceb3b586842..ca9c1855038b5 100644 --- a/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.h +++ b/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.h @@ -354,6 +354,8 @@ class ForLoopIndexUseVisitor bool VisitDeclStmt(DeclStmt *S); bool TraverseStmt(Stmt *S); + bool TraverseStmtImpl(Stmt *S); + /// Add an expression to the list of expressions on which the container /// expression depends. void addComponent(const Expr *E); diff --git a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt index 2e948c23569f6..59475b0dfd3d2 100644 --- a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt +++ b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt @@ -29,6 +29,7 @@ add_clang_library(clangDaemonTweaks OBJECT RemoveUsingNamespace.cpp ScopifyEnum.cpp SpecialMembers.cpp + SwapBinaryOperands.cpp SwapIfBranches.cpp LINK_LIBS diff --git a/clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp b/clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp new file mode 100644 index 0000000000000..9ad0089a5d035 --- /dev/null +++ b/clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp @@ -0,0 +1,217 @@ +//===--- SwapBinaryOperands.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 "ParsedAST.h" +#include "Protocol.h" +#include "Selection.h" +#include "SourceCode.h" +#include "refactor/Tweak.h" +#include "support/Logger.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/AST/OperationKinds.h" +#include "clang/AST/Stmt.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Tooling/Core/Replacement.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/FormatVariadic.h" +#include +#include + +namespace clang { +namespace clangd { +namespace { +/// Check whether it makes logical sense to swap operands to an operator. +/// Assignment or member access operators are rarely swappable +/// while keeping the meaning intact, whereas comparison operators, mathematical +/// operators, etc. are often desired to be swappable for readability, avoiding +/// bugs by assigning to nullptr when comparison was desired, etc. +bool isOpSwappable(const BinaryOperatorKind Opcode) { + switch (Opcode) { + case BinaryOperatorKind::BO_Mul: + case BinaryOperatorKind::BO_Add: + case BinaryOperatorKind::BO_LT: + case BinaryOperatorKind::BO_GT: + case BinaryOperatorKind::BO_LE: + case BinaryOperatorKind::BO_GE: + case BinaryOperatorKind::BO_EQ: + case BinaryOperatorKind::BO_NE: + case BinaryOperatorKind::BO_And: + case BinaryOperatorKind::BO_Xor: + case BinaryOperatorKind::BO_Or: + case BinaryOperatorKind::BO_LAnd: + case BinaryOperatorKind::BO_LOr: + case BinaryOperatorKind::BO_Comma: + return true; + // Noncommutative operators: + case BinaryOperatorKind::BO_Div: + case BinaryOperatorKind::BO_Sub: + case BinaryOperatorKind::BO_Shl: + case BinaryOperatorKind::BO_Shr: + case BinaryOperatorKind::BO_Rem: + // <=> is noncommutative + case BinaryOperatorKind::BO_Cmp: + // Member access: + case BinaryOperatorKind::BO_PtrMemD: + case BinaryOperatorKind::BO_PtrMemI: + // Assignment: + case BinaryOperatorKind::BO_Assign: + case BinaryOperatorKind::BO_MulAssign: + case BinaryOperatorKind::BO_DivAssign: + case BinaryOperatorKind::BO_RemAssign: + case BinaryOperatorKind::BO_AddAssign: + case BinaryOperatorKind::BO_SubAssign: + case BinaryOperatorKind::BO_ShlAssign: + case BinaryOperatorKind::BO_ShrAssign: + case BinaryOperatorKind::BO_AndAssign: + case BinaryOperatorKind::BO_XorAssign: + case BinaryOperatorKind::BO_OrAssign: + return false; + } + return false; +} + +/// Some operators are asymmetric and need to be flipped when swapping their +/// operands +/// @param[out] Opcode the opcode to potentially swap +/// If the opcode does not need to be swapped or is not swappable, does nothing +BinaryOperatorKind swapOperator(const BinaryOperatorKind Opcode) { + switch (Opcode) { + case BinaryOperatorKind::BO_LT: + return BinaryOperatorKind::BO_GT; + + case BinaryOperatorKind::BO_GT: + return BinaryOperatorKind::BO_LT; + + case BinaryOperatorKind::BO_LE: + return BinaryOperatorKind::BO_GE; + + case BinaryOperatorKind::BO_GE: + return BinaryOperatorKind::BO_LE; + + case BinaryOperatorKind::BO_Mul: + case BinaryOperatorKind::BO_Add: + case BinaryOperatorKind::BO_Cmp: + case BinaryOperatorKind::BO_EQ: + case BinaryOperatorKind::BO_NE: + case BinaryOperatorKind::BO_And: + case BinaryOperatorKind::BO_Xor: + case BinaryOperatorKind::BO_Or: + case BinaryOperatorKind::BO_LAnd: + case BinaryOperatorKind::BO_LOr: + case BinaryOperatorKind::BO_Comma: + case BinaryOperatorKind::BO_Div: + case BinaryOperatorKind::BO_Sub: + case BinaryOperatorKind::BO_Shl: + case BinaryOperatorKind::BO_Shr: + case BinaryOperatorKind::BO_Rem: + case BinaryOperatorKind::BO_PtrMemD: + case BinaryOperatorKind::BO_PtrMemI: + case BinaryOperatorKind::BO_Assign: + case BinaryOperatorKind::BO_MulAssign: + case BinaryOperatorKind::BO_DivAssign: + case BinaryOperatorKind::BO_RemAssign: + case BinaryOperatorKind::BO_AddAssign: + case BinaryOperatorKind::BO_SubAssign: + case BinaryOperatorKind::BO_ShlAssign: + case BinaryOperatorKind::BO_ShrAssign: + case BinaryOperatorKind::BO_AndAssign: + case BinaryOperatorKind::BO_XorAssign: + case BinaryOperatorKind::BO_OrAssign: + return Opcode; + } + llvm_unreachable("Unknown BinaryOperatorKind enum"); +} + +/// Swaps the operands to a binary operator +/// Before: +/// x != nullptr +/// ^ ^^^^^^^ +/// After: +/// nullptr != x +class SwapBinaryOperands : public Tweak { +public: + const char *id() const final; + + bool prepare(const Selection &Inputs) override; + Expected apply(const Selection &Inputs) override; + std::string title() const override { + return llvm::formatv("Swap operands to {0}", + Op ? Op->getOpcodeStr() : "binary operator"); + } + llvm::StringLiteral kind() const override { + return CodeAction::REFACTOR_KIND; + } + bool hidden() const override { return false; } + +private: + const BinaryOperator *Op; +}; + +REGISTER_TWEAK(SwapBinaryOperands) + +bool SwapBinaryOperands::prepare(const Selection &Inputs) { + for (const SelectionTree::Node *N = Inputs.ASTSelection.commonAncestor(); + N && !Op; N = N->Parent) { + // Stop once we hit a block, e.g. a lambda in one of the operands. + // This makes sure that the selection point is in the 'scope' of the binary + // operator, not from somewhere inside a lambda for example + // (5 < [](){ ^return 1; }) + if (llvm::isa_and_nonnull(N->ASTNode.get())) + return false; + Op = dyn_cast_or_null(N->ASTNode.get()); + // If we hit upon a nonswappable binary operator, ignore and keep going + if (Op && !isOpSwappable(Op->getOpcode())) { + Op = nullptr; + } + } + return Op != nullptr; +} + +Expected SwapBinaryOperands::apply(const Selection &Inputs) { + const auto &Ctx = Inputs.AST->getASTContext(); + const auto &SrcMgr = Inputs.AST->getSourceManager(); + + const auto LHSRng = toHalfOpenFileRange(SrcMgr, Ctx.getLangOpts(), + Op->getLHS()->getSourceRange()); + if (!LHSRng) + return error( + "Could not obtain range of the 'lhs' of the operator. Macros?"); + const auto RHSRng = toHalfOpenFileRange(SrcMgr, Ctx.getLangOpts(), + Op->getRHS()->getSourceRange()); + if (!RHSRng) + return error( + "Could not obtain range of the 'rhs' of the operator. Macros?"); + const auto OpRng = + toHalfOpenFileRange(SrcMgr, Ctx.getLangOpts(), Op->getOperatorLoc()); + if (!OpRng) + return error("Could not obtain range of the operator itself. Macros?"); + + const auto LHSCode = toSourceCode(SrcMgr, *LHSRng); + const auto RHSCode = toSourceCode(SrcMgr, *RHSRng); + const auto OperatorCode = toSourceCode(SrcMgr, *OpRng); + + tooling::Replacements Result; + if (auto Err = Result.add(tooling::Replacement( + Ctx.getSourceManager(), LHSRng->getBegin(), LHSCode.size(), RHSCode))) + return std::move(Err); + if (auto Err = Result.add(tooling::Replacement( + Ctx.getSourceManager(), RHSRng->getBegin(), RHSCode.size(), LHSCode))) + return std::move(Err); + const auto SwappedOperator = swapOperator(Op->getOpcode()); + if (auto Err = Result.add(tooling::Replacement( + Ctx.getSourceManager(), OpRng->getBegin(), OperatorCode.size(), + Op->getOpcodeStr(SwappedOperator)))) + return std::move(Err); + return Effect::mainFileEdit(SrcMgr, std::move(Result)); +} + +} // namespace +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/unittests/CMakeLists.txt b/clang-tools-extra/clangd/unittests/CMakeLists.txt index 4fa9f18407ae9..dffdcd5d014ca 100644 --- a/clang-tools-extra/clangd/unittests/CMakeLists.txt +++ b/clang-tools-extra/clangd/unittests/CMakeLists.txt @@ -137,6 +137,7 @@ add_unittest(ClangdUnitTests ClangdTests tweaks/ScopifyEnumTests.cpp tweaks/ShowSelectionTreeTests.cpp tweaks/SpecialMembersTests.cpp + tweaks/SwapBinaryOperandsTests.cpp tweaks/SwapIfBranchesTests.cpp tweaks/TweakTesting.cpp tweaks/TweakTests.cpp diff --git a/clang-tools-extra/clangd/unittests/tweaks/SwapBinaryOperandsTests.cpp b/clang-tools-extra/clangd/unittests/tweaks/SwapBinaryOperandsTests.cpp new file mode 100644 index 0000000000000..e157bbefbdaaf --- /dev/null +++ b/clang-tools-extra/clangd/unittests/tweaks/SwapBinaryOperandsTests.cpp @@ -0,0 +1,92 @@ +//===-- SwapBinaryOperandsTests.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 "TweakTesting.h" +#include "gmock/gmock-matchers.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace clang { +namespace clangd { +namespace { + +TWEAK_TEST(SwapBinaryOperands); + +TEST_F(SwapBinaryOperandsTest, Test) { + Context = Function; + EXPECT_EQ(apply("int *p = nullptr; bool c = ^p == nullptr;"), + "int *p = nullptr; bool c = nullptr == p;"); + EXPECT_EQ(apply("int *p = nullptr; bool c = p ^== nullptr;"), + "int *p = nullptr; bool c = nullptr == p;"); + EXPECT_EQ(apply("int x = 3; bool c = ^x >= 5;"), + "int x = 3; bool c = 5 <= x;"); + EXPECT_EQ(apply("int x = 3; bool c = x >^= 5;"), + "int x = 3; bool c = 5 <= x;"); + EXPECT_EQ(apply("int x = 3; bool c = x >=^ 5;"), + "int x = 3; bool c = 5 <= x;"); + EXPECT_EQ(apply("int x = 3; bool c = x >=^ 5;"), + "int x = 3; bool c = 5 <= x;"); + EXPECT_EQ(apply("int f(); int x = 3; bool c = x >=^ f();"), + "int f(); int x = 3; bool c = f() <= x;"); + EXPECT_EQ(apply(R"cpp( + int f(); + #define F f + int x = 3; bool c = x >=^ F(); + )cpp"), + R"cpp( + int f(); + #define F f + int x = 3; bool c = F() <= x; + )cpp"); + EXPECT_EQ(apply(R"cpp( + int f(); + #define F f() + int x = 3; bool c = x >=^ F; + )cpp"), + R"cpp( + int f(); + #define F f() + int x = 3; bool c = F <= x; + )cpp"); + EXPECT_EQ(apply(R"cpp( + int f(bool); + #define F(v) f(v) + int x = 0; + bool c = F(x^ < 5); + )cpp"), + R"cpp( + int f(bool); + #define F(v) f(v) + int x = 0; + bool c = F(5 > x); + )cpp"); + ExtraArgs = {"-std=c++20"}; + Context = CodeContext::File; + EXPECT_UNAVAILABLE(R"cpp( + namespace std { + struct strong_ordering { + int val; + static const strong_ordering less; + static const strong_ordering equivalent; + static const strong_ordering equal; + static const strong_ordering greater; + }; + inline constexpr strong_ordering strong_ordering::less {-1}; + inline constexpr strong_ordering strong_ordering::equivalent {0}; + inline constexpr strong_ordering strong_ordering::equal {0}; + inline constexpr strong_ordering strong_ordering::greater {1}; + }; + #define F(v) v + int x = 0; + auto c = F(5^ <=> x); + )cpp"); +} + +} // namespace +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index e34e296b5a096..b4792d749a86c 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -74,6 +74,8 @@ Code completion Code actions ^^^^^^^^^^^^ +- Added `Swap operands` tweak for certain binary operators. + Signature help ^^^^^^^^^^^^^^ @@ -97,6 +99,10 @@ The improvements are... Improvements to clang-tidy -------------------------- +- Improved :program:`clang-tidy`'s `--verify-config` flag by adding support for + the configuration options of the `Clang Static Analyzer Checks + `_. + - Improved :program:`run-clang-tidy.py` script. Fixed minor shutdown noise happening on certain platforms when interrupting the script. @@ -162,18 +168,18 @@ Changes in existing checks ` check by rewording the diagnostic note that suggests adding ``inline``. +- Improved :doc:`misc-unconventional-assign-operator + ` check to avoid + false positive for C++23 deducing this. + - Improved :doc:`modernize-avoid-c-arrays ` check to suggest using ``std::span`` as a replacement for parameters of incomplete C array type in C++20 and ``std::array`` or ``std::vector`` before C++20. -- Improved :doc:`modernize-use-std-format - ` check to support replacing - member function calls too. - -- Improved :doc:`misc-unconventional-assign-operator - ` check to avoid - false positive for C++23 deducing this. +- Improved :doc:`modernize-loop-convert + ` check to fix false positive when + using loop variable in initializer of lambda capture. - Improved :doc:`modernize-min-max-use-initializer-list ` check by fixing @@ -184,15 +190,14 @@ Changes in existing checks ` check to also recognize ``NULL``/``__null`` (but not ``0``) when used with a templated type. +- Improved :doc:`modernize-use-std-format + ` check to support replacing + member function calls too. + - Improved :doc:`modernize-use-std-print ` check to support replacing member function calls too. -- Improved :doc:`readability-enum-initial-value - ` check by only issuing - diagnostics for the definition of an ``enum``, and by fixing a typo in the - diagnostic. - - Improved :doc:`performance-avoid-endl ` check to use ``std::endl`` as placeholder when lexer cannot get source text. @@ -201,6 +206,11 @@ Changes in existing checks ` check to let it work on any class that has a ``contains`` method. +- Improved :doc:`readability-enum-initial-value + ` check by only issuing + diagnostics for the definition of an ``enum``, and by fixing a typo in the + diagnostic. + - Improved :doc:`readability-implicit-bool-conversion ` check by adding the option `UseUpperCaseLiteralSuffix` to select the diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/loop-convert-basic.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/loop-convert-basic.cpp index 695925a5d877c..df2a2c1af1f54 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/loop-convert-basic.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/loop-convert-basic.cpp @@ -980,3 +980,30 @@ namespace PR78381 { } } } + +namespace GH109083 { +void test() { + const int N = 6; + int Arr[N] = {1, 2, 3, 4, 5, 6}; + + for (int I = 0; I < N; ++I) { + auto V = [T = Arr[I]]() {}; + } + // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead [modernize-loop-convert] + // CHECK-FIXES: for (int I : Arr) + // CHECK-FIXES-NEXT: auto V = [T = I]() {}; + for (int I = 0; I < N; ++I) { + auto V = [T = 10 + Arr[I]]() {}; + } + // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead [modernize-loop-convert] + // CHECK-FIXES: for (int I : Arr) + // CHECK-FIXES-NEXT: auto V = [T = 10 + I]() {}; + + for (int I = 0; I < N; ++I) { + auto V = [T = I]() {}; + } + for (int I = 0; I < N; ++I) { + auto V = [T = I + 10]() {}; + } +} +} // namespace GH109083 diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/verify-config.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/verify-config.cpp index 3659285986482..93f6f9fc156a4 100644 --- a/clang-tools-extra/test/clang-tidy/infrastructure/verify-config.cpp +++ b/clang-tools-extra/test/clang-tidy/infrastructure/verify-config.cpp @@ -30,3 +30,11 @@ // CHECK-VERIFY-BLOCK-BAD: command-line option '-config': warning: check glob 'bugprone-arguments-*' doesn't match any known check [-verify-config] // CHECK-VERIFY-BLOCK-BAD: command-line option '-config': warning: unknown check 'bugprone-assert-side-effects'; did you mean 'bugprone-assert-side-effect' [-verify-config] +// RUN: echo -e 'Checks: "-*,clang-analyzer-optin.cplusplus.UninitializedObject"\nCheckOptions:\n clang-analyzer-optin.cplusplus.UninitializedObject:Pedantic: true' > %T/MyClangTidyConfigCSA +// RUN: clang-tidy --verify-config --config-file=%T/MyClangTidyConfigCSA 2>&1 | FileCheck %s -check-prefix=CHECK-VERIFY-CSA-OK -implicit-check-not='{{warnings|error}}' +// CHECK-VERIFY-CSA-OK: No config errors detected. + +// RUN: echo -e 'Checks: "-*,clang-analyzer-optin.cplusplus.UninitializedObject"\nCheckOptions:\n clang-analyzer-optin.cplusplus.UninitializedObject.Pedantic: true' > %T/MyClangTidyConfigCSABad +// RUN: not clang-tidy --verify-config --config-file=%T/MyClangTidyConfigCSABad 2>&1 | FileCheck %s -check-prefix=CHECK-VERIFY-CSA-BAD -implicit-check-not='{{warnings|error}}' +// CHECK-VERIFY-CSA-BAD: command-line option '-config': warning: unknown check option 'clang-analyzer-optin.cplusplus.UninitializedObject.Pedantic'; did you mean 'clang-analyzer-optin.cplusplus.UninitializedObject:Pedantic' [-verify-config] + diff --git a/clang/cmake/caches/Release.cmake b/clang/cmake/caches/Release.cmake index c93ff40ff3ee4..23e99493087ff 100644 --- a/clang/cmake/caches/Release.cmake +++ b/clang/cmake/caches/Release.cmake @@ -109,3 +109,6 @@ set_final_stage_var(LLVM_ENABLE_PROJECTS "${LLVM_RELEASE_ENABLE_PROJECTS}" STRIN set_final_stage_var(CPACK_GENERATOR "TXZ" STRING) set_final_stage_var(CPACK_ARCHIVE_THREADS "0" STRING) +if(${CMAKE_HOST_SYSTEM_NAME} MATCHES "Darwin") + set_final_stage_var(LLVM_USE_STATIC_ZSTD "ON" BOOL) +endif() diff --git a/clang/cmake/caches/hexagon-unknown-linux-musl-clang-cross.cmake b/clang/cmake/caches/hexagon-unknown-linux-musl-clang-cross.cmake new file mode 100644 index 0000000000000..91bbe26b62105 --- /dev/null +++ b/clang/cmake/caches/hexagon-unknown-linux-musl-clang-cross.cmake @@ -0,0 +1,15 @@ +# This file is for the llvm+clang options that are specific to building +# a cross-toolchain targeting hexagon linux. +set(DEFAULT_SYSROOT "../target/hexagon-unknown-linux-musl/" CACHE STRING "") +set(CLANG_LINKS_TO_CREATE + hexagon-linux-musl-clang++ + hexagon-linux-musl-clang + hexagon-unknown-linux-musl-clang++ + hexagon-unknown-linux-musl-clang + hexagon-none-elf-clang++ + hexagon-none-elf-clang + hexagon-unknown-none-elf-clang++ + hexagon-unknown-none-elf-clang + CACHE STRING "") + +set(LLVM_INSTALL_TOOLCHAIN_ONLY ON CACHE BOOL "") diff --git a/clang/cmake/caches/hexagon-unknown-linux-musl-clang.cmake b/clang/cmake/caches/hexagon-unknown-linux-musl-clang.cmake new file mode 100644 index 0000000000000..9f3eb4678fd17 --- /dev/null +++ b/clang/cmake/caches/hexagon-unknown-linux-musl-clang.cmake @@ -0,0 +1,15 @@ + +set(LLVM_TARGETS_TO_BUILD "Hexagon" CACHE STRING "") +set(LLVM_DEFAULT_TARGET_TRIPLE "hexagon-unknown-linux-musl" CACHE STRING "") +set(CLANG_DEFAULT_CXX_STDLIB "libc++" CACHE STRING "") +set(CLANG_DEFAULT_OBJCOPY "llvm-objcopy" CACHE STRING "") +set(CLANG_DEFAULT_RTLIB "compiler-rt" CACHE STRING "") +set(CLANG_DEFAULT_UNWINDLIB "libunwind" CACHE STRING "") +set(CLANG_DEFAULT_LINKER "lld" CACHE STRING "") +set(LLVM_ENABLE_PROJECTS "clang;lld" CACHE STRING "") + +set(LLVM_INCLUDE_TESTS OFF CACHE BOOL "") +set(LLVM_INCLUDE_DOCS OFF CACHE BOOL "") +# Enabling toolchain-only causes problems when doing some of the +# subsequent builds, will need to investigate: +set(LLVM_INSTALL_TOOLCHAIN_ONLY OFF CACHE BOOL "") diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index a427d7cd40fcd..a16edb0989b05 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -6554,6 +6554,15 @@ the configuration (without a prefix: ``Auto``). let DAGArgOtherID = (other i32:$other1, i32:$other2); let DAGArgBang = (!cast("Some") i32:$src1, i32:$src2) +.. _TemplateNames: + +**TemplateNames** (``List of Strings``) :versionbadge:`clang-format 20` :ref:`¶ ` + A vector of non-keyword identifiers that should be interpreted as + template names. + + A ``<`` after a template name is annotated as a template opener instead of + a binary operator. + .. _TypeNames: **TypeNames** (``List of Strings``) :versionbadge:`clang-format 17` :ref:`¶ ` diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index ea4b4bcec55e7..fe41742a25c64 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -647,68 +647,77 @@ elementwise to the input. Unless specified otherwise operation(±0) = ±0 and operation(±infinity) = ±infinity -=========================================== ================================================================ ========================================= - Name Operation Supported element types -=========================================== ================================================================ ========================================= - T __builtin_elementwise_abs(T x) return the absolute value of a number x; the absolute value of signed integer and floating point types - the most negative integer remains the most negative integer - T __builtin_elementwise_fma(T x, T y, T z) fused multiply add, (x * y) + z. floating point types - T __builtin_elementwise_ceil(T x) return the smallest integral value greater than or equal to x floating point types - T __builtin_elementwise_sin(T x) return the sine of x interpreted as an angle in radians floating point types - T __builtin_elementwise_cos(T x) return the cosine of x interpreted as an angle in radians floating point types - T __builtin_elementwise_tan(T x) return the tangent of x interpreted as an angle in radians floating point types - T __builtin_elementwise_asin(T x) return the arcsine of x interpreted as an angle in radians floating point types - T __builtin_elementwise_acos(T x) return the arccosine of x interpreted as an angle in radians floating point types - T __builtin_elementwise_atan(T x) return the arctangent of x interpreted as an angle in radians floating point types - T __builtin_elementwise_sinh(T x) return the hyperbolic sine of angle x in radians floating point types - T __builtin_elementwise_cosh(T x) return the hyperbolic cosine of angle x in radians floating point types - T __builtin_elementwise_tanh(T x) return the hyperbolic tangent of angle x in radians floating point types - T __builtin_elementwise_floor(T x) return the largest integral value less than or equal to x floating point types - T __builtin_elementwise_log(T x) return the natural logarithm of x floating point types - T __builtin_elementwise_log2(T x) return the base 2 logarithm of x floating point types - T __builtin_elementwise_log10(T x) return the base 10 logarithm of x floating point types - T __builtin_elementwise_popcount(T x) return the number of 1 bits in x integer types - T __builtin_elementwise_pow(T x, T y) return x raised to the power of y floating point types - T __builtin_elementwise_bitreverse(T x) return the integer represented after reversing the bits of x integer types - T __builtin_elementwise_exp(T x) returns the base-e exponential, e^x, of the specified value floating point types - T __builtin_elementwise_exp2(T x) returns the base-2 exponential, 2^x, of the specified value floating point types - - T __builtin_elementwise_sqrt(T x) return the square root of a floating-point number floating point types - T __builtin_elementwise_roundeven(T x) round x to the nearest integer value in floating point format, floating point types - rounding halfway cases to even (that is, to the nearest value - that is an even integer), regardless of the current rounding - direction. - T __builtin_elementwise_round(T x) round x to the nearest integer value in floating point format, floating point types - rounding halfway cases away from zero, regardless of the - current rounding direction. May raise floating-point - exceptions. - T __builtin_elementwise_trunc(T x) return the integral value nearest to but no larger in floating point types - magnitude than x - - T __builtin_elementwise_nearbyint(T x) round x to the nearest integer value in floating point format, floating point types - rounding according to the current rounding direction. - May not raise the inexact floating-point exception. This is - treated the same as ``__builtin_elementwise_rint`` unless - :ref:`FENV_ACCESS is enabled `. - - T __builtin_elementwise_rint(T x) round x to the nearest integer value in floating point format, floating point types - rounding according to the current rounding - direction. May raise floating-point exceptions. This is treated - the same as ``__builtin_elementwise_nearbyint`` unless - :ref:`FENV_ACCESS is enabled `. - - T __builtin_elementwise_canonicalize(T x) return the platform specific canonical encoding floating point types - of a floating-point number - T __builtin_elementwise_copysign(T x, T y) return the magnitude of x with the sign of y. floating point types - T __builtin_elementwise_fmod(T x, T y) return The floating-point remainder of (x/y) whose sign floating point types - matches the sign of x. - T __builtin_elementwise_max(T x, T y) return x or y, whichever is larger integer and floating point types - T __builtin_elementwise_min(T x, T y) return x or y, whichever is smaller integer and floating point types - T __builtin_elementwise_add_sat(T x, T y) return the sum of x and y, clamped to the range of integer types - representable values for the signed/unsigned integer type. - T __builtin_elementwise_sub_sat(T x, T y) return the difference of x and y, clamped to the range of integer types - representable values for the signed/unsigned integer type. -=========================================== ================================================================ ========================================= +============================================== ====================================================================== ========================================= + Name Operation Supported element types +============================================== ====================================================================== ========================================= + T __builtin_elementwise_abs(T x) return the absolute value of a number x; the absolute value of signed integer and floating point types + the most negative integer remains the most negative integer + T __builtin_elementwise_fma(T x, T y, T z) fused multiply add, (x * y) + z. floating point types + T __builtin_elementwise_ceil(T x) return the smallest integral value greater than or equal to x floating point types + T __builtin_elementwise_sin(T x) return the sine of x interpreted as an angle in radians floating point types + T __builtin_elementwise_cos(T x) return the cosine of x interpreted as an angle in radians floating point types + T __builtin_elementwise_tan(T x) return the tangent of x interpreted as an angle in radians floating point types + T __builtin_elementwise_asin(T x) return the arcsine of x interpreted as an angle in radians floating point types + T __builtin_elementwise_acos(T x) return the arccosine of x interpreted as an angle in radians floating point types + T __builtin_elementwise_atan(T x) return the arctangent of x interpreted as an angle in radians floating point types + T __builtin_elementwise_atan2(T y, T x) return the arctangent of y/x floating point types + T __builtin_elementwise_sinh(T x) return the hyperbolic sine of angle x in radians floating point types + T __builtin_elementwise_cosh(T x) return the hyperbolic cosine of angle x in radians floating point types + T __builtin_elementwise_tanh(T x) return the hyperbolic tangent of angle x in radians floating point types + T __builtin_elementwise_floor(T x) return the largest integral value less than or equal to x floating point types + T __builtin_elementwise_log(T x) return the natural logarithm of x floating point types + T __builtin_elementwise_log2(T x) return the base 2 logarithm of x floating point types + T __builtin_elementwise_log10(T x) return the base 10 logarithm of x floating point types + T __builtin_elementwise_popcount(T x) return the number of 1 bits in x integer types + T __builtin_elementwise_pow(T x, T y) return x raised to the power of y floating point types + T __builtin_elementwise_bitreverse(T x) return the integer represented after reversing the bits of x integer types + T __builtin_elementwise_exp(T x) returns the base-e exponential, e^x, of the specified value floating point types + T __builtin_elementwise_exp2(T x) returns the base-2 exponential, 2^x, of the specified value floating point types + + T __builtin_elementwise_sqrt(T x) return the square root of a floating-point number floating point types + T __builtin_elementwise_roundeven(T x) round x to the nearest integer value in floating point format, floating point types + rounding halfway cases to even (that is, to the nearest value + that is an even integer), regardless of the current rounding + direction. + T __builtin_elementwise_round(T x) round x to the nearest integer value in floating point format, floating point types + rounding halfway cases away from zero, regardless of the + current rounding direction. May raise floating-point + exceptions. + T __builtin_elementwise_trunc(T x) return the integral value nearest to but no larger in floating point types + magnitude than x + + T __builtin_elementwise_nearbyint(T x) round x to the nearest integer value in floating point format, floating point types + rounding according to the current rounding direction. + May not raise the inexact floating-point exception. This is + treated the same as ``__builtin_elementwise_rint`` unless + :ref:`FENV_ACCESS is enabled `. + + T __builtin_elementwise_rint(T x) round x to the nearest integer value in floating point format, floating point types + rounding according to the current rounding + direction. May raise floating-point exceptions. This is treated + the same as ``__builtin_elementwise_nearbyint`` unless + :ref:`FENV_ACCESS is enabled `. + + T __builtin_elementwise_canonicalize(T x) return the platform specific canonical encoding floating point types + of a floating-point number + T __builtin_elementwise_copysign(T x, T y) return the magnitude of x with the sign of y. floating point types + T __builtin_elementwise_fmod(T x, T y) return The floating-point remainder of (x/y) whose sign floating point types + matches the sign of x. + T __builtin_elementwise_max(T x, T y) return x or y, whichever is larger integer and floating point types + T __builtin_elementwise_min(T x, T y) return x or y, whichever is smaller integer and floating point types + T __builtin_elementwise_add_sat(T x, T y) return the sum of x and y, clamped to the range of integer types + representable values for the signed/unsigned integer type. + T __builtin_elementwise_sub_sat(T x, T y) return the difference of x and y, clamped to the range of integer types + representable values for the signed/unsigned integer type. + T __builtin_elementwise_maximum(T x, T y) return x or y, whichever is larger. Follows IEEE 754-2019 floating point types + semantics, see `LangRef + `_ + for the comparison. + T __builtin_elementwise_minimum(T x, T y) return x or y, whichever is smaller. Follows IEEE 754-2019 floating point types + semantics, see `LangRef + `_ + for the comparison. +============================================== ====================================================================== ========================================= *Reduction Builtins* @@ -733,21 +742,29 @@ Example: Let ``VT`` be a vector type and ``ET`` the element type of ``VT``. -======================================= ================================================================ ================================== - Name Operation Supported element types -======================================= ================================================================ ================================== - ET __builtin_reduce_max(VT a) return x or y, whichever is larger; If exactly one argument is integer and floating point types +======================================= ====================================================================== ================================== + Name Operation Supported element types +======================================= ====================================================================== ================================== + ET __builtin_reduce_max(VT a) return x or y, whichever is larger; If exactly one argument is integer and floating point types a NaN, return the other argument. If both arguments are NaNs, fmax() return a NaN. - ET __builtin_reduce_min(VT a) return x or y, whichever is smaller; If exactly one argument integer and floating point types + ET __builtin_reduce_min(VT a) return x or y, whichever is smaller; If exactly one argument integer and floating point types is a NaN, return the other argument. If both arguments are NaNs, fmax() return a NaN. - ET __builtin_reduce_add(VT a) \+ integer types - ET __builtin_reduce_mul(VT a) \* integer types - ET __builtin_reduce_and(VT a) & integer types - ET __builtin_reduce_or(VT a) \| integer types - ET __builtin_reduce_xor(VT a) ^ integer types -======================================= ================================================================ ================================== + ET __builtin_reduce_add(VT a) \+ integer types + ET __builtin_reduce_mul(VT a) \* integer types + ET __builtin_reduce_and(VT a) & integer types + ET __builtin_reduce_or(VT a) \| integer types + ET __builtin_reduce_xor(VT a) ^ integer types + ET __builtin_reduce_maximum(VT a) return the largest element of the vector. Follows IEEE 754-2019 floating point types + semantics, see `LangRef + `_ + for the comparison. + ET __builtin_reduce_minimum(VT a) return the smallest element of the vector. Follows IEEE 754-2019 floating point types + semantics, see `LangRef + `_ + for the comparison. +======================================= ====================================================================== ================================== Matrix Types ============ @@ -5759,6 +5776,8 @@ The following builtin intrinsics can be used in constant expressions: The following x86-specific intrinsics can be used in constant expressions: +* ``_addcarry_u32`` +* ``_addcarry_u64`` * ``_bit_scan_forward`` * ``_bit_scan_reverse`` * ``__bsfd`` @@ -5799,6 +5818,8 @@ The following x86-specific intrinsics can be used in constant expressions: * ``_rotwr`` * ``_lrotl`` * ``_lrotr`` +* ``_subborrow_u32`` +* ``_subborrow_u64`` Debugging the Compiler ====================== diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 34d2b584274a5..44d5f348ed2d5 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -104,6 +104,7 @@ ABI Changes in This Version - Fixed Microsoft name mangling of placeholder, auto and decltype(auto), return types for MSVC 1920+. This change resolves incompatibilities with code compiled by MSVC 1920+ but will introduce incompatibilities with code compiled by earlier versions of Clang unless such code is built with the compiler option -fms-compatibility-version=19.14 to imitate the MSVC 1914 mangling behavior. - Fixed the Itanium mangling of the construction vtable name. This change will introduce incompatibilities with code compiled by Clang 19 and earlier versions, unless the -fclang-abi-compat=19 option is used. (#GH108015) +- Mangle member-like friend function templates as members of the enclosing class. (#GH110247, #GH110503) AST Dumping Potentially Breaking Changes ---------------------------------------- @@ -144,6 +145,9 @@ C++ Language Changes - Add ``__builtin_elementwise_fmod`` builtin for floating point types only. +- Add ``__builtin_elementwise_minimum`` and ``__builtin_elementwise_maximum`` + builtin for floating point types only. + - The builtin type alias ``__builtin_common_type`` has been added to improve the performance of ``std::common_type``. @@ -170,6 +174,10 @@ C++23 Feature Support C++20 Feature Support ^^^^^^^^^^^^^^^^^^^^^ +C++17 Feature Support +^^^^^^^^^^^^^^^^^^^^^ +- The implementation of the relaxed template template argument matching rules is + more complete and reliable, and should provide more accurate diagnostics. Resolutions to C++ Defect Reports ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -327,6 +335,10 @@ Improvements to Clang's diagnostics - Clang now diagnoses when the result of a [[nodiscard]] function is discarded after being cast in C. Fixes #GH104391. +- Clang now properly explains the reason a template template argument failed to + match a template template parameter, in terms of the C++17 relaxed matching rules + instead of the old ones. + - Don't emit duplicated dangling diagnostics. (#GH93386). - Improved diagnostic when trying to befriend a concept. (#GH45182). @@ -364,6 +376,8 @@ Improvements to Clang's diagnostics - Clang now diagnoses when a ``requires`` expression has a local parameter of void type, aligning with the function parameter (#GH109831). +- Clang now emits a diagnostic note at the class declaration when the method definition does not match any declaration (#GH110638). + Improvements to Clang's time-trace ---------------------------------- @@ -424,6 +438,8 @@ Bug Fixes to C++ Support - Correctly check constraints of explicit instantiations of member functions. (#GH46029) - When performing partial ordering of function templates, clang now checks that the deduction was consistent. Fixes (#GH18291). +- Fixes to several issues in partial ordering of template template parameters, which + were documented in the test suite. - Fixed an assertion failure about a constraint of a friend function template references to a value with greater template depth than the friend function template. (#GH98258) - Clang now rebuilds the template parameters of out-of-line declarations and specializations in the context @@ -452,7 +468,7 @@ Bug Fixes to C++ Support - Fixed an assertion failure in debug mode, and potential crashes in release mode, when diagnosing a failed cast caused indirectly by a failed implicit conversion to the type of the constructor parameter. - Fixed an assertion failure by adjusting integral to boolean vector conversions (#GH108326) -- Mangle friend function templates with a constraint that depends on a template parameter from an enclosing template as members of the enclosing class. (#GH110247) +- Fixed an issue deducing non-type template arguments of reference type. (#GH73460) - Fixed an issue in constraint evaluation, where type constraints on the lambda expression containing outer unexpanded parameters were not correctly expanded. (#GH101754) - Fixed a bug in constraint expression comparison where the ``sizeof...`` expression was not handled properly @@ -520,6 +536,10 @@ X86 Support * Supported MINMAX intrinsics of ``*_(mask(z)))_minmax(ne)_p[s|d|h|bh]`` and ``*_(mask(z)))_minmax_s[s|d|h]``. +- All intrinsics in adcintrin.h can now be used in constant expressions. + +- All intrinsics in adxintrin.h can now be used in constant expressions. + - All intrinsics in lzcntintrin.h can now be used in constant expressions. - All intrinsics in bmiintrin.h can now be used in constant expressions. @@ -575,6 +595,8 @@ DWARF Support in Clang Floating Point Support in Clang ------------------------------- +- Add ``__builtin_elementwise_atan2`` builtin for floating point types only. + Fixed Point Support in Clang ---------------------------- @@ -593,6 +615,7 @@ clang-format ------------ - Adds ``BreakBinaryOperations`` option. +- Adds ``TemplateNames`` option. libclang -------- @@ -605,11 +628,18 @@ Static Analyzer New features ^^^^^^^^^^^^ +- Now CSA models `__builtin_*_overflow` functions. (#GH102602) + - MallocChecker now checks for ``ownership_returns(class, idx)`` and ``ownership_takes(class, idx)`` attributes with class names different from "malloc". Clang static analyzer now reports an error if class of allocation and deallocation function mismatches. `Documentation `__. +- Function effects, e.g. the ``nonblocking`` and ``nonallocating`` "performance constraint" + attributes, are now verified. For example, for functions declared with the ``nonblocking`` + attribute, the compiler can generate warnings about the use of any language features, or calls to + other functions, which may block. + Crash and bug fixes ^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/APValue.h b/clang/include/clang/AST/APValue.h index c4206b73b1156..7869ee386689d 100644 --- a/clang/include/clang/AST/APValue.h +++ b/clang/include/clang/AST/APValue.h @@ -314,51 +314,95 @@ class APValue { DataType Data; public: + /// Creates an empty APValue of type None. APValue() : Kind(None) {} + /// Creates an integer APValue holding the given value. explicit APValue(APSInt I) : Kind(None) { MakeInt(); setInt(std::move(I)); } + /// Creates a float APValue holding the given value. explicit APValue(APFloat F) : Kind(None) { MakeFloat(); setFloat(std::move(F)); } + /// Creates a fixed-point APValue holding the given value. explicit APValue(APFixedPoint FX) : Kind(None) { MakeFixedPoint(std::move(FX)); } + /// Creates a vector APValue with \p N elements. The elements + /// are read from \p E. explicit APValue(const APValue *E, unsigned N) : Kind(None) { MakeVector(); setVector(E, N); } + /// Creates an integer complex APValue with the given real and imaginary + /// values. APValue(APSInt R, APSInt I) : Kind(None) { MakeComplexInt(); setComplexInt(std::move(R), std::move(I)); } + /// Creates a float complex APValue with the given real and imaginary values. APValue(APFloat R, APFloat I) : Kind(None) { MakeComplexFloat(); setComplexFloat(std::move(R), std::move(I)); } APValue(const APValue &RHS); APValue(APValue &&RHS); - APValue(LValueBase B, const CharUnits &O, NoLValuePath N, + /// Creates an lvalue APValue without an lvalue path. + /// \param Base The base of the lvalue. + /// \param Offset The offset of the lvalue. + /// \param IsNullPtr Whether this lvalue is a null pointer. + APValue(LValueBase Base, const CharUnits &Offset, NoLValuePath, bool IsNullPtr = false) : Kind(None) { - MakeLValue(); setLValue(B, O, N, IsNullPtr); - } - APValue(LValueBase B, const CharUnits &O, ArrayRef Path, - bool OnePastTheEnd, bool IsNullPtr = false) + MakeLValue(); + setLValue(Base, Offset, NoLValuePath{}, IsNullPtr); + } + /// Creates an lvalue APValue with an lvalue path. + /// \param Base The base of the lvalue. + /// \param Offset The offset of the lvalue. + /// \param Path The lvalue path. + /// \param OnePastTheEnd Whether this lvalue is one-past-the-end of the + /// subobject it points to. + /// \param IsNullPtr Whether this lvalue is a null pointer. + APValue(LValueBase Base, const CharUnits &Offset, + ArrayRef Path, bool OnePastTheEnd, + bool IsNullPtr = false) : Kind(None) { - MakeLValue(); setLValue(B, O, Path, OnePastTheEnd, IsNullPtr); - } + MakeLValue(); + setLValue(Base, Offset, Path, OnePastTheEnd, IsNullPtr); + } + /// Creates a new array APValue. + /// \param UninitArray Marker. Pass an empty UninitArray. + /// \param InitElts Number of elements you're going to initialize in the + /// array. + /// \param Size Full size of the array. APValue(UninitArray, unsigned InitElts, unsigned Size) : Kind(None) { MakeArray(InitElts, Size); } - APValue(UninitStruct, unsigned B, unsigned M) : Kind(None) { - MakeStruct(B, M); - } - explicit APValue(const FieldDecl *D, const APValue &V = APValue()) + /// Creates a new struct APValue. + /// \param UninitStruct Marker. Pass an empty UninitStruct. + /// \param NumBases Number of bases. + /// \param NumMembers Number of members. + APValue(UninitStruct, unsigned NumBases, unsigned NumMembers) : Kind(None) { + MakeStruct(NumBases, NumMembers); + } + /// Creates a new union APValue. + /// \param ActiveDecl The FieldDecl of the active union member. + /// \param ActiveValue The value of the active union member. + explicit APValue(const FieldDecl *ActiveDecl, + const APValue &ActiveValue = APValue()) : Kind(None) { - MakeUnion(); setUnion(D, V); + MakeUnion(); + setUnion(ActiveDecl, ActiveValue); } + /// Creates a new member pointer APValue. + /// \param Member Declaration of the member + /// \param IsDerivedMember Whether member is a derived one. + /// \param Path The path of the member. APValue(const ValueDecl *Member, bool IsDerivedMember, ArrayRef Path) : Kind(None) { MakeMemberPointer(Member, IsDerivedMember, Path); } + /// Creates a new address label diff APValue. + /// \param LHSExpr The left-hand side of the difference. + /// \param RHSExpr The right-hand side of the difference. APValue(const AddrLabelExpr* LHSExpr, const AddrLabelExpr* RHSExpr) : Kind(None) { MakeAddrLabelDiff(); setAddrLabelDiff(LHSExpr, RHSExpr); diff --git a/clang/include/clang/AST/CanonicalType.h b/clang/include/clang/AST/CanonicalType.h index dde08f0394c98..6102eb0179353 100644 --- a/clang/include/clang/AST/CanonicalType.h +++ b/clang/include/clang/AST/CanonicalType.h @@ -299,6 +299,7 @@ class CanProxyBase { LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isDependentType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isOverloadableType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isArrayType) + LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isConstantArrayType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, hasPointerRepresentation) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, hasObjCPointerRepresentation) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, hasIntegerRepresentation) diff --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h index 6d3a51c379f9d..1a8507cfbf987 100644 --- a/clang/include/clang/AST/ComputeDependence.h +++ b/clang/include/clang/AST/ComputeDependence.h @@ -107,6 +107,7 @@ class ObjCSubscriptRefExpr; class ObjCIsaExpr; class ObjCIndirectCopyRestoreExpr; class ObjCMessageExpr; +class OpenACCAsteriskSizeExpr; // The following functions are called from constructors of `Expr`, so they // should not access anything beyond basic @@ -203,6 +204,7 @@ ExprDependence computeDependence(ObjCSubscriptRefExpr *E); ExprDependence computeDependence(ObjCIsaExpr *E); ExprDependence computeDependence(ObjCIndirectCopyRestoreExpr *E); ExprDependence computeDependence(ObjCMessageExpr *E); +ExprDependence computeDependence(OpenACCAsteriskSizeExpr *E); } // namespace clang #endif diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 66c746cc25040..57353855c51e7 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -2072,6 +2072,41 @@ class PredefinedExpr final } }; +/// This expression type represents an asterisk in an OpenACC Size-Expr, used in +/// the 'tile' and 'gang' clauses. It is of 'int' type, but should not be +/// evaluated. +class OpenACCAsteriskSizeExpr final : public Expr { + friend class ASTStmtReader; + SourceLocation AsteriskLoc; + + OpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc, QualType IntTy) + : Expr(OpenACCAsteriskSizeExprClass, IntTy, VK_PRValue, OK_Ordinary), + AsteriskLoc(AsteriskLoc) {} + + void setAsteriskLocation(SourceLocation Loc) { AsteriskLoc = Loc; } + +public: + static OpenACCAsteriskSizeExpr *Create(const ASTContext &C, + SourceLocation Loc); + static OpenACCAsteriskSizeExpr *CreateEmpty(const ASTContext &C); + + SourceLocation getBeginLoc() const { return AsteriskLoc; } + SourceLocation getEndLoc() const { return AsteriskLoc; } + SourceLocation getLocation() const { return AsteriskLoc; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == OpenACCAsteriskSizeExprClass; + } + // Iterators + child_range children() { + return child_range(child_iterator(), child_iterator()); + } + + const_child_range children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } +}; + // This represents a use of the __builtin_sycl_unique_stable_name, which takes a // type-id, and at CodeGen time emits a unique string representation of the // type in a way that permits us to properly encode information about the SYCL diff --git a/clang/include/clang/AST/JSONNodeDumper.h b/clang/include/clang/AST/JSONNodeDumper.h index 55bd583e304e8..9422c8fceccfb 100644 --- a/clang/include/clang/AST/JSONNodeDumper.h +++ b/clang/include/clang/AST/JSONNodeDumper.h @@ -283,6 +283,7 @@ class JSONNodeDumper void VisitDeclRefExpr(const DeclRefExpr *DRE); void VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *E); + void VisitOpenACCAsteriskSizeExpr(const OpenACCAsteriskSizeExpr *E); void VisitPredefinedExpr(const PredefinedExpr *PE); void VisitUnaryOperator(const UnaryOperator *UO); void VisitBinaryOperator(const BinaryOperator *BO); diff --git a/clang/include/clang/AST/OpenACCClause.h b/clang/include/clang/AST/OpenACCClause.h index 90f5b7fc9ab6f..e4f2e07222a33 100644 --- a/clang/include/clang/AST/OpenACCClause.h +++ b/clang/include/clang/AST/OpenACCClause.h @@ -481,6 +481,35 @@ class OpenACCNumGangsClause final } }; +class OpenACCTileClause final + : public OpenACCClauseWithExprs, + public llvm::TrailingObjects { + OpenACCTileClause(SourceLocation BeginLoc, SourceLocation LParenLoc, + ArrayRef SizeExprs, SourceLocation EndLoc) + : OpenACCClauseWithExprs(OpenACCClauseKind::Tile, BeginLoc, LParenLoc, + EndLoc) { + std::uninitialized_copy(SizeExprs.begin(), SizeExprs.end(), + getTrailingObjects()); + setExprs(MutableArrayRef(getTrailingObjects(), SizeExprs.size())); + } + +public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Tile; + } + static OpenACCTileClause *Create(const ASTContext &C, SourceLocation BeginLoc, + SourceLocation LParenLoc, + ArrayRef SizeExprs, + SourceLocation EndLoc); + llvm::ArrayRef getSizeExprs() { + return OpenACCClauseWithExprs::getExprs(); + } + + llvm::ArrayRef getSizeExprs() const { + return OpenACCClauseWithExprs::getExprs(); + } +}; + /// Represents one of a handful of clauses that have a single integer /// expression. class OpenACCClauseWithSingleIntExpr : public OpenACCClauseWithExprs { diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index cd9947f7ab980..cbbba9e88b7f5 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2867,6 +2867,7 @@ DEF_TRAVERSE_STMT(ParenListExpr, {}) DEF_TRAVERSE_STMT(SYCLUniqueStableNameExpr, { TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc())); }) +DEF_TRAVERSE_STMT(OpenACCAsteriskSizeExpr, {}) DEF_TRAVERSE_STMT(PredefinedExpr, {}) DEF_TRAVERSE_STMT(ShuffleVectorExpr, {}) DEF_TRAVERSE_STMT(ConvertVectorExpr, {}) diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h index 57100e7ede171..9c320c8ae3e54 100644 --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -410,6 +410,7 @@ class TextNodeDumper void VisitHLSLOutArgExpr(const HLSLOutArgExpr *E); void VisitOpenACCConstructStmt(const OpenACCConstructStmt *S); void VisitOpenACCLoopConstruct(const OpenACCLoopConstruct *S); + void VisitOpenACCAsteriskSizeExpr(const OpenACCAsteriskSizeExpr *S); void VisitEmbedExpr(const EmbedExpr *S); void VisitAtomicExpr(const AtomicExpr *AE); }; diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 7126940058bae..8ff04cf89a6b9 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -49,6 +49,7 @@ #include "llvm/Support/PointerLikeTypeTraits.h" #include "llvm/Support/TrailingObjects.h" #include "llvm/Support/type_traits.h" +#include #include #include #include @@ -119,6 +120,8 @@ class EnumDecl; class Expr; class ExtQualsTypeCommonBase; class FunctionDecl; +class FunctionEffectsRef; +class FunctionEffectKindSet; class FunctionEffectSet; class IdentifierInfo; class NamedDecl; @@ -4712,12 +4715,13 @@ class FunctionEffect { public: /// Identifies the particular effect. enum class Kind : uint8_t { - None = 0, - NonBlocking = 1, - NonAllocating = 2, - Blocking = 3, - Allocating = 4 + NonBlocking, + NonAllocating, + Blocking, + Allocating, + Last = Allocating }; + constexpr static size_t KindCount = static_cast(Kind::Last) + 1; /// Flags describing some behaviors of the effect. using Flags = unsigned; @@ -4743,8 +4747,6 @@ class FunctionEffect { // be considered for uniqueness. public: - FunctionEffect() : FKind(Kind::None) {} - explicit FunctionEffect(Kind K) : FKind(K) {} /// The kind of the effect. @@ -4773,8 +4775,6 @@ class FunctionEffect { case Kind::Blocking: case Kind::Allocating: return 0; - case Kind::None: - break; } llvm_unreachable("unknown effect kind"); } @@ -4782,26 +4782,36 @@ class FunctionEffect { /// The description printed in diagnostics, e.g. 'nonblocking'. StringRef name() const; - /// Return true if the effect is allowed to be inferred on the callee, - /// which is either a FunctionDecl or BlockDecl. + friend raw_ostream &operator<<(raw_ostream &OS, + const FunctionEffect &Effect) { + OS << Effect.name(); + return OS; + } + + /// Determine whether the effect is allowed to be inferred on the callee, + /// which is either a FunctionDecl or BlockDecl. If the returned optional + /// is empty, inference is permitted; otherwise it holds the effect which + /// blocked inference. /// Example: This allows nonblocking(false) to prevent inference for the /// function. - bool canInferOnFunction(const Decl &Callee) const; + std::optional + effectProhibitingInference(const Decl &Callee, + FunctionEffectKindSet CalleeFX) const; // Return false for success. When true is returned for a direct call, then the // FE_InferrableOnCallees flag may trigger inference rather than an immediate // diagnostic. Caller should be assumed to have the effect (it may not have it // explicitly when inferring). bool shouldDiagnoseFunctionCall(bool Direct, - ArrayRef CalleeFX) const; + FunctionEffectKindSet CalleeFX) const; - friend bool operator==(const FunctionEffect &LHS, const FunctionEffect &RHS) { + friend bool operator==(FunctionEffect LHS, FunctionEffect RHS) { return LHS.FKind == RHS.FKind; } - friend bool operator!=(const FunctionEffect &LHS, const FunctionEffect &RHS) { + friend bool operator!=(FunctionEffect LHS, FunctionEffect RHS) { return !(LHS == RHS); } - friend bool operator<(const FunctionEffect &LHS, const FunctionEffect &RHS) { + friend bool operator<(FunctionEffect LHS, FunctionEffect RHS) { return LHS.FKind < RHS.FKind; } }; @@ -4829,13 +4839,14 @@ struct FunctionEffectWithCondition { FunctionEffect Effect; EffectConditionExpr Cond; - FunctionEffectWithCondition() = default; - FunctionEffectWithCondition(const FunctionEffect &E, - const EffectConditionExpr &C) + FunctionEffectWithCondition(FunctionEffect E, const EffectConditionExpr &C) : Effect(E), Cond(C) {} /// Return a textual description of the effect, and its condition, if any. std::string description() const; + + friend raw_ostream &operator<<(raw_ostream &OS, + const FunctionEffectWithCondition &CFE); }; /// Support iteration in parallel through a pair of FunctionEffect and @@ -4940,6 +4951,85 @@ class FunctionEffectsRef { void dump(llvm::raw_ostream &OS) const; }; +/// A mutable set of FunctionEffect::Kind. +class FunctionEffectKindSet { + // For now this only needs to be a bitmap. + constexpr static size_t EndBitPos = FunctionEffect::KindCount; + using KindBitsT = std::bitset; + + KindBitsT KindBits{}; + + explicit FunctionEffectKindSet(KindBitsT KB) : KindBits(KB) {} + + // Functions to translate between an effect kind, starting at 1, and a + // position in the bitset. + + constexpr static size_t kindToPos(FunctionEffect::Kind K) { + return static_cast(K); + } + + constexpr static FunctionEffect::Kind posToKind(size_t Pos) { + return static_cast(Pos); + } + + // Iterates through the bits which are set. + class iterator { + const FunctionEffectKindSet *Outer = nullptr; + size_t Idx = 0; + + // If Idx does not reference a set bit, advance it until it does, + // or until it reaches EndBitPos. + void advanceToNextSetBit() { + while (Idx < EndBitPos && !Outer->KindBits.test(Idx)) + ++Idx; + } + + public: + iterator(); + iterator(const FunctionEffectKindSet &O, size_t I) : Outer(&O), Idx(I) { + advanceToNextSetBit(); + } + bool operator==(const iterator &Other) const { return Idx == Other.Idx; } + bool operator!=(const iterator &Other) const { return Idx != Other.Idx; } + + iterator operator++() { + ++Idx; + advanceToNextSetBit(); + return *this; + } + + FunctionEffect operator*() const { + assert(Idx < EndBitPos && "Dereference of end iterator"); + return FunctionEffect(posToKind(Idx)); + } + }; + +public: + FunctionEffectKindSet() = default; + explicit FunctionEffectKindSet(FunctionEffectsRef FX) { insert(FX); } + + iterator begin() const { return iterator(*this, 0); } + iterator end() const { return iterator(*this, EndBitPos); } + + void insert(FunctionEffect Effect) { KindBits.set(kindToPos(Effect.kind())); } + void insert(FunctionEffectsRef FX) { + for (FunctionEffect Item : FX.effects()) + insert(Item); + } + void insert(FunctionEffectKindSet Set) { KindBits |= Set.KindBits; } + + bool empty() const { return KindBits.none(); } + bool contains(const FunctionEffect::Kind EK) const { + return KindBits.test(kindToPos(EK)); + } + void dump(llvm::raw_ostream &OS) const; + + static FunctionEffectKindSet difference(FunctionEffectKindSet LHS, + FunctionEffectKindSet RHS) { + return FunctionEffectKindSet(LHS.KindBits & ~RHS.KindBits); + } +}; + /// A mutable set of FunctionEffects and possibly conditions attached to them. /// Used to compare and merge effects on declarations. /// diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 53d88482698f0..b1512e22ee2dd 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -8482,9 +8482,9 @@ compiler warnings: - A redeclaration of a ``nonblocking`` or ``nonallocating`` function must also be declared with the same attribute (or a stronger one). A redeclaration may add an attribute. -The warnings are controlled by ``-Wfunction-effects``, which is enabled by default. +The warnings are controlled by ``-Wfunction-effects``, which is disabled by default. -In a future commit, the compiler will diagnose function calls from ``nonblocking`` and ``nonallocating`` +The compiler also diagnoses function calls from ``nonblocking`` and ``nonallocating`` functions to other functions which lack the appropriate attribute. }]; } diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 8090119e512fb..a726a0ef4b4bd 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -1250,6 +1250,12 @@ def ElementwiseATan : Builtin { let Prototype = "void(...)"; } +def ElementwiseATan2 : Builtin { + let Spellings = ["__builtin_elementwise_atan2"]; + let Attributes = [NoThrow, Const, CustomTypeChecking]; + let Prototype = "void(...)"; +} + def ElementwiseBitreverse : Builtin { let Spellings = ["__builtin_elementwise_bitreverse"]; let Attributes = [NoThrow, Const, CustomTypeChecking]; @@ -1268,6 +1274,18 @@ def ElementwiseMin : Builtin { let Prototype = "void(...)"; } +def ElementwiseMaximum : Builtin { + let Spellings = ["__builtin_elementwise_maximum"]; + let Attributes = [NoThrow, Const, CustomTypeChecking]; + let Prototype = "void(...)"; +} + +def ElementwiseMinimum : Builtin { + let Spellings = ["__builtin_elementwise_minimum"]; + let Attributes = [NoThrow, Const, CustomTypeChecking]; + let Prototype = "void(...)"; +} + def ElementwiseCeil : Builtin { let Spellings = ["__builtin_elementwise_ceil"]; let Attributes = [NoThrow, Const, CustomTypeChecking]; @@ -1442,6 +1460,18 @@ def ReduceMin : Builtin { let Prototype = "void(...)"; } +def ReduceMaximum : Builtin { + let Spellings = ["__builtin_reduce_maximum"]; + let Attributes = [NoThrow, Const, CustomTypeChecking]; + let Prototype = "void(...)"; +} + +def ReduceMinimum : Builtin { + let Spellings = ["__builtin_reduce_minimum"]; + let Attributes = [NoThrow, Const, CustomTypeChecking]; + let Prototype = "void(...)"; +} + def ReduceXor : Builtin { let Spellings = ["__builtin_reduce_xor"]; let Attributes = [NoThrow, Const, CustomTypeChecking]; @@ -4709,6 +4739,12 @@ def HLSLClamp : LangBuiltin<"HLSL_LANG"> { let Prototype = "void(...)"; } +def HLSLCross: LangBuiltin<"HLSL_LANG"> { + let Spellings = ["__builtin_hlsl_cross"]; + let Attributes = [NoThrow, Const]; + let Prototype = "void(...)"; +} + def HLSLDotProduct : LangBuiltin<"HLSL_LANG"> { let Spellings = ["__builtin_hlsl_dot"]; let Attributes = [NoThrow, Const]; diff --git a/clang/include/clang/Basic/BuiltinsX86.def b/clang/include/clang/Basic/BuiltinsX86.def index 2a987abcf9a35..4c6b22cca421c 100644 --- a/clang/include/clang/Basic/BuiltinsX86.def +++ b/clang/include/clang/Basic/BuiltinsX86.def @@ -543,8 +543,8 @@ TARGET_BUILTIN(__builtin_ia32_wbinvd, "v", "n", "") TARGET_BUILTIN(__builtin_ia32_wbnoinvd, "v", "n", "wbnoinvd") // ADX -TARGET_BUILTIN(__builtin_ia32_addcarryx_u32, "UcUcUiUiUi*", "n", "") -TARGET_BUILTIN(__builtin_ia32_subborrow_u32, "UcUcUiUiUi*", "n", "") +TARGET_BUILTIN(__builtin_ia32_addcarryx_u32, "UcUcUiUiUi*", "nE", "") +TARGET_BUILTIN(__builtin_ia32_subborrow_u32, "UcUcUiUiUi*", "nE", "") // RDSEED TARGET_BUILTIN(__builtin_ia32_rdseed16_step, "UiUs*", "n", "rdseed") diff --git a/clang/include/clang/Basic/BuiltinsX86_64.def b/clang/include/clang/Basic/BuiltinsX86_64.def index d5fdb272d92d1..2c591edb2835c 100644 --- a/clang/include/clang/Basic/BuiltinsX86_64.def +++ b/clang/include/clang/Basic/BuiltinsX86_64.def @@ -66,8 +66,8 @@ TARGET_BUILTIN(__builtin_ia32_incsspq, "vUOi", "n", "shstk") TARGET_BUILTIN(__builtin_ia32_rdsspq, "UOiUOi", "n", "shstk") TARGET_BUILTIN(__builtin_ia32_wrssq, "vUOiv*", "n", "shstk") TARGET_BUILTIN(__builtin_ia32_wrussq, "vUOiv*", "n", "shstk") -TARGET_BUILTIN(__builtin_ia32_addcarryx_u64, "UcUcUOiUOiUOi*", "n", "") -TARGET_BUILTIN(__builtin_ia32_subborrow_u64, "UcUcUOiUOiUOi*", "n", "") +TARGET_BUILTIN(__builtin_ia32_addcarryx_u64, "UcUcUOiUOiUOi*", "nE", "") +TARGET_BUILTIN(__builtin_ia32_subborrow_u64, "UcUcUOiUOiUOi*", "nE", "") TARGET_BUILTIN(__builtin_ia32_rdrand64_step, "UiUOi*", "n", "rdrnd") TARGET_BUILTIN(__builtin_ia32_rdseed64_step, "UiUOi*", "n", "rdseed") TARGET_BUILTIN(__builtin_ia32_lzcnt_u64, "UOiUOi", "ncE", "lzcnt") diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 7d81bdf827ea0..41e719d4d5781 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1125,6 +1125,11 @@ def ThreadSafety : DiagGroup<"thread-safety", def ThreadSafetyVerbose : DiagGroup<"thread-safety-verbose">; def ThreadSafetyBeta : DiagGroup<"thread-safety-beta">; +// Warnings and notes related to the function effects system which underlies +// the nonblocking and nonallocating attributes. +def FunctionEffects : DiagGroup<"function-effects">; +def PerfConstraintImpliesNoexcept : DiagGroup<"perf-constraint-implies-noexcept">; + // Uniqueness Analysis warnings def Consumed : DiagGroup<"consumed">; @@ -1133,7 +1138,7 @@ def Consumed : DiagGroup<"consumed">; // DefaultIgnore in addition to putting it here. def All : DiagGroup<"all", [Most, Parentheses, Switch, SwitchBool, MisleadingIndentation, PackedNonPod, - VLACxxExtension]>; + VLACxxExtension, PerfConstraintImpliesNoexcept]>; // Warnings that should be in clang-cl /w4. def : DiagGroup<"CL4", [All, Extra]>; @@ -1566,10 +1571,6 @@ def UnsafeBufferUsageInContainer : DiagGroup<"unsafe-buffer-usage-in-container"> def UnsafeBufferUsageInLibcCall : DiagGroup<"unsafe-buffer-usage-in-libc-call">; def UnsafeBufferUsage : DiagGroup<"unsafe-buffer-usage", [UnsafeBufferUsageInContainer, UnsafeBufferUsageInLibcCall]>; -// Warnings and notes related to the function effects system underlying -// the nonblocking and nonallocating attributes. -def FunctionEffects : DiagGroup<"function-effects">; - // Warnings and notes InstallAPI verification. def InstallAPIViolation : DiagGroup<"installapi-violation">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 64e6d0407b0ce..583475327c522 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5262,6 +5262,13 @@ def note_template_arg_refers_here_func : Note< def err_template_arg_template_params_mismatch : Error< "template template argument has different template parameters than its " "corresponding template template parameter">; +def note_template_arg_template_params_mismatch : Note< + "template template argument has different template parameters than its " + "corresponding template template parameter">; +def err_non_deduced_mismatch : Error< + "could not match %diff{$ against $|types}0,1">; +def err_inconsistent_deduction : Error< + "conflicting deduction %diff{$ against $|types}0,1 for parameter">; def err_template_arg_not_integral_or_enumeral : Error< "non-type template argument of type %0 must have an integral or enumeration" " type">; @@ -10503,8 +10510,8 @@ def err_first_argument_to_cwsc_pdtor_call : Error< def err_second_argument_to_cwsc_not_pointer : Error< "second argument to __builtin_call_with_static_chain must be of pointer type">; -def err_vector_incorrect_num_initializers : Error< - "%select{too many|too few}0 elements in vector initialization (expected %1 elements, have %2)">; +def err_vector_incorrect_num_elements : Error< + "%select{too many|too few}0 elements in vector %select{initialization|operand}3 (expected %1 elements, have %2)">; def err_altivec_empty_initializer : Error<"expected initializer">; def err_invalid_neon_type_code : Error< @@ -10965,19 +10972,68 @@ def warn_imp_cast_drops_unaligned : Warning< InGroup>; // Function effects +def warn_func_effect_violation : Warning< + "%select{function|constructor|destructor|lambda|block|member initializer of constructor}0 " + "with '%1' attribute " + "must not %select{allocate or deallocate memory|throw or catch exceptions|" + "have static local variables|use thread-local variables|access ObjC methods or properties}2">, + InGroup, DefaultIgnore; +def note_func_effect_violation : Note< + "%select{function|constructor|destructor|lambda|block|member initializer}0 " + "cannot be inferred '%1' because it " + "%select{allocates or deallocates memory|throws or catches exceptions|" + "has a static local variable|uses a thread-local variable|" + "accesses an ObjC method or property}2">; +def warn_func_effect_calls_func_without_effect : Warning< + "%select{function|constructor|destructor|lambda|block|member initializer of constructor}0 " + "with '%1' attribute " + "must not call non-'%1' " + "%select{function|constructor|destructor|lambda|block}2 " + "'%3'">, + InGroup, DefaultIgnore; +def note_func_effect_calls_func_without_effect : Note< + "%select{function|constructor|destructor|lambda|block|member initializer}0 " + "cannot be inferred '%1' because it calls non-'%1' " + "%select{function|constructor|destructor|lambda|block}2 " + "'%3'">; +def warn_func_effect_calls_expr_without_effect : Warning< + "%select{function|constructor|destructor|lambda|block|member initializer of constructor}0 " + "with '%1' attribute " + "must not call non-'%1' expression">, + InGroup, DefaultIgnore; +def note_func_effect_call_extern : Note< + "declaration cannot be inferred '%0' because it has no definition in this translation unit">; +def note_func_effect_call_disallows_inference : Note< + "%select{function|constructor|destructor|lambda|block}0 " + "does not permit inference of '%1' because it is declared '%2'">; +def note_func_effect_call_indirect : Note< + "%select{virtual method|function pointer}0 cannot be inferred '%1'">; +def warn_perf_constraint_implies_noexcept : Warning< + "%select{function|constructor|destructor|lambda|block}0 " + "with '%1' attribute should be declared noexcept">, + InGroup, DefaultIgnore; + +// FIXME: It would be nice if we could provide fuller template expansion notes. +def note_func_effect_from_template : Note< + "in template expansion here">; +def note_func_effect_in_constructor : Note< + "in%select{| implicit}0 constructor here">; +def note_in_evaluating_default_argument : Note< + "in evaluating default argument here">; + // spoofing nonblocking/nonallocating def warn_invalid_add_func_effects : Warning< "attribute '%0' should not be added via type conversion">, - InGroup; + InGroup, DefaultIgnore; def warn_mismatched_func_effect_override : Warning< "attribute '%0' on overriding function does not match base declaration">, - InGroup; + InGroup, DefaultIgnore; def warn_mismatched_func_effect_redeclaration : Warning< "attribute '%0' on function does not match previous declaration">, - InGroup; + InGroup, DefaultIgnore; def warn_conflicting_func_effects : Warning< "effects conflict when merging declarations; kept '%0', discarded '%1'">, - InGroup; + InGroup, DefaultIgnore; def err_func_with_effects_no_prototype : Error< "'%0' function must have a prototype">; @@ -12214,7 +12270,8 @@ def err_builtin_invalid_arg_type: Error < "a floating point type|" "a vector of integers|" "an unsigned integer|" - "an 'int'}1 (was %2)">; + "an 'int'|" + "a vector of floating points}1 (was %2)">; def err_builtin_matrix_disabled: Error< "matrix types extension is disabled. Pass -fenable-matrix to enable it">; @@ -12609,6 +12666,24 @@ def err_acc_loop_spec_conflict def err_acc_collapse_loop_count : Error<"OpenACC 'collapse' clause loop count must be a %select{constant " "expression|positive integer value, evaluated to %1}0">; +def err_acc_size_expr_value + : Error< + "OpenACC 'tile' clause size expression must be %select{an asterisk " + "or a constant expression|positive integer value, evaluated to %1}0">; +def err_acc_invalid_in_loop + : Error<"%select{OpenACC '%2' construct|while loop|do loop}0 cannot appear " + "in intervening code of a 'loop' with a '%1' clause">; +def note_acc_active_clause_here + : Note<"active '%0' clause defined here">; +def err_acc_clause_multiple_loops + : Error<"more than one for-loop in a loop associated with OpenACC 'loop' " + "construct with a '%select{collapse|tile}0' clause">; +def err_acc_insufficient_loops + : Error<"'%0' clause specifies a loop count greater than the number " + "of available loops">; +def err_acc_intervening_code + : Error<"inner loops must be tightly nested inside a '%0' clause on " + "a 'loop' construct">; // AMDGCN builtins diagnostics def err_amdgcn_global_load_lds_size_invalid_value : Error<"invalid size value">; diff --git a/clang/include/clang/Basic/DiagnosticSerializationKinds.td b/clang/include/clang/Basic/DiagnosticSerializationKinds.td index 253a955431997..3914d3930bec7 100644 --- a/clang/include/clang/Basic/DiagnosticSerializationKinds.td +++ b/clang/include/clang/Basic/DiagnosticSerializationKinds.td @@ -51,9 +51,9 @@ def note_pch_vfsoverlay_files : Note<"%select{PCH|current translation unit}0 has def note_pch_vfsoverlay_empty : Note<"%select{PCH|current translation unit}0 has no VFS overlays">; def err_ast_file_version_too_old : Error< - "%select{PCH|module|AST}0 file '%1' uses an older PCH format that is no longer supported">; + "%select{PCH|module|AST}0 file '%1' uses an older format that is no longer supported">; def err_ast_file_version_too_new : Error< - "%select{PCH|module|AST}0 file '%1' uses a newer PCH format that cannot be read">; + "%select{PCH|module|AST}0 file '%1' uses a newer format that cannot be read">; def err_ast_file_different_branch : Error< "%select{PCH|module|AST}0 file '%1' built from a different branch (%2) than the compiler (%3)">; def err_ast_file_with_compiler_errors : Error< diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index 8c605f6852016..e2d4206c72cc9 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -241,8 +241,10 @@ class LangOptionsBase { /// Attempt to be ABI-compatible with code generated by Clang 19.0.x. /// This causes clang to: - /// - Incorrectly mangles the 'base type' substitutions of the CXX + /// - Incorrectly mangle the 'base type' substitutions of the CXX /// construction vtable because it hasn't added 'type' as a substitution. + /// - Skip mangling enclosing class templates of member-like friend + /// function templates. Ver19, /// Conform to the underlying platform's C and C++ ABIs as closely diff --git a/clang/include/clang/Basic/OpenACCClauses.def b/clang/include/clang/Basic/OpenACCClauses.def index 19cdfe7672133..a380e5ae69c41 100644 --- a/clang/include/clang/Basic/OpenACCClauses.def +++ b/clang/include/clang/Basic/OpenACCClauses.def @@ -52,6 +52,7 @@ VISIT_CLAUSE(Private) VISIT_CLAUSE(Reduction) VISIT_CLAUSE(Self) VISIT_CLAUSE(Seq) +VISIT_CLAUSE(Tile) VISIT_CLAUSE(VectorLength) VISIT_CLAUSE(Wait) diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index 30f2c8f1dbfde..dc82a4b6d1f77 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -308,5 +308,8 @@ def OpenACCAssociatedStmtConstruct def OpenACCComputeConstruct : StmtNode; def OpenACCLoopConstruct : StmtNode; +// OpenACC Additional Expressions. +def OpenACCAsteriskSizeExpr : StmtNode; + // HLSL Constructs. def HLSLOutArgExpr : StmtNode; diff --git a/clang/include/clang/Basic/arm_sme.td b/clang/include/clang/Basic/arm_sme.td index 45673cb915c5e..0f689e82bdb74 100644 --- a/clang/include/clang/Basic/arm_sme.td +++ b/clang/include/clang/Basic/arm_sme.td @@ -818,7 +818,7 @@ multiclass ZAReadzArray{ defm SVREADZ_VG2 : ZAReadzArray<"2">; defm SVREADZ_VG4 : ZAReadzArray<"4">; -let SMETargetGuard = "sme2,sme-lutv2" in { +let SMETargetGuard = "sme-lutv2" in { def SVWRITE_LANE_ZT : SInst<"svwrite_lane_zt[_{d}]", "vidi", "cUcsUsiUilUlfhdb", MergeNone, "aarch64_sme_write_lane_zt", [IsStreaming, IsInOutZT0], [ImmCheck<0, ImmCheck0_0>, ImmCheck<2, ImmCheck1_3>]>; def SVWRITE_ZT : SInst<"svwrite_zt[_{d}]", "vid", "cUcsUsiUilUlfhdb", MergeNone, "aarch64_sme_write_zt", [IsStreaming, IsOutZT0], [ImmCheck<0, ImmCheck0_0>]>; def SVLUTI4_ZT_X4 : SInst<"svluti4_zt_{d}_x4", "4i2.u", "cUc", MergeNone, "aarch64_sme_luti4_zt_x4", [IsStreaming, IsInZT0], [ImmCheck<0, ImmCheck0_0>]>; diff --git a/clang/include/clang/Basic/arm_sve.td b/clang/include/clang/Basic/arm_sve.td index da496e30fbb52..90b1ec242e6ba 100644 --- a/clang/include/clang/Basic/arm_sve.td +++ b/clang/include/clang/Basic/arm_sve.td @@ -943,11 +943,6 @@ defm SVFCVTZS_S64_F16 : SInstCvtMXZ<"svcvt_s64[_f16]", "ddPO", "dPO", "l", "aar defm SVFCVTZS_S32_F32 : SInstCvtMXZ<"svcvt_s32[_f32]", "ddPM", "dPM", "i", "aarch64_sve_fcvtzs", [IsOverloadCvt]>; defm SVFCVTZS_S64_F32 : SInstCvtMXZ<"svcvt_s64[_f32]", "ddPM", "dPM", "l", "aarch64_sve_fcvtzs_i64f32">; -let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { - defm SVCVT_BF16_F32 : SInstCvtMXZ<"svcvt_bf16[_f32]", "ddPM", "dPM", "b", "aarch64_sve_fcvt_bf16f32">; - def SVCVTNT_BF16_F32 : SInst<"svcvtnt_bf16[_f32]", "ddPM", "b", MergeOp1, "aarch64_sve_fcvtnt_bf16f32", [IsOverloadNone, VerifyRuntimeMode]>; -} - // svcvt_s##_f64 defm SVFCVTZS_S32_F64 : SInstCvtMXZ<"svcvt_s32[_f64]", "ttPd", "tPd", "d", "aarch64_sve_fcvtzs_i32f64">; defm SVFCVTZS_S64_F64 : SInstCvtMXZ<"svcvt_s64[_f64]", "ddPN", "dPN", "l", "aarch64_sve_fcvtzs", [IsOverloadCvt]>; @@ -1003,19 +998,26 @@ defm SVFCVT_F32_F64 : SInstCvtMXZ<"svcvt_f32[_f64]", "MMPd", "MPd", "d", "aarc defm SVFCVT_F64_F16 : SInstCvtMXZ<"svcvt_f64[_f16]", "ddPO", "dPO", "d", "aarch64_sve_fcvt_f64f16">; defm SVFCVT_F64_F32 : SInstCvtMXZ<"svcvt_f64[_f32]", "ddPM", "dPM", "d", "aarch64_sve_fcvt_f64f32">; -let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { -defm SVCVTLT_F32 : SInstCvtMX<"svcvtlt_f32[_f16]", "ddPh", "dPh", "f", "aarch64_sve_fcvtlt_f32f16">; -defm SVCVTLT_F64 : SInstCvtMX<"svcvtlt_f64[_f32]", "ddPh", "dPh", "d", "aarch64_sve_fcvtlt_f64f32">; +let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { +defm SVCVT_BF16_F32 : SInstCvtMXZ<"svcvt_bf16[_f32]", "$$Pd", "$Pd", "f", "aarch64_sve_fcvt_bf16f32_v2">; + +def SVCVTNT_BF16_F32 : SInst<"svcvtnt_bf16[_f32]", "$$Pd", "f", MergeOp1, "aarch64_sve_fcvtnt_bf16f32_v2", [IsOverloadNone, VerifyRuntimeMode]>; +// SVCVTNT_X_BF16_F32 : Implemented as macro by SveEmitter.cpp +} -defm SVCVTX_F32 : SInstCvtMXZ<"svcvtx_f32[_f64]", "MMPd", "MPd", "d", "aarch64_sve_fcvtx_f32f64">; +let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { +defm SVCVTLT_F32_F16 : SInstCvtMX<"svcvtlt_f32[_f16]", "ddPh", "dPh", "f", "aarch64_sve_fcvtlt_f32f16">; +defm SVCVTLT_F64_F32 : SInstCvtMX<"svcvtlt_f64[_f32]", "ddPh", "dPh", "d", "aarch64_sve_fcvtlt_f64f32">; -def SVCVTNT_F32 : SInst<"svcvtnt_f16[_f32]", "hhPd", "f", MergeOp1, "aarch64_sve_fcvtnt_f16f32", [IsOverloadNone, VerifyRuntimeMode]>; -def SVCVTNT_F64 : SInst<"svcvtnt_f32[_f64]", "hhPd", "d", MergeOp1, "aarch64_sve_fcvtnt_f32f64", [IsOverloadNone, VerifyRuntimeMode]>; -// SVCVTNT_X : Implemented as macro by SveEmitter.cpp +defm SVCVTX_F32_F64 : SInstCvtMXZ<"svcvtx_f32[_f64]", "MMPd", "MPd", "d", "aarch64_sve_fcvtx_f32f64">; -def SVCVTXNT_F32 : SInst<"svcvtxnt_f32[_f64]", "MMPd", "d", MergeOp1, "aarch64_sve_fcvtxnt_f32f64", [IsOverloadNone, VerifyRuntimeMode]>; -// SVCVTXNT_X_F32 : Implemented as macro by SveEmitter.cpp +def SVCVTNT_F16_F32 : SInst<"svcvtnt_f16[_f32]", "hhPd", "f", MergeOp1, "aarch64_sve_fcvtnt_f16f32", [IsOverloadNone, VerifyRuntimeMode]>; +def SVCVTNT_F32_F64 : SInst<"svcvtnt_f32[_f64]", "hhPd", "d", MergeOp1, "aarch64_sve_fcvtnt_f32f64", [IsOverloadNone, VerifyRuntimeMode]>; +// SVCVTNT_X_F16_F32 : Implemented as macro by SveEmitter.cpp +// SVCVTNT_X_F32_F64 : Implemented as macro by SveEmitter.cpp +def SVCVTXNT_F32_F64 : SInst<"svcvtxnt_f32[_f64]", "MMPd", "d", MergeOp1, "aarch64_sve_fcvtxnt_f32f64", [IsOverloadNone, VerifyRuntimeMode]>; +// SVCVTXNT_X_F32_F64 : Implemented as macro by SveEmitter.cpp } //////////////////////////////////////////////////////////////////////////////// diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 9d183ff2d69b3..445e681313eba 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1195,10 +1195,11 @@ def cxx_isystem : JoinedOrSeparate<["-"], "cxx-isystem">, Group, def c : Flag<["-"], "c">, Flags<[NoXarchOption]>, Visibility<[ClangOption, FlangOption]>, Group, HelpText<"Only run preprocess, compile, and assemble steps">; -defm convergent_functions : BoolFOption<"convergent-functions", - LangOpts<"ConvergentFunctions">, DefaultFalse, - NegFlag, - PosFlag>; +def fconvergent_functions : Flag<["-"], "fconvergent-functions">, + Visibility<[ClangOption, CC1Option]>, + HelpText< "Assume all functions may be convergent.">; +def fno_convergent_functions : Flag<["-"], "fno-convergent-functions">, + Visibility<[ClangOption, CC1Option]>; // Common offloading options let Group = offload_Group in { diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index d8b62c7652a0f..53a9577e0f72e 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -4974,6 +4974,15 @@ struct FormatStyle { /// \version 3.7 unsigned TabWidth; + /// A vector of non-keyword identifiers that should be interpreted as + /// template names. + /// + /// A ``<`` after a template name is annotated as a template opener instead of + /// a binary operator. + /// + /// \version 20 + std::vector TemplateNames; + /// A vector of non-keyword identifiers that should be interpreted as type /// names. /// @@ -5230,6 +5239,7 @@ struct FormatStyle { TableGenBreakingDAGArgOperators == R.TableGenBreakingDAGArgOperators && TableGenBreakInsideDAGArg == R.TableGenBreakInsideDAGArg && + TabWidth == R.TabWidth && TemplateNames == R.TemplateNames && TabWidth == R.TabWidth && TypeNames == R.TypeNames && TypenameMacros == R.TypenameMacros && UseTab == R.UseTab && VerilogBreakBetweenInstancePorts == diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index eb8a851da7e04..93e49d395388a 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -3786,10 +3786,13 @@ class Parser : public CodeCompletionHandler { OpenACCIntExprParseResult ParseOpenACCAsyncArgument(OpenACCDirectiveKind DK, OpenACCClauseKind CK, SourceLocation Loc); + /// Parses the 'size-expr', which is an integral value, or an asterisk. - bool ParseOpenACCSizeExpr(); + /// Asterisk is represented by a OpenACCAsteriskSizeExpr + ExprResult ParseOpenACCSizeExpr(OpenACCClauseKind CK); /// Parses a comma delimited list of 'size-expr's. - bool ParseOpenACCSizeExprList(); + bool ParseOpenACCSizeExprList(OpenACCClauseKind CK, + llvm::SmallVectorImpl &SizeExprs); /// Parses a 'gang-arg-list', used for the 'gang' clause. bool ParseOpenACCGangArgList(SourceLocation GangLoc); /// Parses a 'gang-arg', used for the 'gang' clause. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index d616c3834c429..bede971ce0191 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -457,55 +457,6 @@ enum class FunctionEffectMode : uint8_t { Dependent // effect(expr) where expr is dependent. }; -struct FunctionEffectDiff { - enum class Kind { Added, Removed, ConditionMismatch }; - - FunctionEffect::Kind EffectKind; - Kind DiffKind; - FunctionEffectWithCondition Old; // invalid when Added. - FunctionEffectWithCondition New; // invalid when Removed. - - StringRef effectName() const { - if (Old.Effect.kind() != FunctionEffect::Kind::None) - return Old.Effect.name(); - return New.Effect.name(); - } - - /// Describes the result of effects differing between a base class's virtual - /// method and an overriding method in a subclass. - enum class OverrideResult { - NoAction, - Warn, - Merge // Merge missing effect from base to derived. - }; - - /// Return true if adding or removing the effect as part of a type conversion - /// should generate a diagnostic. - bool shouldDiagnoseConversion(QualType SrcType, - const FunctionEffectsRef &SrcFX, - QualType DstType, - const FunctionEffectsRef &DstFX) const; - - /// Return true if adding or removing the effect in a redeclaration should - /// generate a diagnostic. - bool shouldDiagnoseRedeclaration(const FunctionDecl &OldFunction, - const FunctionEffectsRef &OldFX, - const FunctionDecl &NewFunction, - const FunctionEffectsRef &NewFX) const; - - /// Return true if adding or removing the effect in a C++ virtual method - /// override should generate a diagnostic. - OverrideResult shouldDiagnoseMethodOverride( - const CXXMethodDecl &OldMethod, const FunctionEffectsRef &OldFX, - const CXXMethodDecl &NewMethod, const FunctionEffectsRef &NewFX) const; -}; - -struct FunctionEffectDifferences : public SmallVector { - /// Caller should short-circuit by checking for equality first. - FunctionEffectDifferences(const FunctionEffectsRef &Old, - const FunctionEffectsRef &New); -}; - /// Sema - This implements semantic analysis and AST building for C. /// \nosubgrouping class Sema final : public SemaBase { @@ -546,6 +497,7 @@ class Sema final : public SemaBase { // 32. Constraints and Concepts (SemaConcept.cpp) // 33. Types (SemaType.cpp) // 34. FixIt Helpers (SemaFixItUtils.cpp) + // 35. Function Effects (SemaFunctionEffects.cpp) /// \name Semantic Analysis /// Implementations are in Sema.cpp @@ -851,30 +803,10 @@ class Sema final : public SemaBase { /// Warn when implicitly casting 0 to nullptr. void diagnoseZeroToNullptrConversion(CastKind Kind, const Expr *E); - // ----- function effects --- - /// Warn when implicitly changing function effects. void diagnoseFunctionEffectConversion(QualType DstType, QualType SrcType, SourceLocation Loc); - /// Warn and return true if adding an effect to a set would create a conflict. - bool diagnoseConflictingFunctionEffect(const FunctionEffectsRef &FX, - const FunctionEffectWithCondition &EC, - SourceLocation NewAttrLoc); - - // Report a failure to merge function effects between declarations due to a - // conflict. - void - diagnoseFunctionEffectMergeConflicts(const FunctionEffectSet::Conflicts &Errs, - SourceLocation NewLoc, - SourceLocation OldLoc); - - /// Try to parse the conditional expression attached to an effect attribute - /// (e.g. 'nonblocking'). (c.f. Sema::ActOnNoexceptSpec). Return an empty - /// optional on error. - std::optional - ActOnEffectExpression(Expr *CondExpr, StringRef AttributeName); - /// makeUnavailableInSystemHeader - There is an error in the current /// context. If we're still in a system header, and we can plausibly /// make the relevant declaration unavailable instead of erroring, do @@ -2381,7 +2313,8 @@ class Sema final : public SemaBase { bool CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall, const FunctionProtoType *Proto); - bool BuiltinVectorMath(CallExpr *TheCall, QualType &Res); + /// \param FPOnly restricts the arguments to floating-point types. + bool BuiltinVectorMath(CallExpr *TheCall, QualType &Res, bool FPOnly = false); bool BuiltinVectorToScalarMath(CallExpr *TheCall); /// Handles the checks for format strings, non-POD arguments to vararg @@ -2573,7 +2506,8 @@ class Sema final : public SemaBase { ExprResult AtomicOpsOverloaded(ExprResult TheCallResult, AtomicExpr::AtomicOp Op); - bool BuiltinElementwiseMath(CallExpr *TheCall); + /// \param FPOnly restricts the arguments to floating-point types. + bool BuiltinElementwiseMath(CallExpr *TheCall, bool FPOnly = false); bool PrepareBuiltinReduceMathOneArgCall(CallExpr *TheCall); bool BuiltinNonDeterministicValue(CallExpr *TheCall); @@ -12482,8 +12416,9 @@ class Sema final : public SemaBase { sema::TemplateDeductionInfo &Info); bool isTemplateTemplateParameterAtLeastAsSpecializedAs( - TemplateParameterList *PParam, TemplateDecl *AArg, - const DefaultArguments &DefaultArgs, SourceLocation Loc, bool IsDeduced); + TemplateParameterList *PParam, TemplateDecl *PArg, TemplateDecl *AArg, + const DefaultArguments &DefaultArgs, SourceLocation ArgLoc, + bool IsDeduced); /// Mark which template parameters are used in a given expression. /// @@ -12792,6 +12727,9 @@ class Sema final : public SemaBase { /// We are instantiating a type alias template declaration. TypeAliasTemplateInstantiation, + + /// We are performing partial ordering for template template parameters. + PartialOrderingTTP, } Kind; /// Was the enclosing context a non-instantiation SFINAE context? @@ -13013,6 +12951,12 @@ class Sema final : public SemaBase { TemplateDecl *Entity, BuildingDeductionGuidesTag, SourceRange InstantiationRange = SourceRange()); + struct PartialOrderingTTP {}; + /// \brief Note that we are partial ordering template template parameters. + InstantiatingTemplate(Sema &SemaRef, SourceLocation ArgLoc, + PartialOrderingTTP, TemplateDecl *PArg, + SourceRange InstantiationRange = SourceRange()); + /// Note that we have finished instantiating this template. void Clear(); @@ -15050,6 +14994,12 @@ class Sema final : public SemaBase { return hasAcceptableDefinition(D, &Hidden, Kind); } + /// Try to parse the conditional expression attached to an effect attribute + /// (e.g. 'nonblocking'). (c.f. Sema::ActOnNoexceptSpec). Return an empty + /// optional on error. + std::optional + ActOnEffectExpression(Expr *CondExpr, StringRef AttributeName); + private: /// The implementation of RequireCompleteType bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T, @@ -15080,6 +15030,108 @@ class Sema final : public SemaBase { std::string getFixItZeroLiteralForType(QualType T, SourceLocation Loc) const; ///@} + + // + // + // ------------------------------------------------------------------------- + // + // + + /// \name Function Effects + /// Implementations are in SemaFunctionEffects.cpp + ///@{ +public: + struct FunctionEffectDiff { + enum class Kind { Added, Removed, ConditionMismatch }; + + FunctionEffect::Kind EffectKind; + Kind DiffKind; + std::optional + Old; // Invalid when 'Kind' is 'Added'. + std::optional + New; // Invalid when 'Kind' is 'Removed'. + + StringRef effectName() const { + if (Old) + return Old.value().Effect.name(); + return New.value().Effect.name(); + } + + /// Describes the result of effects differing between a base class's virtual + /// method and an overriding method in a subclass. + enum class OverrideResult { + NoAction, + Warn, + Merge // Merge missing effect from base to derived. + }; + + /// Return true if adding or removing the effect as part of a type + /// conversion should generate a diagnostic. + bool shouldDiagnoseConversion(QualType SrcType, + const FunctionEffectsRef &SrcFX, + QualType DstType, + const FunctionEffectsRef &DstFX) const; + + /// Return true if adding or removing the effect in a redeclaration should + /// generate a diagnostic. + bool shouldDiagnoseRedeclaration(const FunctionDecl &OldFunction, + const FunctionEffectsRef &OldFX, + const FunctionDecl &NewFunction, + const FunctionEffectsRef &NewFX) const; + + /// Return true if adding or removing the effect in a C++ virtual method + /// override should generate a diagnostic. + OverrideResult shouldDiagnoseMethodOverride( + const CXXMethodDecl &OldMethod, const FunctionEffectsRef &OldFX, + const CXXMethodDecl &NewMethod, const FunctionEffectsRef &NewFX) const; + }; + + struct FunctionEffectDiffVector : public SmallVector { + /// Caller should short-circuit by checking for equality first. + FunctionEffectDiffVector(const FunctionEffectsRef &Old, + const FunctionEffectsRef &New); + }; + + /// All functions/lambdas/blocks which have bodies and which have a non-empty + /// FunctionEffectsRef to be verified. + SmallVector DeclsWithEffectsToVerify; + + /// The union of all effects present on DeclsWithEffectsToVerify. Conditions + /// are all null. + FunctionEffectKindSet AllEffectsToVerify; + +public: + /// Warn and return true if adding a function effect to a set would create a + /// conflict. + bool diagnoseConflictingFunctionEffect(const FunctionEffectsRef &FX, + const FunctionEffectWithCondition &EC, + SourceLocation NewAttrLoc); + + // Report a failure to merge function effects between declarations due to a + // conflict. + void + diagnoseFunctionEffectMergeConflicts(const FunctionEffectSet::Conflicts &Errs, + SourceLocation NewLoc, + SourceLocation OldLoc); + + /// Inline checks from the start of maybeAddDeclWithEffects, to + /// minimize performance impact on code not using effects. + template + void maybeAddDeclWithEffects(FuncOrBlockDecl *D) { + if (Context.hasAnyFunctionEffects()) + if (FunctionEffectsRef FX = D->getFunctionEffects(); !FX.empty()) + maybeAddDeclWithEffects(D, FX); + } + + /// Potentially add a FunctionDecl or BlockDecl to DeclsWithEffectsToVerify. + void maybeAddDeclWithEffects(const Decl *D, const FunctionEffectsRef &FX); + + /// Unconditionally add a Decl to DeclsWithEfffectsToVerify. + void addDeclWithEffects(const Decl *D, const FunctionEffectsRef &FX); + + void performFunctionEffectAnalysis(TranslationUnitDecl *TU); + + ///@} }; DeductionFailureInfo diff --git a/clang/include/clang/Sema/SemaOpenACC.h b/clang/include/clang/Sema/SemaOpenACC.h index 839fdb79cd0ac..97386d2378b75 100644 --- a/clang/include/clang/Sema/SemaOpenACC.h +++ b/clang/include/clang/Sema/SemaOpenACC.h @@ -22,6 +22,7 @@ #include "clang/Sema/Ownership.h" #include "clang/Sema/SemaBase.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Compiler.h" #include #include #include @@ -41,6 +42,62 @@ class SemaOpenACC : public SemaBase { /// above collection. bool InsideComputeConstruct = false; + /// Certain clauses care about the same things that aren't specific to the + /// individual clause, but can be shared by a few, so store them here. All + /// require a 'no intervening constructs' rule, so we know they are all from + /// the same 'place'. + struct LoopCheckingInfo { + /// Records whether we've seen the top level 'for'. We already diagnose + /// later that the 'top level' is a for loop, so we use this to suppress the + /// 'collapse inner loop not a 'for' loop' diagnostic. + LLVM_PREFERRED_TYPE(bool) + unsigned TopLevelLoopSeen : 1; + + /// Records whether this 'tier' of the loop has already seen a 'for' loop, + /// used to diagnose if there are multiple 'for' loops at any one level. + LLVM_PREFERRED_TYPE(bool) + unsigned CurLevelHasLoopAlready : 1; + } LoopInfo{/*TopLevelLoopSeen=*/false, /*CurLevelHasLoopAlready=*/false}; + + /// The 'collapse' clause requires quite a bit of checking while + /// parsing/instantiating its body, so this structure/object keeps all of the + /// necessary information as we do checking. This should rarely be directly + /// modified, and typically should be controlled by the RAII objects. + /// + /// Collapse has an 'N' count that makes it apply to a number of loops 'below' + /// it. + struct CollapseCheckingInfo { + OpenACCCollapseClause *ActiveCollapse = nullptr; + + /// This is a value that maintains the current value of the 'N' on the + /// current collapse, minus the depth that has already been traversed. When + /// there is not an active collapse, or a collapse whose depth we don't know + /// (for example, if it is a dependent value), this should be `nullopt`, + /// else it should be 'N' minus the current depth traversed. + std::optional CurCollapseCount; + + /// Records whether we've hit a CurCollapseCount of '0' on the way down, + /// which allows us to diagnose if the value of 'N' is too large for the + /// current number of 'for' loops. + bool CollapseDepthSatisfied = true; + } CollapseInfo; + + /// The 'tile' clause requires a bit of additional checking as well, so like + /// the `CollapseCheckingInfo`, ensure we maintain information here too. + struct TileCheckingInfo { + OpenACCTileClause *ActiveTile = nullptr; + + /// This is the number of expressions on a 'tile' clause. This doesn't have + /// to be an APSInt because it isn't the result of a constexpr, just by our + /// own counting of elements. + std::optional CurTileCount; + + /// Records whether we've hit a 'CurTileCount' of '0' on the wya down, + /// which allows us to diagnose if the number of arguments is too large for + /// the current number of 'for' loops. + bool TileDepthSatisfied = true; + } TileInfo; + public: // Redeclaration of the version in OpenACCClause.h. using DeviceTypeArgument = std::pair; @@ -141,6 +198,7 @@ class SemaOpenACC : public SemaBase { assert((ClauseKind == OpenACCClauseKind::NumGangs || ClauseKind == OpenACCClauseKind::NumWorkers || ClauseKind == OpenACCClauseKind::Async || + ClauseKind == OpenACCClauseKind::Tile || ClauseKind == OpenACCClauseKind::VectorLength) && "Parsed clause kind does not have a int exprs"); @@ -186,6 +244,7 @@ class SemaOpenACC : public SemaBase { assert((ClauseKind == OpenACCClauseKind::NumGangs || ClauseKind == OpenACCClauseKind::NumWorkers || ClauseKind == OpenACCClauseKind::Async || + ClauseKind == OpenACCClauseKind::Tile || ClauseKind == OpenACCClauseKind::VectorLength) && "Parsed clause kind does not have a int exprs"); @@ -297,6 +356,7 @@ class SemaOpenACC : public SemaBase { assert((ClauseKind == OpenACCClauseKind::NumGangs || ClauseKind == OpenACCClauseKind::NumWorkers || ClauseKind == OpenACCClauseKind::Async || + ClauseKind == OpenACCClauseKind::Tile || ClauseKind == OpenACCClauseKind::VectorLength) && "Parsed clause kind does not have a int exprs"); Details = IntExprDetails{{IntExprs.begin(), IntExprs.end()}}; @@ -305,6 +365,7 @@ class SemaOpenACC : public SemaBase { assert((ClauseKind == OpenACCClauseKind::NumGangs || ClauseKind == OpenACCClauseKind::NumWorkers || ClauseKind == OpenACCClauseKind::Async || + ClauseKind == OpenACCClauseKind::Tile || ClauseKind == OpenACCClauseKind::VectorLength) && "Parsed clause kind does not have a int exprs"); Details = IntExprDetails{std::move(IntExprs)}; @@ -411,6 +472,17 @@ class SemaOpenACC : public SemaBase { SemaOpenACC(Sema &S); + // Called when we encounter a 'while' statement, before looking at its 'body'. + void ActOnWhileStmt(SourceLocation WhileLoc); + // Called when we encounter a 'do' statement, before looking at its 'body'. + void ActOnDoStmt(SourceLocation DoLoc); + // Called when we encounter a 'for' statement, before looking at its 'body'. + void ActOnForStmtBegin(SourceLocation ForLoc); + // Called when we encounter a 'for' statement, after we've consumed/checked + // the body. This is necessary for a number of checks on the contents of the + // 'for' statement. + void ActOnForStmtEnd(SourceLocation ForLoc, StmtResult Body); + /// Called after parsing an OpenACC Clause so that it can be checked. OpenACCClause *ActOnClause(ArrayRef ExistingClauses, OpenACCParsedClause &Clause); @@ -473,6 +545,50 @@ class SemaOpenACC : public SemaBase { SourceLocation RBLoc); /// Checks the loop depth value for a collapse clause. ExprResult CheckCollapseLoopCount(Expr *LoopCount); + /// Checks a single size expr for a tile clause. 'gang' could possibly call + /// this, but has slightly stricter rules as to valid values. + ExprResult CheckTileSizeExpr(Expr *SizeExpr); + + ExprResult BuildOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc); + ExprResult ActOnOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc); + + /// Helper type to restore the state of various 'loop' constructs when we run + /// into a loop (for, etc) inside the construct. + class LoopInConstructRAII { + SemaOpenACC &SemaRef; + LoopCheckingInfo OldLoopInfo; + CollapseCheckingInfo OldCollapseInfo; + TileCheckingInfo OldTileInfo; + bool PreserveDepth; + + public: + LoopInConstructRAII(SemaOpenACC &SemaRef, bool PreserveDepth = true) + : SemaRef(SemaRef), OldLoopInfo(SemaRef.LoopInfo), + OldCollapseInfo(SemaRef.CollapseInfo), OldTileInfo(SemaRef.TileInfo), + PreserveDepth(PreserveDepth) {} + ~LoopInConstructRAII() { + // The associated-statement level of this should NOT preserve this, as it + // is a new construct, but other loop uses need to preserve the depth so + // it makes it to the 'top level' for diagnostics. + bool CollapseDepthSatisified = + PreserveDepth ? SemaRef.CollapseInfo.CollapseDepthSatisfied + : OldCollapseInfo.CollapseDepthSatisfied; + bool TileDepthSatisfied = PreserveDepth + ? SemaRef.TileInfo.TileDepthSatisfied + : OldTileInfo.TileDepthSatisfied; + bool CurLevelHasLoopAlready = + PreserveDepth ? SemaRef.LoopInfo.CurLevelHasLoopAlready + : OldLoopInfo.CurLevelHasLoopAlready; + + SemaRef.LoopInfo = OldLoopInfo; + SemaRef.CollapseInfo = OldCollapseInfo; + SemaRef.TileInfo = OldTileInfo; + + SemaRef.CollapseInfo.CollapseDepthSatisfied = CollapseDepthSatisified; + SemaRef.TileInfo.TileDepthSatisfied = TileDepthSatisfied; + SemaRef.LoopInfo.CurLevelHasLoopAlready = CurLevelHasLoopAlready; + } + }; /// Helper type for the registration/assignment of constructs that need to /// 'know' about their parent constructs and hold a reference to them, such as @@ -482,9 +598,18 @@ class SemaOpenACC : public SemaBase { bool WasInsideComputeConstruct; OpenACCDirectiveKind DirKind; llvm::SmallVector ParentlessLoopConstructs; + LoopInConstructRAII LoopRAII; public: - AssociatedStmtRAII(SemaOpenACC &, OpenACCDirectiveKind); + AssociatedStmtRAII(SemaOpenACC &, OpenACCDirectiveKind, + ArrayRef, + ArrayRef); + void SetCollapseInfoBeforeAssociatedStmt( + ArrayRef UnInstClauses, + ArrayRef Clauses); + void SetTileInfoBeforeAssociatedStmt( + ArrayRef UnInstClauses, + ArrayRef Clauses); ~AssociatedStmtRAII(); }; }; diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index bde19a09d6ae0..4b79d4b771190 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -730,6 +730,10 @@ enum ASTRecordTypes { /// canonical declaration for the lambda class from the same module as /// enclosing function. FUNCTION_DECL_TO_LAMBDAS_MAP = 71, + + /// Record code for Sema's vector of functions/blocks with effects to + /// be verified. + DECLS_WITH_EFFECTS_TO_VERIFY = 72, }; /// Record types used within a source manager block. @@ -1998,12 +2002,14 @@ enum StmtCode { // SYCLUniqueStableNameExpr EXPR_SYCL_UNIQUE_STABLE_NAME, - // OpenACC Constructs + // OpenACC Constructs/Exprs STMT_OPENACC_COMPUTE_CONSTRUCT, STMT_OPENACC_LOOP_CONSTRUCT, + EXPR_OPENACC_ASTERISK_SIZE, // HLSL Constructs EXPR_HLSL_OUT_ARG, + }; /// The kinds of designators that can occur in a diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index c1843218a4b8b..aa88560b259a3 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -984,6 +984,9 @@ class ASTReader /// Sema tracks these to emit deferred diags. llvm::SmallSetVector DeclsToCheckForDeferredDiags; + /// The IDs of all decls with function effects to be checked. + SmallVector DeclsWithEffectsToVerify; + private: struct ImportedSubmodule { serialization::SubmoduleID ID; diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h index e21d41c867314..198dd01b8d07a 100644 --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -604,6 +604,7 @@ class ASTWriter : public ASTDeserializationListener, void WriteMSPointersToMembersPragmaOptions(Sema &SemaRef); void WritePackPragmaOptions(Sema &SemaRef); void WriteFloatControlPragmaOptions(Sema &SemaRef); + void WriteDeclsWithEffectsToVerify(Sema &SemaRef); void WriteModuleFileExtension(Sema &SemaRef, ModuleFileExtensionWriter &Writer); diff --git a/clang/include/module.modulemap b/clang/include/module.modulemap index b6ab99bb85d8a..6f18c0a49641c 100644 --- a/clang/include/module.modulemap +++ b/clang/include/module.modulemap @@ -65,6 +65,7 @@ module Clang_Basic { textual header "clang/Basic/BuiltinsX86.def" textual header "clang/Basic/BuiltinsX86_64.def" textual header "clang/Basic/BuiltinsXCore.def" + textual header "clang/Basic/CFProtectionOptions.def" textual header "clang/Basic/CodeGenOptions.def" textual header "clang/Basic/DebugOptions.def" textual header "clang/Basic/DiagnosticOptions.def" diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 735def67f7840..a81429ad6a238 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -14439,8 +14439,6 @@ bool ASTContext::useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl, Mangler->mangleThunk(Method, Thunk, /* elideOverrideInfo */ false, mangledNameStream); - if (Thunks.find(ElidedName) == Thunks.end()) - Thunks[ElidedName] = {}; Thunks[ElidedName].push_back(std::string(MangledName)); } } diff --git a/clang/lib/AST/ByteCode/Descriptor.cpp b/clang/lib/AST/ByteCode/Descriptor.cpp index 65ac7a3129aba..5a8a2b64d5582 100644 --- a/clang/lib/AST/ByteCode/Descriptor.cpp +++ b/clang/lib/AST/ByteCode/Descriptor.cpp @@ -103,6 +103,7 @@ static void ctorArrayDesc(Block *B, std::byte *Ptr, bool IsConst, Desc->IsConst = IsConst || D->IsConst; Desc->IsFieldMutable = IsMutable || D->IsMutable; Desc->InUnion = InUnion; + Desc->IsArrayElement = true; if (auto Fn = D->ElemDesc->CtorFn) Fn(B, ElemLoc, Desc->IsConst, Desc->IsFieldMutable, IsActive, @@ -408,6 +409,8 @@ QualType Descriptor::getType() const { QualType Descriptor::getElemQualType() const { assert(isArray()); QualType T = getType(); + if (T->isPointerOrReferenceType()) + return T->getPointeeType(); if (const auto *AT = T->getAsArrayTypeUnsafe()) return AT->getElementType(); if (const auto *CT = T->getAs()) diff --git a/clang/lib/AST/ByteCode/Descriptor.h b/clang/lib/AST/ByteCode/Descriptor.h index 5460199e0e991..bcb635e157f00 100644 --- a/clang/lib/AST/ByteCode/Descriptor.h +++ b/clang/lib/AST/ByteCode/Descriptor.h @@ -96,12 +96,16 @@ struct InlineDescriptor { /// Flag indicating if the field is mutable (if in a record). LLVM_PREFERRED_TYPE(bool) unsigned IsFieldMutable : 1; + /// Flag indicating if the field is an element of a composite array. + LLVM_PREFERRED_TYPE(bool) + unsigned IsArrayElement : 1; const Descriptor *Desc; InlineDescriptor(const Descriptor *D) : Offset(sizeof(InlineDescriptor)), IsConst(false), IsInitialized(false), - IsBase(false), IsActive(false), IsFieldMutable(false), Desc(D) {} + IsBase(false), IsActive(false), IsFieldMutable(false), + IsArrayElement(false), Desc(D) {} void dump() const { dump(llvm::errs()); } void dump(llvm::raw_ostream &OS) const; diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index fd9a256843a0e..b3ba81f04ff1f 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -1406,6 +1406,10 @@ bool handleFixedPointOverflow(InterpState &S, CodePtr OpPC, return S.noteUndefinedBehavior(); } +// https://github.com/llvm/llvm-project/issues/102513 +#if defined(_WIN32) && !defined(__clang__) && !defined(NDEBUG) +#pragma optimize("", off) +#endif bool Interpret(InterpState &S, APValue &Result) { // The current stack frame when we started Interpret(). // This is being used by the ops to determine wheter @@ -1430,6 +1434,10 @@ bool Interpret(InterpState &S, APValue &Result) { } } } +// https://github.com/llvm/llvm-project/issues/102513 +#if defined(_WIN32) && !defined(__clang__) && !defined(NDEBUG) +#pragma optimize("", on) +#endif } // namespace interp } // namespace clang diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index ebc800623f0d4..72c94e6fad3e0 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -1205,6 +1205,10 @@ static bool interp__builtin_ia32_lzcnt(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *Func, const CallExpr *Call) { + QualType CallType = Call->getType(); + if (!CallType->isIntegerType()) + return false; + APSInt Val = peekToAPSInt(S.Stk, *S.Ctx.classify(Call->getArg(0))); pushInteger(S, Val.countLeadingZeros(), Call->getType()); return true; @@ -1214,6 +1218,10 @@ static bool interp__builtin_ia32_tzcnt(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *Func, const CallExpr *Call) { + QualType CallType = Call->getType(); + if (!CallType->isIntegerType()) + return false; + APSInt Val = peekToAPSInt(S.Stk, *S.Ctx.classify(Call->getArg(0))); pushInteger(S, Val.countTrailingZeros(), Call->getType()); return true; diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp index 387cad9b137c0..a52f0e336ef29 100644 --- a/clang/lib/AST/ByteCode/Pointer.cpp +++ b/clang/lib/AST/ByteCode/Pointer.cpp @@ -204,18 +204,22 @@ APValue Pointer::toAPValue(const ASTContext &ASTCtx) const { Path.push_back(APValue::LValuePathEntry( {Ptr.getFieldDesc()->asDecl(), /*IsVirtual=*/false})); - if (const auto *FD = dyn_cast(Ptr.getFieldDesc()->asDecl())) + if (const auto *FD = + dyn_cast_if_present(Ptr.getFieldDesc()->asDecl())) Offset += getFieldOffset(FD); Ptr = Ptr.getBase(); } else if (Ptr.isArrayElement()) { + Ptr = Ptr.expand(); unsigned Index; if (Ptr.isOnePastEnd()) Index = Ptr.getArray().getNumElems(); else Index = Ptr.getIndex(); - Offset += (Index * ASTCtx.getTypeSizeInChars(Ptr.getType())); + QualType ElemType = Ptr.getFieldDesc()->getElemQualType(); + Offset += (Index * ASTCtx.getTypeSizeInChars(ElemType)); + Path.push_back(APValue::LValuePathEntry::ArrayIndex(Index)); Ptr = Ptr.getArray(); } else { diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h index ac9b9ed4091b6..08bc4b7e40b63 100644 --- a/clang/lib/AST/ByteCode/Pointer.h +++ b/clang/lib/AST/ByteCode/Pointer.h @@ -420,8 +420,18 @@ class Pointer { } /// Checks if the pointer points to an array. bool isArrayElement() const { - if (isBlockPointer()) - return inArray() && asBlockPointer().Base != Offset; + if (!isBlockPointer()) + return false; + + const BlockPointer &BP = asBlockPointer(); + if (inArray() && BP.Base != Offset) + return true; + + // Might be a narrow()'ed element in a composite array. + // Check the inline descriptor. + if (BP.Base >= sizeof(InlineDescriptor) && getInlineDesc()->IsArrayElement) + return true; + return false; } /// Pointer points directly to a block. diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp index 6ef49790481ac..8c79df8317a2e 100644 --- a/clang/lib/AST/ComputeDependence.cpp +++ b/clang/lib/AST/ComputeDependence.cpp @@ -958,3 +958,9 @@ ExprDependence clang::computeDependence(ObjCMessageExpr *E) { D |= A->getDependence(); return D; } + +ExprDependence clang::computeDependence(OpenACCAsteriskSizeExpr *E) { + // This represents a simple asterisk as typed, so cannot be dependent in any + // way. + return ExprDependence::None; +} diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 2e463fc00c6b6..9ecbf121e3fc0 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -3640,6 +3640,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case SYCLUniqueStableNameExprClass: case PackIndexingExprClass: case HLSLOutArgExprClass: + case OpenACCAsteriskSizeExprClass: // These never have a side-effect. return false; @@ -5408,3 +5409,13 @@ HLSLOutArgExpr *HLSLOutArgExpr::Create(const ASTContext &C, QualType Ty, HLSLOutArgExpr *HLSLOutArgExpr::CreateEmpty(const ASTContext &C) { return new (C) HLSLOutArgExpr(EmptyShell()); } + +OpenACCAsteriskSizeExpr *OpenACCAsteriskSizeExpr::Create(const ASTContext &C, + SourceLocation Loc) { + return new (C) OpenACCAsteriskSizeExpr(Loc, C.IntTy); +} + +OpenACCAsteriskSizeExpr * +OpenACCAsteriskSizeExpr::CreateEmpty(const ASTContext &C) { + return new (C) OpenACCAsteriskSizeExpr({}, C.IntTy); +} diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index 5dde923312698..3f37d06cc8f3a 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -471,6 +471,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::CoyieldExprClass: return ClassifyInternal(Ctx, cast(E)->getResumeExpr()); case Expr::SYCLUniqueStableNameExprClass: + case Expr::OpenACCAsteriskSizeExprClass: return Cl::CL_PRValue; break; @@ -704,7 +705,8 @@ static Cl::ModifiableType IsModifiable(ASTContext &Ctx, const Expr *E, return Cl::CM_ConstAddrSpace; // Arrays are not modifiable, only their elements are. - if (CT->isArrayType()) + if (CT->isArrayType() && + !(Ctx.getLangOpts().HLSL && CT->isConstantArrayType())) return Cl::CM_ArrayType; // Incomplete types are not modifiable. if (CT->isIncompleteType()) diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 48816d3078826..4d5af96093cfe 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -11873,6 +11873,13 @@ class IntExprEvaluator return Success(E->getValue(), E); } + bool VisitOpenACCAsteriskSizeExpr(const OpenACCAsteriskSizeExpr *E) { + // This should not be evaluated during constant expr evaluation, as it + // should always be in an unevaluated context (the args list of a 'gang' or + // 'tile' clause). + return Error(E); + } + bool VisitUnaryReal(const UnaryOperator *E); bool VisitUnaryImag(const UnaryOperator *E); @@ -13464,6 +13471,38 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, return Success(DidOverflow, E); } + case clang::X86::BI__builtin_ia32_addcarryx_u32: + case clang::X86::BI__builtin_ia32_addcarryx_u64: + case clang::X86::BI__builtin_ia32_subborrow_u32: + case clang::X86::BI__builtin_ia32_subborrow_u64: { + LValue ResultLValue; + APSInt CarryIn, LHS, RHS; + QualType ResultType = E->getArg(3)->getType()->getPointeeType(); + if (!EvaluateInteger(E->getArg(0), CarryIn, Info) || + !EvaluateInteger(E->getArg(1), LHS, Info) || + !EvaluateInteger(E->getArg(2), RHS, Info) || + !EvaluatePointer(E->getArg(3), ResultLValue, Info)) + return false; + + bool IsAdd = BuiltinOp == clang::X86::BI__builtin_ia32_addcarryx_u32 || + BuiltinOp == clang::X86::BI__builtin_ia32_addcarryx_u64; + + unsigned BitWidth = LHS.getBitWidth(); + unsigned CarryInBit = CarryIn.ugt(0) ? 1 : 0; + APInt ExResult = + IsAdd + ? (LHS.zext(BitWidth + 1) + (RHS.zext(BitWidth + 1) + CarryInBit)) + : (LHS.zext(BitWidth + 1) - (RHS.zext(BitWidth + 1) + CarryInBit)); + + APInt Result = ExResult.extractBits(BitWidth, 0); + uint64_t CarryOut = ExResult.extractBitsAsZExtValue(1, BitWidth); + + APValue APV{APSInt(Result, /*isUnsigned=*/true)}; + if (!handleAssignment(Info, E, ResultLValue, ResultType, APV)) + return false; + return Success(CarryOut, E); + } + case clang::X86::BI__builtin_ia32_bextr_u32: case clang::X86::BI__builtin_ia32_bextr_u64: case clang::X86::BI__builtin_ia32_bextri_u32: @@ -16876,6 +16915,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::GNUNullExprClass: case Expr::SourceLocExprClass: case Expr::EmbedExprClass: + case Expr::OpenACCAsteriskSizeExprClass: return NoDiag(); case Expr::PackIndexingExprClass: diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 1feec13f9deef..769a863c2b676 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -693,7 +693,10 @@ ItaniumMangleContextImpl::getEffectiveDeclContext(const Decl *D) { if (VD->isExternC()) return getASTContext().getTranslationUnitDecl(); - if (const auto *FD = D->getAsFunction()) { + if (const auto *FD = getASTContext().getLangOpts().getClangABICompat() > + LangOptions::ClangABI::Ver19 + ? D->getAsFunction() + : dyn_cast(D)) { if (FD->isExternC()) return getASTContext().getTranslationUnitDecl(); // Member-like constrained friends are mangled as if they were members of @@ -5742,6 +5745,15 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, case Expr::HLSLOutArgExprClass: llvm_unreachable( "cannot mangle hlsl temporary value; mangling wrong thing?"); + case Expr::OpenACCAsteriskSizeExprClass: { + // We shouldn't ever be able to get here, but diagnose anyway. + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID( + DiagnosticsEngine::Error, + "cannot yet mangle OpenACC Asterisk Size expression"); + Diags.Report(DiagID); + return; + } } if (AsTemplateArg && !IsPrimaryExpr) diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp index 565f1e05710c8..ddbe2136a671f 100644 --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -1354,6 +1354,9 @@ void JSONNodeDumper::VisitSYCLUniqueStableNameExpr( createQualType(E->getTypeSourceInfo()->getType())); } +void JSONNodeDumper::VisitOpenACCAsteriskSizeExpr( + const OpenACCAsteriskSizeExpr *E) {} + void JSONNodeDumper::VisitPredefinedExpr(const PredefinedExpr *PE) { JOS.attribute("name", PredefinedExpr::getIdentKindName(PE->getIdentKind())); } diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index e4c8663c134fd..4ccf3f76bf0ce 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -1024,6 +1024,7 @@ void MicrosoftCXXNameMangler::mangleFloat(llvm::APFloat Number) { case APFloat::S_Float8E4M3B11FNUZ: case APFloat::S_Float8E3M4: case APFloat::S_FloatTF32: + case APFloat::S_Float8E8M0FNU: case APFloat::S_Float6E3M2FN: case APFloat::S_Float6E2M3FN: case APFloat::S_Float4E2M1FN: diff --git a/clang/lib/AST/OpenACCClause.cpp b/clang/lib/AST/OpenACCClause.cpp index d864ded33e8d1..0b34ed6189593 100644 --- a/clang/lib/AST/OpenACCClause.cpp +++ b/clang/lib/AST/OpenACCClause.cpp @@ -24,6 +24,7 @@ bool OpenACCClauseWithParams::classof(const OpenACCClause *C) { } bool OpenACCClauseWithExprs::classof(const OpenACCClause *C) { return OpenACCWaitClause::classof(C) || OpenACCNumGangsClause::classof(C) || + OpenACCTileClause::classof(C) || OpenACCClauseWithSingleIntExpr::classof(C) || OpenACCClauseWithVarList::classof(C); } @@ -221,6 +222,16 @@ OpenACCNumGangsClause *OpenACCNumGangsClause::Create(const ASTContext &C, return new (Mem) OpenACCNumGangsClause(BeginLoc, LParenLoc, IntExprs, EndLoc); } +OpenACCTileClause *OpenACCTileClause::Create(const ASTContext &C, + SourceLocation BeginLoc, + SourceLocation LParenLoc, + ArrayRef SizeExprs, + SourceLocation EndLoc) { + void *Mem = + C.Allocate(OpenACCTileClause::totalSizeToAlloc(SizeExprs.size())); + return new (Mem) OpenACCTileClause(BeginLoc, LParenLoc, SizeExprs, EndLoc); +} + OpenACCPrivateClause *OpenACCPrivateClause::Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, @@ -420,6 +431,13 @@ void OpenACCClausePrinter::VisitNumGangsClause(const OpenACCNumGangsClause &C) { OS << ")"; } +void OpenACCClausePrinter::VisitTileClause(const OpenACCTileClause &C) { + OS << "tile("; + llvm::interleaveComma(C.getSizeExprs(), OS, + [&](const Expr *E) { printExpr(E); }); + OS << ")"; +} + void OpenACCClausePrinter::VisitNumWorkersClause( const OpenACCNumWorkersClause &C) { OS << "num_workers("; diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index e1b5bec7a50d0..641f7b52de6df 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1309,6 +1309,10 @@ void StmtPrinter::VisitPredefinedExpr(PredefinedExpr *Node) { OS << PredefinedExpr::getIdentKindName(Node->getIdentKind()); } +void StmtPrinter::VisitOpenACCAsteriskSizeExpr(OpenACCAsteriskSizeExpr *Node) { + OS << '*'; +} + void StmtPrinter::VisitCharacterLiteral(CharacterLiteral *Node) { CharacterLiteral::print(Node->getValue(), Node->getKind(), OS); } diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index c3812844ab8a3..299ac005c7fdb 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -1360,6 +1360,11 @@ void StmtProfiler::VisitPredefinedExpr(const PredefinedExpr *S) { ID.AddInteger(llvm::to_underlying(S->getIdentKind())); } +void StmtProfiler::VisitOpenACCAsteriskSizeExpr( + const OpenACCAsteriskSizeExpr *S) { + VisitExpr(S); +} + void StmtProfiler::VisitIntegerLiteral(const IntegerLiteral *S) { VisitExpr(S); S->getValue().Profile(ID); @@ -2552,6 +2557,11 @@ void OpenACCClauseProfiler::VisitNumGangsClause( Profiler.VisitStmt(E); } +void OpenACCClauseProfiler::VisitTileClause(const OpenACCTileClause &Clause) { + for (auto *E : Clause.getSizeExprs()) + Profiler.VisitStmt(E); +} + void OpenACCClauseProfiler::VisitNumWorkersClause( const OpenACCNumWorkersClause &Clause) { assert(Clause.hasIntExpr() && "num_workers clause requires a valid int expr"); diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 8a74159c7c93e..15b23d60c3ffa 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -381,6 +381,11 @@ void TextNodeDumper::Visit(const OMPClause *C) { OS << " "; } +void TextNodeDumper::VisitOpenACCAsteriskSizeExpr( + const OpenACCAsteriskSizeExpr *E) { + // Nothing to do here, only location exists, and that is printed elsewhere. +} + void TextNodeDumper::Visit(const OpenACCClause *C) { if (!C) { ColorScope Color(OS, ShowColors, NullColor); @@ -414,6 +419,7 @@ void TextNodeDumper::Visit(const OpenACCClause *C) { case OpenACCClauseKind::Private: case OpenACCClauseKind::Self: case OpenACCClauseKind::Seq: + case OpenACCClauseKind::Tile: case OpenACCClauseKind::VectorLength: // The condition expression will be printed as a part of the 'children', // but print 'clause' here so it is clear what is happening from the dump. diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index c703e43f12a9a..6f4958801cfe8 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -5117,8 +5117,6 @@ FunctionEffect::Kind FunctionEffect::oppositeKind() const { return Kind::Allocating; case Kind::Allocating: return Kind::NonAllocating; - case Kind::None: - return Kind::None; } llvm_unreachable("unknown effect kind"); } @@ -5133,53 +5131,41 @@ StringRef FunctionEffect::name() const { return "blocking"; case Kind::Allocating: return "allocating"; - case Kind::None: - return "(none)"; } llvm_unreachable("unknown effect kind"); } -bool FunctionEffect::canInferOnFunction(const Decl &Callee) const { +std::optional FunctionEffect::effectProhibitingInference( + const Decl &Callee, FunctionEffectKindSet CalleeFX) const { switch (kind()) { case Kind::NonAllocating: case Kind::NonBlocking: { - FunctionEffectsRef CalleeFX; - if (auto *FD = Callee.getAsFunction()) - CalleeFX = FD->getFunctionEffects(); - else if (auto *BD = dyn_cast(&Callee)) - CalleeFX = BD->getFunctionEffects(); - else - return false; - for (const FunctionEffectWithCondition &CalleeEC : CalleeFX) { + for (FunctionEffect Effect : CalleeFX) { // nonblocking/nonallocating cannot call allocating. - if (CalleeEC.Effect.kind() == Kind::Allocating) - return false; + if (Effect.kind() == Kind::Allocating) + return Effect; // nonblocking cannot call blocking. - if (kind() == Kind::NonBlocking && - CalleeEC.Effect.kind() == Kind::Blocking) - return false; + if (kind() == Kind::NonBlocking && Effect.kind() == Kind::Blocking) + return Effect; } - return true; + return std::nullopt; } case Kind::Allocating: case Kind::Blocking: - return false; - - case Kind::None: - assert(0 && "canInferOnFunction with None"); + assert(0 && "effectProhibitingInference with non-inferable effect kind"); break; } llvm_unreachable("unknown effect kind"); } bool FunctionEffect::shouldDiagnoseFunctionCall( - bool Direct, ArrayRef CalleeFX) const { + bool Direct, FunctionEffectKindSet CalleeFX) const { switch (kind()) { case Kind::NonAllocating: case Kind::NonBlocking: { const Kind CallerKind = kind(); - for (const auto &Effect : CalleeFX) { + for (FunctionEffect Effect : CalleeFX) { const Kind EK = Effect.kind(); // Does callee have same or stronger constraint? if (EK == CallerKind || @@ -5192,9 +5178,6 @@ bool FunctionEffect::shouldDiagnoseFunctionCall( case Kind::Allocating: case Kind::Blocking: return false; - case Kind::None: - assert(0 && "shouldDiagnoseFunctionCall with None"); - break; } llvm_unreachable("unknown effect kind"); } @@ -5300,21 +5283,24 @@ FunctionEffectSet FunctionEffectSet::getUnion(FunctionEffectsRef LHS, return Combined; } +namespace clang { + +raw_ostream &operator<<(raw_ostream &OS, + const FunctionEffectWithCondition &CFE) { + OS << CFE.Effect.name(); + if (Expr *E = CFE.Cond.getCondition()) { + OS << '('; + E->dump(); + OS << ')'; + } + return OS; +} + +} // namespace clang + LLVM_DUMP_METHOD void FunctionEffectsRef::dump(llvm::raw_ostream &OS) const { OS << "Effects{"; - bool First = true; - for (const auto &CFE : *this) { - if (!First) - OS << ", "; - else - First = false; - OS << CFE.Effect.name(); - if (Expr *E = CFE.Cond.getCondition()) { - OS << '('; - E->dump(); - OS << ')'; - } - } + llvm::interleaveComma(*this, OS); OS << "}"; } @@ -5322,6 +5308,12 @@ LLVM_DUMP_METHOD void FunctionEffectSet::dump(llvm::raw_ostream &OS) const { FunctionEffectsRef(*this).dump(OS); } +LLVM_DUMP_METHOD void FunctionEffectKindSet::dump(llvm::raw_ostream &OS) const { + OS << "Effects{"; + llvm::interleaveComma(*this, OS); + OS << "}"; +} + FunctionEffectsRef FunctionEffectsRef::create(ArrayRef FX, ArrayRef Conds) { diff --git a/clang/lib/Analysis/ExprMutationAnalyzer.cpp b/clang/lib/Analysis/ExprMutationAnalyzer.cpp index 6d726ae44104e..5a95ef36d0502 100644 --- a/clang/lib/Analysis/ExprMutationAnalyzer.cpp +++ b/clang/lib/Analysis/ExprMutationAnalyzer.cpp @@ -231,12 +231,11 @@ ExprMutationAnalyzer::Analyzer::findPointeeMutation(const Decl *Dec) { const Stmt *ExprMutationAnalyzer::Analyzer::findMutationMemoized( const Expr *Exp, llvm::ArrayRef Finders, Memoized::ResultMap &MemoizedResults) { - const auto Memoized = MemoizedResults.find(Exp); - if (Memoized != MemoizedResults.end()) + auto [Memoized, Inserted] = MemoizedResults.try_emplace(Exp); + if (!Inserted) return Memoized->second; // Assume Exp is not mutated before analyzing Exp. - MemoizedResults[Exp] = nullptr; if (isUnevaluated(Exp)) return nullptr; diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp index 5f5dfcb722f9d..61889861c9c80 100644 --- a/clang/lib/Basic/Targets/AArch64.cpp +++ b/clang/lib/Basic/Targets/AArch64.cpp @@ -373,6 +373,12 @@ void AArch64TargetInfo::getTargetDefinesARMV95A(const LangOptions &Opts, getTargetDefinesARMV94A(Opts, Builder); } +void AArch64TargetInfo::getTargetDefinesARMV96A(const LangOptions &Opts, + MacroBuilder &Builder) const { + // Armv9.6-A does not have a v8.* equivalent, but is a superset of v9.5-A. + getTargetDefinesARMV95A(Opts, Builder); +} + void AArch64TargetInfo::getTargetDefines(const LangOptions &Opts, MacroBuilder &Builder) const { // Target identification. @@ -657,6 +663,8 @@ void AArch64TargetInfo::getTargetDefines(const LangOptions &Opts, getTargetDefinesARMV94A(Opts, Builder); else if (*ArchInfo == llvm::AArch64::ARMV9_5A) getTargetDefinesARMV95A(Opts, Builder); + else if (*ArchInfo == llvm::AArch64::ARMV9_6A) + getTargetDefinesARMV96A(Opts, Builder); // All of the __sync_(bool|val)_compare_and_swap_(1|2|4|8|16) builtins work. Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1"); @@ -1044,6 +1052,9 @@ bool AArch64TargetInfo::handleTargetFeatures(std::vector &Features, if (Feature == "+v9.5a" && ArchInfo->Version < llvm::AArch64::ARMV9_5A.Version) ArchInfo = &llvm::AArch64::ARMV9_5A; + if (Feature == "+v9.6a" && + ArchInfo->Version < llvm::AArch64::ARMV9_6A.Version) + ArchInfo = &llvm::AArch64::ARMV9_6A; if (Feature == "+v8r") ArchInfo = &llvm::AArch64::ARMV8R; if (Feature == "+fullfp16") { diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h index 526f7f30a3861..1226ce4d4355c 100644 --- a/clang/lib/Basic/Targets/AArch64.h +++ b/clang/lib/Basic/Targets/AArch64.h @@ -148,6 +148,8 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public TargetInfo { MacroBuilder &Builder) const; void getTargetDefinesARMV95A(const LangOptions &Opts, MacroBuilder &Builder) const; + void getTargetDefinesARMV96A(const LangOptions &Opts, + MacroBuilder &Builder) const; void getTargetDefines(const LangOptions &Opts, MacroBuilder &Builder) const override; diff --git a/clang/lib/Basic/Targets/ARM.cpp b/clang/lib/Basic/Targets/ARM.cpp index 7423626d7c3cb..c56b8d9a44850 100644 --- a/clang/lib/Basic/Targets/ARM.cpp +++ b/clang/lib/Basic/Targets/ARM.cpp @@ -228,6 +228,8 @@ StringRef ARMTargetInfo::getCPUAttr() const { return "9_4A"; case llvm::ARM::ArchKind::ARMV9_5A: return "9_5A"; + case llvm::ARM::ArchKind::ARMV9_6A: + return "9_6A"; case llvm::ARM::ArchKind::ARMV8MBaseline: return "8M_BASE"; case llvm::ARM::ArchKind::ARMV8MMainline: @@ -891,6 +893,7 @@ void ARMTargetInfo::getTargetDefines(const LangOptions &Opts, case llvm::ARM::ArchKind::ARMV9_3A: case llvm::ARM::ArchKind::ARMV9_4A: case llvm::ARM::ArchKind::ARMV9_5A: + case llvm::ARM::ArchKind::ARMV9_6A: // Filter __arm_cdp, __arm_ldcl, __arm_stcl in arm_acle.h FeatureCoprocBF = FEATURE_COPROC_B1 | FEATURE_COPROC_B3; break; @@ -1060,6 +1063,7 @@ void ARMTargetInfo::getTargetDefines(const LangOptions &Opts, case llvm::ARM::ArchKind::ARMV9_3A: case llvm::ARM::ArchKind::ARMV9_4A: case llvm::ARM::ArchKind::ARMV9_5A: + case llvm::ARM::ArchKind::ARMV9_6A: getTargetDefinesARMV83A(Opts, Builder); break; } diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 62c6a57e8b7c8..abc936f2c686d 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -1321,10 +1321,10 @@ static void runThinLTOBackend( Conf.CGFileType = getCodeGenFileType(Action); break; } - if (Error E = - thinBackend(Conf, -1, AddStream, *M, *CombinedIndex, ImportList, - ModuleToDefinedGVSummaries[M->getModuleIdentifier()], - /* ModuleMap */ nullptr, CGOpts.CmdArgs)) { + if (Error E = thinBackend( + Conf, -1, AddStream, *M, *CombinedIndex, ImportList, + ModuleToDefinedGVSummaries[M->getModuleIdentifier()], + /* ModuleMap */ nullptr, Conf.CodeGenOnly, CGOpts.CmdArgs)) { handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) { errs() << "Error running ThinLTO backend: " << EIB.message() << '\n'; }); diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index da3eca73bfb57..43700ea9dd3cf 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -3836,6 +3836,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, case Builtin::BI__builtin_elementwise_atan: return RValue::get(emitBuiltinWithOneOverloadedType<1>( *this, E, llvm::Intrinsic::atan, "elt.atan")); + case Builtin::BI__builtin_elementwise_atan2: + return RValue::get(emitBuiltinWithOneOverloadedType<2>( + *this, E, llvm::Intrinsic::atan2, "elt.atan2")); case Builtin::BI__builtin_elementwise_ceil: return RValue::get(emitBuiltinWithOneOverloadedType<1>( *this, E, llvm::Intrinsic::ceil, "elt.ceil")); @@ -3961,6 +3964,22 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, return RValue::get(Result); } + case Builtin::BI__builtin_elementwise_maximum: { + Value *Op0 = EmitScalarExpr(E->getArg(0)); + Value *Op1 = EmitScalarExpr(E->getArg(1)); + Value *Result = Builder.CreateBinaryIntrinsic(llvm::Intrinsic::maximum, Op0, + Op1, nullptr, "elt.maximum"); + return RValue::get(Result); + } + + case Builtin::BI__builtin_elementwise_minimum: { + Value *Op0 = EmitScalarExpr(E->getArg(0)); + Value *Op1 = EmitScalarExpr(E->getArg(1)); + Value *Result = Builder.CreateBinaryIntrinsic(llvm::Intrinsic::minimum, Op0, + Op1, nullptr, "elt.minimum"); + return RValue::get(Result); + } + case Builtin::BI__builtin_reduce_max: { auto GetIntrinsicID = [this](QualType QT) { if (auto *VecTy = QT->getAs()) @@ -4013,6 +4032,12 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, case Builtin::BI__builtin_reduce_and: return RValue::get(emitBuiltinWithOneOverloadedType<1>( *this, E, llvm::Intrinsic::vector_reduce_and, "rdx.and")); + case Builtin::BI__builtin_reduce_maximum: + return RValue::get(emitBuiltinWithOneOverloadedType<1>( + *this, E, llvm::Intrinsic::vector_reduce_fmaximum, "rdx.maximum")); + case Builtin::BI__builtin_reduce_minimum: + return RValue::get(emitBuiltinWithOneOverloadedType<1>( + *this, E, llvm::Intrinsic::vector_reduce_fminimum, "rdx.minimum")); case Builtin::BI__builtin_matrix_transpose: { auto *MatrixTy = E->getArg(0)->getType()->castAs(); @@ -18642,6 +18667,21 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, IsUnsigned ? Intrinsic::dx_uclamp : Intrinsic::dx_clamp, ArrayRef{OpX, OpMin, OpMax}, nullptr, "dx.clamp"); } + case Builtin::BI__builtin_hlsl_cross: { + Value *Op0 = EmitScalarExpr(E->getArg(0)); + Value *Op1 = EmitScalarExpr(E->getArg(1)); + assert(E->getArg(0)->getType()->hasFloatingRepresentation() && + E->getArg(1)->getType()->hasFloatingRepresentation() && + "cross operands must have a float representation"); + // make sure each vector has exactly 3 elements + assert( + E->getArg(0)->getType()->getAs()->getNumElements() == 3 && + E->getArg(1)->getType()->getAs()->getNumElements() == 3 && + "input vectors must have 3 elements each"); + return Builder.CreateIntrinsic( + /*ReturnType=*/Op0->getType(), CGM.getHLSLRuntime().getCrossIntrinsic(), + ArrayRef{Op0, Op1}, nullptr, "hlsl.cross"); + } case Builtin::BI__builtin_hlsl_dot: { Value *Op0 = EmitScalarExpr(E->getArg(0)); Value *Op1 = EmitScalarExpr(E->getArg(1)); diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 8887c4de7c4c8..609957b75d6e7 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -1472,15 +1472,6 @@ llvm::DIType *CGDebugInfo::CreateType(const TemplateSpecializationType *Ty, return AliasTy; } - // Disable PrintCanonicalTypes here because we want - // the DW_AT_name to benefit from the TypePrinter's ability - // to skip defaulted template arguments. - // - // FIXME: Once -gsimple-template-names is enabled by default - // and we attach template parameters to alias template DIEs - // we don't need to worry about customizing the PrintingPolicy - // here anymore. - PP.PrintCanonicalTypes = false; printTemplateArgumentList(OS, Ty->template_arguments(), PP, TD->getTemplateParameters()); return DBuilder.createTypedef(Src, OS.str(), getOrCreateFile(Loc), diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index df4994ba9af6e..52d2f6d52abf9 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1095,6 +1095,8 @@ class StructAccessBase return Visit(E->getBase()); } const Expr *VisitCastExpr(const CastExpr *E) { + if (E->getCastKind() == CK_LValueToRValue) + return E; return Visit(E->getSubExpr()); } const Expr *VisitParenExpr(const ParenExpr *E) { @@ -1161,19 +1163,15 @@ llvm::Value *CodeGenFunction::EmitLoadOfCountedByField( return nullptr; llvm::Value *Res = nullptr; - if (const auto *DRE = dyn_cast(StructBase)) { - Res = EmitDeclRefLValue(DRE).getPointer(*this); - Res = Builder.CreateAlignedLoad(ConvertType(DRE->getType()), Res, - getPointerAlign(), "dre.load"); - } else if (const MemberExpr *ME = dyn_cast(StructBase)) { - LValue LV = EmitMemberExpr(ME); - Address Addr = LV.getAddress(); - Res = Addr.emitRawPointer(*this); - } else if (StructBase->getType()->isPointerType()) { + if (StructBase->getType()->isPointerType()) { LValueBaseInfo BaseInfo; TBAAAccessInfo TBAAInfo; Address Addr = EmitPointerWithAlignment(StructBase, &BaseInfo, &TBAAInfo); Res = Addr.emitRawPointer(*this); + } else if (StructBase->isLValue()) { + LValue LV = EmitLValue(StructBase); + Address Addr = LV.getAddress(); + Res = Addr.emitRawPointer(*this); } else { return nullptr; } @@ -5797,11 +5795,26 @@ LValue CodeGenFunction::EmitBinaryOperatorLValue(const BinaryOperator *E) { return EmitComplexAssignmentLValue(E); case TEK_Aggregate: + // If the lang opt is HLSL and the LHS is a constant array + // then we are performing a copy assignment and call a special + // function because EmitAggExprToLValue emits to a temporary LValue + if (getLangOpts().HLSL && E->getLHS()->getType()->isConstantArrayType()) + return EmitHLSLArrayAssignLValue(E); + return EmitAggExprToLValue(E); } llvm_unreachable("bad evaluation kind"); } +// This function implements trivial copy assignment for HLSL's +// assignable constant arrays. +LValue CodeGenFunction::EmitHLSLArrayAssignLValue(const BinaryOperator *E) { + LValue TrivialAssignmentRHS = EmitLValue(E->getRHS()); + LValue LHS = EmitLValue(E->getLHS()); + EmitAggregateAssign(LHS, TrivialAssignmentRHS, E->getLHS()->getType()); + return LHS; +} + LValue CodeGenFunction::EmitCallExprLValue(const CallExpr *E, llvm::CallBase **CallOrInvoke) { RValue RV = EmitCallExpr(E, ReturnValueSlot(), CallOrInvoke); diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index bbfc6672ecc25..74d4c5ea7bcaa 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -39,7 +39,7 @@ namespace llvm { extern cl::opt EnableSingleByteCoverage; } // namespace llvm -namespace { +namespace { class AggExprEmitter : public StmtVisitor { CodeGenFunction &CGF; CGBuilderTy &Builder; diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index b7f5b932c56b6..7529d4465f2c3 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -531,6 +531,10 @@ class ScalarExprEmitter return CGF.getOrCreateOpaqueRValueMapping(E).getScalarVal(); } + Value *VisitOpenACCAsteriskSizeExpr(OpenACCAsteriskSizeExpr *E) { + llvm_unreachable("Codegen for this isn't defined/implemented"); + } + // l-values. Value *VisitDeclRefExpr(DeclRefExpr *E) { if (CodeGenFunction::ConstantEmission Constant = CGF.tryEmitAsConstant(E)) diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h index a8aabca7348ff..6722d2c7c50a2 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -74,6 +74,7 @@ class CGHLSLRuntime { GENERATE_HLSL_INTRINSIC_FUNCTION(All, all) GENERATE_HLSL_INTRINSIC_FUNCTION(Any, any) + GENERATE_HLSL_INTRINSIC_FUNCTION(Cross, cross) GENERATE_HLSL_INTRINSIC_FUNCTION(Frac, frac) GENERATE_HLSL_INTRINSIC_FUNCTION(Length, length) GENERATE_HLSL_INTRINSIC_FUNCTION(Lerp, lerp) diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index 9bf15fca0de48..41dc91c578c80 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -801,10 +801,12 @@ void CodeGenFunction::EmitIndirectGotoStmt(const IndirectGotoStmt &S) { } void CodeGenFunction::EmitIfStmt(const IfStmt &S) { + const Stmt *Else = S.getElse(); + // The else branch of a consteval if statement is always the only branch that // can be runtime evaluated. if (S.isConsteval()) { - const Stmt *Executed = S.isNegatedConsteval() ? S.getThen() : S.getElse(); + const Stmt *Executed = S.isNegatedConsteval() ? S.getThen() : Else; if (Executed) { RunCleanupsScope ExecutedScope(*this); EmitStmt(Executed); @@ -830,8 +832,8 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) { S.isConstexpr())) { // Figure out which block (then or else) is executed. const Stmt *Executed = S.getThen(); - const Stmt *Skipped = S.getElse(); - if (!CondConstant) // Condition false? + const Stmt *Skipped = Else; + if (!CondConstant) // Condition false? std::swap(Executed, Skipped); // If the skipped block has no labels in it, just emit the executed block. @@ -852,7 +854,7 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) { llvm::BasicBlock *ThenBlock = createBasicBlock("if.then"); llvm::BasicBlock *ContBlock = createBasicBlock("if.end"); llvm::BasicBlock *ElseBlock = ContBlock; - if (S.getElse()) + if (Else) ElseBlock = createBasicBlock("if.else"); // Prefer the PGO based weights over the likelihood attribute. @@ -870,7 +872,7 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) { uint64_t ThenCount = getProfileCount(S.getThen()); if (!ThenCount && !getCurrentProfileCount() && CGM.getCodeGenOpts().OptimizationLevel) - LH = Stmt::getLikelihood(S.getThen(), S.getElse()); + LH = Stmt::getLikelihood(S.getThen(), Else); // When measuring MC/DC, always fully evaluate the condition up front using // EvaluateExprAsBool() so that the test vector bitmap can be updated prior to @@ -898,7 +900,7 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) { EmitBranch(ContBlock); // Emit the 'else' code if present. - if (const Stmt *Else = S.getElse()) { + if (Else) { { // There is no need to emit line number for an unconditional branch. auto NL = ApplyDebugLocation::CreateEmpty(*this); diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index eda96f3e352ce..e1fd9b72b8d7b 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -850,6 +850,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, for (const FunctionEffectWithCondition &Fe : FD->getFunctionEffects()) { if (Fe.Effect.kind() == FunctionEffect::Kind::NonBlocking) Fn->addFnAttr(llvm::Attribute::SanitizeRealtime); + else if (Fe.Effect.kind() == FunctionEffect::Kind::Blocking) + Fn->addFnAttr(llvm::Attribute::SanitizeRealtimeUnsafe); } // Apply fuzzing attribute to the function. @@ -1762,6 +1764,8 @@ void CodeGenFunction::EmitBranchToCounterBlock( if (!InstrumentRegions || !isInstrumentedCondition(Cond)) return EmitBranchOnBoolExpr(Cond, TrueBlock, FalseBlock, TrueCount, LH); + const Stmt *CntrStmt = (CntrIdx ? CntrIdx : Cond); + llvm::BasicBlock *ThenBlock = nullptr; llvm::BasicBlock *ElseBlock = nullptr; llvm::BasicBlock *NextBlock = nullptr; @@ -1814,7 +1818,7 @@ void CodeGenFunction::EmitBranchToCounterBlock( EmitBlock(CounterIncrBlock); // Increment corresponding counter; if index not provided, use Cond as index. - incrementProfileCounter(CntrIdx ? CntrIdx : Cond); + incrementProfileCounter(CntrStmt); // Go to the next block. EmitBranch(NextBlock); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 3e2abbd9bc109..9ba0ed02a564d 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -4296,6 +4296,7 @@ class CodeGenFunction : public CodeGenTypeCache { LValue EmitCastLValue(const CastExpr *E); LValue EmitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E); LValue EmitOpaqueValueLValue(const OpaqueValueExpr *e); + LValue EmitHLSLArrayAssignLValue(const BinaryOperator *E); void EmitHLSLOutArgExpr(const HLSLOutArgExpr *E, CallArgList &Args, QualType Ty); diff --git a/clang/lib/CodeGen/CodeGenPGO.cpp b/clang/lib/CodeGen/CodeGenPGO.cpp index b745ad37fc96b..820bb521ccf85 100644 --- a/clang/lib/CodeGen/CodeGenPGO.cpp +++ b/clang/lib/CodeGen/CodeGenPGO.cpp @@ -1206,14 +1206,12 @@ void CodeGenPGO::emitCounterSetOrIncrement(CGBuilderTy &Builder, const Stmt *S, if (llvm::EnableSingleByteCoverage) Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::instrprof_cover), ArrayRef(Args, 4)); - else { - if (!StepV) - Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::instrprof_increment), - ArrayRef(Args, 4)); - else - Builder.CreateCall( - CGM.getIntrinsic(llvm::Intrinsic::instrprof_increment_step), Args); - } + else if (!StepV) + Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::instrprof_increment), + ArrayRef(Args, 4)); + else + Builder.CreateCall( + CGM.getIntrinsic(llvm::Intrinsic::instrprof_increment_step), Args); } bool CodeGenPGO::canEmitMCDCCoverage(const CGBuilderTy &Builder) { diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp index 79dcdc04b0996..0b0b45ffead92 100644 --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -518,7 +518,7 @@ class MicrosoftCXXABI : public CGCXXABI { CGM.IntTy, CGM.IntTy, CGM.IntTy, - getImageRelativeType(getClassHierarchyDescriptorType()->getPointerTo()), + getImageRelativeType(CGM.UnqualPtrTy), }; BaseClassDescriptorType = llvm::StructType::create( CGM.getLLVMContext(), FieldTypes, "rtti.BaseClassDescriptor"); @@ -531,13 +531,8 @@ class MicrosoftCXXABI : public CGCXXABI { // Forward-declare RTTIClassHierarchyDescriptor to break a cycle. ClassHierarchyDescriptorType = llvm::StructType::create( CGM.getLLVMContext(), "rtti.ClassHierarchyDescriptor"); - llvm::Type *FieldTypes[] = { - CGM.IntTy, - CGM.IntTy, - CGM.IntTy, - getImageRelativeType( - getBaseClassDescriptorType()->getPointerTo()->getPointerTo()), - }; + llvm::Type *FieldTypes[] = {CGM.IntTy, CGM.IntTy, CGM.IntTy, + getImageRelativeType(CGM.UnqualPtrTy)}; ClassHierarchyDescriptorType->setBody(FieldTypes); return ClassHierarchyDescriptorType; } @@ -552,7 +547,7 @@ class MicrosoftCXXABI : public CGCXXABI { CGM.IntTy, CGM.IntTy, getImageRelativeType(CGM.Int8PtrTy), - getImageRelativeType(getClassHierarchyDescriptorType()->getPointerTo()), + getImageRelativeType(CGM.UnqualPtrTy), getImageRelativeType(CompleteObjectLocatorType), }; llvm::ArrayRef FieldTypesRef(FieldTypes); @@ -749,8 +744,7 @@ class MicrosoftCXXABI : public CGCXXABI { llvm::SmallString<23> CTATypeName("eh.CatchableTypeArray."); CTATypeName += llvm::utostr(NumEntries); - llvm::Type *CTType = - getImageRelativeType(getCatchableTypeType()->getPointerTo()); + llvm::Type *CTType = getImageRelativeType(CGM.UnqualPtrTy); llvm::Type *FieldTypes[] = { CGM.IntTy, // NumEntries llvm::ArrayType::get(CTType, NumEntries) // CatchableTypes @@ -777,7 +771,7 @@ class MicrosoftCXXABI : public CGCXXABI { llvm::FunctionCallee getThrowFn() { // _CxxThrowException is passed an exception object and a ThrowInfo object // which describes the exception. - llvm::Type *Args[] = {CGM.Int8PtrTy, getThrowInfoType()->getPointerTo()}; + llvm::Type *Args[] = {CGM.Int8PtrTy, CGM.UnqualPtrTy}; llvm::FunctionType *FTy = llvm::FunctionType::get(CGM.VoidTy, Args, /*isVarArg=*/false); llvm::FunctionCallee Throw = @@ -909,9 +903,8 @@ void MicrosoftCXXABI::emitVirtualObjectDelete(CodeGenFunction &CGF, } void MicrosoftCXXABI::emitRethrow(CodeGenFunction &CGF, bool isNoReturn) { - llvm::Value *Args[] = { - llvm::ConstantPointerNull::get(CGM.Int8PtrTy), - llvm::ConstantPointerNull::get(getThrowInfoType()->getPointerTo())}; + llvm::Value *Args[] = {llvm::ConstantPointerNull::get(CGM.Int8PtrTy), + llvm::ConstantPointerNull::get(CGM.UnqualPtrTy)}; llvm::FunctionCallee Fn = getThrowFn(); if (isNoReturn) CGF.EmitNoreturnRuntimeCallOrInvoke(Fn, Args); @@ -1958,13 +1951,13 @@ CGCallee MicrosoftCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF, SourceLocation Loc) { CGBuilderTy &Builder = CGF.Builder; - Ty = Ty->getPointerTo(); + Ty = CGF.UnqualPtrTy; Address VPtr = adjustThisArgumentForVirtualFunctionCall(CGF, GD, This, true); auto *MethodDecl = cast(GD.getDecl()); - llvm::Value *VTable = CGF.GetVTablePtr(VPtr, Ty->getPointerTo(), - MethodDecl->getParent()); + llvm::Value *VTable = + CGF.GetVTablePtr(VPtr, CGF.UnqualPtrTy, MethodDecl->getParent()); MicrosoftVTableContext &VFTContext = CGM.getMicrosoftVTableContext(); MethodVFTableLocation ML = VFTContext.getMethodVFTableLocation(GD); @@ -2125,9 +2118,9 @@ MicrosoftCXXABI::EmitVirtualMemPtrThunk(const CXXMethodDecl *MD, // Load the vfptr and then callee from the vftable. The callee should have // adjusted 'this' so that the vfptr is at offset zero. - llvm::Type *ThunkPtrTy = ThunkTy->getPointerTo(); - llvm::Value *VTable = CGF.GetVTablePtr( - getThisAddress(CGF), ThunkPtrTy->getPointerTo(), MD->getParent()); + llvm::Type *ThunkPtrTy = CGF.UnqualPtrTy; + llvm::Value *VTable = + CGF.GetVTablePtr(getThisAddress(CGF), CGF.UnqualPtrTy, MD->getParent()); llvm::Value *VFuncPtr = CGF.Builder.CreateConstInBoundsGEP1_64( ThunkPtrTy, VTable, ML.Index, "vfn"); @@ -2551,7 +2544,7 @@ static ConstantAddress getInitThreadEpochPtr(CodeGenModule &CGM) { static llvm::FunctionCallee getInitThreadHeaderFn(CodeGenModule &CGM) { llvm::FunctionType *FTy = llvm::FunctionType::get(llvm::Type::getVoidTy(CGM.getLLVMContext()), - CGM.IntTy->getPointerTo(), /*isVarArg=*/false); + CGM.UnqualPtrTy, /*isVarArg=*/false); return CGM.CreateRuntimeFunction( FTy, "_Init_thread_header", llvm::AttributeList::get(CGM.getLLVMContext(), @@ -2563,7 +2556,7 @@ static llvm::FunctionCallee getInitThreadHeaderFn(CodeGenModule &CGM) { static llvm::FunctionCallee getInitThreadFooterFn(CodeGenModule &CGM) { llvm::FunctionType *FTy = llvm::FunctionType::get(llvm::Type::getVoidTy(CGM.getLLVMContext()), - CGM.IntTy->getPointerTo(), /*isVarArg=*/false); + CGM.UnqualPtrTy, /*isVarArg=*/false); return CGM.CreateRuntimeFunction( FTy, "_Init_thread_footer", llvm::AttributeList::get(CGM.getLLVMContext(), @@ -2575,7 +2568,7 @@ static llvm::FunctionCallee getInitThreadFooterFn(CodeGenModule &CGM) { static llvm::FunctionCallee getInitThreadAbortFn(CodeGenModule &CGM) { llvm::FunctionType *FTy = llvm::FunctionType::get(llvm::Type::getVoidTy(CGM.getLLVMContext()), - CGM.IntTy->getPointerTo(), /*isVarArg=*/false); + CGM.UnqualPtrTy, /*isVarArg=*/false); return CGM.CreateRuntimeFunction( FTy, "_Init_thread_abort", llvm::AttributeList::get(CGM.getLLVMContext(), @@ -3157,8 +3150,8 @@ MicrosoftCXXABI::GetVBaseOffsetFromVBPtr(CodeGenFunction &CGF, VBPtrAlign = CGF.getPointerAlign(); } - llvm::Value *VBTable = Builder.CreateAlignedLoad( - CGM.Int32Ty->getPointerTo(0), VBPtr, VBPtrAlign, "vbtable"); + llvm::Value *VBTable = + Builder.CreateAlignedLoad(CGM.UnqualPtrTy, VBPtr, VBPtrAlign, "vbtable"); // Translate from byte offset to table index. It improves analyzability. llvm::Value *VBTableIndex = Builder.CreateAShr( @@ -3813,8 +3806,7 @@ MSRTTIBuilder::getBaseClassArray(SmallVectorImpl &Classes) { // mode) bytes of padding. We provide a pointer sized amount of padding by // adding +1 to Classes.size(). The sections have pointer alignment and are // marked pick-any so it shouldn't matter. - llvm::Type *PtrType = ABI.getImageRelativeType( - ABI.getBaseClassDescriptorType()->getPointerTo()); + llvm::Type *PtrType = ABI.getImageRelativeType(CGM.UnqualPtrTy); auto *ArrType = llvm::ArrayType::get(PtrType, Classes.size() + 1); auto *BCA = new llvm::GlobalVariable(Module, ArrType, @@ -4360,8 +4352,7 @@ llvm::GlobalVariable *MicrosoftCXXABI::getCatchableTypeArray(QualType T) { CatchableTypes.insert(getCatchableType(getContext().VoidPtrTy)); uint32_t NumEntries = CatchableTypes.size(); - llvm::Type *CTType = - getImageRelativeType(getCatchableTypeType()->getPointerTo()); + llvm::Type *CTType = getImageRelativeType(CGM.UnqualPtrTy); llvm::ArrayType *AT = llvm::ArrayType::get(CTType, NumEntries); llvm::StructType *CTAType = getCatchableTypeArrayType(NumEntries); llvm::Constant *Fields[] = { diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index fba6a8853c396..e9bf60d5e2ee4 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -229,7 +229,14 @@ Driver::Driver(StringRef ClangExecutable, StringRef TargetTriple, } #if defined(CLANG_CONFIG_FILE_SYSTEM_DIR) - SystemConfigDir = CLANG_CONFIG_FILE_SYSTEM_DIR; + if (llvm::sys::path::is_absolute(CLANG_CONFIG_FILE_SYSTEM_DIR)) { + SystemConfigDir = CLANG_CONFIG_FILE_SYSTEM_DIR; + } else { + SmallString<128> configFileDir(Dir); + llvm::sys::path::append(configFileDir, CLANG_CONFIG_FILE_SYSTEM_DIR); + llvm::sys::path::remove_dots(configFileDir, true); + SystemConfigDir = static_cast(configFileDir); + } #endif #if defined(CLANG_CONFIG_FILE_USER_DIR) { @@ -4022,7 +4029,8 @@ void Driver::handleArguments(Compilation &C, DerivedArgList &Args, // Emitting LLVM while linking disabled except in HIPAMD Toolchain if (Args.hasArg(options::OPT_emit_llvm) && !Args.hasArg(options::OPT_hip_link)) Diag(clang::diag::err_drv_emit_llvm_link); - if (IsCLMode() && LTOMode != LTOK_None && + if (C.getDefaultToolChain().getTriple().isWindowsMSVCEnvironment() && + LTOMode != LTOK_None && !Args.getLastArgValue(options::OPT_fuse_ld_EQ) .equals_insensitive("lld")) Diag(clang::diag::err_drv_lto_without_lld); diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp index 16f9b629fc538..de250322b3b34 100644 --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -227,6 +227,11 @@ static void getAArch64MultilibFlags(const Driver &D, if (BranchProtectionArg) { Result.push_back(BranchProtectionArg->getAsString(Args)); } + + const Arg *ABIArg = Args.getLastArgNoClaim(options::OPT_mabi_EQ); + if (ABIArg) { + Result.push_back(ABIArg->getAsString(Args)); + } } static void getARMMultilibFlags(const Driver &D, diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index b9987288d82d1..66ec0a7fd32f9 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -3604,7 +3604,8 @@ static void RenderSSPOptions(const Driver &D, const ToolChain &TC, if (Arg *A = Args.getLastArg(options::OPT_mstack_protector_guard_EQ)) { StringRef Value = A->getValue(); if (!EffectiveTriple.isX86() && !EffectiveTriple.isAArch64() && - !EffectiveTriple.isARM() && !EffectiveTriple.isThumb()) + !EffectiveTriple.isARM() && !EffectiveTriple.isThumb() && + !EffectiveTriple.isRISCV()) D.Diag(diag::err_drv_unsupported_opt_for_target) << A->getAsString(Args) << TripleStr; if ((EffectiveTriple.isX86() || EffectiveTriple.isARM() || @@ -3644,13 +3645,28 @@ static void RenderSSPOptions(const Driver &D, const ToolChain &TC, << A->getOption().getName() << Value << "sysreg global"; return; } + if (EffectiveTriple.isRISCV()) { + if (Value != "tls" && Value != "global") { + D.Diag(diag::err_drv_invalid_value_with_suggestion) + << A->getOption().getName() << Value << "tls global"; + return; + } + if (Value == "tls") { + if (!Args.hasArg(options::OPT_mstack_protector_guard_offset_EQ)) { + D.Diag(diag::err_drv_ssp_missing_offset_argument) + << A->getAsString(Args); + return; + } + } + } A->render(Args, CmdArgs); } if (Arg *A = Args.getLastArg(options::OPT_mstack_protector_guard_offset_EQ)) { StringRef Value = A->getValue(); if (!EffectiveTriple.isX86() && !EffectiveTriple.isAArch64() && - !EffectiveTriple.isARM() && !EffectiveTriple.isThumb()) + !EffectiveTriple.isARM() && !EffectiveTriple.isThumb() && + !EffectiveTriple.isRISCV()) D.Diag(diag::err_drv_unsupported_opt_for_target) << A->getAsString(Args) << TripleStr; int Offset; @@ -3669,7 +3685,8 @@ static void RenderSSPOptions(const Driver &D, const ToolChain &TC, if (Arg *A = Args.getLastArg(options::OPT_mstack_protector_guard_reg_EQ)) { StringRef Value = A->getValue(); - if (!EffectiveTriple.isX86() && !EffectiveTriple.isAArch64()) + if (!EffectiveTriple.isX86() && !EffectiveTriple.isAArch64() && + !EffectiveTriple.isRISCV()) D.Diag(diag::err_drv_unsupported_opt_for_target) << A->getAsString(Args) << TripleStr; if (EffectiveTriple.isX86() && (Value != "fs" && Value != "gs")) { @@ -3681,6 +3698,11 @@ static void RenderSSPOptions(const Driver &D, const ToolChain &TC, D.Diag(diag::err_drv_invalid_value) << A->getOption().getName() << Value; return; } + if (EffectiveTriple.isRISCV() && Value != "tp") { + D.Diag(diag::err_drv_invalid_value_with_suggestion) + << A->getOption().getName() << Value << "tp"; + return; + } A->render(Args, CmdArgs); } @@ -6258,8 +6280,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, options::OPT_fno_unique_internal_linkage_names); Args.addOptInFlag(CmdArgs, options::OPT_funique_basic_block_section_names, options::OPT_fno_unique_basic_block_section_names); - Args.addOptInFlag(CmdArgs, options::OPT_fconvergent_functions, - options::OPT_fno_convergent_functions); if (Arg *A = Args.getLastArg(options::OPT_fsplit_machine_functions, options::OPT_fno_split_machine_functions)) { @@ -6276,6 +6296,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.AddLastArg(CmdArgs, options::OPT_finstrument_functions, options::OPT_finstrument_functions_after_inlining, options::OPT_finstrument_function_entry_bare); + Args.AddLastArg(CmdArgs, options::OPT_fconvergent_functions, + options::OPT_fno_convergent_functions); // NVPTX/AMDGCN doesn't support PGO or coverage. There's no runtime support // for sampling, overhead of call arc collection is way too high and there's diff --git a/clang/lib/Driver/ToolChains/ZOS.cpp b/clang/lib/Driver/ToolChains/ZOS.cpp index 074e0556ecd2a..c5ad3ef1b00f1 100644 --- a/clang/lib/Driver/ToolChains/ZOS.cpp +++ b/clang/lib/Driver/ToolChains/ZOS.cpp @@ -37,6 +37,10 @@ void ZOS::addClangTargetOptions(const ArgList &DriverArgs, options::OPT_fno_aligned_allocation)) CC1Args.push_back("-faligned-alloc-unavailable"); + if (DriverArgs.hasFlag(options::OPT_fxl_pragma_pack, + options::OPT_fno_xl_pragma_pack, true)) + CC1Args.push_back("-fxl-pragma-pack"); + // Pass "-fno-sized-deallocation" only when the user hasn't manually enabled // or disabled sized deallocations. if (!DriverArgs.hasArgNoClaim(options::OPT_fsized_deallocation, diff --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp index 9cb45c8fbf9cb..66c03863293c2 100644 --- a/clang/lib/ExtractAPI/DeclarationFragments.cpp +++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp @@ -1621,6 +1621,9 @@ DeclarationFragmentsBuilder::getSubHeading(const NamedDecl *Decl) { cast(Decl)->isOverloadedOperator()) { Fragments.append(Decl->getNameAsString(), DeclarationFragments::FragmentKind::Identifier); + } else if (isa(Decl) && + cast(Decl)->getTypedefNameForAnonDecl()) { + return getSubHeading(cast(Decl)->getTypedefNameForAnonDecl()); } else if (Decl->getIdentifier()) { Fragments.append(Decl->getName(), DeclarationFragments::FragmentKind::Identifier); diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index d2463b892fbb9..5350c66ea5132 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -1147,6 +1147,7 @@ template <> struct MappingTraits { IO.mapOptional("TableGenBreakInsideDAGArg", Style.TableGenBreakInsideDAGArg); IO.mapOptional("TabWidth", Style.TabWidth); + IO.mapOptional("TemplateNames", Style.TemplateNames); IO.mapOptional("TypeNames", Style.TypeNames); IO.mapOptional("TypenameMacros", Style.TypenameMacros); IO.mapOptional("UseTab", Style.UseTab); diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h index 03c0cbd60961a..7d342a7dcca01 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -179,6 +179,7 @@ namespace format { TYPE(TrailingReturnArrow) \ TYPE(TrailingUnaryOperator) \ TYPE(TypeDeclarationParen) \ + TYPE(TemplateName) \ TYPE(TypeName) \ TYPE(TypenameMacro) \ TYPE(UnaryOperator) \ diff --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp index 63949b2e26bdc..2cdf6cd286b28 100644 --- a/clang/lib/Format/FormatTokenLexer.cpp +++ b/clang/lib/Format/FormatTokenLexer.cpp @@ -72,6 +72,8 @@ FormatTokenLexer::FormatTokenLexer( Macros.insert({Identifier, TT_StatementAttributeLikeMacro}); } + for (const auto &TemplateName : Style.TemplateNames) + TemplateNames.insert(&IdentTable.get(TemplateName)); for (const auto &TypeName : Style.TypeNames) TypeNames.insert(&IdentTable.get(TypeName)); } @@ -1368,6 +1370,8 @@ FormatToken *FormatTokenLexer::getNextToken() { FormatTok->setType(TT_MacroBlockBegin); else if (MacroBlockEndRegex.match(Text)) FormatTok->setType(TT_MacroBlockEnd); + else if (TemplateNames.contains(Identifier)) + FormatTok->setFinalizedType(TT_TemplateName); else if (TypeNames.contains(Identifier)) FormatTok->setFinalizedType(TT_TypeName); } diff --git a/clang/lib/Format/FormatTokenLexer.h b/clang/lib/Format/FormatTokenLexer.h index 277cc0a2dfde6..71389d2ade2b7 100644 --- a/clang/lib/Format/FormatTokenLexer.h +++ b/clang/lib/Format/FormatTokenLexer.h @@ -129,7 +129,7 @@ class FormatTokenLexer { llvm::SmallMapVector Macros; - llvm::SmallPtrSet TypeNames; + llvm::SmallPtrSet TemplateNames, TypeNames; bool FormattingDisabled; diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index f665ce2ad81eb..e2068e557732a 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -149,36 +149,36 @@ class AnnotatingParser { } bool parseAngle() { - if (!CurrentToken || !CurrentToken->Previous) + if (!CurrentToken) return false; - if (NonTemplateLess.count(CurrentToken->Previous) > 0) + + auto *Left = CurrentToken->Previous; // The '<'. + if (!Left) return false; - if (const auto &Previous = *CurrentToken->Previous; // The '<'. - Previous.Previous) { - if (Previous.Previous->Tok.isLiteral()) + if (NonTemplateLess.count(Left) > 0) + return false; + + const auto *BeforeLess = Left->Previous; + + if (BeforeLess) { + if (BeforeLess->Tok.isLiteral()) return false; - if (Previous.Previous->is(tok::r_brace)) + if (BeforeLess->is(tok::r_brace)) return false; - if (Previous.Previous->is(tok::r_paren) && Contexts.size() > 1 && - (!Previous.Previous->MatchingParen || - Previous.Previous->MatchingParen->isNot( - TT_OverloadedOperatorLParen))) { + if (BeforeLess->is(tok::r_paren) && Contexts.size() > 1 && + !(BeforeLess->MatchingParen && + BeforeLess->MatchingParen->is(TT_OverloadedOperatorLParen))) { return false; } - if (Previous.Previous->is(tok::kw_operator) && - CurrentToken->is(tok::l_paren)) { + if (BeforeLess->is(tok::kw_operator) && CurrentToken->is(tok::l_paren)) return false; - } } - FormatToken *Left = CurrentToken->Previous; Left->ParentBracket = Contexts.back().ContextKind; ScopedContextCreator ContextCreator(*this, tok::less, 12); Contexts.back().IsExpression = false; - const auto *BeforeLess = Left->Previous; - // If there's a template keyword before the opening angle bracket, this is a // template parameter, not an argument. if (BeforeLess && BeforeLess->isNot(tok::kw_template)) @@ -189,25 +189,29 @@ class AnnotatingParser { next(); } - for (bool SeenTernaryOperator = false; CurrentToken;) { + for (bool SeenTernaryOperator = false, MaybeAngles = true; CurrentToken;) { const bool InExpr = Contexts[Contexts.size() - 2].IsExpression; if (CurrentToken->is(tok::greater)) { const auto *Next = CurrentToken->Next; - // Try to do a better job at looking for ">>" within the condition of - // a statement. Conservatively insert spaces between consecutive ">" - // tokens to prevent splitting right bitshift operators and potentially - // altering program semantics. This check is overly conservative and - // will prevent spaces from being inserted in select nested template - // parameter cases, but should not alter program semantics. - if (Next && Next->is(tok::greater) && - Left->ParentBracket != tok::less && - CurrentToken->getStartOfNonWhitespace() == - Next->getStartOfNonWhitespace().getLocWithOffset(-1)) { - return false; - } - if (InExpr && SeenTernaryOperator && - (!Next || !Next->isOneOf(tok::l_paren, tok::l_brace))) { - return false; + if (CurrentToken->isNot(TT_TemplateCloser)) { + // Try to do a better job at looking for ">>" within the condition of + // a statement. Conservatively insert spaces between consecutive ">" + // tokens to prevent splitting right shift operators and potentially + // altering program semantics. This check is overly conservative and + // will prevent spaces from being inserted in select nested template + // parameter cases, but should not alter program semantics. + if (Next && Next->is(tok::greater) && + Left->ParentBracket != tok::less && + CurrentToken->getStartOfNonWhitespace() == + Next->getStartOfNonWhitespace().getLocWithOffset(-1)) { + return false; + } + if (InExpr && SeenTernaryOperator && + (!Next || !Next->isOneOf(tok::l_paren, tok::l_brace))) { + return false; + } + if (!MaybeAngles) + return false; } Left->MatchingParen = CurrentToken; CurrentToken->MatchingParen = Left; @@ -229,6 +233,10 @@ class AnnotatingParser { next(); return true; } + if (BeforeLess && BeforeLess->is(TT_TemplateName)) { + next(); + continue; + } if (CurrentToken->is(tok::question) && Style.Language == FormatStyle::LK_Java) { next(); @@ -243,11 +251,11 @@ class AnnotatingParser { // operator that was misinterpreted because we are parsing template // parameters. // FIXME: This is getting out of hand, write a decent parser. - if (InExpr && !Line.startsWith(tok::kw_template) && + if (MaybeAngles && InExpr && !Line.startsWith(tok::kw_template) && Prev.is(TT_BinaryOperator)) { const auto Precedence = Prev.getPrecedence(); if (Precedence > prec::Conditional && Precedence < prec::Relational) - return false; + MaybeAngles = false; } if (Prev.isOneOf(tok::question, tok::colon) && !Style.isProto()) SeenTernaryOperator = true; @@ -358,7 +366,7 @@ class AnnotatingParser { } else if (OpeningParen.Previous && (OpeningParen.Previous->isOneOf( tok::kw_static_assert, tok::kw_noexcept, tok::kw_explicit, - tok::kw_while, tok::l_paren, tok::comma, + tok::kw_while, tok::l_paren, tok::comma, TT_CastRParen, TT_BinaryOperator) || OpeningParen.Previous->isIf())) { // static_assert, if and while usually contain expressions. @@ -468,16 +476,18 @@ class AnnotatingParser { OpeningParen.Previous && OpeningParen.Previous->is(tok::kw_for); FormatToken *PossibleObjCForInToken = nullptr; while (CurrentToken) { - if (CurrentToken->Previous->is(TT_PointerOrReference) && - CurrentToken->Previous->Previous->isOneOf(tok::l_paren, - tok::coloncolon)) { + const auto &Prev = *CurrentToken->Previous; + if (Prev.is(TT_PointerOrReference) && + Prev.Previous->isOneOf(tok::l_paren, tok::coloncolon)) { ProbablyFunctionType = true; } if (CurrentToken->is(tok::comma)) MightBeFunctionType = false; - if (CurrentToken->Previous->is(TT_BinaryOperator)) + if (Prev.is(TT_BinaryOperator)) Contexts.back().IsExpression = true; if (CurrentToken->is(tok::r_paren)) { + if (Prev.is(TT_PointerOrReference) && Prev.Previous == &OpeningParen) + MightBeFunctionType = true; if (OpeningParen.isNot(TT_CppCastLParen) && MightBeFunctionType && ProbablyFunctionType && CurrentToken->Next && (CurrentToken->Next->is(tok::l_paren) || @@ -550,8 +560,8 @@ class AnnotatingParser { bool ProbablyFunctionTypeLParen = (CurrentToken->is(tok::l_paren) && CurrentToken->Next && CurrentToken->Next->isOneOf(tok::star, tok::amp, tok::caret)); - if ((CurrentToken->Previous->isOneOf(tok::kw_const, tok::kw_auto) || - CurrentToken->Previous->isTypeName(LangOpts)) && + if ((Prev.isOneOf(tok::kw_const, tok::kw_auto) || + Prev.isTypeName(LangOpts)) && !(CurrentToken->is(tok::l_brace) || (CurrentToken->is(tok::l_paren) && !ProbablyFunctionTypeLParen))) { Contexts.back().IsExpression = false; @@ -1165,19 +1175,26 @@ class AnnotatingParser { ScopedContextCreator ContextCreator(*this, tok::l_brace, 1); Contexts.back().ColonIsDictLiteral = true; - if (OpeningBrace.is(BK_BracedInit)) + + const auto *Prev = OpeningBrace.getPreviousNonComment(); + + if (OpeningBrace.is(BK_BracedInit)) { Contexts.back().IsExpression = true; - if (Style.isJavaScript() && OpeningBrace.Previous && - OpeningBrace.Previous->is(TT_JsTypeColon)) { - Contexts.back().IsExpression = false; - } - if (Style.isVerilog() && - (!OpeningBrace.getPreviousNonComment() || - OpeningBrace.getPreviousNonComment()->isNot(Keywords.kw_apostrophe))) { - Contexts.back().VerilogMayBeConcatenation = true; + if (Prev) { + for (auto *Tok = Prev->Previous; Tok && Tok->isPointerOrReference(); + Tok = Tok->Previous) { + Tok->setFinalizedType(TT_PointerOrReference); + } + } } + + if (Style.isJavaScript() && Prev && Prev->is(TT_JsTypeColon)) + Contexts.back().IsExpression = false; + if (Style.isTableGen()) Contexts.back().ColonIsDictLiteral = false; + else if (Style.isVerilog() && !(Prev && Prev->is(Keywords.kw_apostrophe))) + Contexts.back().VerilogMayBeConcatenation = true; unsigned CommaCount = 0; while (CurrentToken) { @@ -1614,7 +1631,7 @@ class AnnotatingParser { return false; break; case tok::greater: - if (Style.Language != FormatStyle::LK_TextProto) + if (Style.Language != FormatStyle::LK_TextProto && Tok->is(TT_Unknown)) Tok->setType(TT_BinaryOperator); if (Tok->Previous && Tok->Previous->is(TT_TemplateCloser)) Tok->SpacesRequiredBefore = 1; diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 40f77266fabdc..a38a86764d68c 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -2556,7 +2556,7 @@ bool UnwrappedLineParser::parseParens(TokenType AmpAmpTokenType) { parseChildBlock(); break; case tok::r_paren: { - const auto *Prev = LeftParen->Previous; + auto *Prev = LeftParen->Previous; if (!MightBeStmtExpr && !MightBeFoldExpr && !Line->InMacroBody && Style.RemoveParentheses > FormatStyle::RPS_Leave) { const auto *Next = Tokens->peekNextToken(); @@ -2585,9 +2585,13 @@ bool UnwrappedLineParser::parseParens(TokenType AmpAmpTokenType) { FormatTok->Optional = true; } } - if (Prev && Prev->is(TT_TypenameMacro)) { - LeftParen->setFinalizedType(TT_TypeDeclarationParen); - FormatTok->setFinalizedType(TT_TypeDeclarationParen); + if (Prev) { + if (Prev->is(TT_TypenameMacro)) { + LeftParen->setFinalizedType(TT_TypeDeclarationParen); + FormatTok->setFinalizedType(TT_TypeDeclarationParen); + } else if (Prev->is(tok::greater) && FormatTok->Previous == LeftParen) { + Prev->setFinalizedType(TT_TemplateCloser); + } } nextToken(); return SeenEqual; diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index a0291ccfea245..db7c791059a32 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3687,10 +3687,10 @@ void CompilerInvocationBase::GenerateLangArgs(const LangOptions &Opts, if (Opts.Blocks && !(Opts.OpenCL && Opts.OpenCLVersion == 200)) GenerateArg(Consumer, OPT_fblocks); - if (Opts.ConvergentFunctions && - !(Opts.OpenCL || (Opts.CUDA && Opts.CUDAIsDevice) || Opts.SYCLIsDevice || - Opts.HLSL)) + if (Opts.ConvergentFunctions) GenerateArg(Consumer, OPT_fconvergent_functions); + else + GenerateArg(Consumer, OPT_fno_convergent_functions); if (Opts.NoBuiltin && !Opts.Freestanding) GenerateArg(Consumer, OPT_fno_builtin); @@ -4106,9 +4106,12 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.Blocks = Args.hasArg(OPT_fblocks) || (Opts.OpenCL && Opts.OpenCLVersion == 200); - Opts.ConvergentFunctions = Args.hasArg(OPT_fconvergent_functions) || - Opts.OpenCL || (Opts.CUDA && Opts.CUDAIsDevice) || - Opts.SYCLIsDevice || Opts.HLSL; + bool HasConvergentOperations = Opts.OpenMPIsTargetDevice || Opts.OpenCL || + Opts.CUDAIsDevice || Opts.SYCLIsDevice || + Opts.HLSL || T.isAMDGPU() || T.isNVPTX(); + Opts.ConvergentFunctions = + Args.hasFlag(OPT_fconvergent_functions, OPT_fno_convergent_functions, + HasConvergentOperations); Opts.NoBuiltin = Args.hasArg(OPT_fno_builtin) || Opts.Freestanding; if (!Opts.NoBuiltin) @@ -4164,9 +4167,6 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args, bool IsTargetSpecified = Opts.OpenMPIsTargetDevice || Args.hasArg(options::OPT_fopenmp_targets_EQ); - Opts.ConvergentFunctions = - Opts.ConvergentFunctions || Opts.OpenMPIsTargetDevice; - if (Opts.OpenMP || Opts.OpenMPSimd) { if (int Version = getLastArgIntValue( Args, OPT_fopenmp_version_EQ, diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index 64f90c493c105..e4b462b9b0fd8 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -457,6 +457,8 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback { return "BuildingDeductionGuides"; case CodeSynthesisContext::TypeAliasTemplateInstantiation: return "TypeAliasTemplateInstantiation"; + case CodeSynthesisContext::PartialOrderingTTP: + return "PartialOrderingTTP"; } return ""; } diff --git a/clang/lib/Headers/adcintrin.h b/clang/lib/Headers/adcintrin.h index 0065a1b543f81..5c68fce9370b2 100644 --- a/clang/lib/Headers/adcintrin.h +++ b/clang/lib/Headers/adcintrin.h @@ -15,7 +15,12 @@ #endif /* Define the default attributes for the functions in this file. */ +#if defined(__cplusplus) && (__cplusplus >= 201103L) +#define __DEFAULT_FN_ATTRS \ + __attribute__((__always_inline__, __nodebug__)) constexpr +#else #define __DEFAULT_FN_ATTRS __attribute__((__always_inline__, __nodebug__)) +#endif /* Use C++ inline semantics in C++, GNU inline for C mode. */ #if defined(__cplusplus) diff --git a/clang/lib/Headers/adxintrin.h b/clang/lib/Headers/adxintrin.h index bc6a4caf35337..055e91f8e2b30 100644 --- a/clang/lib/Headers/adxintrin.h +++ b/clang/lib/Headers/adxintrin.h @@ -15,8 +15,13 @@ #define __ADXINTRIN_H /* Define the default attributes for the functions in this file. */ +#if defined(__cplusplus) && (__cplusplus >= 201103L) +#define __DEFAULT_FN_ATTRS \ + __attribute__((__always_inline__, __nodebug__, __target__("adx"))) constexpr +#else #define __DEFAULT_FN_ATTRS \ __attribute__((__always_inline__, __nodebug__, __target__("adx"))) +#endif /* Use C++ inline semantics in C++, GNU inline for C mode. */ #if defined(__cplusplus) diff --git a/clang/lib/Headers/bmiintrin.h b/clang/lib/Headers/bmiintrin.h index 673f043ccfb30..59c5ece3977f3 100644 --- a/clang/lib/Headers/bmiintrin.h +++ b/clang/lib/Headers/bmiintrin.h @@ -36,8 +36,7 @@ /// bits in the operand. /// \see _tzcnt_u16 static __inline__ unsigned short __RELAXED_FN_ATTRS -__tzcnt_u16(unsigned short __X) -{ +__tzcnt_u16(unsigned short __X) { return __builtin_ia32_tzcnt_u16(__X); } @@ -70,8 +69,7 @@ __tzcnt_u16(unsigned short __X) /// bits in the operand. /// \see { _mm_tzcnt_32 _tzcnt_u32 } static __inline__ unsigned int __RELAXED_FN_ATTRS -__tzcnt_u32(unsigned int __X) -{ +__tzcnt_u32(unsigned int __X) { return __builtin_ia32_tzcnt_u32(__X); } @@ -87,8 +85,7 @@ __tzcnt_u32(unsigned int __X) /// the operand. /// \see { __tzcnt_u32 _tzcnt_u32 } static __inline__ int __RELAXED_FN_ATTRS -_mm_tzcnt_32(unsigned int __X) -{ +_mm_tzcnt_32(unsigned int __X) { return (int)__builtin_ia32_tzcnt_u32(__X); } @@ -123,8 +120,7 @@ _mm_tzcnt_32(unsigned int __X) /// bits in the operand. /// \see { _mm_tzcnt_64 _tzcnt_u64 } static __inline__ unsigned long long __RELAXED_FN_ATTRS -__tzcnt_u64(unsigned long long __X) -{ +__tzcnt_u64(unsigned long long __X) { return __builtin_ia32_tzcnt_u64(__X); } @@ -140,8 +136,7 @@ __tzcnt_u64(unsigned long long __X) /// the operand. /// \see { __tzcnt_u64 _tzcnt_u64 } static __inline__ long long __RELAXED_FN_ATTRS -_mm_tzcnt_64(unsigned long long __X) -{ +_mm_tzcnt_64(unsigned long long __X) { return (long long)__builtin_ia32_tzcnt_u64(__X); } @@ -192,8 +187,7 @@ _mm_tzcnt_64(unsigned long long __X) /// operand with the one's complement of the first operand. /// \see _andn_u32 static __inline__ unsigned int __DEFAULT_FN_ATTRS -__andn_u32(unsigned int __X, unsigned int __Y) -{ +__andn_u32(unsigned int __X, unsigned int __Y) { return ~__X & __Y; } @@ -298,8 +292,7 @@ _bextr2_u32(unsigned int __X, unsigned int __Y) { /// the source operand. /// \see _blsi_u32 static __inline__ unsigned int __DEFAULT_FN_ATTRS -__blsi_u32(unsigned int __X) -{ +__blsi_u32(unsigned int __X) { return __X & -__X; } @@ -334,8 +327,7 @@ __blsi_u32(unsigned int __X) /// \returns An unsigned integer containing the newly created mask. /// \see _blsmsk_u32 static __inline__ unsigned int __DEFAULT_FN_ATTRS -__blsmsk_u32(unsigned int __X) -{ +__blsmsk_u32(unsigned int __X) { return __X ^ (__X - 1); } @@ -370,8 +362,7 @@ __blsmsk_u32(unsigned int __X) /// operand. /// \see _blsr_u32 static __inline__ unsigned int __DEFAULT_FN_ATTRS -__blsr_u32(unsigned int __X) -{ +__blsr_u32(unsigned int __X) { return __X & (__X - 1); } @@ -410,8 +401,7 @@ __blsr_u32(unsigned int __X) /// operand with the one's complement of the first operand. /// \see _andn_u64 static __inline__ unsigned long long __DEFAULT_FN_ATTRS -__andn_u64 (unsigned long long __X, unsigned long long __Y) -{ +__andn_u64 (unsigned long long __X, unsigned long long __Y) { return ~__X & __Y; } @@ -517,8 +507,7 @@ _bextr2_u64(unsigned long long __X, unsigned long long __Y) { /// bits from the source operand. /// \see _blsi_u64 static __inline__ unsigned long long __DEFAULT_FN_ATTRS -__blsi_u64(unsigned long long __X) -{ +__blsi_u64(unsigned long long __X) { return __X & -__X; } @@ -553,8 +542,7 @@ __blsi_u64(unsigned long long __X) /// \returns An unsigned 64-bit integer containing the newly created mask. /// \see _blsmsk_u64 static __inline__ unsigned long long __DEFAULT_FN_ATTRS -__blsmsk_u64(unsigned long long __X) -{ +__blsmsk_u64(unsigned long long __X) { return __X ^ (__X - 1); } @@ -589,8 +577,7 @@ __blsmsk_u64(unsigned long long __X) /// source operand. /// \see _blsr_u64 static __inline__ unsigned long long __DEFAULT_FN_ATTRS -__blsr_u64(unsigned long long __X) -{ +__blsr_u64(unsigned long long __X) { return __X & (__X - 1); } diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_intrinsics.h index 810a16d75f022..c3ecfc7c90d43 100644 --- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h +++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h @@ -466,6 +466,36 @@ float3 atan(float3); _HLSL_BUILTIN_ALIAS(__builtin_elementwise_atan) float4 atan(float4); +//===----------------------------------------------------------------------===// +// atan2 builtins +//===----------------------------------------------------------------------===// + +/// \fn T atan2(T y, T x) +/// \brief Returns the arctangent of y/x, using the signs of the arguments to +/// determine the correct quadrant. +/// \param y The y-coordinate. +/// \param x The x-coordinate. + +#ifdef __HLSL_ENABLE_16_BIT +_HLSL_BUILTIN_ALIAS(__builtin_elementwise_atan2) +half atan2(half y, half x); +_HLSL_BUILTIN_ALIAS(__builtin_elementwise_atan2) +half2 atan2(half2 y, half2 x); +_HLSL_BUILTIN_ALIAS(__builtin_elementwise_atan2) +half3 atan2(half3 y, half3 x); +_HLSL_BUILTIN_ALIAS(__builtin_elementwise_atan2) +half4 atan2(half4 y, half4 x); +#endif + +_HLSL_BUILTIN_ALIAS(__builtin_elementwise_atan2) +float atan2(float y, float x); +_HLSL_BUILTIN_ALIAS(__builtin_elementwise_atan2) +float2 atan2(float2 y, float2 x); +_HLSL_BUILTIN_ALIAS(__builtin_elementwise_atan2) +float3 atan2(float3 y, float3 x); +_HLSL_BUILTIN_ALIAS(__builtin_elementwise_atan2) +float4 atan2(float4 y, float4 x); + //===----------------------------------------------------------------------===// // ceil builtins //===----------------------------------------------------------------------===// @@ -1612,6 +1642,28 @@ uint64_t3 reversebits(uint64_t3); _HLSL_BUILTIN_ALIAS(__builtin_elementwise_bitreverse) uint64_t4 reversebits(uint64_t4); +//===----------------------------------------------------------------------===// +// cross builtins +//===----------------------------------------------------------------------===// + +/// \fn T cross(T x, T y) +/// \brief Returns the cross product of two floating-point, 3D vectors. +/// \param x [in] The first floating-point, 3D vector. +/// \param y [in] The second floating-point, 3D vector. +/// +/// Result is the cross product of x and y, i.e., the resulting +/// components are, in order : +/// x[1] * y[2] - y[1] * x[2] +/// x[2] * y[0] - y[2] * x[0] +/// x[0] * y[1] - y[0] * x[1] + +_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2) +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_cross) +half3 cross(half3, half3); + +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_cross) +float3 cross(float3, float3); + //===----------------------------------------------------------------------===// // rcp builtins //===----------------------------------------------------------------------===// diff --git a/clang/lib/Headers/lzcntintrin.h b/clang/lib/Headers/lzcntintrin.h index db00474ffd394..27509021ec258 100644 --- a/clang/lib/Headers/lzcntintrin.h +++ b/clang/lib/Headers/lzcntintrin.h @@ -49,8 +49,7 @@ /// bits in the operand. /// \see _lzcnt_u32 static __inline__ unsigned int __DEFAULT_FN_ATTRS -__lzcnt32(unsigned int __X) -{ +__lzcnt32(unsigned int __X) { return __builtin_ia32_lzcnt_u32(__X); } @@ -66,8 +65,7 @@ __lzcnt32(unsigned int __X) /// bits in the operand. /// \see __lzcnt32 static __inline__ unsigned int __DEFAULT_FN_ATTRS -_lzcnt_u32(unsigned int __X) -{ +_lzcnt_u32(unsigned int __X) { return __builtin_ia32_lzcnt_u32(__X); } @@ -99,8 +97,7 @@ _lzcnt_u32(unsigned int __X) /// bits in the operand. /// \see __lzcnt64 static __inline__ unsigned long long __DEFAULT_FN_ATTRS -_lzcnt_u64(unsigned long long __X) -{ +_lzcnt_u64(unsigned long long __X) { return __builtin_ia32_lzcnt_u64(__X); } #endif diff --git a/clang/lib/Headers/tbmintrin.h b/clang/lib/Headers/tbmintrin.h index 48a9d07568ff2..cf92d5a7b3b0f 100644 --- a/clang/lib/Headers/tbmintrin.h +++ b/clang/lib/Headers/tbmintrin.h @@ -28,56 +28,47 @@ (unsigned int)(b))) static __inline__ unsigned int __DEFAULT_FN_ATTRS -__blcfill_u32(unsigned int __a) -{ +__blcfill_u32(unsigned int __a) { return __a & (__a + 1); } static __inline__ unsigned int __DEFAULT_FN_ATTRS -__blci_u32(unsigned int __a) -{ +__blci_u32(unsigned int __a) { return __a | ~(__a + 1); } static __inline__ unsigned int __DEFAULT_FN_ATTRS -__blcic_u32(unsigned int __a) -{ +__blcic_u32(unsigned int __a) { return ~__a & (__a + 1); } static __inline__ unsigned int __DEFAULT_FN_ATTRS -__blcmsk_u32(unsigned int __a) -{ +__blcmsk_u32(unsigned int __a) { return __a ^ (__a + 1); } static __inline__ unsigned int __DEFAULT_FN_ATTRS -__blcs_u32(unsigned int __a) -{ +__blcs_u32(unsigned int __a) { return __a | (__a + 1); } static __inline__ unsigned int __DEFAULT_FN_ATTRS -__blsfill_u32(unsigned int __a) -{ +__blsfill_u32(unsigned int __a) { return __a | (__a - 1); } static __inline__ unsigned int __DEFAULT_FN_ATTRS -__blsic_u32(unsigned int __a) -{ +__blsic_u32(unsigned int __a) { return ~__a | (__a - 1); } static __inline__ unsigned int __DEFAULT_FN_ATTRS -__t1mskc_u32(unsigned int __a) -{ +__t1mskc_u32(unsigned int __a) { return ~__a | (__a + 1); } static __inline__ unsigned int __DEFAULT_FN_ATTRS -__tzmsk_u32(unsigned int __a) -{ +__tzmsk_u32(unsigned int __a) { return ~__a & (__a - 1); } @@ -87,56 +78,47 @@ __tzmsk_u32(unsigned int __a) (unsigned long long)(b))) static __inline__ unsigned long long __DEFAULT_FN_ATTRS -__blcfill_u64(unsigned long long __a) -{ +__blcfill_u64(unsigned long long __a) { return __a & (__a + 1); } static __inline__ unsigned long long __DEFAULT_FN_ATTRS -__blci_u64(unsigned long long __a) -{ +__blci_u64(unsigned long long __a) { return __a | ~(__a + 1); } static __inline__ unsigned long long __DEFAULT_FN_ATTRS -__blcic_u64(unsigned long long __a) -{ +__blcic_u64(unsigned long long __a) { return ~__a & (__a + 1); } static __inline__ unsigned long long __DEFAULT_FN_ATTRS -__blcmsk_u64(unsigned long long __a) -{ +__blcmsk_u64(unsigned long long __a) { return __a ^ (__a + 1); } static __inline__ unsigned long long __DEFAULT_FN_ATTRS -__blcs_u64(unsigned long long __a) -{ +__blcs_u64(unsigned long long __a) { return __a | (__a + 1); } static __inline__ unsigned long long __DEFAULT_FN_ATTRS -__blsfill_u64(unsigned long long __a) -{ +__blsfill_u64(unsigned long long __a) { return __a | (__a - 1); } static __inline__ unsigned long long __DEFAULT_FN_ATTRS -__blsic_u64(unsigned long long __a) -{ +__blsic_u64(unsigned long long __a) { return ~__a | (__a - 1); } static __inline__ unsigned long long __DEFAULT_FN_ATTRS -__t1mskc_u64(unsigned long long __a) -{ +__t1mskc_u64(unsigned long long __a) { return ~__a | (__a + 1); } static __inline__ unsigned long long __DEFAULT_FN_ATTRS -__tzmsk_u64(unsigned long long __a) -{ +__tzmsk_u64(unsigned long long __a) { return ~__a & (__a - 1); } #endif diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index a04eed9873c0d..122a05be1c039 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -7950,21 +7950,8 @@ void Parser::ParseParameterDeclarationClause( // Parse a C++23 Explicit Object Parameter // We do that in all language modes to produce a better diagnostic. SourceLocation ThisLoc; - if (getLangOpts().CPlusPlus && Tok.is(tok::kw_this)) { + if (getLangOpts().CPlusPlus && Tok.is(tok::kw_this)) ThisLoc = ConsumeToken(); - // C++23 [dcl.fct]p6: - // An explicit-object-parameter-declaration is a parameter-declaration - // with a this specifier. An explicit-object-parameter-declaration - // shall appear only as the first parameter-declaration of a - // parameter-declaration-list of either: - // - a member-declarator that declares a member function, or - // - a lambda-declarator. - // - // The parameter-declaration-list of a requires-expression is not such - // a context. - if (DeclaratorCtx == DeclaratorContext::RequiresExpr) - Diag(ThisLoc, diag::err_requires_expr_explicit_object_parameter); - } ParsedTemplateInfo TemplateInfo; ParseDeclarationSpecifiers(DS, TemplateInfo, AS_none, diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index e7514500dc53a..2fb4be0035b66 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -3694,7 +3694,7 @@ bool Parser::ParseExpressionList(SmallVectorImpl &Exprs, SawError = true; if (FailImmediatelyOnInvalidExpr) break; - SkipUntil(tok::comma, tok::r_paren, StopBeforeMatch); + SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch); } else { Exprs.push_back(Expr.get()); } diff --git a/clang/lib/Parse/ParseOpenACC.cpp b/clang/lib/Parse/ParseOpenACC.cpp index e66abd6873794..b27e50b147f4a 100644 --- a/clang/lib/Parse/ParseOpenACC.cpp +++ b/clang/lib/Parse/ParseOpenACC.cpp @@ -743,38 +743,50 @@ bool Parser::ParseOpenACCDeviceTypeList( // int-expr // Note that this is specified under 'gang-arg-list', but also applies to 'tile' // via reference. -bool Parser::ParseOpenACCSizeExpr() { - // FIXME: Ensure these are constant expressions. - +ExprResult Parser::ParseOpenACCSizeExpr(OpenACCClauseKind CK) { // The size-expr ends up being ambiguous when only looking at the current // token, as it could be a deref of a variable/expression. if (getCurToken().is(tok::star) && NextToken().isOneOf(tok::comma, tok::r_paren, tok::annot_pragma_openacc_end)) { - ConsumeToken(); - return false; + SourceLocation AsteriskLoc = ConsumeToken(); + return getActions().OpenACC().ActOnOpenACCAsteriskSizeExpr(AsteriskLoc); } - return getActions() - .CorrectDelayedTyposInExpr(ParseAssignmentExpression()) - .isInvalid(); + ExprResult SizeExpr = + getActions().CorrectDelayedTyposInExpr(ParseConstantExpression()); + + if (!SizeExpr.isUsable()) + return SizeExpr; + + SizeExpr = getActions().OpenACC().ActOnIntExpr( + OpenACCDirectiveKind::Invalid, CK, SizeExpr.get()->getBeginLoc(), + SizeExpr.get()); + + return SizeExpr; } -bool Parser::ParseOpenACCSizeExprList() { - if (ParseOpenACCSizeExpr()) { +bool Parser::ParseOpenACCSizeExprList( + OpenACCClauseKind CK, llvm::SmallVectorImpl &SizeExprs) { + ExprResult SizeExpr = ParseOpenACCSizeExpr(CK); + if (!SizeExpr.isUsable()) { SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end, Parser::StopBeforeMatch); - return false; + return true; } + SizeExprs.push_back(SizeExpr.get()); + while (!getCurToken().isOneOf(tok::r_paren, tok::annot_pragma_openacc_end)) { ExpectAndConsume(tok::comma); - if (ParseOpenACCSizeExpr()) { + SizeExpr = ParseOpenACCSizeExpr(CK); + if (!SizeExpr.isUsable()) { SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end, Parser::StopBeforeMatch); - return false; + return true; } + SizeExprs.push_back(SizeExpr.get()); } return false; } @@ -792,7 +804,7 @@ bool Parser::ParseOpenACCGangArg(SourceLocation GangLoc) { // 'static' just takes a size-expr, which is an int-expr or an asterisk. ConsumeToken(); ConsumeToken(); - return ParseOpenACCSizeExpr(); + return ParseOpenACCSizeExpr(OpenACCClauseKind::Gang).isInvalid(); } if (isOpenACCSpecialToken(OpenACCSpecialTokenKind::Dim, getCurToken()) && @@ -1052,12 +1064,16 @@ Parser::OpenACCClauseParseResult Parser::ParseOpenACCClauseParams( } break; } - case OpenACCClauseKind::Tile: - if (ParseOpenACCSizeExprList()) { + case OpenACCClauseKind::Tile: { + llvm::SmallVector SizeExprs; + if (ParseOpenACCSizeExprList(OpenACCClauseKind::Tile, SizeExprs)) { Parens.skipToEnd(); return OpenACCCanContinue(); } + + ParsedClause.setIntExprDetails(std::move(SizeExprs)); break; + } default: llvm_unreachable("Not a required parens type?"); } @@ -1461,8 +1477,8 @@ StmtResult Parser::ParseOpenACCDirectiveStmt() { return StmtError(); StmtResult AssocStmt; - SemaOpenACC::AssociatedStmtRAII AssocStmtRAII(getActions().OpenACC(), - DirInfo.DirKind); + SemaOpenACC::AssociatedStmtRAII AssocStmtRAII( + getActions().OpenACC(), DirInfo.DirKind, {}, DirInfo.Clauses); if (doesDirectiveHaveAssociatedStmt(DirInfo.DirKind)) { ParsingOpenACCDirectiveRAII DirScope(*this, /*Value=*/false); ParseScope ACCScope(this, getOpenACCScopeFlags(DirInfo.DirKind)); diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp index cc6f18b5b319f..12fed448d477c 100644 --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -2126,6 +2126,7 @@ void PragmaGCCVisibilityHandler::HandlePragma(Preprocessor &PP, // pack '(' [integer] ')' // pack '(' 'show' ')' // pack '(' ('push' | 'pop') [',' identifier] [, integer] ')' +// pack '(' 'packed' | 'full' | 'twobyte' | 'reset' ')' with -fzos-extensions void PragmaPackHandler::HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, Token &PackTok) { @@ -2155,10 +2156,35 @@ void PragmaPackHandler::HandlePragma(Preprocessor &PP, ? Sema::PSK_Push_Set : Sema::PSK_Set; } else if (Tok.is(tok::identifier)) { + // Map pragma pack options to pack (integer). + auto MapPack = [&](const char *Literal) { + Action = Sema::PSK_Push_Set; + Alignment = Tok; + Alignment.setKind(tok::numeric_constant); + Alignment.setLiteralData(Literal); + Alignment.setLength(1); + }; + const IdentifierInfo *II = Tok.getIdentifierInfo(); if (II->isStr("show")) { Action = Sema::PSK_Show; PP.Lex(Tok); + } else if (II->isStr("packed") && PP.getLangOpts().ZOSExt) { + // #pragma pack(packed) is the same as #pragma pack(1) + MapPack("1"); + PP.Lex(Tok); + } else if (II->isStr("full") && PP.getLangOpts().ZOSExt) { + // #pragma pack(full) is the same as #pragma pack(4) + MapPack("4"); + PP.Lex(Tok); + } else if (II->isStr("twobyte") && PP.getLangOpts().ZOSExt) { + // #pragma pack(twobyte) is the same as #pragma pack(2) + MapPack("2"); + PP.Lex(Tok); + } else if (II->isStr("reset") && PP.getLangOpts().ZOSExt) { + // #pragma pack(reset) is the same as #pragma pack(pop) on XL + Action = Sema::PSK_Pop; + PP.Lex(Tok); } else { if (II->isStr("push")) { Action = Sema::PSK_Push; diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index 9188799fce13e..6480e88316a7d 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -24,6 +24,7 @@ #include "clang/Sema/Scope.h" #include "clang/Sema/SemaCodeCompletion.h" #include "clang/Sema/SemaObjC.h" +#include "clang/Sema/SemaOpenACC.h" #include "clang/Sema/SemaOpenMP.h" #include "clang/Sema/TypoCorrection.h" #include "llvm/ADT/STLExtras.h" @@ -1854,6 +1855,11 @@ StmtResult Parser::ParseWhileStatement(SourceLocation *TrailingElseLoc) { Sema::ConditionKind::Boolean, LParen, RParen)) return StmtError(); + // OpenACC Restricts a while-loop inside of certain construct/clause + // combinations, so diagnose that here in OpenACC mode. + SemaOpenACC::LoopInConstructRAII LCR{getActions().OpenACC()}; + getActions().OpenACC().ActOnWhileStmt(WhileLoc); + // C99 6.8.5p5 - In C99, the body of the while statement is a scope, even if // there is no compound stmt. C90 does not have this clause. We only do this // if the body isn't a compound statement to avoid push/pop in common cases. @@ -1902,6 +1908,11 @@ StmtResult Parser::ParseDoStatement() { ParseScope DoScope(this, ScopeFlags); + // OpenACC Restricts a do-while-loop inside of certain construct/clause + // combinations, so diagnose that here in OpenACC mode. + SemaOpenACC::LoopInConstructRAII LCR{getActions().OpenACC()}; + getActions().OpenACC().ActOnDoStmt(DoLoc); + // C99 6.8.5p5 - In C99, the body of the do statement is a scope, even if // there is no compound stmt. C90 does not have this clause. We only do this // if the body isn't a compound statement to avoid push/pop in common cases. @@ -2326,6 +2337,11 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) { } } + // OpenACC Restricts a for-loop inside of certain construct/clause + // combinations, so diagnose that here in OpenACC mode. + SemaOpenACC::LoopInConstructRAII LCR{getActions().OpenACC()}; + getActions().OpenACC().ActOnForStmtBegin(ForLoc); + // C99 6.8.5p5 - In C99, the body of the for statement is a scope, even if // there is no compound stmt. C90 does not have this clause. We only do this // if the body isn't a compound statement to avoid push/pop in common cases. @@ -2358,6 +2374,8 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) { // Pop the body scope if needed. InnerScope.Exit(); + getActions().OpenACC().ActOnForStmtEnd(ForLoc, Body); + // Leave the for-scope. ForScope.Exit(); diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index 2cee4f5ef6e99..719c3a9312ec1 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -55,6 +55,7 @@ add_clang_library(clangSema SemaExprMember.cpp SemaExprObjC.cpp SemaFixItUtils.cpp + SemaFunctionEffects.cpp SemaHLSL.cpp SemaHexagon.cpp SemaInit.cpp diff --git a/clang/lib/Sema/JumpDiagnostics.cpp b/clang/lib/Sema/JumpDiagnostics.cpp index 8af36d5c24e3d..00fdffca4e9ea 100644 --- a/clang/lib/Sema/JumpDiagnostics.cpp +++ b/clang/lib/Sema/JumpDiagnostics.cpp @@ -761,8 +761,7 @@ void JumpScopeChecker::VerifyIndirectJumps() { if (CHECK_PERMISSIVE(!LabelAndGotoScopes.count(IG))) continue; unsigned IGScope = LabelAndGotoScopes[IG]; - if (!JumpScopesMap.contains(IGScope)) - JumpScopesMap[IGScope] = IG; + JumpScopesMap.try_emplace(IGScope, IG); } JumpScopes.reserve(JumpScopesMap.size()); for (auto &Pair : JumpScopesMap) diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 4be7dfbc29392..f05760428458b 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -643,7 +643,7 @@ void Sema::diagnoseFunctionEffectConversion(QualType DstType, QualType SrcType, const auto SrcFX = FunctionEffectsRef::get(SrcType); const auto DstFX = FunctionEffectsRef::get(DstType); if (SrcFX != DstFX) { - for (const auto &Diff : FunctionEffectDifferences(SrcFX, DstFX)) { + for (const auto &Diff : FunctionEffectDiffVector(SrcFX, DstFX)) { if (Diff.shouldDiagnoseConversion(SrcType, SrcFX, DstType, DstFX)) Diag(Loc, diag::warn_invalid_add_func_effects) << Diff.effectName(); } @@ -1529,6 +1529,9 @@ void Sema::ActOnEndOfTranslationUnit() { AnalysisWarnings.IssueWarnings(Context.getTranslationUnitDecl()); + if (Context.hasAnyFunctionEffects()) + performFunctionEffectAnalysis(Context.getTranslationUnitDecl()); + // Check we've noticed that we're no longer parsing the initializer for every // variable. If we miss cases, then at best we have a performance issue and // at worst a rejects-valid bug. @@ -2774,156 +2777,3 @@ bool Sema::isDeclaratorFunctionLike(Declarator &D) { }); return Result; } - -FunctionEffectDifferences::FunctionEffectDifferences( - const FunctionEffectsRef &Old, const FunctionEffectsRef &New) { - - FunctionEffectsRef::iterator POld = Old.begin(); - FunctionEffectsRef::iterator OldEnd = Old.end(); - FunctionEffectsRef::iterator PNew = New.begin(); - FunctionEffectsRef::iterator NewEnd = New.end(); - - while (true) { - int cmp = 0; - if (POld == OldEnd) { - if (PNew == NewEnd) - break; - cmp = 1; - } else if (PNew == NewEnd) - cmp = -1; - else { - FunctionEffectWithCondition Old = *POld; - FunctionEffectWithCondition New = *PNew; - if (Old.Effect.kind() < New.Effect.kind()) - cmp = -1; - else if (New.Effect.kind() < Old.Effect.kind()) - cmp = 1; - else { - cmp = 0; - if (Old.Cond.getCondition() != New.Cond.getCondition()) { - // FIXME: Cases where the expressions are equivalent but - // don't have the same identity. - push_back(FunctionEffectDiff{ - Old.Effect.kind(), FunctionEffectDiff::Kind::ConditionMismatch, - Old, New}); - } - } - } - - if (cmp < 0) { - // removal - FunctionEffectWithCondition Old = *POld; - push_back(FunctionEffectDiff{ - Old.Effect.kind(), FunctionEffectDiff::Kind::Removed, Old, {}}); - ++POld; - } else if (cmp > 0) { - // addition - FunctionEffectWithCondition New = *PNew; - push_back(FunctionEffectDiff{ - New.Effect.kind(), FunctionEffectDiff::Kind::Added, {}, New}); - ++PNew; - } else { - ++POld; - ++PNew; - } - } -} - -bool FunctionEffectDiff::shouldDiagnoseConversion( - QualType SrcType, const FunctionEffectsRef &SrcFX, QualType DstType, - const FunctionEffectsRef &DstFX) const { - - switch (EffectKind) { - case FunctionEffect::Kind::NonAllocating: - // nonallocating can't be added (spoofed) during a conversion, unless we - // have nonblocking. - if (DiffKind == Kind::Added) { - for (const auto &CFE : SrcFX) { - if (CFE.Effect.kind() == FunctionEffect::Kind::NonBlocking) - return false; - } - } - [[fallthrough]]; - case FunctionEffect::Kind::NonBlocking: - // nonblocking can't be added (spoofed) during a conversion. - switch (DiffKind) { - case Kind::Added: - return true; - case Kind::Removed: - return false; - case Kind::ConditionMismatch: - // FIXME: Condition mismatches are too coarse right now -- expressions - // which are equivalent but don't have the same identity are detected as - // mismatches. We're going to diagnose those anyhow until expression - // matching is better. - return true; - } - llvm_unreachable("Unhandled FunctionEffectDiff::Kind enum"); - case FunctionEffect::Kind::Blocking: - case FunctionEffect::Kind::Allocating: - return false; - case FunctionEffect::Kind::None: - break; - } - llvm_unreachable("unknown effect kind"); -} - -bool FunctionEffectDiff::shouldDiagnoseRedeclaration( - const FunctionDecl &OldFunction, const FunctionEffectsRef &OldFX, - const FunctionDecl &NewFunction, const FunctionEffectsRef &NewFX) const { - switch (EffectKind) { - case FunctionEffect::Kind::NonAllocating: - case FunctionEffect::Kind::NonBlocking: - // nonblocking/nonallocating can't be removed in a redeclaration. - switch (DiffKind) { - case Kind::Added: - return false; // No diagnostic. - case Kind::Removed: - return true; // Issue diagnostic. - case Kind::ConditionMismatch: - // All these forms of mismatches are diagnosed. - return true; - } - llvm_unreachable("Unhandled FunctionEffectDiff::Kind enum"); - case FunctionEffect::Kind::Blocking: - case FunctionEffect::Kind::Allocating: - return false; - case FunctionEffect::Kind::None: - break; - } - llvm_unreachable("unknown effect kind"); -} - -FunctionEffectDiff::OverrideResult -FunctionEffectDiff::shouldDiagnoseMethodOverride( - const CXXMethodDecl &OldMethod, const FunctionEffectsRef &OldFX, - const CXXMethodDecl &NewMethod, const FunctionEffectsRef &NewFX) const { - switch (EffectKind) { - case FunctionEffect::Kind::NonAllocating: - case FunctionEffect::Kind::NonBlocking: - switch (DiffKind) { - - // If added on an override, that's fine and not diagnosed. - case Kind::Added: - return OverrideResult::NoAction; - - // If missing from an override (removed), propagate from base to derived. - case Kind::Removed: - return OverrideResult::Merge; - - // If there's a mismatch involving the effect's polarity or condition, - // issue a warning. - case Kind::ConditionMismatch: - return OverrideResult::Warn; - } - llvm_unreachable("Unhandled FunctionEffectDiff::Kind enum"); - - case FunctionEffect::Kind::Blocking: - case FunctionEffect::Kind::Allocating: - return OverrideResult::NoAction; - - case FunctionEffect::Kind::None: - break; - } - llvm_unreachable("unknown effect kind"); -} diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index d0236d08c98e6..ec43a0def9c1e 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -913,7 +913,15 @@ void Sema::ProcessAPINotes(Decl *D) { // Tags if (auto Tag = dyn_cast(D)) { - std::string LookupName = Tag->getName().str(); + // Determine the name of the entity to search for. If this is an + // anonymous tag that gets its linked name from a typedef, look for the + // typedef name. This allows tag-specific information to be added + // to the declaration. + std::string LookupName; + if (auto typedefName = Tag->getTypedefNameForAnonDecl()) + LookupName = typedefName->getName().str(); + else + LookupName = Tag->getName().str(); // Use the source location to discern if this Tag is an OPTIONS macro. // For now we would like to limit this trick of looking up the APINote tag diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 8634b54b0535d..2bcb930acdcb5 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -2755,16 +2755,12 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, // These builtins restrict the element type to floating point // types only, and take in two arguments. + case Builtin::BI__builtin_elementwise_minimum: + case Builtin::BI__builtin_elementwise_maximum: + case Builtin::BI__builtin_elementwise_atan2: case Builtin::BI__builtin_elementwise_fmod: case Builtin::BI__builtin_elementwise_pow: { - if (BuiltinElementwiseMath(TheCall)) - return ExprError(); - - QualType ArgTy = TheCall->getArg(0)->getType(); - if (checkFPMathBuiltinElementType(*this, TheCall->getArg(0)->getBeginLoc(), - ArgTy, 1) || - checkFPMathBuiltinElementType(*this, TheCall->getArg(1)->getBeginLoc(), - ArgTy, 2)) + if (BuiltinElementwiseMath(TheCall, /*FPOnly=*/true)) return ExprError(); break; } @@ -2868,6 +2864,29 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, TheCall->setType(ElTy); break; } + case Builtin::BI__builtin_reduce_maximum: + case Builtin::BI__builtin_reduce_minimum: { + if (PrepareBuiltinReduceMathOneArgCall(TheCall)) + return ExprError(); + + const Expr *Arg = TheCall->getArg(0); + const auto *TyA = Arg->getType()->getAs(); + + QualType ElTy; + if (TyA) + ElTy = TyA->getElementType(); + else if (Arg->getType()->isSizelessVectorType()) + ElTy = Arg->getType()->getSizelessVectorEltType(Context); + + if (ElTy.isNull() || !ElTy->isFloatingType()) { + Diag(Arg->getBeginLoc(), diag::err_builtin_invalid_arg_type) + << 1 << /* vector of floating points */ 9 << Arg->getType(); + return ExprError(); + } + + TheCall->setType(ElTy); + break; + } // These builtins support vectors of integers only. // TODO: ADD/MUL should support floating-point types. @@ -14378,9 +14397,9 @@ bool Sema::PrepareBuiltinElementwiseMathOneArgCall(CallExpr *TheCall) { return false; } -bool Sema::BuiltinElementwiseMath(CallExpr *TheCall) { +bool Sema::BuiltinElementwiseMath(CallExpr *TheCall, bool FPOnly) { QualType Res; - if (BuiltinVectorMath(TheCall, Res)) + if (BuiltinVectorMath(TheCall, Res, FPOnly)) return true; TheCall->setType(Res); return false; @@ -14399,7 +14418,7 @@ bool Sema::BuiltinVectorToScalarMath(CallExpr *TheCall) { return false; } -bool Sema::BuiltinVectorMath(CallExpr *TheCall, QualType &Res) { +bool Sema::BuiltinVectorMath(CallExpr *TheCall, QualType &Res, bool FPOnly) { if (checkArgCount(TheCall, 2)) return true; @@ -14419,8 +14438,13 @@ bool Sema::BuiltinVectorMath(CallExpr *TheCall, QualType &Res) { diag::err_typecheck_call_different_arg_types) << TyA << TyB; - if (checkMathBuiltinElementType(*this, A.get()->getBeginLoc(), TyA, 1)) - return true; + if (FPOnly) { + if (checkFPMathBuiltinElementType(*this, A.get()->getBeginLoc(), TyA, 1)) + return true; + } else { + if (checkMathBuiltinElementType(*this, A.get()->getBeginLoc(), TyA, 1)) + return true; + } TheCall->setArg(0, A.get()); TheCall->setArg(1, B.get()); diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 67fc603e9ce1d..998a148a7d24a 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -728,6 +728,7 @@ bool Sema::addInstantiatedCapturesToScope( ValueDecl *CapturedPattern = CapturePattern.getCapturedVar(); if (!CapturedPattern->isInitCapture()) { + Instantiated++; continue; } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 0e536f71a2f70..2bf610746bc31 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -3803,7 +3803,7 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, const auto OldFX = Old->getFunctionEffects(); const auto NewFX = New->getFunctionEffects(); if (OldFX != NewFX) { - const auto Diffs = FunctionEffectDifferences(OldFX, NewFX); + const auto Diffs = FunctionEffectDiffVector(OldFX, NewFX); for (const auto &Diff : Diffs) { if (Diff.shouldDiagnoseRedeclaration(*Old, OldFX, *New, NewFX)) { Diag(New->getLocation(), @@ -3839,6 +3839,11 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, OldQTypeForComparison = Context.getFunctionType( OldFPT->getReturnType(), OldFPT->getParamTypes(), EPI); } + if (OldFX.empty()) { + // A redeclaration may add the attribute to a previously seen function + // body which needs to be verified. + maybeAddDeclWithEffects(Old, MergedFX); + } } } } @@ -4974,6 +4979,9 @@ void Sema::setTagNameForLinkagePurposes(TagDecl *TagFromDeclSpec, // Otherwise, set this as the anon-decl typedef for the tag. TagFromDeclSpec->setTypedefNameForAnonDecl(NewTD); + + // Now that we have a name for the tag, process API notes again. + ProcessAPINotes(TagFromDeclSpec); } static unsigned GetDiagnosticTypeSpecifierID(const DeclSpec &DS) { @@ -9086,9 +9094,14 @@ static NamedDecl *DiagnoseInvalidRedeclaration( SemaRef.Diag(NewFD->getLocation(), DiagMsg) << Name << NewDC << IsDefinition << NewFD->getLocation(); - bool NewFDisConst = false; - if (CXXMethodDecl *NewMD = dyn_cast(NewFD)) - NewFDisConst = NewMD->isConst(); + CXXMethodDecl *NewMD = dyn_cast(NewFD); + if (NewMD && DiagMsg == diag::err_member_decl_does_not_match) { + CXXRecordDecl *RD = NewMD->getParent(); + SemaRef.Diag(RD->getLocation(), diag::note_defined_here) + << RD->getName() << RD->getLocation(); + } + + bool NewFDisConst = NewMD && NewMD->isConst(); for (SmallVectorImpl >::iterator NearMatch = NearMatches.begin(), NearMatchEnd = NearMatches.end(); @@ -15669,6 +15682,8 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D, getCurLexicalContext()->getDeclKind() != Decl::ObjCImplementation) Diag(FD->getLocation(), diag::warn_function_def_in_objc_container); + maybeAddDeclWithEffects(FD); + return D; } @@ -20295,59 +20310,3 @@ bool Sema::shouldIgnoreInHostDeviceCheck(FunctionDecl *Callee) { return LangOpts.CUDA && !LangOpts.CUDAIsDevice && CUDA().IdentifyTarget(Callee) == CUDAFunctionTarget::Global; } - -void Sema::diagnoseFunctionEffectMergeConflicts( - const FunctionEffectSet::Conflicts &Errs, SourceLocation NewLoc, - SourceLocation OldLoc) { - for (const FunctionEffectSet::Conflict &Conflict : Errs) { - Diag(NewLoc, diag::warn_conflicting_func_effects) - << Conflict.Kept.description() << Conflict.Rejected.description(); - Diag(OldLoc, diag::note_previous_declaration); - } -} - -bool Sema::diagnoseConflictingFunctionEffect( - const FunctionEffectsRef &FX, const FunctionEffectWithCondition &NewEC, - SourceLocation NewAttrLoc) { - // If the new effect has a condition, we can't detect conflicts until the - // condition is resolved. - if (NewEC.Cond.getCondition() != nullptr) - return false; - - // Diagnose the new attribute as incompatible with a previous one. - auto Incompatible = [&](const FunctionEffectWithCondition &PrevEC) { - Diag(NewAttrLoc, diag::err_attributes_are_not_compatible) - << ("'" + NewEC.description() + "'") - << ("'" + PrevEC.description() + "'") << false; - // We don't necessarily have the location of the previous attribute, - // so no note. - return true; - }; - - // Compare against previous attributes. - FunctionEffect::Kind NewKind = NewEC.Effect.kind(); - - for (const FunctionEffectWithCondition &PrevEC : FX) { - // Again, can't check yet when the effect is conditional. - if (PrevEC.Cond.getCondition() != nullptr) - continue; - - FunctionEffect::Kind PrevKind = PrevEC.Effect.kind(); - // Note that we allow PrevKind == NewKind; it's redundant and ignored. - - if (PrevEC.Effect.oppositeKind() == NewKind) - return Incompatible(PrevEC); - - // A new allocating is incompatible with a previous nonblocking. - if (PrevKind == FunctionEffect::Kind::NonBlocking && - NewKind == FunctionEffect::Kind::Allocating) - return Incompatible(PrevEC); - - // A new nonblocking is incompatible with a previous allocating. - if (PrevKind == FunctionEffect::Kind::Allocating && - NewKind == FunctionEffect::Kind::NonBlocking) - return Incompatible(PrevEC); - } - - return false; -} diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index d8cdfcf8c6ec0..9cb2ed02a3f76 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -18181,7 +18181,7 @@ bool Sema::CheckOverridingFunctionAttributes(CXXMethodDecl *New, if (OldFX != NewFXOrig) { FunctionEffectSet NewFX(NewFXOrig); - const auto Diffs = FunctionEffectDifferences(OldFX, NewFX); + const auto Diffs = FunctionEffectDiffVector(OldFX, NewFX); FunctionEffectSet::Conflicts Errs; for (const auto &Diff : Diffs) { switch (Diff.shouldDiagnoseMethodOverride(*Old, OldFX, *New, NewFX)) { @@ -18194,7 +18194,7 @@ bool Sema::CheckOverridingFunctionAttributes(CXXMethodDecl *New, << Old->getReturnTypeSourceRange(); break; case FunctionEffectDiff::OverrideResult::Merge: { - NewFX.insert(Diff.Old, Errs); + NewFX.insert(Diff.Old.value(), Errs); const auto *NewFT = New->getType()->castAs(); FunctionProtoType::ExtProtoInfo EPI = NewFT->getExtProtoInfo(); EPI.FunctionEffects = FunctionEffectsRef(NewFX); diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 8aedbfcf878a1..dbddd6c370aa0 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1593,6 +1593,8 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Stmt::SYCLUniqueStableNameExprClass: return CT_Cannot; + case Stmt::OpenACCAsteriskSizeExprClass: + return CT_Cannot; case Stmt::NoStmtClass: llvm_unreachable("Invalid class for statement"); } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 2db9d1fc69ed1..ae7bcedfb28c7 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -16212,6 +16212,8 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, BlockScopeInfo *BSI = cast(FunctionScopes.back()); BlockDecl *BD = BSI->TheDecl; + maybeAddDeclWithEffects(BD); + if (BSI->HasImplicitReturnType) deduceClosureReturnType(*BSI); diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index b30414a8a8277..d490452e91c3b 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -9519,15 +9519,28 @@ Sema::ActOnStartRequiresExpr(SourceLocation RequiresKWLoc, } else if (Param->getType().hasQualifiers()) { Diag(Param->getBeginLoc(), diag::err_void_param_qualified); } - } - - if (Param->hasDefaultArg()) + } else if (Param->hasDefaultArg()) { // C++2a [expr.prim.req] p4 // [...] A local parameter of a requires-expression shall not have a // default argument. [...] Diag(Param->getDefaultArgRange().getBegin(), diag::err_requires_expr_local_parameter_default_argument); - // Ignore default argument and move on + // Ignore default argument and move on + } else if (Param->isExplicitObjectParameter()) { + // C++23 [dcl.fct]p6: + // An explicit-object-parameter-declaration is a parameter-declaration + // with a this specifier. An explicit-object-parameter-declaration + // shall appear only as the first parameter-declaration of a + // parameter-declaration-list of either: + // - a member-declarator that declares a member function, or + // - a lambda-declarator. + // + // The parameter-declaration-list of a requires-expression is not such + // a context. + Diag(Param->getExplicitObjectParamThisLoc(), + diag::err_requires_expr_explicit_object_parameter); + Param->setExplicitObjectParameterLoc(SourceLocation()); + } Param->setDeclContext(Body); // If this has an identifier, add it to the scope stack. diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp b/clang/lib/Sema/SemaFunctionEffects.cpp new file mode 100644 index 0000000000000..0fb18d207a50b --- /dev/null +++ b/clang/lib/Sema/SemaFunctionEffects.cpp @@ -0,0 +1,1577 @@ +//=== SemaFunctionEffects.cpp - Sema handling of function effects ---------===// +// +// 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 implements Sema handling of function effects. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/Type.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Sema/SemaInternal.h" + +#define DEBUG_TYPE "effectanalysis" + +using namespace clang; + +namespace { + +enum class ViolationID : uint8_t { + None = 0, // Sentinel for an empty Violation. + // These first 5 map to a %select{} in one of several FunctionEffects + // diagnostics, e.g. warn_func_effect_violation. + BaseDiagnosticIndex, + AllocatesMemory = BaseDiagnosticIndex, + ThrowsOrCatchesExceptions, + HasStaticLocalVariable, + AccessesThreadLocalVariable, + AccessesObjCMethodOrProperty, + + // These only apply to callees, where the analysis stops at the Decl. + DeclDisallowsInference, + + // These both apply to indirect calls. The difference is that sometimes + // we have an actual Decl (generally a variable) which is the function + // pointer being called, and sometimes, typically due to a cast, we only + // have an expression. + CallsDeclWithoutEffect, + CallsExprWithoutEffect, +}; + +// Information about the AST context in which a violation was found, so +// that diagnostics can point to the correct source. +class ViolationSite { +public: + enum class Kind : uint8_t { + Default, // Function body. + MemberInitializer, + DefaultArgExpr + }; + +private: + llvm::PointerIntPair Impl; + +public: + ViolationSite() = default; + + explicit ViolationSite(CXXDefaultArgExpr *E) + : Impl(E, Kind::DefaultArgExpr) {} + + Kind kind() const { return static_cast(Impl.getInt()); } + CXXDefaultArgExpr *defaultArgExpr() const { return Impl.getPointer(); } + + void setKind(Kind K) { Impl.setPointerAndInt(nullptr, K); } +}; + +// Represents a violation of the rules, potentially for the entire duration of +// the analysis phase, in order to refer to it when explaining why a caller has +// been made unsafe by a callee. Can be transformed into either a Diagnostic +// (warning or a note), depending on whether the violation pertains to a +// function failing to be verifed as holding an effect vs. a function failing to +// be inferred as holding that effect. +struct Violation { + FunctionEffect Effect; + std::optional + CalleeEffectPreventingInference; // Only for certain IDs; can be nullopt. + ViolationID ID = ViolationID::None; + ViolationSite Site; + SourceLocation Loc; + const Decl *Callee = + nullptr; // Only valid for ViolationIDs Calls{Decl,Expr}WithoutEffect. + + Violation(FunctionEffect Effect, ViolationID ID, ViolationSite VS, + SourceLocation Loc, const Decl *Callee = nullptr, + std::optional CalleeEffect = std::nullopt) + : Effect(Effect), CalleeEffectPreventingInference(CalleeEffect), ID(ID), + Site(VS), Loc(Loc), Callee(Callee) {} + + unsigned diagnosticSelectIndex() const { + return unsigned(ID) - unsigned(ViolationID::BaseDiagnosticIndex); + } +}; + +enum class SpecialFuncType : uint8_t { None, OperatorNew, OperatorDelete }; +enum class CallableType : uint8_t { + // Unknown: probably function pointer. + Unknown, + Function, + Virtual, + Block +}; + +// Return whether a function's effects CAN be verified. +// The question of whether it SHOULD be verified is independent. +static bool functionIsVerifiable(const FunctionDecl *FD) { + if (FD->isTrivial()) { + // Otherwise `struct x { int a; };` would have an unverifiable default + // constructor. + return true; + } + return FD->hasBody(); +} + +static bool isNoexcept(const FunctionDecl *FD) { + const auto *FPT = FD->getType()->getAs(); + return FPT && (FPT->isNothrow() || FD->hasAttr()); +} + +// This list is probably incomplete. +// FIXME: Investigate: +// __builtin_eh_return? +// __builtin_allow_runtime_check? +// __builtin_unwind_init and other similar things that sound exception-related. +// va_copy? +// coroutines? +static FunctionEffectKindSet getBuiltinFunctionEffects(unsigned BuiltinID) { + FunctionEffectKindSet Result; + + switch (BuiltinID) { + case 0: // Not builtin. + default: // By default, builtins have no known effects. + break; + + // These allocate/deallocate heap memory. + case Builtin::ID::BI__builtin_calloc: + case Builtin::ID::BI__builtin_malloc: + case Builtin::ID::BI__builtin_realloc: + case Builtin::ID::BI__builtin_free: + case Builtin::ID::BI__builtin_operator_delete: + case Builtin::ID::BI__builtin_operator_new: + case Builtin::ID::BIaligned_alloc: + case Builtin::ID::BIcalloc: + case Builtin::ID::BImalloc: + case Builtin::ID::BImemalign: + case Builtin::ID::BIrealloc: + case Builtin::ID::BIfree: + + case Builtin::ID::BIfopen: + case Builtin::ID::BIpthread_create: + case Builtin::ID::BI_Block_object_dispose: + Result.insert(FunctionEffect(FunctionEffect::Kind::Allocating)); + break; + + // These block in some other way than allocating memory. + // longjmp() and friends are presumed unsafe because they are the moral + // equivalent of throwing a C++ exception, which is unsafe. + case Builtin::ID::BIlongjmp: + case Builtin::ID::BI_longjmp: + case Builtin::ID::BIsiglongjmp: + case Builtin::ID::BI__builtin_longjmp: + case Builtin::ID::BIobjc_exception_throw: + + // Objective-C runtime. + case Builtin::ID::BIobjc_msgSend: + case Builtin::ID::BIobjc_msgSend_fpret: + case Builtin::ID::BIobjc_msgSend_fp2ret: + case Builtin::ID::BIobjc_msgSend_stret: + case Builtin::ID::BIobjc_msgSendSuper: + case Builtin::ID::BIobjc_getClass: + case Builtin::ID::BIobjc_getMetaClass: + case Builtin::ID::BIobjc_enumerationMutation: + case Builtin::ID::BIobjc_assign_ivar: + case Builtin::ID::BIobjc_assign_global: + case Builtin::ID::BIobjc_sync_enter: + case Builtin::ID::BIobjc_sync_exit: + case Builtin::ID::BINSLog: + case Builtin::ID::BINSLogv: + + // stdio.h + case Builtin::ID::BIfread: + case Builtin::ID::BIfwrite: + + // stdio.h: printf family. + case Builtin::ID::BIprintf: + case Builtin::ID::BI__builtin_printf: + case Builtin::ID::BIfprintf: + case Builtin::ID::BIsnprintf: + case Builtin::ID::BIsprintf: + case Builtin::ID::BIvprintf: + case Builtin::ID::BIvfprintf: + case Builtin::ID::BIvsnprintf: + case Builtin::ID::BIvsprintf: + + // stdio.h: scanf family. + case Builtin::ID::BIscanf: + case Builtin::ID::BIfscanf: + case Builtin::ID::BIsscanf: + case Builtin::ID::BIvscanf: + case Builtin::ID::BIvfscanf: + case Builtin::ID::BIvsscanf: + Result.insert(FunctionEffect(FunctionEffect::Kind::Blocking)); + break; + } + + return Result; +} + +// Transitory, more extended information about a callable, which can be a +// function, block, or function pointer. +struct CallableInfo { + // CDecl holds the function's definition, if any. + // FunctionDecl if CallableType::Function or Virtual + // BlockDecl if CallableType::Block + const Decl *CDecl; + + // Remember whether the callable is a function, block, virtual method, + // or (presumed) function pointer. + CallableType CType = CallableType::Unknown; + + // Remember whether the callable is an operator new or delete function, + // so that calls to them are reported more meaningfully, as memory + // allocations. + SpecialFuncType FuncType = SpecialFuncType::None; + + // We inevitably want to know the callable's declared effects, so cache them. + FunctionEffectKindSet Effects; + + CallableInfo(const Decl &CD, SpecialFuncType FT = SpecialFuncType::None) + : CDecl(&CD), FuncType(FT) { + FunctionEffectsRef DeclEffects; + if (auto *FD = dyn_cast(CDecl)) { + // Use the function's definition, if any. + if (const FunctionDecl *Def = FD->getDefinition()) + CDecl = FD = Def; + CType = CallableType::Function; + if (auto *Method = dyn_cast(FD); + Method && Method->isVirtual()) + CType = CallableType::Virtual; + DeclEffects = FD->getFunctionEffects(); + } else if (auto *BD = dyn_cast(CDecl)) { + CType = CallableType::Block; + DeclEffects = BD->getFunctionEffects(); + } else if (auto *VD = dyn_cast(CDecl)) { + // ValueDecl is function, enum, or variable, so just look at its type. + DeclEffects = FunctionEffectsRef::get(VD->getType()); + } + Effects = FunctionEffectKindSet(DeclEffects); + } + + CallableType type() const { return CType; } + + bool isCalledDirectly() const { + return CType == CallableType::Function || CType == CallableType::Block; + } + + bool isVerifiable() const { + switch (CType) { + case CallableType::Unknown: + case CallableType::Virtual: + return false; + case CallableType::Block: + return true; + case CallableType::Function: + return functionIsVerifiable(dyn_cast(CDecl)); + } + llvm_unreachable("undefined CallableType"); + } + + /// Generate a name for logging and diagnostics. + std::string getNameForDiagnostic(Sema &S) const { + std::string Name; + llvm::raw_string_ostream OS(Name); + + if (auto *FD = dyn_cast(CDecl)) + FD->getNameForDiagnostic(OS, S.getPrintingPolicy(), + /*Qualified=*/true); + else if (auto *BD = dyn_cast(CDecl)) + OS << "(block " << BD->getBlockManglingNumber() << ")"; + else if (auto *VD = dyn_cast(CDecl)) + VD->printQualifiedName(OS); + return Name; + } +}; + +// ---------- +// Map effects to single Violations, to hold the first (of potentially many) +// violations pertaining to an effect, per function. +class EffectToViolationMap { + // Since we currently only have a tiny number of effects (typically no more + // than 1), use a SmallVector with an inline capacity of 1. Since it + // is often empty, use a unique_ptr to the SmallVector. + // Note that Violation itself contains a FunctionEffect which is the key. + // FIXME: Is there a way to simplify this using existing data structures? + using ImplVec = llvm::SmallVector; + std::unique_ptr Impl; + +public: + // Insert a new Violation if we do not already have one for its effect. + void maybeInsert(const Violation &Viol) { + if (Impl == nullptr) + Impl = std::make_unique(); + else if (lookup(Viol.Effect) != nullptr) + return; + + Impl->push_back(Viol); + } + + const Violation *lookup(FunctionEffect Key) { + if (Impl == nullptr) + return nullptr; + + auto *Iter = llvm::find_if( + *Impl, [&](const auto &Item) { return Item.Effect == Key; }); + return Iter != Impl->end() ? &*Iter : nullptr; + } + + size_t size() const { return Impl ? Impl->size() : 0; } +}; + +// ---------- +// State pertaining to a function whose AST is walked and whose effect analysis +// is dependent on a subsequent analysis of other functions. +class PendingFunctionAnalysis { + friend class CompleteFunctionAnalysis; + +public: + struct DirectCall { + const Decl *Callee; + SourceLocation CallLoc; + // Not all recursive calls are detected, just enough + // to break cycles. + bool Recursed = false; + ViolationSite VSite; + + DirectCall(const Decl *D, SourceLocation CallLoc, ViolationSite VSite) + : Callee(D), CallLoc(CallLoc), VSite(VSite) {} + }; + + // We always have two disjoint sets of effects to verify: + // 1. Effects declared explicitly by this function. + // 2. All other inferrable effects needing verification. + FunctionEffectKindSet DeclaredVerifiableEffects; + FunctionEffectKindSet EffectsToInfer; + +private: + // Violations pertaining to the function's explicit effects. + SmallVector ViolationsForExplicitEffects; + + // Violations pertaining to other, non-explicit, inferrable effects. + EffectToViolationMap InferrableEffectToFirstViolation; + + // These unverified direct calls are what keeps the analysis "pending", + // until the callees can be verified. + SmallVector UnverifiedDirectCalls; + +public: + PendingFunctionAnalysis(Sema &S, const CallableInfo &CInfo, + FunctionEffectKindSet AllInferrableEffectsToVerify) + : DeclaredVerifiableEffects(CInfo.Effects) { + // Check for effects we are not allowed to infer. + FunctionEffectKindSet InferrableEffects; + + for (FunctionEffect effect : AllInferrableEffectsToVerify) { + std::optional ProblemCalleeEffect = + effect.effectProhibitingInference(*CInfo.CDecl, CInfo.Effects); + if (!ProblemCalleeEffect) + InferrableEffects.insert(effect); + else { + // Add a Violation for this effect if a caller were to + // try to infer it. + InferrableEffectToFirstViolation.maybeInsert(Violation( + effect, ViolationID::DeclDisallowsInference, ViolationSite{}, + CInfo.CDecl->getLocation(), nullptr, ProblemCalleeEffect)); + } + } + // InferrableEffects is now the set of inferrable effects which are not + // prohibited. + EffectsToInfer = FunctionEffectKindSet::difference( + InferrableEffects, DeclaredVerifiableEffects); + } + + // Hide the way that Violations for explicitly required effects vs. inferred + // ones are handled differently. + void checkAddViolation(bool Inferring, const Violation &NewViol) { + if (!Inferring) + ViolationsForExplicitEffects.push_back(NewViol); + else + InferrableEffectToFirstViolation.maybeInsert(NewViol); + } + + void addUnverifiedDirectCall(const Decl *D, SourceLocation CallLoc, + ViolationSite VSite) { + UnverifiedDirectCalls.emplace_back(D, CallLoc, VSite); + } + + // Analysis is complete when there are no unverified direct calls. + bool isComplete() const { return UnverifiedDirectCalls.empty(); } + + const Violation *violationForInferrableEffect(FunctionEffect effect) { + return InferrableEffectToFirstViolation.lookup(effect); + } + + // Mutable because caller may need to set a DirectCall's Recursing flag. + MutableArrayRef unverifiedCalls() { + assert(!isComplete()); + return UnverifiedDirectCalls; + } + + ArrayRef getSortedViolationsForExplicitEffects(SourceManager &SM) { + if (!ViolationsForExplicitEffects.empty()) + llvm::sort(ViolationsForExplicitEffects, + [&SM](const Violation &LHS, const Violation &RHS) { + return SM.isBeforeInTranslationUnit(LHS.Loc, RHS.Loc); + }); + return ViolationsForExplicitEffects; + } + + void dump(Sema &SemaRef, llvm::raw_ostream &OS) const { + OS << "Pending: Declared "; + DeclaredVerifiableEffects.dump(OS); + OS << ", " << ViolationsForExplicitEffects.size() << " violations; "; + OS << " Infer "; + EffectsToInfer.dump(OS); + OS << ", " << InferrableEffectToFirstViolation.size() << " violations"; + if (!UnverifiedDirectCalls.empty()) { + OS << "; Calls: "; + for (const DirectCall &Call : UnverifiedDirectCalls) { + CallableInfo CI(*Call.Callee); + OS << " " << CI.getNameForDiagnostic(SemaRef); + } + } + OS << "\n"; + } +}; + +// ---------- +class CompleteFunctionAnalysis { + // Current size: 2 pointers +public: + // Has effects which are both the declared ones -- not to be inferred -- plus + // ones which have been successfully inferred. These are all considered + // "verified" for the purposes of callers; any issue with verifying declared + // effects has already been reported and is not the problem of any caller. + FunctionEffectKindSet VerifiedEffects; + +private: + // This is used to generate notes about failed inference. + EffectToViolationMap InferrableEffectToFirstViolation; + +public: + // The incoming Pending analysis is consumed (member(s) are moved-from). + CompleteFunctionAnalysis(ASTContext &Ctx, PendingFunctionAnalysis &&Pending, + FunctionEffectKindSet DeclaredEffects, + FunctionEffectKindSet AllInferrableEffectsToVerify) + : VerifiedEffects(DeclaredEffects) { + for (FunctionEffect effect : AllInferrableEffectsToVerify) + if (Pending.violationForInferrableEffect(effect) == nullptr) + VerifiedEffects.insert(effect); + + InferrableEffectToFirstViolation = + std::move(Pending.InferrableEffectToFirstViolation); + } + + const Violation *firstViolationForEffect(FunctionEffect Effect) { + return InferrableEffectToFirstViolation.lookup(Effect); + } + + void dump(llvm::raw_ostream &OS) const { + OS << "Complete: Verified "; + VerifiedEffects.dump(OS); + OS << "; Infer "; + OS << InferrableEffectToFirstViolation.size() << " violations\n"; + } +}; + +// ========== +class Analyzer { + Sema &S; + + // Subset of Sema.AllEffectsToVerify + FunctionEffectKindSet AllInferrableEffectsToVerify; + + using FuncAnalysisPtr = + llvm::PointerUnion; + + // Map all Decls analyzed to FuncAnalysisPtr. Pending state is larger + // than complete state, so use different objects to represent them. + // The state pointers are owned by the container. + class AnalysisMap : llvm::DenseMap { + using Base = llvm::DenseMap; + + public: + ~AnalysisMap(); + + // Use non-public inheritance in order to maintain the invariant + // that lookups and insertions are via the canonical Decls. + + FuncAnalysisPtr lookup(const Decl *Key) const { + return Base::lookup(Key->getCanonicalDecl()); + } + + FuncAnalysisPtr &operator[](const Decl *Key) { + return Base::operator[](Key->getCanonicalDecl()); + } + + /// Shortcut for the case where we only care about completed analysis. + CompleteFunctionAnalysis *completedAnalysisForDecl(const Decl *D) const { + if (FuncAnalysisPtr AP = lookup(D); + isa_and_nonnull(AP)) + return AP.get(); + return nullptr; + } + + void dump(Sema &SemaRef, llvm::raw_ostream &OS) { + OS << "\nAnalysisMap:\n"; + for (const auto &item : *this) { + CallableInfo CI(*item.first); + const auto AP = item.second; + OS << item.first << " " << CI.getNameForDiagnostic(SemaRef) << " : "; + if (AP.isNull()) { + OS << "null\n"; + } else if (isa(AP)) { + auto *CFA = AP.get(); + OS << CFA << " "; + CFA->dump(OS); + } else if (isa(AP)) { + auto *PFA = AP.get(); + OS << PFA << " "; + PFA->dump(SemaRef, OS); + } else + llvm_unreachable("never"); + } + OS << "---\n"; + } + }; + AnalysisMap DeclAnalysis; + +public: + Analyzer(Sema &S) : S(S) {} + + void run(const TranslationUnitDecl &TU) { + // Gather all of the effects to be verified to see what operations need to + // be checked, and to see which ones are inferrable. + for (FunctionEffect Effect : S.AllEffectsToVerify) { + const FunctionEffect::Flags Flags = Effect.flags(); + if (Flags & FunctionEffect::FE_InferrableOnCallees) + AllInferrableEffectsToVerify.insert(Effect); + } + LLVM_DEBUG(llvm::dbgs() << "AllInferrableEffectsToVerify: "; + AllInferrableEffectsToVerify.dump(llvm::dbgs()); + llvm::dbgs() << "\n";); + + // We can use DeclsWithEffectsToVerify as a stack for a + // depth-first traversal; there's no need for a second container. But first, + // reverse it, so when working from the end, Decls are verified in the order + // they are declared. + SmallVector &VerificationQueue = S.DeclsWithEffectsToVerify; + std::reverse(VerificationQueue.begin(), VerificationQueue.end()); + + while (!VerificationQueue.empty()) { + const Decl *D = VerificationQueue.back(); + if (FuncAnalysisPtr AP = DeclAnalysis.lookup(D)) { + if (auto *Pending = AP.dyn_cast()) { + // All children have been traversed; finish analysis. + finishPendingAnalysis(D, Pending); + } + VerificationQueue.pop_back(); + continue; + } + + // Not previously visited; begin a new analysis for this Decl. + PendingFunctionAnalysis *Pending = verifyDecl(D); + if (Pending == nullptr) { + // Completed now. + VerificationQueue.pop_back(); + continue; + } + + // Analysis remains pending because there are direct callees to be + // verified first. Push them onto the queue. + for (PendingFunctionAnalysis::DirectCall &Call : + Pending->unverifiedCalls()) { + FuncAnalysisPtr AP = DeclAnalysis.lookup(Call.Callee); + if (AP.isNull()) { + VerificationQueue.push_back(Call.Callee); + continue; + } + + // This indicates recursion (not necessarily direct). For the + // purposes of effect analysis, we can just ignore it since + // no effects forbid recursion. + assert(isa(AP)); + Call.Recursed = true; + } + } + } + +private: + // Verify a single Decl. Return the pending structure if that was the result, + // else null. This method must not recurse. + PendingFunctionAnalysis *verifyDecl(const Decl *D) { + CallableInfo CInfo(*D); + bool isExternC = false; + + if (const FunctionDecl *FD = dyn_cast(D)) + isExternC = FD->getCanonicalDecl()->isExternCContext(); + + // For C++, with non-extern "C" linkage only - if any of the Decl's declared + // effects forbid throwing (e.g. nonblocking) then the function should also + // be declared noexcept. + if (S.getLangOpts().CPlusPlus && !isExternC) { + for (FunctionEffect Effect : CInfo.Effects) { + if (!(Effect.flags() & FunctionEffect::FE_ExcludeThrow)) + continue; + + bool IsNoexcept = false; + if (auto *FD = D->getAsFunction()) { + IsNoexcept = isNoexcept(FD); + } else if (auto *BD = dyn_cast(D)) { + if (auto *TSI = BD->getSignatureAsWritten()) { + auto *FPT = TSI->getType()->getAs(); + IsNoexcept = FPT->isNothrow() || BD->hasAttr(); + } + } + if (!IsNoexcept) + S.Diag(D->getBeginLoc(), diag::warn_perf_constraint_implies_noexcept) + << GetCallableDeclKind(D, nullptr) << Effect.name(); + break; + } + } + + // Build a PendingFunctionAnalysis on the stack. If it turns out to be + // complete, we'll have avoided a heap allocation; if it's incomplete, it's + // a fairly trivial move to a heap-allocated object. + PendingFunctionAnalysis FAnalysis(S, CInfo, AllInferrableEffectsToVerify); + + LLVM_DEBUG(llvm::dbgs() + << "\nVerifying " << CInfo.getNameForDiagnostic(S) << " "; + FAnalysis.dump(S, llvm::dbgs());); + + FunctionBodyASTVisitor Visitor(*this, FAnalysis, CInfo); + + Visitor.run(); + if (FAnalysis.isComplete()) { + completeAnalysis(CInfo, std::move(FAnalysis)); + return nullptr; + } + // Move the pending analysis to the heap and save it in the map. + PendingFunctionAnalysis *PendingPtr = + new PendingFunctionAnalysis(std::move(FAnalysis)); + DeclAnalysis[D] = PendingPtr; + LLVM_DEBUG(llvm::dbgs() << "inserted pending " << PendingPtr << "\n"; + DeclAnalysis.dump(S, llvm::dbgs());); + return PendingPtr; + } + + // Consume PendingFunctionAnalysis, create with it a CompleteFunctionAnalysis, + // inserted in the container. + void completeAnalysis(const CallableInfo &CInfo, + PendingFunctionAnalysis &&Pending) { + if (ArrayRef Viols = + Pending.getSortedViolationsForExplicitEffects(S.getSourceManager()); + !Viols.empty()) + emitDiagnostics(Viols, CInfo); + + CompleteFunctionAnalysis *CompletePtr = new CompleteFunctionAnalysis( + S.getASTContext(), std::move(Pending), CInfo.Effects, + AllInferrableEffectsToVerify); + DeclAnalysis[CInfo.CDecl] = CompletePtr; + LLVM_DEBUG(llvm::dbgs() << "inserted complete " << CompletePtr << "\n"; + DeclAnalysis.dump(S, llvm::dbgs());); + } + + // Called after all direct calls requiring inference have been found -- or + // not. Repeats calls to FunctionBodyASTVisitor::followCall() but without + // the possibility of inference. Deletes Pending. + void finishPendingAnalysis(const Decl *D, PendingFunctionAnalysis *Pending) { + CallableInfo Caller(*D); + LLVM_DEBUG(llvm::dbgs() << "finishPendingAnalysis for " + << Caller.getNameForDiagnostic(S) << " : "; + Pending->dump(S, llvm::dbgs()); llvm::dbgs() << "\n";); + for (const PendingFunctionAnalysis::DirectCall &Call : + Pending->unverifiedCalls()) { + if (Call.Recursed) + continue; + + CallableInfo Callee(*Call.Callee); + followCall(Caller, *Pending, Callee, Call.CallLoc, + /*AssertNoFurtherInference=*/true, Call.VSite); + } + completeAnalysis(Caller, std::move(*Pending)); + delete Pending; + } + + // Here we have a call to a Decl, either explicitly via a CallExpr or some + // other AST construct. PFA pertains to the caller. + void followCall(const CallableInfo &Caller, PendingFunctionAnalysis &PFA, + const CallableInfo &Callee, SourceLocation CallLoc, + bool AssertNoFurtherInference, ViolationSite VSite) { + const bool DirectCall = Callee.isCalledDirectly(); + + // Initially, the declared effects; inferred effects will be added. + FunctionEffectKindSet CalleeEffects = Callee.Effects; + + bool IsInferencePossible = DirectCall; + + if (DirectCall) + if (CompleteFunctionAnalysis *CFA = + DeclAnalysis.completedAnalysisForDecl(Callee.CDecl)) { + // Combine declared effects with those which may have been inferred. + CalleeEffects.insert(CFA->VerifiedEffects); + IsInferencePossible = false; // We've already traversed it. + } + + if (AssertNoFurtherInference) { + assert(!IsInferencePossible); + } + + if (!Callee.isVerifiable()) + IsInferencePossible = false; + + LLVM_DEBUG(llvm::dbgs() + << "followCall from " << Caller.getNameForDiagnostic(S) + << " to " << Callee.getNameForDiagnostic(S) + << "; verifiable: " << Callee.isVerifiable() << "; callee "; + CalleeEffects.dump(llvm::dbgs()); llvm::dbgs() << "\n"; + llvm::dbgs() << " callee " << Callee.CDecl << " canonical " + << Callee.CDecl->getCanonicalDecl() << "\n";); + + auto Check1Effect = [&](FunctionEffect Effect, bool Inferring) { + if (!Effect.shouldDiagnoseFunctionCall(DirectCall, CalleeEffects)) + return; + + // If inference is not allowed, or the target is indirect (virtual + // method/function ptr?), generate a Violation now. + if (!IsInferencePossible || + !(Effect.flags() & FunctionEffect::FE_InferrableOnCallees)) { + if (Callee.FuncType == SpecialFuncType::None) + PFA.checkAddViolation(Inferring, + {Effect, ViolationID::CallsDeclWithoutEffect, + VSite, CallLoc, Callee.CDecl}); + else + PFA.checkAddViolation( + Inferring, + {Effect, ViolationID::AllocatesMemory, VSite, CallLoc}); + } else { + // Inference is allowed and necessary; defer it. + PFA.addUnverifiedDirectCall(Callee.CDecl, CallLoc, VSite); + } + }; + + for (FunctionEffect Effect : PFA.DeclaredVerifiableEffects) + Check1Effect(Effect, false); + + for (FunctionEffect Effect : PFA.EffectsToInfer) + Check1Effect(Effect, true); + } + + // Describe a callable Decl for a diagnostic. + // (Not an enum class because the value is always converted to an integer for + // use in a diagnostic.) + enum CallableDeclKind { + CDK_Function, + CDK_Constructor, + CDK_Destructor, + CDK_Lambda, + CDK_Block, + CDK_MemberInitializer, + }; + + // Describe a call site or target using an enum mapping to a %select{} + // in a diagnostic, e.g. warn_func_effect_violation, + // warn_perf_constraint_implies_noexcept, and others. + static CallableDeclKind GetCallableDeclKind(const Decl *D, + const Violation *V) { + if (V != nullptr && + V->Site.kind() == ViolationSite::Kind::MemberInitializer) + return CDK_MemberInitializer; + if (isa(D)) + return CDK_Block; + if (auto *Method = dyn_cast(D)) { + if (isa(D)) + return CDK_Constructor; + if (isa(D)) + return CDK_Destructor; + const CXXRecordDecl *Rec = Method->getParent(); + if (Rec->isLambda()) + return CDK_Lambda; + } + return CDK_Function; + }; + + // Should only be called when function's analysis is determined to be + // complete. + void emitDiagnostics(ArrayRef Viols, const CallableInfo &CInfo) { + if (Viols.empty()) + return; + + auto MaybeAddTemplateNote = [&](const Decl *D) { + if (const FunctionDecl *FD = dyn_cast(D)) { + while (FD != nullptr && FD->isTemplateInstantiation()) { + S.Diag(FD->getPointOfInstantiation(), + diag::note_func_effect_from_template); + FD = FD->getTemplateInstantiationPattern(); + } + } + }; + + // For note_func_effect_call_indirect. + enum { Indirect_VirtualMethod, Indirect_FunctionPtr }; + + auto MaybeAddSiteContext = [&](const Decl *D, const Violation &V) { + // If a violation site is a member initializer, add a note pointing to + // the constructor which invoked it. + if (V.Site.kind() == ViolationSite::Kind::MemberInitializer) { + unsigned ImplicitCtor = 0; + if (auto *Ctor = dyn_cast(D); + Ctor && Ctor->isImplicit()) + ImplicitCtor = 1; + S.Diag(D->getLocation(), diag::note_func_effect_in_constructor) + << ImplicitCtor; + } + + // If a violation site is a default argument expression, add a note + // pointing to the call site using the default argument. + else if (V.Site.kind() == ViolationSite::Kind::DefaultArgExpr) + S.Diag(V.Site.defaultArgExpr()->getUsedLocation(), + diag::note_in_evaluating_default_argument); + }; + + // Top-level violations are warnings. + for (const Violation &Viol1 : Viols) { + StringRef effectName = Viol1.Effect.name(); + switch (Viol1.ID) { + case ViolationID::None: + case ViolationID::DeclDisallowsInference: // Shouldn't happen + // here. + llvm_unreachable("Unexpected violation kind"); + break; + case ViolationID::AllocatesMemory: + case ViolationID::ThrowsOrCatchesExceptions: + case ViolationID::HasStaticLocalVariable: + case ViolationID::AccessesThreadLocalVariable: + case ViolationID::AccessesObjCMethodOrProperty: + S.Diag(Viol1.Loc, diag::warn_func_effect_violation) + << GetCallableDeclKind(CInfo.CDecl, &Viol1) << effectName + << Viol1.diagnosticSelectIndex(); + MaybeAddSiteContext(CInfo.CDecl, Viol1); + MaybeAddTemplateNote(CInfo.CDecl); + break; + case ViolationID::CallsExprWithoutEffect: + S.Diag(Viol1.Loc, diag::warn_func_effect_calls_expr_without_effect) + << GetCallableDeclKind(CInfo.CDecl, &Viol1) << effectName; + MaybeAddSiteContext(CInfo.CDecl, Viol1); + MaybeAddTemplateNote(CInfo.CDecl); + break; + + case ViolationID::CallsDeclWithoutEffect: { + CallableInfo CalleeInfo(*Viol1.Callee); + std::string CalleeName = CalleeInfo.getNameForDiagnostic(S); + + S.Diag(Viol1.Loc, diag::warn_func_effect_calls_func_without_effect) + << GetCallableDeclKind(CInfo.CDecl, &Viol1) << effectName + << GetCallableDeclKind(CalleeInfo.CDecl, nullptr) << CalleeName; + MaybeAddSiteContext(CInfo.CDecl, Viol1); + MaybeAddTemplateNote(CInfo.CDecl); + + // Emit notes explaining the transitive chain of inferences: Why isn't + // the callee safe? + for (const Decl *Callee = Viol1.Callee; Callee != nullptr;) { + std::optional MaybeNextCallee; + CompleteFunctionAnalysis *Completed = + DeclAnalysis.completedAnalysisForDecl(CalleeInfo.CDecl); + if (Completed == nullptr) { + // No result - could be + // - non-inline and extern + // - indirect (virtual or through function pointer) + // - effect has been explicitly disclaimed (e.g. "blocking") + + CallableType CType = CalleeInfo.type(); + if (CType == CallableType::Virtual) + S.Diag(Callee->getLocation(), + diag::note_func_effect_call_indirect) + << Indirect_VirtualMethod << effectName; + else if (CType == CallableType::Unknown) + S.Diag(Callee->getLocation(), + diag::note_func_effect_call_indirect) + << Indirect_FunctionPtr << effectName; + else if (CalleeInfo.Effects.contains(Viol1.Effect.oppositeKind())) + S.Diag(Callee->getLocation(), + diag::note_func_effect_call_disallows_inference) + << GetCallableDeclKind(CInfo.CDecl, nullptr) << effectName + << FunctionEffect(Viol1.Effect.oppositeKind()).name(); + else if (const FunctionDecl *FD = dyn_cast(Callee); + FD == nullptr || FD->getBuiltinID() == 0) { + // A builtin callee generally doesn't have a useful source + // location at which to insert a note. + S.Diag(Callee->getLocation(), diag::note_func_effect_call_extern) + << effectName; + } + break; + } + const Violation *PtrViol2 = + Completed->firstViolationForEffect(Viol1.Effect); + if (PtrViol2 == nullptr) + break; + + const Violation &Viol2 = *PtrViol2; + switch (Viol2.ID) { + case ViolationID::None: + llvm_unreachable("Unexpected violation kind"); + break; + case ViolationID::DeclDisallowsInference: + S.Diag(Viol2.Loc, diag::note_func_effect_call_disallows_inference) + << GetCallableDeclKind(CalleeInfo.CDecl, nullptr) << effectName + << Viol2.CalleeEffectPreventingInference->name(); + break; + case ViolationID::CallsExprWithoutEffect: + S.Diag(Viol2.Loc, diag::note_func_effect_call_indirect) + << Indirect_FunctionPtr << effectName; + break; + case ViolationID::AllocatesMemory: + case ViolationID::ThrowsOrCatchesExceptions: + case ViolationID::HasStaticLocalVariable: + case ViolationID::AccessesThreadLocalVariable: + case ViolationID::AccessesObjCMethodOrProperty: + S.Diag(Viol2.Loc, diag::note_func_effect_violation) + << GetCallableDeclKind(CalleeInfo.CDecl, &Viol2) << effectName + << Viol2.diagnosticSelectIndex(); + MaybeAddSiteContext(CalleeInfo.CDecl, Viol2); + break; + case ViolationID::CallsDeclWithoutEffect: + MaybeNextCallee.emplace(*Viol2.Callee); + S.Diag(Viol2.Loc, diag::note_func_effect_calls_func_without_effect) + << GetCallableDeclKind(CalleeInfo.CDecl, &Viol2) << effectName + << GetCallableDeclKind(Viol2.Callee, nullptr) + << MaybeNextCallee->getNameForDiagnostic(S); + break; + } + MaybeAddTemplateNote(Callee); + Callee = Viol2.Callee; + if (MaybeNextCallee) { + CalleeInfo = *MaybeNextCallee; + CalleeName = CalleeInfo.getNameForDiagnostic(S); + } + } + } break; + } + } + } + + // ---------- + // This AST visitor is used to traverse the body of a function during effect + // verification. This happens in 2 situations: + // [1] The function has declared effects which need to be validated. + // [2] The function has not explicitly declared an effect in question, and is + // being checked for implicit conformance. + // + // Violations are always routed to a PendingFunctionAnalysis. + struct FunctionBodyASTVisitor : RecursiveASTVisitor { + using Base = RecursiveASTVisitor; + + Analyzer &Outer; + PendingFunctionAnalysis &CurrentFunction; + CallableInfo &CurrentCaller; + ViolationSite VSite; + + FunctionBodyASTVisitor(Analyzer &Outer, + PendingFunctionAnalysis &CurrentFunction, + CallableInfo &CurrentCaller) + : Outer(Outer), CurrentFunction(CurrentFunction), + CurrentCaller(CurrentCaller) {} + + // -- Entry point -- + void run() { + // The target function may have implicit code paths beyond the + // body: member and base destructors. Visit these first. + if (auto *Dtor = dyn_cast(CurrentCaller.CDecl)) + followDestructor(dyn_cast(Dtor->getParent()), Dtor); + + // Do an AST traversal of the function/block body + TraverseDecl(const_cast(CurrentCaller.CDecl)); + } + + // -- Methods implementing common logic -- + + // Handle a language construct forbidden by some effects. Only effects whose + // flags include the specified flag receive a violation. \p Flag describes + // the construct. + void diagnoseLanguageConstruct(FunctionEffect::FlagBit Flag, + ViolationID VID, SourceLocation Loc, + const Decl *Callee = nullptr) { + // If there are any declared verifiable effects which forbid the construct + // represented by the flag, store just one violation. + for (FunctionEffect Effect : CurrentFunction.DeclaredVerifiableEffects) { + if (Effect.flags() & Flag) { + addViolation(/*inferring=*/false, Effect, VID, Loc, Callee); + break; + } + } + // For each inferred effect which forbids the construct, store a + // violation, if we don't already have a violation for that effect. + for (FunctionEffect Effect : CurrentFunction.EffectsToInfer) + if (Effect.flags() & Flag) + addViolation(/*inferring=*/true, Effect, VID, Loc, Callee); + } + + void addViolation(bool Inferring, FunctionEffect Effect, ViolationID VID, + SourceLocation Loc, const Decl *Callee = nullptr) { + CurrentFunction.checkAddViolation( + Inferring, Violation(Effect, VID, VSite, Loc, Callee)); + } + + // Here we have a call to a Decl, either explicitly via a CallExpr or some + // other AST construct. CallableInfo pertains to the callee. + void followCall(CallableInfo &CI, SourceLocation CallLoc) { + // Check for a call to a builtin function, whose effects are + // handled specially. + if (const auto *FD = dyn_cast(CI.CDecl)) { + if (unsigned BuiltinID = FD->getBuiltinID()) { + CI.Effects = getBuiltinFunctionEffects(BuiltinID); + if (CI.Effects.empty()) { + // A builtin with no known effects is assumed safe. + return; + } + // A builtin WITH effects doesn't get any special treatment for + // being noreturn/noexcept, e.g. longjmp(), so we skip the check + // below. + } else { + // If the callee is both `noreturn` and `noexcept`, it presumably + // terminates. Ignore it for the purposes of effect analysis. + // If not C++, `noreturn` alone is sufficient. + if (FD->isNoReturn() && + (!Outer.S.getLangOpts().CPlusPlus || isNoexcept(FD))) + return; + } + } + + Outer.followCall(CurrentCaller, CurrentFunction, CI, CallLoc, + /*AssertNoFurtherInference=*/false, VSite); + } + + void checkIndirectCall(CallExpr *Call, QualType CalleeType) { + auto *FPT = + CalleeType->getAs(); // Null if FunctionType. + FunctionEffectKindSet CalleeEffects; + if (FPT) + CalleeEffects.insert(FPT->getFunctionEffects()); + + auto Check1Effect = [&](FunctionEffect Effect, bool Inferring) { + if (FPT == nullptr || Effect.shouldDiagnoseFunctionCall( + /*direct=*/false, CalleeEffects)) + addViolation(Inferring, Effect, ViolationID::CallsExprWithoutEffect, + Call->getBeginLoc()); + }; + + for (FunctionEffect Effect : CurrentFunction.DeclaredVerifiableEffects) + Check1Effect(Effect, false); + + for (FunctionEffect Effect : CurrentFunction.EffectsToInfer) + Check1Effect(Effect, true); + } + + // This destructor's body should be followed by the caller, but here we + // follow the field and base destructors. + void followDestructor(const CXXRecordDecl *Rec, + const CXXDestructorDecl *Dtor) { + SourceLocation DtorLoc = Dtor->getLocation(); + for (const FieldDecl *Field : Rec->fields()) + followTypeDtor(Field->getType(), DtorLoc); + + if (const auto *Class = dyn_cast(Rec)) + for (const CXXBaseSpecifier &Base : Class->bases()) + followTypeDtor(Base.getType(), DtorLoc); + } + + void followTypeDtor(QualType QT, SourceLocation CallSite) { + const Type *Ty = QT.getTypePtr(); + while (Ty->isArrayType()) { + const ArrayType *Arr = Ty->getAsArrayTypeUnsafe(); + QT = Arr->getElementType(); + Ty = QT.getTypePtr(); + } + + if (Ty->isRecordType()) { + if (const CXXRecordDecl *Class = Ty->getAsCXXRecordDecl()) { + if (CXXDestructorDecl *Dtor = Class->getDestructor(); + Dtor && !Dtor->isDeleted()) { + CallableInfo CI(*Dtor); + followCall(CI, CallSite); + } + } + } + } + + // -- Methods for use of RecursiveASTVisitor -- + + bool shouldVisitImplicitCode() const { return true; } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool VisitCXXThrowExpr(CXXThrowExpr *Throw) { + diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeThrow, + ViolationID::ThrowsOrCatchesExceptions, + Throw->getThrowLoc()); + return true; + } + + bool VisitCXXCatchStmt(CXXCatchStmt *Catch) { + diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeCatch, + ViolationID::ThrowsOrCatchesExceptions, + Catch->getCatchLoc()); + return true; + } + + bool VisitObjCAtThrowStmt(ObjCAtThrowStmt *Throw) { + diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeThrow, + ViolationID::ThrowsOrCatchesExceptions, + Throw->getThrowLoc()); + return true; + } + + bool VisitObjCAtCatchStmt(ObjCAtCatchStmt *Catch) { + diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeCatch, + ViolationID::ThrowsOrCatchesExceptions, + Catch->getAtCatchLoc()); + return true; + } + + bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) { + diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeObjCMessageSend, + ViolationID::AccessesObjCMethodOrProperty, + Msg->getBeginLoc()); + return true; + } + + bool VisitSEHExceptStmt(SEHExceptStmt *Exc) { + diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeCatch, + ViolationID::ThrowsOrCatchesExceptions, + Exc->getExceptLoc()); + return true; + } + + bool VisitCallExpr(CallExpr *Call) { + LLVM_DEBUG(llvm::dbgs() + << "VisitCallExpr : " + << Call->getBeginLoc().printToString(Outer.S.SourceMgr) + << "\n";); + + Expr *CalleeExpr = Call->getCallee(); + if (const Decl *Callee = CalleeExpr->getReferencedDeclOfCallee()) { + CallableInfo CI(*Callee); + followCall(CI, Call->getBeginLoc()); + return true; + } + + if (isa(CalleeExpr)) { + // Just destroying a scalar, fine. + return true; + } + + // No Decl, just an Expr. Just check based on its type. + checkIndirectCall(Call, CalleeExpr->getType()); + + return true; + } + + bool VisitVarDecl(VarDecl *Var) { + LLVM_DEBUG(llvm::dbgs() + << "VisitVarDecl : " + << Var->getBeginLoc().printToString(Outer.S.SourceMgr) + << "\n";); + + if (Var->isStaticLocal()) + diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeStaticLocalVars, + ViolationID::HasStaticLocalVariable, + Var->getLocation()); + + const QualType::DestructionKind DK = + Var->needsDestruction(Outer.S.getASTContext()); + if (DK == QualType::DK_cxx_destructor) + followTypeDtor(Var->getType(), Var->getLocation()); + return true; + } + + bool VisitCXXNewExpr(CXXNewExpr *New) { + // RecursiveASTVisitor does not visit the implicit call to operator new. + if (FunctionDecl *FD = New->getOperatorNew()) { + CallableInfo CI(*FD, SpecialFuncType::OperatorNew); + followCall(CI, New->getBeginLoc()); + } + + // It's a bit excessive to check operator delete here, since it's + // just a fallback for operator new followed by a failed constructor. + // We could check it via New->getOperatorDelete(). + + // It DOES however visit the called constructor + return true; + } + + bool VisitCXXDeleteExpr(CXXDeleteExpr *Delete) { + // RecursiveASTVisitor does not visit the implicit call to operator + // delete. + if (FunctionDecl *FD = Delete->getOperatorDelete()) { + CallableInfo CI(*FD, SpecialFuncType::OperatorDelete); + followCall(CI, Delete->getBeginLoc()); + } + + // It DOES however visit the called destructor + + return true; + } + + bool VisitCXXConstructExpr(CXXConstructExpr *Construct) { + LLVM_DEBUG(llvm::dbgs() << "VisitCXXConstructExpr : " + << Construct->getBeginLoc().printToString( + Outer.S.SourceMgr) + << "\n";); + + // RecursiveASTVisitor does not visit the implicit call to the + // constructor. + const CXXConstructorDecl *Ctor = Construct->getConstructor(); + CallableInfo CI(*Ctor); + followCall(CI, Construct->getLocation()); + + return true; + } + + bool TraverseConstructorInitializer(CXXCtorInitializer *Init) { + ViolationSite PrevVS = VSite; + if (Init->isAnyMemberInitializer()) + VSite.setKind(ViolationSite::Kind::MemberInitializer); + bool Result = Base::TraverseConstructorInitializer(Init); + VSite = PrevVS; + return Result; + } + + bool TraverseCXXDefaultArgExpr(CXXDefaultArgExpr *E) { + LLVM_DEBUG(llvm::dbgs() + << "TraverseCXXDefaultArgExpr : " + << E->getUsedLocation().printToString(Outer.S.SourceMgr) + << "\n";); + + ViolationSite PrevVS = VSite; + if (VSite.kind() == ViolationSite::Kind::Default) + VSite = ViolationSite{E}; + + bool Result = Base::TraverseCXXDefaultArgExpr(E); + VSite = PrevVS; + return Result; + } + + bool TraverseLambdaExpr(LambdaExpr *Lambda) { + // We override this so as to be able to skip traversal of the lambda's + // body. We have to explicitly traverse the captures. Why not return + // false from shouldVisitLambdaBody()? Because we need to visit a lambda's + // body when we are verifying the lambda itself; we only want to skip it + // in the context of the outer function. + for (unsigned I = 0, N = Lambda->capture_size(); I < N; ++I) + TraverseLambdaCapture(Lambda, Lambda->capture_begin() + I, + Lambda->capture_init_begin()[I]); + + return true; + } + + bool TraverseBlockExpr(BlockExpr * /*unused*/) { + // TODO: are the capture expressions (ctor call?) safe? + return true; + } + + bool VisitDeclRefExpr(const DeclRefExpr *E) { + const ValueDecl *Val = E->getDecl(); + if (const auto *Var = dyn_cast(Val)) { + if (Var->getTLSKind() != VarDecl::TLS_None) { + // At least on macOS, thread-local variables are initialized on + // first access, including a heap allocation. + diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeThreadLocalVars, + ViolationID::AccessesThreadLocalVariable, + E->getLocation()); + } + } + return true; + } + + bool TraverseGenericSelectionExpr(GenericSelectionExpr *Node) { + return TraverseStmt(Node->getResultExpr()); + } + bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) { + return true; + } + + bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc Node) { return true; } + + bool TraverseDecltypeTypeLoc(DecltypeTypeLoc Node) { return true; } + + bool TraverseCXXNoexceptExpr(CXXNoexceptExpr *Node) { return true; } + + bool TraverseCXXTypeidExpr(CXXTypeidExpr *Node) { return true; } + + // Skip concept requirements since they don't generate code. + bool TraverseConceptRequirement(concepts::Requirement *R) { return true; } + }; +}; + +Analyzer::AnalysisMap::~AnalysisMap() { + for (const auto &Item : *this) { + FuncAnalysisPtr AP = Item.second; + if (isa(AP)) + delete AP.get(); + else + delete AP.get(); + } +} + +} // anonymous namespace + +namespace clang { + +bool Sema::diagnoseConflictingFunctionEffect( + const FunctionEffectsRef &FX, const FunctionEffectWithCondition &NewEC, + SourceLocation NewAttrLoc) { + // If the new effect has a condition, we can't detect conflicts until the + // condition is resolved. + if (NewEC.Cond.getCondition() != nullptr) + return false; + + // Diagnose the new attribute as incompatible with a previous one. + auto Incompatible = [&](const FunctionEffectWithCondition &PrevEC) { + Diag(NewAttrLoc, diag::err_attributes_are_not_compatible) + << ("'" + NewEC.description() + "'") + << ("'" + PrevEC.description() + "'") << false; + // We don't necessarily have the location of the previous attribute, + // so no note. + return true; + }; + + // Compare against previous attributes. + FunctionEffect::Kind NewKind = NewEC.Effect.kind(); + + for (const FunctionEffectWithCondition &PrevEC : FX) { + // Again, can't check yet when the effect is conditional. + if (PrevEC.Cond.getCondition() != nullptr) + continue; + + FunctionEffect::Kind PrevKind = PrevEC.Effect.kind(); + // Note that we allow PrevKind == NewKind; it's redundant and ignored. + + if (PrevEC.Effect.oppositeKind() == NewKind) + return Incompatible(PrevEC); + + // A new allocating is incompatible with a previous nonblocking. + if (PrevKind == FunctionEffect::Kind::NonBlocking && + NewKind == FunctionEffect::Kind::Allocating) + return Incompatible(PrevEC); + + // A new nonblocking is incompatible with a previous allocating. + if (PrevKind == FunctionEffect::Kind::Allocating && + NewKind == FunctionEffect::Kind::NonBlocking) + return Incompatible(PrevEC); + } + + return false; +} + +void Sema::diagnoseFunctionEffectMergeConflicts( + const FunctionEffectSet::Conflicts &Errs, SourceLocation NewLoc, + SourceLocation OldLoc) { + for (const FunctionEffectSet::Conflict &Conflict : Errs) { + Diag(NewLoc, diag::warn_conflicting_func_effects) + << Conflict.Kept.description() << Conflict.Rejected.description(); + Diag(OldLoc, diag::note_previous_declaration); + } +} + +// Decl should be a FunctionDecl or BlockDecl. +void Sema::maybeAddDeclWithEffects(const Decl *D, + const FunctionEffectsRef &FX) { + if (!D->hasBody()) { + if (const auto *FD = D->getAsFunction(); FD && !FD->willHaveBody()) + return; + } + + if (Diags.getIgnoreAllWarnings() || + (Diags.getSuppressSystemWarnings() && + SourceMgr.isInSystemHeader(D->getLocation()))) + return; + + if (hasUncompilableErrorOccurred()) + return; + + // For code in dependent contexts, we'll do this at instantiation time. + // Without this check, we would analyze the function based on placeholder + // template parameters, and potentially generate spurious diagnostics. + if (cast(D)->isDependentContext()) + return; + + addDeclWithEffects(D, FX); +} + +void Sema::addDeclWithEffects(const Decl *D, const FunctionEffectsRef &FX) { + // To avoid the possibility of conflict, don't add effects which are + // not FE_InferrableOnCallees and therefore not verified; this removes + // blocking/allocating but keeps nonblocking/nonallocating. + // Also, ignore any conditions when building the list of effects. + bool AnyVerifiable = false; + for (const FunctionEffectWithCondition &EC : FX) + if (EC.Effect.flags() & FunctionEffect::FE_InferrableOnCallees) { + AllEffectsToVerify.insert(EC.Effect); + AnyVerifiable = true; + } + + // Record the declaration for later analysis. + if (AnyVerifiable) + DeclsWithEffectsToVerify.push_back(D); +} + +void Sema::performFunctionEffectAnalysis(TranslationUnitDecl *TU) { + if (hasUncompilableErrorOccurred() || Diags.getIgnoreAllWarnings()) + return; + if (TU == nullptr) + return; + Analyzer{*this}.run(*TU); +} + +Sema::FunctionEffectDiffVector::FunctionEffectDiffVector( + const FunctionEffectsRef &Old, const FunctionEffectsRef &New) { + + FunctionEffectsRef::iterator POld = Old.begin(); + FunctionEffectsRef::iterator OldEnd = Old.end(); + FunctionEffectsRef::iterator PNew = New.begin(); + FunctionEffectsRef::iterator NewEnd = New.end(); + + while (true) { + int cmp = 0; + if (POld == OldEnd) { + if (PNew == NewEnd) + break; + cmp = 1; + } else if (PNew == NewEnd) + cmp = -1; + else { + FunctionEffectWithCondition Old = *POld; + FunctionEffectWithCondition New = *PNew; + if (Old.Effect.kind() < New.Effect.kind()) + cmp = -1; + else if (New.Effect.kind() < Old.Effect.kind()) + cmp = 1; + else { + cmp = 0; + if (Old.Cond.getCondition() != New.Cond.getCondition()) { + // FIXME: Cases where the expressions are equivalent but + // don't have the same identity. + push_back(FunctionEffectDiff{ + Old.Effect.kind(), FunctionEffectDiff::Kind::ConditionMismatch, + Old, New}); + } + } + } + + if (cmp < 0) { + // removal + FunctionEffectWithCondition Old = *POld; + push_back(FunctionEffectDiff{Old.Effect.kind(), + FunctionEffectDiff::Kind::Removed, Old, + std::nullopt}); + ++POld; + } else if (cmp > 0) { + // addition + FunctionEffectWithCondition New = *PNew; + push_back(FunctionEffectDiff{New.Effect.kind(), + FunctionEffectDiff::Kind::Added, + std::nullopt, New}); + ++PNew; + } else { + ++POld; + ++PNew; + } + } +} + +bool Sema::FunctionEffectDiff::shouldDiagnoseConversion( + QualType SrcType, const FunctionEffectsRef &SrcFX, QualType DstType, + const FunctionEffectsRef &DstFX) const { + + switch (EffectKind) { + case FunctionEffect::Kind::NonAllocating: + // nonallocating can't be added (spoofed) during a conversion, unless we + // have nonblocking. + if (DiffKind == Kind::Added) { + for (const auto &CFE : SrcFX) { + if (CFE.Effect.kind() == FunctionEffect::Kind::NonBlocking) + return false; + } + } + [[fallthrough]]; + case FunctionEffect::Kind::NonBlocking: + // nonblocking can't be added (spoofed) during a conversion. + switch (DiffKind) { + case Kind::Added: + return true; + case Kind::Removed: + return false; + case Kind::ConditionMismatch: + // FIXME: Condition mismatches are too coarse right now -- expressions + // which are equivalent but don't have the same identity are detected as + // mismatches. We're going to diagnose those anyhow until expression + // matching is better. + return true; + } + case FunctionEffect::Kind::Blocking: + case FunctionEffect::Kind::Allocating: + return false; + } + llvm_unreachable("unknown effect kind"); +} + +bool Sema::FunctionEffectDiff::shouldDiagnoseRedeclaration( + const FunctionDecl &OldFunction, const FunctionEffectsRef &OldFX, + const FunctionDecl &NewFunction, const FunctionEffectsRef &NewFX) const { + switch (EffectKind) { + case FunctionEffect::Kind::NonAllocating: + case FunctionEffect::Kind::NonBlocking: + // nonblocking/nonallocating can't be removed in a redeclaration. + switch (DiffKind) { + case Kind::Added: + return false; // No diagnostic. + case Kind::Removed: + return true; // Issue diagnostic. + case Kind::ConditionMismatch: + // All these forms of mismatches are diagnosed. + return true; + } + case FunctionEffect::Kind::Blocking: + case FunctionEffect::Kind::Allocating: + return false; + } + llvm_unreachable("unknown effect kind"); +} + +Sema::FunctionEffectDiff::OverrideResult +Sema::FunctionEffectDiff::shouldDiagnoseMethodOverride( + const CXXMethodDecl &OldMethod, const FunctionEffectsRef &OldFX, + const CXXMethodDecl &NewMethod, const FunctionEffectsRef &NewFX) const { + switch (EffectKind) { + case FunctionEffect::Kind::NonAllocating: + case FunctionEffect::Kind::NonBlocking: + switch (DiffKind) { + + // If added on an override, that's fine and not diagnosed. + case Kind::Added: + return OverrideResult::NoAction; + + // If missing from an override (removed), propagate from base to derived. + case Kind::Removed: + return OverrideResult::Merge; + + // If there's a mismatch involving the effect's polarity or condition, + // issue a warning. + case Kind::ConditionMismatch: + return OverrideResult::Warn; + } + + case FunctionEffect::Kind::Blocking: + case FunctionEffect::Kind::Allocating: + return OverrideResult::NoAction; + } + llvm_unreachable("unknown effect kind"); +} + +} // namespace clang diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 43cc6c81ae5cb..fbcba201a351a 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -1828,6 +1828,41 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { return true; break; } + case Builtin::BI__builtin_hlsl_cross: { + if (SemaRef.checkArgCount(TheCall, 2)) + return true; + if (CheckVectorElementCallArgs(&SemaRef, TheCall)) + return true; + if (CheckFloatOrHalfRepresentations(&SemaRef, TheCall)) + return true; + // ensure both args have 3 elements + int NumElementsArg1 = + TheCall->getArg(0)->getType()->getAs()->getNumElements(); + int NumElementsArg2 = + TheCall->getArg(1)->getType()->getAs()->getNumElements(); + + if (NumElementsArg1 != 3) { + int LessOrMore = NumElementsArg1 > 3 ? 1 : 0; + SemaRef.Diag(TheCall->getBeginLoc(), + diag::err_vector_incorrect_num_elements) + << LessOrMore << 3 << NumElementsArg1 << /*operand*/ 1; + return true; + } + if (NumElementsArg2 != 3) { + int LessOrMore = NumElementsArg2 > 3 ? 1 : 0; + + SemaRef.Diag(TheCall->getBeginLoc(), + diag::err_vector_incorrect_num_elements) + << LessOrMore << 3 << NumElementsArg2 << /*operand*/ 1; + return true; + } + + ExprResult A = TheCall->getArg(0); + QualType ArgTyA = A.get()->getType(); + // return type is the same as the input type + TheCall->setType(ArgTyA); + break; + } case Builtin::BI__builtin_hlsl_dot: { if (SemaRef.checkArgCount(TheCall, 2)) return true; @@ -1959,6 +1994,7 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { case Builtin::BI__builtin_elementwise_acos: case Builtin::BI__builtin_elementwise_asin: case Builtin::BI__builtin_elementwise_atan: + case Builtin::BI__builtin_elementwise_atan2: case Builtin::BI__builtin_elementwise_ceil: case Builtin::BI__builtin_elementwise_cos: case Builtin::BI__builtin_elementwise_cosh: diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 4d11f2a43fcc6..edd1fe40fdf27 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -1976,8 +1976,9 @@ void InitListChecker::CheckVectorType(const InitializedEntity &Entity, if (numEltsInit != maxElements) { if (!VerifyOnly) SemaRef.Diag(IList->getBeginLoc(), - diag::err_vector_incorrect_num_initializers) - << (numEltsInit < maxElements) << maxElements << numEltsInit; + diag::err_vector_incorrect_num_elements) + << (numEltsInit < maxElements) << maxElements << numEltsInit + << /*initialization*/ 0; hadError = true; } } diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index 15ed8572e6084..aeb20299b714a 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -423,11 +423,11 @@ bool Sema::DiagnoseInvalidExplicitObjectParameterInLambda( // is an empty cast path for the method stored in the context (signalling that // we've already diagnosed it) and then just not building the call, but that // doesn't really seem any simpler than diagnosing it at the call site... - if (auto It = Context.LambdaCastPaths.find(Method); - It != Context.LambdaCastPaths.end()) + auto [It, Inserted] = Context.LambdaCastPaths.try_emplace(Method); + if (!Inserted) return It->second.empty(); - CXXCastPath &Path = Context.LambdaCastPaths[Method]; + CXXCastPath &Path = It->second; CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true, /*DetectVirtual=*/false); if (!IsDerivedFrom(RD->getLocation(), ExplicitObjectParameterType, LambdaType, @@ -1951,6 +1951,9 @@ ExprResult Sema::BuildCaptureInit(const Capture &Cap, ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body) { LambdaScopeInfo LSI = *cast(FunctionScopes.back()); ActOnFinishFunctionBody(LSI.CallOperator, Body); + + maybeAddDeclWithEffects(LSI.CallOperator); + return BuildLambdaExpr(StartLoc, Body->getEndLoc(), &LSI); } diff --git a/clang/lib/Sema/SemaOpenACC.cpp b/clang/lib/Sema/SemaOpenACC.cpp index 89142b837e60a..66f8029a2754b 100644 --- a/clang/lib/Sema/SemaOpenACC.cpp +++ b/clang/lib/Sema/SemaOpenACC.cpp @@ -354,6 +354,17 @@ bool doesClauseApplyToDirective(OpenACCDirectiveKind DirectiveKind, return false; } } + case OpenACCClauseKind::Tile: { + switch (DirectiveKind) { + case OpenACCDirectiveKind::Loop: + case OpenACCDirectiveKind::ParallelLoop: + case OpenACCDirectiveKind::SerialLoop: + case OpenACCDirectiveKind::KernelsLoop: + return true; + default: + return false; + } + } default: // Do nothing so we can go to the 'unimplemented' diagnostic instead. @@ -526,6 +537,36 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitDefaultClause( Clause.getLParenLoc(), Clause.getEndLoc()); } +OpenACCClause *SemaOpenACCClauseVisitor::VisitTileClause( + SemaOpenACC::OpenACCParsedClause &Clause) { + if (Clause.getDirectiveKind() != OpenACCDirectiveKind::Loop) + return isNotImplemented(); + + // Duplicates here are not really sensible. We could possible permit + // multiples if they all had the same value, but there isn't really a good + // reason to do so. Also, this simplifies the suppression of duplicates, in + // that we know if we 'find' one after instantiation, that it is the same + // clause, which simplifies instantiation/checking/etc. + if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause)) + return nullptr; + + llvm::SmallVector NewSizeExprs; + + // Make sure these are all positive constant expressions or *. + for (Expr *E : Clause.getIntExprs()) { + ExprResult Res = SemaRef.CheckTileSizeExpr(E); + + if (!Res.isUsable()) + return nullptr; + + NewSizeExprs.push_back(Res.get()); + } + + return OpenACCTileClause::Create(Ctx, Clause.getBeginLoc(), + Clause.getLParenLoc(), NewSizeExprs, + Clause.getEndLoc()); +} + OpenACCClause *SemaOpenACCClauseVisitor::VisitIfClause( SemaOpenACC::OpenACCParsedClause &Clause) { // Restrictions only properly implemented on 'compute' constructs, and @@ -1073,19 +1114,90 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitCollapseClause( SemaOpenACC::SemaOpenACC(Sema &S) : SemaBase(S) {} -SemaOpenACC::AssociatedStmtRAII::AssociatedStmtRAII(SemaOpenACC &S, - OpenACCDirectiveKind DK) +SemaOpenACC::AssociatedStmtRAII::AssociatedStmtRAII( + SemaOpenACC &S, OpenACCDirectiveKind DK, + ArrayRef UnInstClauses, + ArrayRef Clauses) : SemaRef(S), WasInsideComputeConstruct(S.InsideComputeConstruct), - DirKind(DK) { + DirKind(DK), LoopRAII(SemaRef, /*PreserveDepth=*/false) { // Compute constructs end up taking their 'loop'. if (DirKind == OpenACCDirectiveKind::Parallel || DirKind == OpenACCDirectiveKind::Serial || DirKind == OpenACCDirectiveKind::Kernels) { SemaRef.InsideComputeConstruct = true; SemaRef.ParentlessLoopConstructs.swap(ParentlessLoopConstructs); + } else if (DirKind == OpenACCDirectiveKind::Loop) { + SetCollapseInfoBeforeAssociatedStmt(UnInstClauses, Clauses); + SetTileInfoBeforeAssociatedStmt(UnInstClauses, Clauses); } } +void SemaOpenACC::AssociatedStmtRAII::SetCollapseInfoBeforeAssociatedStmt( + ArrayRef UnInstClauses, + ArrayRef Clauses) { + + // Reset this checking for loops that aren't covered in a RAII object. + SemaRef.LoopInfo.CurLevelHasLoopAlready = false; + SemaRef.CollapseInfo.CollapseDepthSatisfied = true; + SemaRef.TileInfo.TileDepthSatisfied = true; + + // We make sure to take an optional list of uninstantiated clauses, so that + // we can check to make sure we don't 'double diagnose' in the event that + // the value of 'N' was not dependent in a template. We also ensure during + // Sema that there is only 1 collapse on each construct, so we can count on + // the fact that if both find a 'collapse', that they are the same one. + auto *CollapseClauseItr = + llvm::find_if(Clauses, llvm::IsaPred); + auto *UnInstCollapseClauseItr = + llvm::find_if(UnInstClauses, llvm::IsaPred); + + if (Clauses.end() == CollapseClauseItr) + return; + + OpenACCCollapseClause *CollapseClause = + cast(*CollapseClauseItr); + + SemaRef.CollapseInfo.ActiveCollapse = CollapseClause; + Expr *LoopCount = CollapseClause->getLoopCount(); + + // If the loop count is still instantiation dependent, setting the depth + // counter isn't necessary, so return here. + if (!LoopCount || LoopCount->isInstantiationDependent()) + return; + + // Suppress diagnostics if we've done a 'transform' where the previous version + // wasn't dependent, meaning we already diagnosed it. + if (UnInstCollapseClauseItr != UnInstClauses.end() && + !cast(*UnInstCollapseClauseItr) + ->getLoopCount() + ->isInstantiationDependent()) + return; + + SemaRef.CollapseInfo.CollapseDepthSatisfied = false; + SemaRef.CollapseInfo.CurCollapseCount = + cast(LoopCount)->getResultAsAPSInt(); +} + +void SemaOpenACC::AssociatedStmtRAII::SetTileInfoBeforeAssociatedStmt( + ArrayRef UnInstClauses, + ArrayRef Clauses) { + // We don't diagnose if this is during instantiation, since the only thing we + // care about is the number of arguments, which we can figure out without + // instantiation, so we don't want to double-diagnose. + if (UnInstClauses.size() > 0) + return; + auto *TileClauseItr = + llvm::find_if(Clauses, llvm::IsaPred); + + if (Clauses.end() == TileClauseItr) + return; + + OpenACCTileClause *TileClause = cast(*TileClauseItr); + SemaRef.TileInfo.ActiveTile = TileClause; + SemaRef.TileInfo.TileDepthSatisfied = false; + SemaRef.TileInfo.CurTileCount = TileClause->getSizeExprs().size(); +} + SemaOpenACC::AssociatedStmtRAII::~AssociatedStmtRAII() { SemaRef.InsideComputeConstruct = WasInsideComputeConstruct; if (DirKind == OpenACCDirectiveKind::Parallel || @@ -1094,6 +1206,9 @@ SemaOpenACC::AssociatedStmtRAII::~AssociatedStmtRAII() { assert(SemaRef.ParentlessLoopConstructs.empty() && "Didn't consume loop construct list?"); SemaRef.ParentlessLoopConstructs.swap(ParentlessLoopConstructs); + } else if (DirKind == OpenACCDirectiveKind::Loop) { + // Nothing really to do here, the LoopInConstruct should handle restorations + // correctly. } } @@ -1646,10 +1761,242 @@ ExprResult SemaOpenACC::CheckCollapseLoopCount(Expr *LoopCount) { ConstantExpr::Create(getASTContext(), LoopCount, APValue{*ICE})}; } +ExprResult SemaOpenACC::CheckTileSizeExpr(Expr *SizeExpr) { + if (!SizeExpr) + return ExprError(); + + assert((SizeExpr->isInstantiationDependent() || + SizeExpr->getType()->isIntegerType()) && + "size argument non integer?"); + + // If dependent, or an asterisk, the expression is fine. + if (SizeExpr->isInstantiationDependent() || + isa(SizeExpr)) + return ExprResult{SizeExpr}; + + std::optional ICE = + SizeExpr->getIntegerConstantExpr(getASTContext()); + + // OpenACC 3.3 2.9.8 + // where each tile size is a constant positive integer expression or asterisk. + if (!ICE || *ICE <= 0) { + Diag(SizeExpr->getBeginLoc(), diag::err_acc_size_expr_value) + << ICE.has_value() << ICE.value_or(llvm::APSInt{}).getExtValue(); + return ExprError(); + } + + return ExprResult{ + ConstantExpr::Create(getASTContext(), SizeExpr, APValue{*ICE})}; +} + +void SemaOpenACC::ActOnWhileStmt(SourceLocation WhileLoc) { + if (!getLangOpts().OpenACC) + return; + + if (!LoopInfo.TopLevelLoopSeen) + return; + + if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0) { + Diag(WhileLoc, diag::err_acc_invalid_in_loop) + << /*while loop*/ 1 << OpenACCClauseKind::Collapse; + assert(CollapseInfo.ActiveCollapse && "Collapse count without object?"); + Diag(CollapseInfo.ActiveCollapse->getBeginLoc(), + diag::note_acc_active_clause_here) + << OpenACCClauseKind::Collapse; + + // Remove the value so that we don't get cascading errors in the body. The + // caller RAII object will restore this. + CollapseInfo.CurCollapseCount = std::nullopt; + } + + if (TileInfo.CurTileCount && *TileInfo.CurTileCount > 0) { + Diag(WhileLoc, diag::err_acc_invalid_in_loop) + << /*while loop*/ 1 << OpenACCClauseKind::Tile; + assert(TileInfo.ActiveTile && "tile count without object?"); + Diag(TileInfo.ActiveTile->getBeginLoc(), diag::note_acc_active_clause_here) + << OpenACCClauseKind::Tile; + + // Remove the value so that we don't get cascading errors in the body. The + // caller RAII object will restore this. + TileInfo.CurTileCount = std::nullopt; + } +} + +void SemaOpenACC::ActOnDoStmt(SourceLocation DoLoc) { + if (!getLangOpts().OpenACC) + return; + + if (!LoopInfo.TopLevelLoopSeen) + return; + + if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0) { + Diag(DoLoc, diag::err_acc_invalid_in_loop) + << /*do loop*/ 2 << OpenACCClauseKind::Collapse; + assert(CollapseInfo.ActiveCollapse && "Collapse count without object?"); + Diag(CollapseInfo.ActiveCollapse->getBeginLoc(), + diag::note_acc_active_clause_here) + << OpenACCClauseKind::Collapse; + + // Remove the value so that we don't get cascading errors in the body. The + // caller RAII object will restore this. + CollapseInfo.CurCollapseCount = std::nullopt; + } + + if (TileInfo.CurTileCount && *TileInfo.CurTileCount > 0) { + Diag(DoLoc, diag::err_acc_invalid_in_loop) + << /*do loop*/ 2 << OpenACCClauseKind::Tile; + assert(TileInfo.ActiveTile && "tile count without object?"); + Diag(TileInfo.ActiveTile->getBeginLoc(), diag::note_acc_active_clause_here) + << OpenACCClauseKind::Tile; + + // Remove the value so that we don't get cascading errors in the body. The + // caller RAII object will restore this. + TileInfo.CurTileCount = std::nullopt; + } +} + +void SemaOpenACC::ActOnForStmtBegin(SourceLocation ForLoc) { + if (!getLangOpts().OpenACC) + return; + + // Enable the while/do-while checking. + LoopInfo.TopLevelLoopSeen = true; + + if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0) { + + // OpenACC 3.3 2.9.1: + // Each associated loop, except the innermost, must contain exactly one loop + // or loop nest. + // This checks for more than 1 loop at the current level, the + // 'depth'-satisifed checking manages the 'not zero' case. + if (LoopInfo.CurLevelHasLoopAlready) { + Diag(ForLoc, diag::err_acc_clause_multiple_loops) << /*Collapse*/ 0; + assert(CollapseInfo.ActiveCollapse && "No collapse object?"); + Diag(CollapseInfo.ActiveCollapse->getBeginLoc(), + diag::note_acc_active_clause_here) + << OpenACCClauseKind::Collapse; + } else { + --(*CollapseInfo.CurCollapseCount); + + // Once we've hit zero here, we know we have deep enough 'for' loops to + // get to the bottom. + if (*CollapseInfo.CurCollapseCount == 0) + CollapseInfo.CollapseDepthSatisfied = true; + } + } + + if (TileInfo.CurTileCount && *TileInfo.CurTileCount > 0) { + if (LoopInfo.CurLevelHasLoopAlready) { + Diag(ForLoc, diag::err_acc_clause_multiple_loops) << /*Tile*/ 1; + assert(TileInfo.ActiveTile && "No tile object?"); + Diag(TileInfo.ActiveTile->getBeginLoc(), + diag::note_acc_active_clause_here) + << OpenACCClauseKind::Tile; + } else { + --(*TileInfo.CurTileCount); + // Once we've hit zero here, we know we have deep enough 'for' loops to + // get to the bottom. + if (*TileInfo.CurTileCount == 0) + TileInfo.TileDepthSatisfied = true; + } + } + + // Set this to 'false' for the body of this loop, so that the next level + // checks independently. + LoopInfo.CurLevelHasLoopAlready = false; +} + +namespace { +SourceLocation FindInterveningCodeInLoop(const Stmt *CurStmt) { + // We should diagnose on anything except `CompoundStmt`, `NullStmt`, + // `ForStmt`, `CXXForRangeStmt`, since those are legal, and `WhileStmt` and + // `DoStmt`, as those are caught as a violation elsewhere. + // For `CompoundStmt` we need to search inside of it. + if (!CurStmt || + isa( + CurStmt)) + return SourceLocation{}; + + // Any other construct is an error anyway, so it has already been diagnosed. + if (isa(CurStmt)) + return SourceLocation{}; + + // Search inside the compound statement, this allows for arbitrary nesting + // of compound statements, as long as there isn't any code inside. + if (const auto *CS = dyn_cast(CurStmt)) { + for (const auto *ChildStmt : CS->children()) { + SourceLocation ChildStmtLoc = FindInterveningCodeInLoop(ChildStmt); + if (ChildStmtLoc.isValid()) + return ChildStmtLoc; + } + // Empty/not invalid compound statements are legal. + return SourceLocation{}; + } + return CurStmt->getBeginLoc(); +} +} // namespace + +void SemaOpenACC::ActOnForStmtEnd(SourceLocation ForLoc, StmtResult Body) { + if (!getLangOpts().OpenACC) + return; + // Set this to 'true' so if we find another one at this level we can diagnose. + LoopInfo.CurLevelHasLoopAlready = true; + + if (!Body.isUsable()) + return; + + bool IsActiveCollapse = CollapseInfo.CurCollapseCount && + *CollapseInfo.CurCollapseCount > 0 && + !CollapseInfo.ActiveCollapse->hasForce(); + bool IsActiveTile = TileInfo.CurTileCount && *TileInfo.CurTileCount > 0; + + if (IsActiveCollapse || IsActiveTile) { + SourceLocation OtherStmtLoc = FindInterveningCodeInLoop(Body.get()); + + if (OtherStmtLoc.isValid() && IsActiveCollapse) { + Diag(OtherStmtLoc, diag::err_acc_intervening_code) + << OpenACCClauseKind::Collapse; + Diag(CollapseInfo.ActiveCollapse->getBeginLoc(), + diag::note_acc_active_clause_here) + << OpenACCClauseKind::Collapse; + } + + if (OtherStmtLoc.isValid() && IsActiveTile) { + Diag(OtherStmtLoc, diag::err_acc_intervening_code) + << OpenACCClauseKind::Tile; + Diag(TileInfo.ActiveTile->getBeginLoc(), + diag::note_acc_active_clause_here) + << OpenACCClauseKind::Tile; + } + } +} + bool SemaOpenACC::ActOnStartStmtDirective(OpenACCDirectiveKind K, SourceLocation StartLoc) { SemaRef.DiscardCleanupsInEvaluationContext(); SemaRef.PopExpressionEvaluationContext(); + + // OpenACC 3.3 2.9.1: + // Intervening code must not contain other OpenACC directives or calls to API + // routines. + // + // ALL constructs are ill-formed if there is an active 'collapse' + if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0) { + Diag(StartLoc, diag::err_acc_invalid_in_loop) + << /*OpenACC Construct*/ 0 << OpenACCClauseKind::Collapse << K; + assert(CollapseInfo.ActiveCollapse && "Collapse count without object?"); + Diag(CollapseInfo.ActiveCollapse->getBeginLoc(), + diag::note_acc_active_clause_here) + << OpenACCClauseKind::Collapse; + } + if (TileInfo.CurTileCount && *TileInfo.CurTileCount > 0) { + Diag(StartLoc, diag::err_acc_invalid_in_loop) + << /*OpenACC Construct*/ 0 << OpenACCClauseKind::Tile << K; + assert(TileInfo.ActiveTile && "Tile count without object?"); + Diag(TileInfo.ActiveTile->getBeginLoc(), diag::note_acc_active_clause_here) + << OpenACCClauseKind::Tile; + } + return diagnoseConstructAppertainment(*this, K, StartLoc, /*IsStmt=*/true); } @@ -1713,12 +2060,36 @@ StmtResult SemaOpenACC::ActOnAssociatedStmt(SourceLocation DirectiveLoc, // the 'structured block'. return AssocStmt; case OpenACCDirectiveKind::Loop: - if (AssocStmt.isUsable() && - !isa(AssocStmt.get())) { + if (!AssocStmt.isUsable()) + return StmtError(); + + if (!isa(AssocStmt.get())) { Diag(AssocStmt.get()->getBeginLoc(), diag::err_acc_loop_not_for_loop); Diag(DirectiveLoc, diag::note_acc_construct_here) << K; return StmtError(); } + + if (!CollapseInfo.CollapseDepthSatisfied || !TileInfo.TileDepthSatisfied) { + if (!CollapseInfo.CollapseDepthSatisfied) { + Diag(DirectiveLoc, diag::err_acc_insufficient_loops) + << OpenACCClauseKind::Collapse; + assert(CollapseInfo.ActiveCollapse && "Collapse count without object?"); + Diag(CollapseInfo.ActiveCollapse->getBeginLoc(), + diag::note_acc_active_clause_here) + << OpenACCClauseKind::Collapse; + } + + if (!TileInfo.TileDepthSatisfied) { + Diag(DirectiveLoc, diag::err_acc_insufficient_loops) + << OpenACCClauseKind::Tile; + assert(TileInfo.ActiveTile && "Collapse count without object?"); + Diag(TileInfo.ActiveTile->getBeginLoc(), + diag::note_acc_active_clause_here) + << OpenACCClauseKind::Tile; + } + return StmtError(); + } + // TODO OpenACC: 2.9 ~ line 2010 specifies that the associated loop has some // restrictions when there is a 'seq' clause in place. We probably need to // implement that, including piping in the clauses here. @@ -1738,3 +2109,13 @@ bool SemaOpenACC::ActOnStartDeclDirective(OpenACCDirectiveKind K, } DeclGroupRef SemaOpenACC::ActOnEndDeclDirective() { return DeclGroupRef{}; } + +ExprResult +SemaOpenACC::BuildOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc) { + return OpenACCAsteriskSizeExpr::Create(getASTContext(), AsteriskLoc); +} + +ExprResult +SemaOpenACC::ActOnOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc) { + return BuildOpenACCAsteriskSizeExpr(AsteriskLoc); +} diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 0c1e054f7c30a..2cde8131108fb 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -2232,16 +2232,21 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, // just strip the qualifiers because they don't matter. FromType = FromType.getUnqualifiedType(); } else if (S.getLangOpts().HLSL && FromType->isConstantArrayType() && - ToType->isArrayParameterType()) { + ToType->isConstantArrayType()) { // HLSL constant array parameters do not decay, so if the argument is a // constant array and the parameter is an ArrayParameterType we have special // handling here. - FromType = S.Context.getArrayParameterType(FromType); + if (ToType->isArrayParameterType()) { + FromType = S.Context.getArrayParameterType(FromType); + SCS.First = ICK_HLSL_Array_RValue; + } else { + SCS.First = ICK_Identity; + } + if (S.Context.getCanonicalType(FromType) != S.Context.getCanonicalType(ToType)) return false; - SCS.First = ICK_HLSL_Array_RValue; SCS.setAllToTypes(ToType); return true; } else if (FromType->isArrayType()) { diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index c7d48b81bc034..eeaa1ebd7ba83 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -5502,8 +5502,7 @@ bool Sema::CheckTemplateArgumentList( DefaultArgs && ParamIdx >= DefaultArgs.StartPos) { // All written arguments should have been consumed by this point. assert(ArgIdx == NumArgs && "bad default argument deduction"); - // FIXME: Don't ignore parameter packs. - if (ParamIdx == DefaultArgs.StartPos && !(*Param)->isParameterPack()) { + if (ParamIdx == DefaultArgs.StartPos) { assert(Param + DefaultArgs.Args.size() <= ParamEnd); // Default arguments from a DeducedTemplateName are already converted. for (const TemplateArgument &DefArg : DefaultArgs.Args) { @@ -5580,9 +5579,6 @@ bool Sema::CheckTemplateArgumentList( return true; } - // We're now done with this argument. - ++ArgIdx; - if ((*Param)->isTemplateParameterPack()) { // The template parameter was a template parameter pack, so take the // deduced argument and place it on the argument pack. Note that we @@ -5593,8 +5589,19 @@ bool Sema::CheckTemplateArgumentList( } else { // Move to the next template parameter. ++Param; + if (PartialOrderingTTP && PackExpansionIntoNonPack) { + // Keep converting the pattern in the argument against + // subsequent parameters. The argument is converted + // in place and will be added back when we are done. + SugaredConverted.pop_back(); + CanonicalConverted.pop_back(); + continue; + } } + // We're now done with this argument. + ++ArgIdx; + // If we just saw a pack expansion into a non-pack, then directly convert // the remaining arguments, because we don't know what parameters they'll // match up with. @@ -5728,14 +5735,10 @@ bool Sema::CheckTemplateArgumentList( // pack expansions; they might be empty. This can happen even if // PartialTemplateArgs is false (the list of arguments is complete but // still dependent). - if (ArgIdx < NumArgs && CurrentInstantiationScope && - CurrentInstantiationScope->getPartiallySubstitutedPack()) { - while (ArgIdx < NumArgs && - NewArgs[ArgIdx].getArgument().isPackExpansion()) { - const TemplateArgument &Arg = NewArgs[ArgIdx++].getArgument(); - SugaredConverted.push_back(Arg); - CanonicalConverted.push_back(Context.getCanonicalTemplateArgument(Arg)); - } + while (ArgIdx < NumArgs && NewArgs[ArgIdx].getArgument().isPackExpansion()) { + const TemplateArgument &Arg = NewArgs[ArgIdx++].getArgument(); + SugaredConverted.push_back(Arg); + CanonicalConverted.push_back(Context.getCanonicalTemplateArgument(Arg)); } // If we have any leftover arguments, then there were too many arguments. @@ -7327,64 +7330,46 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, << Template; } + if (!getLangOpts().RelaxedTemplateTemplateArgs) + return !TemplateParameterListsAreEqual( + Template->getTemplateParameters(), Params, /*Complain=*/true, + TPL_TemplateTemplateArgumentMatch, Arg.getLocation()); + // C++1z [temp.arg.template]p3: (DR 150) // A template-argument matches a template template-parameter P when P // is at least as specialized as the template-argument A. - if (getLangOpts().RelaxedTemplateTemplateArgs) { - // Quick check for the common case: - // If P contains a parameter pack, then A [...] matches P if each of A's - // template parameters matches the corresponding template parameter in - // the template-parameter-list of P. - if (TemplateParameterListsAreEqual( - Template->getTemplateParameters(), Params, false, - TPL_TemplateTemplateArgumentMatch, Arg.getLocation()) && - // If the argument has no associated constraints, then the parameter is - // definitely at least as specialized as the argument. - // Otherwise - we need a more thorough check. - !Template->hasAssociatedConstraints()) - return false; - - if (isTemplateTemplateParameterAtLeastAsSpecializedAs( - Params, Template, DefaultArgs, Arg.getLocation(), IsDeduced)) { - // P2113 - // C++20[temp.func.order]p2 - // [...] If both deductions succeed, the partial ordering selects the - // more constrained template (if one exists) as determined below. - SmallVector ParamsAC, TemplateAC; - Params->getAssociatedConstraints(ParamsAC); - // C++2a[temp.arg.template]p3 - // [...] In this comparison, if P is unconstrained, the constraints on A - // are not considered. - if (ParamsAC.empty()) - return false; + if (!isTemplateTemplateParameterAtLeastAsSpecializedAs( + Params, Param, Template, DefaultArgs, Arg.getLocation(), IsDeduced)) + return true; + // P2113 + // C++20[temp.func.order]p2 + // [...] If both deductions succeed, the partial ordering selects the + // more constrained template (if one exists) as determined below. + SmallVector ParamsAC, TemplateAC; + Params->getAssociatedConstraints(ParamsAC); + // C++20[temp.arg.template]p3 + // [...] In this comparison, if P is unconstrained, the constraints on A + // are not considered. + if (ParamsAC.empty()) + return false; - Template->getAssociatedConstraints(TemplateAC); + Template->getAssociatedConstraints(TemplateAC); - bool IsParamAtLeastAsConstrained; - if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC, - IsParamAtLeastAsConstrained)) - return true; - if (!IsParamAtLeastAsConstrained) { - Diag(Arg.getLocation(), - diag::err_template_template_parameter_not_at_least_as_constrained) - << Template << Param << Arg.getSourceRange(); - Diag(Param->getLocation(), diag::note_entity_declared_at) << Param; - Diag(Template->getLocation(), diag::note_entity_declared_at) - << Template; - MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template, - TemplateAC); - return true; - } - return false; - } - // FIXME: Produce better diagnostics for deduction failures. + bool IsParamAtLeastAsConstrained; + if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC, + IsParamAtLeastAsConstrained)) + return true; + if (!IsParamAtLeastAsConstrained) { + Diag(Arg.getLocation(), + diag::err_template_template_parameter_not_at_least_as_constrained) + << Template << Param << Arg.getSourceRange(); + Diag(Param->getLocation(), diag::note_entity_declared_at) << Param; + Diag(Template->getLocation(), diag::note_entity_declared_at) << Template; + MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template, + TemplateAC); + return true; } - - return !TemplateParameterListsAreEqual(Template->getTemplateParameters(), - Params, - true, - TPL_TemplateTemplateArgumentMatch, - Arg.getLocation()); + return false; } static Sema::SemaDiagnosticBuilder noteLocation(Sema &S, const NamedDecl &Decl, diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index cc095ae67ac40..ce3317db5a820 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -145,7 +145,9 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( PartialOrderingKind POK, bool DeducedFromArrayBound, bool *HasDeducedAnyParam); -enum class PackFold { ParameterToArgument, ArgumentToParameter }; +/// What directions packs are allowed to match non-packs. +enum class PackFold { ParameterToArgument, ArgumentToParameter, Both }; + static TemplateDeductionResult DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, ArrayRef Ps, @@ -440,18 +442,18 @@ DeduceNonTypeTemplateArgument(Sema &S, TemplateParameterList *TemplateParams, // FIXME: It's not clear how deduction of a parameter of reference // type from an argument (of non-reference type) should be performed. - // For now, we just remove reference types from both sides and let - // the final check for matching types sort out the mess. - ValueType = ValueType.getNonReferenceType(); - if (ParamType->isReferenceType()) - ParamType = ParamType.getNonReferenceType(); - else - // Top-level cv-qualifiers are irrelevant for a non-reference type. - ValueType = ValueType.getUnqualifiedType(); + // For now, we just make the argument have same reference type as the + // parameter. + if (ParamType->isReferenceType() && !ValueType->isReferenceType()) { + if (ParamType->isRValueReferenceType()) + ValueType = S.Context.getRValueReferenceType(ValueType); + else + ValueType = S.Context.getLValueReferenceType(ValueType); + } return DeduceTemplateArgumentsByTypeMatch( S, TemplateParams, ParamType, ValueType, Info, Deduced, - TDF_SkipNonDependent, + TDF_SkipNonDependent | TDF_IgnoreQualifiers, PartialOrdering ? PartialOrderingKind::NonCall : PartialOrderingKind::None, /*ArrayBound=*/NewDeduced.wasDeducedFromArrayBound(), HasDeducedAnyParam); @@ -1711,7 +1713,21 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( DeducedTemplateArgument Result = checkDeducedTemplateArguments(S.Context, Deduced[Index], NewDeduced); if (Result.isNull()) { - Info.Param = cast(TemplateParams->getParam(Index)); + // We can also get inconsistencies when matching NTTP type. + switch (NamedDecl *Param = TemplateParams->getParam(Index); + Param->getKind()) { + case Decl::TemplateTypeParm: + Info.Param = cast(Param); + break; + case Decl::NonTypeTemplateParm: + Info.Param = cast(Param); + break; + case Decl::TemplateTemplateParm: + Info.Param = cast(Param); + break; + default: + llvm_unreachable("unexpected kind"); + } Info.FirstArg = Deduced[Index]; Info.SecondArg = NewDeduced; return TemplateDeductionResult::Inconsistent; @@ -2549,8 +2565,31 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, if (const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(Info, P.getAsExpr())) { switch (A.getKind()) { + case TemplateArgument::Expression: { + const Expr *E = A.getAsExpr(); + // When checking NTTP, if either the parameter or the argument is + // dependent, as there would be otherwise nothing to deduce, we force + // the argument to the parameter type using this dependent implicit + // cast, in order to maintain invariants. Now we can deduce the + // resulting type from the original type, and deduce the original type + // against the parameter we are checking. + if (const auto *ICE = dyn_cast(E); + ICE && ICE->getCastKind() == clang::CK_Dependent) { + E = ICE->getSubExpr(); + if (auto Result = DeduceTemplateArgumentsByTypeMatch( + S, TemplateParams, ICE->getType(), E->getType(), Info, + Deduced, TDF_SkipNonDependent, + PartialOrdering ? PartialOrderingKind::NonCall + : PartialOrderingKind::None, + /*DeducedFromArrayBound=*/false, HasDeducedAnyParam); + Result != TemplateDeductionResult::Success) + return Result; + } + return DeduceNonTypeTemplateArgument( + S, TemplateParams, NTTP, DeducedTemplateArgument(A), E->getType(), + Info, PartialOrdering, Deduced, HasDeducedAnyParam); + } case TemplateArgument::Integral: - case TemplateArgument::Expression: case TemplateArgument::StructuralValue: return DeduceNonTypeTemplateArgument( S, TemplateParams, NTTP, DeducedTemplateArgument(A), @@ -2639,50 +2678,75 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, SmallVectorImpl &Deduced, bool NumberOfArgumentsMustMatch, bool PartialOrdering, PackFold PackFold, bool *HasDeducedAnyParam) { - if (PackFold == PackFold::ArgumentToParameter) - std::swap(Ps, As); + bool FoldPackParameter = PackFold == PackFold::ParameterToArgument || + PackFold == PackFold::Both, + FoldPackArgument = PackFold == PackFold::ArgumentToParameter || + PackFold == PackFold::Both; + // C++0x [temp.deduct.type]p9: // If the template argument list of P contains a pack expansion that is not // the last template argument, the entire template argument list is a // non-deduced context. - if (hasPackExpansionBeforeEnd(Ps)) + if (FoldPackParameter && hasPackExpansionBeforeEnd(Ps)) + return TemplateDeductionResult::Success; + + if (FoldPackArgument && hasPackExpansionBeforeEnd(As)) return TemplateDeductionResult::Success; // C++0x [temp.deduct.type]p9: // If P has a form that contains or , then each argument Pi of the // respective template argument list P is compared with the corresponding // argument Ai of the corresponding template argument list of A. - unsigned ArgIdx = 0, ParamIdx = 0; - for (; hasTemplateArgumentForDeduction(Ps, ParamIdx); ++ParamIdx) { - const TemplateArgument &P = Ps[ParamIdx]; - if (!P.isPackExpansion()) { + for (unsigned ArgIdx = 0, ParamIdx = 0; /**/; /**/) { + if (!hasTemplateArgumentForDeduction(Ps, ParamIdx)) + return !FoldPackParameter && hasTemplateArgumentForDeduction(As, ArgIdx) + ? TemplateDeductionResult::MiscellaneousDeductionFailure + : TemplateDeductionResult::Success; + + if (!Ps[ParamIdx].isPackExpansion()) { // The simple case: deduce template arguments by matching Pi and Ai. // Check whether we have enough arguments. if (!hasTemplateArgumentForDeduction(As, ArgIdx)) - return NumberOfArgumentsMustMatch + return !FoldPackArgument && NumberOfArgumentsMustMatch ? TemplateDeductionResult::MiscellaneousDeductionFailure : TemplateDeductionResult::Success; - // C++1z [temp.deduct.type]p9: - // During partial ordering, if Ai was originally a pack expansion [and] - // Pi is not a pack expansion, template argument deduction fails. - if (As[ArgIdx].isPackExpansion()) - return TemplateDeductionResult::MiscellaneousDeductionFailure; + if (As[ArgIdx].isPackExpansion()) { + // C++1z [temp.deduct.type]p9: + // During partial ordering, if Ai was originally a pack expansion + // [and] Pi is not a pack expansion, template argument deduction + // fails. + if (!FoldPackArgument) + return TemplateDeductionResult::MiscellaneousDeductionFailure; + + TemplateArgument Pattern = As[ArgIdx].getPackExpansionPattern(); + for (;;) { + // Deduce template parameters from the pattern. + if (auto Result = DeduceTemplateArguments( + S, TemplateParams, Ps[ParamIdx], Pattern, Info, + PartialOrdering, Deduced, HasDeducedAnyParam); + Result != TemplateDeductionResult::Success) + return Result; - // Perform deduction for this Pi/Ai pair. - TemplateArgument Pi = P, Ai = As[ArgIdx]; - if (PackFold == PackFold::ArgumentToParameter) - std::swap(Pi, Ai); - if (auto Result = DeduceTemplateArguments(S, TemplateParams, Pi, Ai, Info, - PartialOrdering, Deduced, - HasDeducedAnyParam); - Result != TemplateDeductionResult::Success) - return Result; + ++ParamIdx; + if (!hasTemplateArgumentForDeduction(Ps, ParamIdx)) + return TemplateDeductionResult::Success; + if (Ps[ParamIdx].isPackExpansion()) + break; + } + } else { + // Perform deduction for this Pi/Ai pair. + if (auto Result = DeduceTemplateArguments( + S, TemplateParams, Ps[ParamIdx], As[ArgIdx], Info, + PartialOrdering, Deduced, HasDeducedAnyParam); + Result != TemplateDeductionResult::Success) + return Result; - // Move to the next argument. - ++ArgIdx; - continue; + ++ArgIdx; + ++ParamIdx; + continue; + } } // The parameter is a pack expansion. @@ -2692,7 +2756,7 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, // each remaining argument in the template argument list of A. Each // comparison deduces template arguments for subsequent positions in the // template parameter packs expanded by Pi. - TemplateArgument Pattern = P.getPackExpansionPattern(); + TemplateArgument Pattern = Ps[ParamIdx].getPackExpansionPattern(); // Prepare to deduce the packs within the pattern. PackDeductionScope PackScope(S, TemplateParams, Deduced, Info, Pattern); @@ -2703,13 +2767,12 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, for (; hasTemplateArgumentForDeduction(As, ArgIdx) && PackScope.hasNextElement(); ++ArgIdx) { - TemplateArgument Pi = Pattern, Ai = As[ArgIdx]; - if (PackFold == PackFold::ArgumentToParameter) - std::swap(Pi, Ai); + if (!FoldPackParameter && !As[ArgIdx].isPackExpansion()) + return TemplateDeductionResult::MiscellaneousDeductionFailure; // Deduce template arguments from the pattern. - if (auto Result = DeduceTemplateArguments(S, TemplateParams, Pi, Ai, Info, - PartialOrdering, Deduced, - HasDeducedAnyParam); + if (auto Result = DeduceTemplateArguments( + S, TemplateParams, Pattern, As[ArgIdx], Info, PartialOrdering, + Deduced, HasDeducedAnyParam); Result != TemplateDeductionResult::Success) return Result; @@ -2718,12 +2781,8 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, // Build argument packs for each of the parameter packs expanded by this // pack expansion. - if (auto Result = PackScope.finish(); - Result != TemplateDeductionResult::Success) - return Result; + return PackScope.finish(); } - - return TemplateDeductionResult::Success; } TemplateDeductionResult Sema::DeduceTemplateArguments( @@ -3302,7 +3361,6 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // Unevaluated SFINAE context. EnterExpressionEvaluationContext Unevaluated( S, Sema::ExpressionEvaluationContext::Unevaluated); - Sema::SFINAETrap Trap(S); Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(Template)); @@ -3319,20 +3377,41 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( return Result; // Check that we produced the correct argument list. - TemplateParameterList *TemplateParams = Template->getTemplateParameters(); - for (unsigned I = 0, E = TemplateParams->size(); I != E; ++I) { - TemplateArgument InstArg = CanonicalBuilder[I]; - if (!isSameTemplateArg(S.Context, TemplateArgs[I], InstArg, PartialOrdering, - /*PackExpansionMatchesPack=*/true)) { - Info.Param = makeTemplateParameter(TemplateParams->getParam(I)); - Info.FirstArg = TemplateArgs[I]; - Info.SecondArg = InstArg; - return TemplateDeductionResult::NonDeducedMismatch; + for (ArrayRef Ps = TemplateArgs, As = CanonicalBuilder; + !Ps.empty() && !As.empty(); + /**/) { + TemplateArgument P = Ps.front(), A = As.front(); + if (P.getKind() == TemplateArgument::Pack) { + assert(Ps.size() == 1 && "Pack not last element?"); + Ps = P.getPackAsArray(); + continue; + } + if (A.getKind() == TemplateArgument::Pack) { + assert(As.size() == 1 && "Pack not last element?"); + As = A.getPackAsArray(); + continue; } - } - if (Trap.hasErrorOccurred()) - return TemplateDeductionResult::SubstitutionFailure; + if (P.isPackExpansion()) + P = P.getPackExpansionPattern(); + else + Ps = Ps.drop_front(); + if (A.isPackExpansion()) + A = A.getPackExpansionPattern(); + else + As = As.drop_front(); + + if (isSameTemplateArg(S.Context, P, A, PartialOrdering)) + continue; + unsigned I = As.end() == CanonicalBuilder.end() + ? As.begin() - CanonicalBuilder.begin() + : CanonicalBuilder.size() - 1; + Info.Param = + makeTemplateParameter(Template->getTemplateParameters()->getParam(I)); + Info.FirstArg = P; + Info.SecondArg = A; + return TemplateDeductionResult::NonDeducedMismatch; + } if (!PartialOrdering) { if (auto Result = CheckDeducedArgumentConstraints( @@ -3354,7 +3433,6 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // Unevaluated SFINAE context. EnterExpressionEvaluationContext Unevaluated( S, Sema::ExpressionEvaluationContext::Unevaluated); - Sema::SFINAETrap Trap(S); Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(TD)); @@ -3363,20 +3441,13 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // explicitly specified, template argument deduction fails. SmallVector SugaredBuilder, CanonicalBuilder; if (auto Result = ConvertDeducedTemplateArguments( - S, TD, /*IsPartialOrdering=*/false, Deduced, Info, SugaredBuilder, + S, TD, /*IsDeduced=*/false, Deduced, Info, SugaredBuilder, CanonicalBuilder); Result != TemplateDeductionResult::Success) return Result; - if (Trap.hasErrorOccurred()) - return TemplateDeductionResult::SubstitutionFailure; - - if (auto Result = CheckDeducedArgumentConstraints(S, TD, SugaredBuilder, - CanonicalBuilder, Info); - Result != TemplateDeductionResult::Success) - return Result; - - return TemplateDeductionResult::Success; + return ::CheckDeducedArgumentConstraints(S, TD, SugaredBuilder, + CanonicalBuilder, Info); } /// Perform template argument deduction to determine whether the given template @@ -3423,16 +3494,20 @@ DeduceTemplateArguments(Sema &S, T *Partial, if (Inst.isInvalid()) return TemplateDeductionResult::InstantiationDepth; - if (Trap.hasErrorOccurred()) - return TemplateDeductionResult::SubstitutionFailure; - TemplateDeductionResult Result; S.runWithSufficientStackSpace(Info.getLocation(), [&] { Result = ::FinishTemplateArgumentDeduction(S, Partial, /*IsPartialOrdering=*/false, TemplateArgs, Deduced, Info); }); - return Result; + + if (Result != TemplateDeductionResult::Success) + return Result; + + if (Trap.hasErrorOccurred()) + return TemplateDeductionResult::SubstitutionFailure; + + return TemplateDeductionResult::Success; } TemplateDeductionResult @@ -3488,14 +3563,18 @@ Sema::DeduceTemplateArgumentsFromType(TemplateDecl *TD, QualType FromType, if (Inst.isInvalid()) return TemplateDeductionResult::InstantiationDepth; - if (Trap.hasErrorOccurred()) - return TemplateDeductionResult::SubstitutionFailure; - TemplateDeductionResult Result; runWithSufficientStackSpace(Info.getLocation(), [&] { Result = ::FinishTemplateArgumentDeduction(*this, TD, Deduced, Info); }); - return Result; + + if (Result != TemplateDeductionResult::Success) + return Result; + + if (Trap.hasErrorOccurred()) + return TemplateDeductionResult::SubstitutionFailure; + + return TemplateDeductionResult::Success; } /// Determine whether the given type T is a simple-template-id type. @@ -6136,14 +6215,23 @@ static bool isAtLeastAsSpecializedAs(Sema &S, QualType T1, QualType T2, return false; const auto *TST1 = cast(T1); - bool AtLeastAsSpecialized; + + Sema::SFINAETrap Trap(S); + + TemplateDeductionResult Result; S.runWithSufficientStackSpace(Info.getLocation(), [&] { - AtLeastAsSpecialized = - FinishTemplateArgumentDeduction( - S, P2, /*IsPartialOrdering=*/true, TST1->template_arguments(), - Deduced, Info) == TemplateDeductionResult::Success; + Result = ::FinishTemplateArgumentDeduction( + S, P2, /*IsPartialOrdering=*/true, TST1->template_arguments(), Deduced, + Info); }); - return AtLeastAsSpecialized; + + if (Result != TemplateDeductionResult::Success) + return false; + + if (Trap.hasErrorOccurred()) + return false; + + return true; } namespace { @@ -6381,8 +6469,9 @@ bool Sema::isMoreSpecializedThanPrimary( } bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( - TemplateParameterList *P, TemplateDecl *AArg, - const DefaultArguments &DefaultArgs, SourceLocation Loc, bool IsDeduced) { + TemplateParameterList *P, TemplateDecl *PArg, TemplateDecl *AArg, + const DefaultArguments &DefaultArgs, SourceLocation ArgLoc, + bool IsDeduced) { // C++1z [temp.arg.template]p4: (DR 150) // A template template-parameter P is at least as specialized as a // template template-argument A if, given the following rewrite to two @@ -6394,6 +6483,12 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // TemplateParameterList *A = AArg->getTemplateParameters(); + Sema::InstantiatingTemplate Inst( + *this, ArgLoc, Sema::InstantiatingTemplate::PartialOrderingTTP(), PArg, + SourceRange(P->getTemplateLoc(), P->getRAngleLoc())); + if (Inst.isInvalid()) + return false; + // Given an invented class template X with the template parameter list of // A (including default arguments): // - Each function template has a single function parameter whose type is @@ -6408,8 +6503,6 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // templates. SmallVector PArgs; { - SFINAETrap Trap(*this); - Context.getInjectedTemplateArgs(P, PArgs); TemplateArgumentListInfo PArgList(P->getLAngleLoc(), P->getRAngleLoc()); @@ -6429,18 +6522,17 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // C++1z [temp.arg.template]p3: // If the rewrite produces an invalid type, then P is not at least as // specialized as A. - SmallVector SugaredPArgs; - if (CheckTemplateArgumentList(AArg, Loc, PArgList, DefaultArgs, false, - SugaredPArgs, PArgs, + SmallVector CanonicalPArgs; + if (CheckTemplateArgumentList(AArg, ArgLoc, PArgList, DefaultArgs, false, + PArgs, CanonicalPArgs, /*UpdateArgsWithConversions=*/true, /*ConstraintsNotSatisfied=*/nullptr, - /*PartialOrderTTP=*/true) || - Trap.hasErrorOccurred()) + /*PartialOrderingTTP=*/true)) return false; } // Determine whether P1 is at least as specialized as P2. - TemplateDeductionInfo Info(Loc, A->getDepth()); + TemplateDeductionInfo Info(ArgLoc, A->getDepth()); SmallVector Deduced; Deduced.resize(A->size()); @@ -6455,29 +6547,89 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // be inverted between Ps and As. On non-deduced context, matching needs to // happen both ways, according to [temp.arg.template]p3, but this is // currently implemented as a special case elsewhere. - if (::DeduceTemplateArguments(*this, A, AArgs, PArgs, Info, Deduced, - /*NumberOfArgumentsMustMatch=*/false, - /*PartialOrdering=*/true, - IsDeduced ? PackFold::ArgumentToParameter - : PackFold::ParameterToArgument, - /*HasDeducedAnyParam=*/nullptr) != - TemplateDeductionResult::Success) + switch (::DeduceTemplateArguments( + *this, A, AArgs, PArgs, Info, Deduced, + /*NumberOfArgumentsMustMatch=*/false, /*PartialOrdering=*/true, + IsDeduced ? PackFold::ArgumentToParameter : PackFold::Both, + /*HasDeducedAnyParam=*/nullptr)) { + case clang::TemplateDeductionResult::Success: + break; + + case TemplateDeductionResult::MiscellaneousDeductionFailure: + Diag(AArg->getLocation(), diag::err_template_param_list_different_arity) + << (A->size() > P->size()) << /*isTemplateTemplateParameter=*/true + << SourceRange(A->getTemplateLoc(), P->getRAngleLoc()); return false; + case TemplateDeductionResult::NonDeducedMismatch: + Diag(AArg->getLocation(), diag::err_non_deduced_mismatch) + << Info.FirstArg << Info.SecondArg; + return false; + case TemplateDeductionResult::Inconsistent: + Diag(getAsNamedDecl(Info.Param)->getLocation(), + diag::err_inconsistent_deduction) + << Info.FirstArg << Info.SecondArg; + return false; + case TemplateDeductionResult::AlreadyDiagnosed: + return false; + + // None of these should happen for a plain deduction. + case TemplateDeductionResult::Invalid: + case TemplateDeductionResult::InstantiationDepth: + case TemplateDeductionResult::Incomplete: + case TemplateDeductionResult::IncompletePack: + case TemplateDeductionResult::Underqualified: + case TemplateDeductionResult::SubstitutionFailure: + case TemplateDeductionResult::DeducedMismatch: + case TemplateDeductionResult::DeducedMismatchNested: + case TemplateDeductionResult::TooManyArguments: + case TemplateDeductionResult::TooFewArguments: + case TemplateDeductionResult::InvalidExplicitArguments: + case TemplateDeductionResult::NonDependentConversionFailure: + case TemplateDeductionResult::ConstraintsNotSatisfied: + case TemplateDeductionResult::CUDATargetMismatch: + llvm_unreachable("Unexpected Result"); + } SmallVector DeducedArgs(Deduced.begin(), Deduced.end()); - Sema::InstantiatingTemplate Inst(*this, Info.getLocation(), AArg, DeducedArgs, - Info); - if (Inst.isInvalid()) - return false; - bool AtLeastAsSpecialized; + TemplateDeductionResult TDK; runWithSufficientStackSpace(Info.getLocation(), [&] { - AtLeastAsSpecialized = - ::FinishTemplateArgumentDeduction( - *this, AArg, /*IsPartialOrdering=*/true, PArgs, Deduced, Info) == - TemplateDeductionResult::Success; + TDK = ::FinishTemplateArgumentDeduction( + *this, AArg, /*IsPartialOrdering=*/true, PArgs, Deduced, Info); }); - return AtLeastAsSpecialized; + switch (TDK) { + case TemplateDeductionResult::Success: + return true; + + // It doesn't seem possible to get a non-deduced mismatch when partial + // ordering TTPs. + case TemplateDeductionResult::NonDeducedMismatch: + llvm_unreachable("Unexpected NonDeducedMismatch"); + + // Substitution failures should have already been diagnosed. + case TemplateDeductionResult::AlreadyDiagnosed: + case TemplateDeductionResult::SubstitutionFailure: + case TemplateDeductionResult::InstantiationDepth: + return false; + + // None of these should happen when just converting deduced arguments. + case TemplateDeductionResult::Invalid: + case TemplateDeductionResult::Incomplete: + case TemplateDeductionResult::IncompletePack: + case TemplateDeductionResult::Inconsistent: + case TemplateDeductionResult::Underqualified: + case TemplateDeductionResult::DeducedMismatch: + case TemplateDeductionResult::DeducedMismatchNested: + case TemplateDeductionResult::TooManyArguments: + case TemplateDeductionResult::TooFewArguments: + case TemplateDeductionResult::InvalidExplicitArguments: + case TemplateDeductionResult::NonDependentConversionFailure: + case TemplateDeductionResult::ConstraintsNotSatisfied: + case TemplateDeductionResult::MiscellaneousDeductionFailure: + case TemplateDeductionResult::CUDATargetMismatch: + llvm_unreachable("Unexpected Result"); + } + llvm_unreachable("Unexpected TDK"); } namespace { diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index b36381422851f..898255ff7c6a3 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -572,6 +572,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const { case LambdaExpressionSubstitution: case BuildingDeductionGuides: case TypeAliasTemplateInstantiation: + case PartialOrderingTTP: return false; // This function should never be called when Kind's value is Memoization. @@ -804,6 +805,11 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( SemaRef, CodeSynthesisContext::BuildingDeductionGuides, PointOfInstantiation, InstantiationRange, Entity) {} +Sema::InstantiatingTemplate::InstantiatingTemplate( + Sema &SemaRef, SourceLocation ArgLoc, PartialOrderingTTP, + TemplateDecl *PArg, SourceRange InstantiationRange) + : InstantiatingTemplate(SemaRef, CodeSynthesisContext::PartialOrderingTTP, + ArgLoc, InstantiationRange, PArg) {} void Sema::pushCodeSynthesisContext(CodeSynthesisContext Ctx) { Ctx.SavedInNonInstantiationSFINAEContext = InNonInstantiationSFINAEContext; @@ -1243,6 +1249,14 @@ void Sema::PrintInstantiationStack() { << cast(Active->Entity) << Active->InstantiationRange; break; + case CodeSynthesisContext::PartialOrderingTTP: + Diags.Report(Active->PointOfInstantiation, + diag::note_template_arg_template_params_mismatch); + if (SourceLocation ParamLoc = Active->Entity->getLocation(); + ParamLoc.isValid()) + Diags.Report(ParamLoc, diag::note_template_prev_declaration) + << /*isTemplateTemplateParam=*/true << Active->InstantiationRange; + break; } } } @@ -1285,6 +1299,7 @@ std::optional Sema::isSFINAEContext() const { case CodeSynthesisContext::PriorTemplateArgumentSubstitution: case CodeSynthesisContext::DefaultTemplateArgumentChecking: case CodeSynthesisContext::RewritingOperatorAsSpaceship: + case CodeSynthesisContext::PartialOrderingTTP: // A default template argument instantiation and substitution into // template parameters with arguments for prior parameters may or may // not be a SFINAE context; look further up the stack. diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 6fdb18d51acef..76efc9fe83185 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -4090,6 +4090,10 @@ class TreeTransform { OpenACCDirectiveKind::Loop, BeginLoc, DirLoc, EndLoc, Clauses, Loop); } + ExprResult RebuildOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc) { + return getSema().OpenACC().ActOnOpenACCAsteriskSizeExpr(AsteriskLoc); + } + private: TypeLoc TransformTypeInObjectScope(TypeLoc TL, QualType ObjectType, @@ -8200,6 +8204,11 @@ TreeTransform::TransformWhileStmt(WhileStmt *S) { if (Cond.isInvalid()) return StmtError(); + // OpenACC Restricts a while-loop inside of certain construct/clause + // combinations, so diagnose that here in OpenACC mode. + SemaOpenACC::LoopInConstructRAII LCR{SemaRef.OpenACC()}; + SemaRef.OpenACC().ActOnWhileStmt(S->getBeginLoc()); + // Transform the body StmtResult Body = getDerived().TransformStmt(S->getBody()); if (Body.isInvalid()) @@ -8217,6 +8226,11 @@ TreeTransform::TransformWhileStmt(WhileStmt *S) { template StmtResult TreeTransform::TransformDoStmt(DoStmt *S) { + // OpenACC Restricts a do-loop inside of certain construct/clause + // combinations, so diagnose that here in OpenACC mode. + SemaOpenACC::LoopInConstructRAII LCR{SemaRef.OpenACC()}; + SemaRef.OpenACC().ActOnDoStmt(S->getBeginLoc()); + // Transform the body StmtResult Body = getDerived().TransformStmt(S->getBody()); if (Body.isInvalid()) @@ -8270,11 +8284,18 @@ TreeTransform::TransformForStmt(ForStmt *S) { if (S->getInc() && !FullInc.get()) return StmtError(); + // OpenACC Restricts a for-loop inside of certain construct/clause + // combinations, so diagnose that here in OpenACC mode. + SemaOpenACC::LoopInConstructRAII LCR{SemaRef.OpenACC()}; + SemaRef.OpenACC().ActOnForStmtBegin(S->getBeginLoc()); + // Transform the body StmtResult Body = getDerived().TransformStmt(S->getBody()); if (Body.isInvalid()) return StmtError(); + SemaRef.OpenACC().ActOnForStmtEnd(S->getBeginLoc(), Body); + if (!getDerived().AlwaysRebuild() && Init.get() == S->getInit() && Cond.get() == std::make_pair(S->getConditionVariable(), S->getCond()) && @@ -9008,10 +9029,17 @@ TreeTransform::TransformCXXForRangeStmt(CXXForRangeStmt *S) { } } + // OpenACC Restricts a while-loop inside of certain construct/clause + // combinations, so diagnose that here in OpenACC mode. + SemaOpenACC::LoopInConstructRAII LCR{SemaRef.OpenACC()}; + SemaRef.OpenACC().ActOnForStmtBegin(S->getBeginLoc()); + StmtResult Body = getDerived().TransformStmt(S->getBody()); if (Body.isInvalid()) return StmtError(); + SemaRef.OpenACC().ActOnForStmtEnd(S->getBeginLoc(), Body); + // Body has changed but we didn't rebuild the for-range statement. Rebuild // it now so we have a new statement to attach the body to. if (Body.get() != S->getBody() && NewStmt.get() == S) { @@ -11847,6 +11875,36 @@ void OpenACCClauseTransform::VisitCollapseClause( ParsedClause.getLParenLoc(), ParsedClause.isForce(), ParsedClause.getLoopCount(), ParsedClause.getEndLoc()); } + +template +void OpenACCClauseTransform::VisitTileClause( + const OpenACCTileClause &C) { + + llvm::SmallVector TransformedExprs; + + for (Expr *E : C.getSizeExprs()) { + ExprResult NewSizeExpr = Self.TransformExpr(E); + + if (!NewSizeExpr.isUsable()) + return; + + NewSizeExpr = Self.getSema().OpenACC().ActOnIntExpr( + OpenACCDirectiveKind::Invalid, ParsedClause.getClauseKind(), + NewSizeExpr.get()->getBeginLoc(), NewSizeExpr.get()); + + NewSizeExpr = Self.getSema().OpenACC().CheckTileSizeExpr(NewSizeExpr.get()); + + if (!NewSizeExpr.isUsable()) + return; + TransformedExprs.push_back(NewSizeExpr.get()); + } + + ParsedClause.setIntExprDetails(TransformedExprs); + NewClause = OpenACCTileClause::Create( + Self.getSema().getASTContext(), ParsedClause.getBeginLoc(), + ParsedClause.getLParenLoc(), ParsedClause.getIntExprs(), + ParsedClause.getEndLoc()); +} } // namespace template OpenACCClause *TreeTransform::TransformOpenACCClause( @@ -11894,8 +11952,9 @@ StmtResult TreeTransform::TransformOpenACCComputeConstruct( return StmtError(); // Transform Structured Block. - SemaOpenACC::AssociatedStmtRAII AssocStmtRAII(getSema().OpenACC(), - C->getDirectiveKind()); + SemaOpenACC::AssociatedStmtRAII AssocStmtRAII( + getSema().OpenACC(), C->getDirectiveKind(), C->clauses(), + TransformedClauses); StmtResult StrBlock = getDerived().TransformStmt(C->getStructuredBlock()); StrBlock = getSema().OpenACC().ActOnAssociatedStmt( C->getBeginLoc(), C->getDirectiveKind(), StrBlock); @@ -11920,8 +11979,9 @@ TreeTransform::TransformOpenACCLoopConstruct(OpenACCLoopConstruct *C) { return StmtError(); // Transform Loop. - SemaOpenACC::AssociatedStmtRAII AssocStmtRAII(getSema().OpenACC(), - C->getDirectiveKind()); + SemaOpenACC::AssociatedStmtRAII AssocStmtRAII( + getSema().OpenACC(), C->getDirectiveKind(), C->clauses(), + TransformedClauses); StmtResult Loop = getDerived().TransformStmt(C->getLoop()); Loop = getSema().OpenACC().ActOnAssociatedStmt(C->getBeginLoc(), C->getDirectiveKind(), Loop); @@ -11931,6 +11991,15 @@ TreeTransform::TransformOpenACCLoopConstruct(OpenACCLoopConstruct *C) { TransformedClauses, Loop); } +template +ExprResult TreeTransform::TransformOpenACCAsteriskSizeExpr( + OpenACCAsteriskSizeExpr *E) { + if (getDerived().AlwaysRebuild()) + return getDerived().RebuildOpenACCAsteriskSizeExpr(E->getLocation()); + // Nothing can ever change, so there is never anything to transform. + return E; +} + //===----------------------------------------------------------------------===// // Expression transformation //===----------------------------------------------------------------------===// diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 0a4251c0e5240..97b79bd1381c0 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -3912,6 +3912,11 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F, FPPragmaOptions.swap(Record); break; + case DECLS_WITH_EFFECTS_TO_VERIFY: + for (unsigned I = 0, N = Record.size(); I != N; /*in loop*/) + DeclsWithEffectsToVerify.push_back(ReadDeclID(F, Record, I)); + break; + case OPENCL_EXTENSIONS: for (unsigned I = 0, E = Record.size(); I != E; ) { auto Name = ReadString(Record, I); @@ -8413,6 +8418,17 @@ void ASTReader::InitializeSema(Sema &S) { NewOverrides.applyOverrides(SemaObj->getLangOpts()); } + for (GlobalDeclID ID : DeclsWithEffectsToVerify) { + Decl *D = GetDecl(ID); + if (auto *FD = dyn_cast(D)) + SemaObj->addDeclWithEffects(FD, FD->getFunctionEffects()); + else if (auto *BD = dyn_cast(D)) + SemaObj->addDeclWithEffects(BD, BD->getFunctionEffects()); + else + llvm_unreachable("unexpected Decl type in DeclsWithEffectsToVerify"); + } + DeclsWithEffectsToVerify.clear(); + SemaObj->OpenCLFeatures = OpenCLExtensions; UpdateSema(); @@ -12290,6 +12306,15 @@ OpenACCClause *ASTRecordReader::readOpenACCClause() { return OpenACCCollapseClause::Create(getContext(), BeginLoc, LParenLoc, HasForce, LoopCount, EndLoc); } + case OpenACCClauseKind::Tile: { + SourceLocation LParenLoc = readSourceLocation(); + unsigned NumClauses = readInt(); + llvm::SmallVector SizeExprs; + for (unsigned I = 0; I < NumClauses; ++I) + SizeExprs.push_back(readSubExpr()); + return OpenACCTileClause::Create(getContext(), BeginLoc, LParenLoc, + SizeExprs, EndLoc); + } case OpenACCClauseKind::Finalize: case OpenACCClauseKind::IfPresent: @@ -12306,7 +12331,6 @@ OpenACCClause *ASTRecordReader::readOpenACCClause() { case OpenACCClauseKind::Bind: case OpenACCClauseKind::DeviceNum: case OpenACCClauseKind::DefaultAsync: - case OpenACCClauseKind::Tile: case OpenACCClauseKind::Gang: case OpenACCClauseKind::Invalid: llvm_unreachable("Clause serialization not yet implemented"); diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 84743a52d4c8b..2038fe7829af9 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -575,6 +575,11 @@ void ASTStmtReader::VisitConstantExpr(ConstantExpr *E) { E->setSubExpr(Record.readSubExpr()); } +void ASTStmtReader::VisitOpenACCAsteriskSizeExpr(OpenACCAsteriskSizeExpr *E) { + VisitExpr(E); + E->setAsteriskLocation(readSourceLocation()); +} + void ASTStmtReader::VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E) { VisitExpr(E); @@ -3054,6 +3059,10 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { S = SYCLUniqueStableNameExpr::CreateEmpty(Context); break; + case EXPR_OPENACC_ASTERISK_SIZE: + S = OpenACCAsteriskSizeExpr::CreateEmpty(Context); + break; + case EXPR_PREDEFINED: S = PredefinedExpr::CreateEmpty( Context, diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index aa9764e25c323..836532ca402ff 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -4642,6 +4642,17 @@ void ASTWriter::WriteFloatControlPragmaOptions(Sema &SemaRef) { Stream.EmitRecord(FLOAT_CONTROL_PRAGMA_OPTIONS, Record); } +/// Write Sema's collected list of declarations with unverified effects. +void ASTWriter::WriteDeclsWithEffectsToVerify(Sema &SemaRef) { + if (SemaRef.DeclsWithEffectsToVerify.empty()) + return; + RecordData Record; + for (const auto *D : SemaRef.DeclsWithEffectsToVerify) { + AddDeclRef(D, Record); + } + Stream.EmitRecord(DECLS_WITH_EFFECTS_TO_VERIFY, Record); +} + void ASTWriter::WriteModuleFileExtension(Sema &SemaRef, ModuleFileExtensionWriter &Writer) { // Enter the extension block. @@ -5599,6 +5610,7 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, } WritePackPragmaOptions(SemaRef); WriteFloatControlPragmaOptions(SemaRef); + WriteDeclsWithEffectsToVerify(SemaRef); // Some simple statistics RecordData::value_type Record[] = { @@ -8155,6 +8167,14 @@ void ASTRecordWriter::writeOpenACCClause(const OpenACCClause *C) { AddStmt(const_cast(CC->getLoopCount())); return; } + case OpenACCClauseKind::Tile: { + const auto *TC = cast(C); + writeSourceLocation(TC->getLParenLoc()); + writeUInt32(TC->getSizeExprs().size()); + for (Expr *E : TC->getSizeExprs()) + AddStmt(E); + return; + } case OpenACCClauseKind::Finalize: case OpenACCClauseKind::IfPresent: @@ -8171,7 +8191,6 @@ void ASTRecordWriter::writeOpenACCClause(const OpenACCClause *C) { case OpenACCClauseKind::Bind: case OpenACCClauseKind::DeviceNum: case OpenACCClauseKind::DefaultAsync: - case OpenACCClauseKind::Tile: case OpenACCClauseKind::Gang: case OpenACCClauseKind::Invalid: llvm_unreachable("Clause serialization not yet implemented"); diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 837136600181c..64e4894dc29fb 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -643,6 +643,12 @@ void ASTStmtWriter::VisitConstantExpr(ConstantExpr *E) { Code = serialization::EXPR_CONSTANT; } +void ASTStmtWriter::VisitOpenACCAsteriskSizeExpr(OpenACCAsteriskSizeExpr *E) { + VisitExpr(E); + Record.AddSourceLocation(E->getLocation()); + Code = serialization::EXPR_OPENACC_ASTERISK_SIZE; +} + void ASTStmtWriter::VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E) { VisitExpr(E); diff --git a/clang/lib/Serialization/GlobalModuleIndex.cpp b/clang/lib/Serialization/GlobalModuleIndex.cpp index 1163943c5dffa..9c48712a0b3fb 100644 --- a/clang/lib/Serialization/GlobalModuleIndex.cpp +++ b/clang/lib/Serialization/GlobalModuleIndex.cpp @@ -430,14 +430,13 @@ namespace { /// Retrieve the module file information for the given file. ModuleFileInfo &getModuleFileInfo(FileEntryRef File) { - auto Known = ModuleFiles.find(File); - if (Known != ModuleFiles.end()) - return Known->second; - - unsigned NewID = ModuleFiles.size(); - ModuleFileInfo &Info = ModuleFiles[File]; - Info.ID = NewID; - return Info; + auto [It, Inserted] = ModuleFiles.try_emplace(File); + if (Inserted) { + unsigned NewID = ModuleFiles.size(); + ModuleFileInfo &Info = It->second; + Info.ID = NewID; + } + return It->second; } public: diff --git a/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp index b198b1c2ff4d1..69d8e968283b3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp @@ -16,21 +16,93 @@ #include "clang/Basic/Builtins.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Checkers/Taint.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" using namespace clang; using namespace ento; +using namespace taint; namespace { +QualType getSufficientTypeForOverflowOp(CheckerContext &C, const QualType &T) { + // Calling a builtin with a non-integer type result produces compiler error. + assert(T->isIntegerType()); + + ASTContext &ACtx = C.getASTContext(); + + unsigned BitWidth = ACtx.getIntWidth(T); + return ACtx.getIntTypeForBitwidth(BitWidth * 2, T->isSignedIntegerType()); +} + +QualType getOverflowBuiltinResultType(const CallEvent &Call) { + // Calling a builtin with an incorrect argument count produces compiler error. + assert(Call.getNumArgs() == 3); + + return Call.getArgExpr(2)->getType()->getPointeeType(); +} + +QualType getOverflowBuiltinResultType(const CallEvent &Call, CheckerContext &C, + unsigned BI) { + // Calling a builtin with an incorrect argument count produces compiler error. + assert(Call.getNumArgs() == 3); + + ASTContext &ACtx = C.getASTContext(); + + switch (BI) { + case Builtin::BI__builtin_smul_overflow: + case Builtin::BI__builtin_ssub_overflow: + case Builtin::BI__builtin_sadd_overflow: + return ACtx.IntTy; + case Builtin::BI__builtin_smull_overflow: + case Builtin::BI__builtin_ssubl_overflow: + case Builtin::BI__builtin_saddl_overflow: + return ACtx.LongTy; + case Builtin::BI__builtin_smulll_overflow: + case Builtin::BI__builtin_ssubll_overflow: + case Builtin::BI__builtin_saddll_overflow: + return ACtx.LongLongTy; + case Builtin::BI__builtin_umul_overflow: + case Builtin::BI__builtin_usub_overflow: + case Builtin::BI__builtin_uadd_overflow: + return ACtx.UnsignedIntTy; + case Builtin::BI__builtin_umull_overflow: + case Builtin::BI__builtin_usubl_overflow: + case Builtin::BI__builtin_uaddl_overflow: + return ACtx.UnsignedLongTy; + case Builtin::BI__builtin_umulll_overflow: + case Builtin::BI__builtin_usubll_overflow: + case Builtin::BI__builtin_uaddll_overflow: + return ACtx.UnsignedLongLongTy; + case Builtin::BI__builtin_mul_overflow: + case Builtin::BI__builtin_sub_overflow: + case Builtin::BI__builtin_add_overflow: + return getOverflowBuiltinResultType(Call); + default: + assert(false && "Unknown overflow builtin"); + return ACtx.IntTy; + } +} + class BuiltinFunctionChecker : public Checker { public: bool evalCall(const CallEvent &Call, CheckerContext &C) const; + void handleOverflowBuiltin(const CallEvent &Call, CheckerContext &C, + BinaryOperator::Opcode Op, + QualType ResultType) const; + const NoteTag *createBuiltinNoOverflowNoteTag(CheckerContext &C, + bool BothFeasible, SVal Arg1, + SVal Arg2, SVal Result) const; + const NoteTag *createBuiltinOverflowNoteTag(CheckerContext &C) const; + std::pair checkOverflow(CheckerContext &C, SVal RetVal, + QualType Res) const; private: // From: clang/include/clang/Basic/Builtins.def @@ -50,6 +122,102 @@ class BuiltinFunctionChecker : public Checker { } // namespace +const NoteTag *BuiltinFunctionChecker::createBuiltinNoOverflowNoteTag( + CheckerContext &C, bool BothFeasible, SVal Arg1, SVal Arg2, + SVal Result) const { + return C.getNoteTag([Result, Arg1, Arg2, BothFeasible]( + PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { + if (!BR.isInteresting(Result)) + return; + + // Propagate interestingness to input argumets if result is interesting. + BR.markInteresting(Arg1); + BR.markInteresting(Arg2); + + if (BothFeasible) + OS << "Assuming no overflow"; + }); +} + +const NoteTag * +BuiltinFunctionChecker::createBuiltinOverflowNoteTag(CheckerContext &C) const { + return C.getNoteTag([](PathSensitiveBugReport &BR, + llvm::raw_ostream &OS) { OS << "Assuming overflow"; }, + /*isPrunable=*/true); +} + +std::pair +BuiltinFunctionChecker::checkOverflow(CheckerContext &C, SVal RetVal, + QualType Res) const { + // Calling a builtin with a non-integer type result produces compiler error. + assert(Res->isIntegerType()); + + unsigned BitWidth = C.getASTContext().getIntWidth(Res); + bool IsUnsigned = Res->isUnsignedIntegerType(); + + auto MinValType = llvm::APSInt::getMinValue(BitWidth, IsUnsigned); + auto MaxValType = llvm::APSInt::getMaxValue(BitWidth, IsUnsigned); + nonloc::ConcreteInt MinVal{MinValType}; + nonloc::ConcreteInt MaxVal{MaxValType}; + + SValBuilder &SVB = C.getSValBuilder(); + ProgramStateRef State = C.getState(); + SVal IsLeMax = SVB.evalBinOp(State, BO_LE, RetVal, MaxVal, Res); + SVal IsGeMin = SVB.evalBinOp(State, BO_GE, RetVal, MinVal, Res); + + auto [MayNotOverflow, MayOverflow] = + State->assume(IsLeMax.castAs()); + auto [MayNotUnderflow, MayUnderflow] = + State->assume(IsGeMin.castAs()); + + return {MayOverflow || MayUnderflow, MayNotOverflow && MayNotUnderflow}; +} + +void BuiltinFunctionChecker::handleOverflowBuiltin(const CallEvent &Call, + CheckerContext &C, + BinaryOperator::Opcode Op, + QualType ResultType) const { + // Calling a builtin with an incorrect argument count produces compiler error. + assert(Call.getNumArgs() == 3); + + ProgramStateRef State = C.getState(); + SValBuilder &SVB = C.getSValBuilder(); + const Expr *CE = Call.getOriginExpr(); + + SVal Arg1 = Call.getArgSVal(0); + SVal Arg2 = Call.getArgSVal(1); + + SVal RetValMax = SVB.evalBinOp(State, Op, Arg1, Arg2, + getSufficientTypeForOverflowOp(C, ResultType)); + SVal RetVal = SVB.evalBinOp(State, Op, Arg1, Arg2, ResultType); + + auto [Overflow, NotOverflow] = checkOverflow(C, RetValMax, ResultType); + if (NotOverflow) { + ProgramStateRef StateNoOverflow = + State->BindExpr(CE, C.getLocationContext(), SVB.makeTruthVal(false)); + + if (auto L = Call.getArgSVal(2).getAs()) { + StateNoOverflow = + StateNoOverflow->bindLoc(*L, RetVal, C.getLocationContext()); + + // Propagate taint if any of the argumets were tainted + if (isTainted(State, Arg1) || isTainted(State, Arg2)) + StateNoOverflow = addTaint(StateNoOverflow, *L); + } + + C.addTransition( + StateNoOverflow, + createBuiltinNoOverflowNoteTag( + C, /*BothFeasible=*/NotOverflow && Overflow, Arg1, Arg2, RetVal)); + } + + if (Overflow) { + C.addTransition( + State->BindExpr(CE, C.getLocationContext(), SVB.makeTruthVal(true)), + createBuiltinOverflowNoteTag(C)); + } +} + bool BuiltinFunctionChecker::isBuiltinLikeFunction( const CallEvent &Call) const { const auto *FD = llvm::dyn_cast_or_null(Call.getDecl()); @@ -82,10 +250,41 @@ bool BuiltinFunctionChecker::evalCall(const CallEvent &Call, return true; } - switch (FD->getBuiltinID()) { + unsigned BI = FD->getBuiltinID(); + + switch (BI) { default: return false; - + case Builtin::BI__builtin_mul_overflow: + case Builtin::BI__builtin_smul_overflow: + case Builtin::BI__builtin_smull_overflow: + case Builtin::BI__builtin_smulll_overflow: + case Builtin::BI__builtin_umul_overflow: + case Builtin::BI__builtin_umull_overflow: + case Builtin::BI__builtin_umulll_overflow: + handleOverflowBuiltin(Call, C, BO_Mul, + getOverflowBuiltinResultType(Call, C, BI)); + return true; + case Builtin::BI__builtin_sub_overflow: + case Builtin::BI__builtin_ssub_overflow: + case Builtin::BI__builtin_ssubl_overflow: + case Builtin::BI__builtin_ssubll_overflow: + case Builtin::BI__builtin_usub_overflow: + case Builtin::BI__builtin_usubl_overflow: + case Builtin::BI__builtin_usubll_overflow: + handleOverflowBuiltin(Call, C, BO_Sub, + getOverflowBuiltinResultType(Call, C, BI)); + return true; + case Builtin::BI__builtin_add_overflow: + case Builtin::BI__builtin_sadd_overflow: + case Builtin::BI__builtin_saddl_overflow: + case Builtin::BI__builtin_saddll_overflow: + case Builtin::BI__builtin_uadd_overflow: + case Builtin::BI__builtin_uaddl_overflow: + case Builtin::BI__builtin_uaddll_overflow: + handleOverflowBuiltin(Call, C, BO_Add, + getOverflowBuiltinResultType(Call, C, BI)); + return true; case Builtin::BI__builtin_assume: case Builtin::BI__assume: { assert (Call.getNumArgs() > 0); diff --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp index 72fd6781a7561..beb3c8574b5fd 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp @@ -42,7 +42,6 @@ class ErrnoChecker ArrayRef ExplicitRegions, ArrayRef Regions, const LocationContext *LCtx, const CallEvent *Call) const; - void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const; /// Indicates if a read (load) of \c errno is allowed in a non-condition part /// of \c if, \c switch, loop and conditional statements when the errno diff --git a/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp index e7fde3edc7f9e..f7fd92db7757e 100644 --- a/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp @@ -43,6 +43,12 @@ void FixedAddressChecker::checkPreStmt(const BinaryOperator *B, if (!T->isPointerType()) return; + // Omit warning if the RHS has already pointer type. Without this passing + // around one fixed value in several pointer variables would produce several + // redundant warnings. + if (B->getRHS()->IgnoreParenCasts()->getType()->isPointerType()) + return; + SVal RV = C.getSVal(B->getRHS()); if (!RV.isConstant() || RV.isZeroConstant()) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index fdabba46992b0..43ab646d398b3 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1966,6 +1966,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::OMPArrayShapingExprClass: case Stmt::OMPIteratorExprClass: case Stmt::SYCLUniqueStableNameExprClass: + case Stmt::OpenACCAsteriskSizeExprClass: case Stmt::TypeTraitExprClass: { Bldr.takeNodes(Pred); ExplodedNodeSet preVisit; @@ -3810,7 +3811,9 @@ void ExprEngine::VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred, assert(!isa(X)); // Should be an Lval, or unknown, undef. if (std::optional LV = X.getAs()) - state = state->bindLoc(*LV, UnknownVal(), Pred->getLocationContext()); + state = state->invalidateRegions(*LV, A, currBldrCtx->blockCount(), + Pred->getLocationContext(), + /*CausedByPointerEscape=*/true); } // Do not reason about locations passed inside inline assembly. @@ -3818,7 +3821,9 @@ void ExprEngine::VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred, SVal X = state->getSVal(I, Pred->getLocationContext()); if (std::optional LV = X.getAs()) - state = state->bindLoc(*LV, UnknownVal(), Pred->getLocationContext()); + state = state->invalidateRegions(*LV, A, currBldrCtx->blockCount(), + Pred->getLocationContext(), + /*CausedByPointerEscape=*/true); } Bldr.generateNode(A, Pred, state); diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp index 677f426590ab9..77f9d07175c2c 100644 --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -549,7 +549,7 @@ void ModuleDepCollectorPP::EndOfMainFile() { auto It = MDC.ModularDeps.find(M); // Only report direct dependencies that were successfully handled. if (It != MDC.ModularDeps.end()) - MDC.Consumer.handleDirectModuleDependency(MDC.ModularDeps[M]->ID); + MDC.Consumer.handleDirectModuleDependency(It->second->ID); } for (auto &&I : MDC.FileDeps) diff --git a/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes index ef6e44c51c21c..f51811354eb00 100644 --- a/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes @@ -46,3 +46,5 @@ Tags: SwiftName: SuccessfullyRenamedA - Name: RenamedAgainInAPINotesB SwiftName: SuccessfullyRenamedB + - Name: AnonEnumWithTypedefName + SwiftName: SuccessfullyRenamedC diff --git a/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h index bd73926e9d6af..7342c3f83141b 100644 --- a/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h +++ b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h @@ -27,3 +27,7 @@ void *getCFAuditedToNone_DUMP(void); - (id)getOwnedToUnowned __attribute__((__ns_returns_retained__)); - (id)getUnownedToOwned __attribute__((__ns_returns_not_retained__)); @end + +typedef enum { + kConstantInAnonEnum +} AnonEnumWithTypedefName; diff --git a/clang/test/APINotes/types.m b/clang/test/APINotes/types.m index 133d504713d76..752f102643284 100644 --- a/clang/test/APINotes/types.m +++ b/clang/test/APINotes/types.m @@ -7,6 +7,9 @@ // CHECK: struct __attribute__((swift_name("SuccessfullyRenamedA"))) RenamedAgainInAPINotesA { // CHECK: struct __attribute__((swift_name("SuccessfullyRenamedB"))) RenamedAgainInAPINotesB { +// CHECK: typedef enum __attribute__((swift_name("SuccessfullyRenamedC"))) { +// CHECK-NEXT: kConstantInAnonEnum +// CHECK-NEXT: } AnonEnumWithTypedefName void test(OverriddenTypes *overridden) { int *ip1 = global_int_ptr; // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'double (*)(int, int)'}} diff --git a/clang/test/APINotes/yaml-roundtrip-2.test b/clang/test/APINotes/yaml-roundtrip-2.test index b0b777b595060..63717bda7c099 100644 --- a/clang/test/APINotes/yaml-roundtrip-2.test +++ b/clang/test/APINotes/yaml-roundtrip-2.test @@ -7,5 +7,5 @@ REQUIRES: shell We expect only the document markers to be emitted -CHECK: 50d +CHECK: 52d CHECK: 1d diff --git a/clang/test/AST/HLSL/ArrayAssignable.hlsl b/clang/test/AST/HLSL/ArrayAssignable.hlsl new file mode 100644 index 0000000000000..b32d478983edf --- /dev/null +++ b/clang/test/AST/HLSL/ArrayAssignable.hlsl @@ -0,0 +1,106 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -ast-dump %s | FileCheck %s + +// CHECK-LABEL: arr_assign1 +// CHECK: BinaryOperator 0x{{[0-9a-f]+}} {{.*}} 'int[2]' lvalue '=' +// CHECK: DeclRefExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2]' lvalue Var 0x{{[0-9a-f]+}} 'Arr' 'int[2]' +// CHECK: DeclRefExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2]' lvalue Var 0x{{[0-9a-f]+}} 'Arr2' 'int[2]' +void arr_assign1() { + int Arr[2] = {0, 1}; + int Arr2[2] = {0, 0}; + Arr = Arr2; +} + +// CHECK-LABEL: arr_assign2 +// CHECK: BinaryOperator 0x{{[0-9a-f]+}} {{.*}} 'int[2]' lvalue '=' +// CHECK: DeclRefExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2]' lvalue Var 0x{{[0-9a-f]+}} 'Arr' 'int[2]' +// CHECK: BinaryOperator 0x{{[0-9a-f]+}} {{.*}} 'int[2]' lvalue '=' +// CHECK: DeclRefExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2]' lvalue Var 0x{{[0-9a-f]+}} 'Arr2' 'int[2]' +// CHECK: DeclRefExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2]' lvalue Var 0x{{[0-9a-f]+}} 'Arr3' 'int[2]' +void arr_assign2() { + int Arr[2] = {0, 1}; + int Arr2[2] = {0, 0}; + int Arr3[2] = {2, 2}; + Arr = Arr2 = Arr3; +} + +// CHECK-LABEL: arr_assign3 +// CHECK: BinaryOperator 0x{{[0-9a-f]+}} {{.*}} 'int[2][2]' lvalue '=' +// CHECK: DeclRefExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2][2]' lvalue Var 0x{{[0-9a-f]+}} 'Arr' 'int[2][2]' +// CHECK: DeclRefExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2][2]' lvalue Var 0x{{[0-9a-f]+}} 'Arr2' 'int[2][2]' +void arr_assign3() { + int Arr[2][2] = {{0, 1}, {2, 3}}; + int Arr2[2][2] = {{0, 0}, {1, 1}}; + Arr = Arr2; +} + +// CHECK-LABEL: arr_assign4 +// CHECK: BinaryOperator 0x{{[0-9a-f]+}} {{.*}} 'int' lvalue '=' +// CHECK: ArraySubscriptExpr 0x{{[0-9a-f]+}} {{.*}} 'int' lvalue +// CHECK: ImplicitCastExpr 0x{{[0-9a-f]+}} {{.*}} 'int *' +// CHECK: ParenExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2]' lvalue +// CHECK: BinaryOperator 0x{{[0-9a-f]+}} {{.*}} 'int[2]' lvalue '=' +// CHECK: DeclRefExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2]' lvalue Var 0x{{[0-9a-f]+}} 'Arr' 'int[2]' +// CHECK: DeclRefExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2]' lvalue Var 0x{{[0-9a-f]+}} 'Arr2' 'int[2]' +// CHECK: IntegerLiteral 0x{{[0-9a-f]+}} {{.*}} 'int' 0 +// CHECK: IntegerLiteral 0x{{[0-9a-f]+}} {{.*}} 'int' 6 +void arr_assign4() { + int Arr[2] = {0, 1}; + int Arr2[2] = {0, 0}; + (Arr = Arr2)[0] = 6; +} + +// CHECK-LABEL: arr_assign5 +// CHECK: BinaryOperator 0x{{[0-9a-f]+}} {{.*}} 'int' lvalue '=' +// CHECK: ArraySubscriptExpr 0x{{[0-9a-f]+}} {{.*}} 'int' lvalue +// CHECK: ImplicitCastExpr 0x{{[0-9a-f]+}} {{.*}} 'int *' +// CHECK: ParenExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2]' lvalue +// CHECK: BinaryOperator 0x{{[0-9a-f]+}} {{.*}} 'int[2]' lvalue '=' +// CHECK: DeclRefExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2]' lvalue Var 0x{{[0-9a-f]+}} 'Arr' 'int[2]' +// CHECK: BinaryOperator 0x{{[0-9a-f]+}} {{.*}} 'int[2]' lvalue '=' +// CHECK: DeclRefExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2]' lvalue Var 0x{{[0-9a-f]+}} 'Arr2' 'int[2]' +// CHECK: DeclRefExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2]' lvalue Var 0x{{[0-9a-f]+}} 'Arr3' 'int[2]' +// CHECK: IntegerLiteral 0x{{[0-9a-f]+}} {{.*}} 'int' 0 +// CHECK: IntegerLiteral 0x{{[0-9a-f]+}} {{.*}} 'int' 6 +void arr_assign5() { + int Arr[2] = {0, 1}; + int Arr2[2] = {0, 0}; + int Arr3[2] = {3, 4}; + (Arr = Arr2 = Arr3)[0] = 6; +} + +// CHECK-LABEL: arr_assign6 +// CHECK: BinaryOperator 0x{{[0-9a-f]+}} {{.*}} 'int' lvalue '=' +// CHECK: ArraySubscriptExpr 0x{{[0-9a-f]+}} {{.*}} 'int' lvalue +// CHECK: ImplicitCastExpr 0x{{[0-9a-f]+}} {{.*}} 'int *' +// CHECK: ArraySubscriptExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2]' lvalue +// CHECK: ImplicitCastExpr 0x{{[0-9a-f]+}} {{.*}} 'int (*)[2]' +// CHECK: ParenExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2][2]' lvalue +// CHECK: BinaryOperator 0x{{[0-9a-f]+}} {{.*}} 'int[2][2]' lvalue '=' +// CHECK: DeclRefExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2][2]' lvalue Var 0x{{[0-9a-f]+}} 'Arr' 'int[2][2]' +// CHECK: DeclRefExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2][2]' lvalue Var 0x{{[0-9a-f]+}} 'Arr2' 'int[2][2]' +// CHECK: IntegerLiteral 0x{{[0-9a-f]+}} {{.*}} 'int' 0 +// CHECK: IntegerLiteral 0x{{[0-9a-f]+}} {{.*}} 'int' 0 +// CHECK: IntegerLiteral 0x{{[0-9a-f]+}} {{.*}} 'int' 6 +void arr_assign6() { + int Arr[2][2] = {{0, 1}, {2, 3}}; + int Arr2[2][2] = {{0, 0}, {1, 1}}; + (Arr = Arr2)[0][0] = 6; +} + +// CHECK-LABEL: arr_assign7 +// CHECK: BinaryOperator 0x{{[0-9a-f]+}} {{.*}} 'int[2]' lvalue '=' +// CHECK: ArraySubscriptExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2]' lvalue +// CHECK: ImplicitCastExpr 0x{{[0-9a-f]+}} {{.*}} 'int (*)[2]' +// CHECK: ParenExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2][2]' lvalue +// CHECK: BinaryOperator 0x{{[0-9a-f]+}} {{.*}} 'int[2][2]' lvalue '=' +// CHECK: DeclRefExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2][2]' lvalue Var 0x{{[0-9a-f]+}} 'Arr' 'int[2][2]' +// CHECK: DeclRefExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2][2]' lvalue Var 0x{{[0-9a-f]+}} 'Arr2' 'int[2][2]' +// CHECK: IntegerLiteral 0x{{[0-9a-f]+}} {{.*}} 'int' 0 +// CHECK: InitListExpr 0x{{[0-9a-f]+}} {{.*}} 'int[2]' +// CHECK: IntegerLiteral 0x{{[0-9a-f]+}} {{.*}} 'int' 6 +// CHECK: IntegerLiteral 0x{{[0-9a-f]+}} {{.*}} 'int' 6 +void arr_assign7() { + int Arr[2][2] = {{0, 1}, {2, 3}}; + int Arr2[2][2] = {{0, 0}, {1, 1}}; + (Arr = Arr2)[0] = {6, 6}; +} diff --git a/clang/test/AST/ast-print-openacc-loop-construct.cpp b/clang/test/AST/ast-print-openacc-loop-construct.cpp index ae1f7964f019e..aee4591cab428 100644 --- a/clang/test/AST/ast-print-openacc-loop-construct.cpp +++ b/clang/test/AST/ast-print-openacc-loop-construct.cpp @@ -2,6 +2,7 @@ struct SomeStruct{}; +constexpr int get_value() { return 1; } void foo() { // CHECK: #pragma acc loop // CHECK-NEXT: for (;;) @@ -82,4 +83,16 @@ void foo() { #pragma acc loop collapse(force:2) for(;;) for(;;); + +// CHECK: #pragma acc loop tile(1, 3, *, get_value()) +// CHECK-NEXT: for (;;) +// CHECK-NEXT: for (;;) +// CHECK-NEXT: for (;;) +// CHECK-NEXT: for (;;) +// CHECK-NEXT: ; +#pragma acc loop tile(1, 3, *, get_value()) + for(;;) + for(;;) + for(;;) + for(;;); } diff --git a/clang/test/AST/new-unknown-type.cpp b/clang/test/AST/new-unknown-type.cpp new file mode 100644 index 0000000000000..e7dd3c90145d2 --- /dev/null +++ b/clang/test/AST/new-unknown-type.cpp @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -verify -ast-dump %s | FileCheck %s + +extern void foo(Unknown*); // expected-error {{unknown type name 'Unknown'}} + +namespace a { + void computeSomething() { + foo(new Unknown()); // expected-error {{unknown type name 'Unknown'}} + foo(new Unknown{}); // expected-error {{unknown type name 'Unknown'}} + foo(new Unknown); // expected-error {{unknown type name 'Unknown'}} + } +} // namespace a + +namespace b { + struct Bar{}; +} // namespace b + +// CHECK: |-NamespaceDecl 0x{{[^ ]*}} line:5:11 a +// CHECK-NEXT: | `-FunctionDecl 0x{{[^ ]*}} line:6:8 computeSomething 'void ()' +// CHECK-NEXT: | `-CompoundStmt 0x{{[^ ]*}} +// CHECK-NEXT: |-NamespaceDecl 0x{{[^ ]*}} line:13:11 b +// CHECK-NEXT: | `-CXXRecordDecl 0x{{[^ ]*}} col:10 referenced struct Bar definition + +static b::Bar bar; +// CHECK: `-VarDecl 0x{{[^ ]*}} col:15 bar 'b::Bar' static callinit +// CHECK-NEXT: `-CXXConstructExpr 0x{{[^ ]*}} 'b::Bar' 'void () noexcept' diff --git a/clang/test/Analysis/asm.cpp b/clang/test/Analysis/asm.cpp index e0691dc4d794f..a795400ebbcaf 100644 --- a/clang/test/Analysis/asm.cpp +++ b/clang/test/Analysis/asm.cpp @@ -7,11 +7,10 @@ void clang_analyzer_dump_ptr(void *); int global; void testRValueOutput() { - int &ref = global; - ref = 1; + int origVal = global; __asm__("" : "=r"(((int)(global)))); // don't crash on rvalue output operand - clang_analyzer_eval(global == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(ref == 1); // expected-warning{{UNKNOWN}} + int newVal = global; // Value "after" the invalidation. + clang_analyzer_eval(origVal == newVal); // expected-warning{{TRUE}} expected-warning{{FALSE}} } void *MyMemcpy(void *d, const void *s, const int n) { @@ -40,7 +39,20 @@ void testInlineAsmMemcpyUninit(void) { int a[10], b[10] = {}, c; MyMemcpy(&a[1], &b[1], sizeof(b) - sizeof(b[1])); - c = a[0]; // expected-warning{{Assigned value is garbage or undefined}} + c = a[0]; // FIXME: should be warning about uninitialized value, but invalidateRegions() also + // invalidates super region. +} + +void testInlineAsmMemcpyUninitLoop(const void *src, unsigned long len) +{ + int a[10], c; + unsigned long toCopy = sizeof(a) < len ? sizeof(a) : len; + + MyMemcpy(a, src, toCopy); + + // Use index 1, since before use of invalidateRegions in VisitGCCAsmStmt, engine bound unknown SVal only to + // first element. + c = a[1]; // no-warning } void testAsmWithVoidPtrArgument() @@ -49,6 +61,6 @@ void testAsmWithVoidPtrArgument() clang_analyzer_dump(*(int *)globalVoidPtr); // expected-warning-re {{reg_${{[0-9]+}}},0 S64b,int}>}} clang_analyzer_dump_ptr(globalVoidPtr); // expected-warning-re {{&SymRegion{reg_${{[0-9]+}}}}} asm ("" : : "a"(globalVoidPtr)); // no crash - clang_analyzer_dump(*(int *)globalVoidPtr); // expected-warning {{Unknown}} + clang_analyzer_dump(*(int *)globalVoidPtr); // expected-warning {{derived_}} clang_analyzer_dump_ptr(globalVoidPtr); // expected-warning-re {{&SymRegion{reg_${{[0-9]+}}}}} } diff --git a/clang/test/Analysis/builtin_overflow.c b/clang/test/Analysis/builtin_overflow.c new file mode 100644 index 0000000000000..5c61795661d09 --- /dev/null +++ b/clang/test/Analysis/builtin_overflow.c @@ -0,0 +1,157 @@ +// RUN: %clang_analyze_cc1 -triple x86_64-unknown-unknown -verify %s \ +// RUN: -analyzer-checker=core,debug.ExprInspection + +#define __UINT_MAX__ (__INT_MAX__ * 2U + 1U) +#define __INT_MIN__ (-__INT_MAX__ - 1) + +void clang_analyzer_dump_int(int); +void clang_analyzer_dump_long(long); +void clang_analyzer_eval(int); +void clang_analyzer_warnIfReached(void); + +void test_add_nooverflow(void) +{ + int res; + + if (__builtin_add_overflow(10, 20, &res)) { + clang_analyzer_warnIfReached(); + return; + } + + clang_analyzer_dump_int(res); //expected-warning{{30 S32b}} +} + +void test_add_overflow(void) +{ + int res; + + if (__builtin_add_overflow(__INT_MAX__, 1, &res)) { + clang_analyzer_dump_int(res); //expected-warning{{1st function call argument is an uninitialized value}} + return; + } + + clang_analyzer_warnIfReached(); +} + +void test_add_underoverflow(void) +{ + int res; + + if (__builtin_add_overflow(__INT_MIN__, -1, &res)) { + clang_analyzer_dump_int(res); //expected-warning{{1st function call argument is an uninitialized value}} + return; + } + + clang_analyzer_warnIfReached(); +} + +void test_sub_underflow(void) +{ + int res; + + if (__builtin_sub_overflow(__INT_MIN__, 10, &res)) { + return; + } + + clang_analyzer_warnIfReached(); +} + +void test_sub_overflow(void) +{ + int res; + + if (__builtin_sub_overflow(__INT_MAX__, -1, &res)) { + return; + } + + clang_analyzer_warnIfReached(); +} + +void test_sub_nooverflow(void) +{ + int res; + + if (__builtin_sub_overflow(__INT_MAX__, 1, &res)) { + clang_analyzer_warnIfReached(); + return; + } + + clang_analyzer_dump_int(res); //expected-warning{{2147483646 S32b}} +} + +void test_mul_overflow(void) +{ + int res; + + if (__builtin_mul_overflow(__INT_MAX__, 2, &res)) { + return; + } + + clang_analyzer_warnIfReached(); +} + +void test_mul_underflow(void) +{ + int res; + + if (__builtin_mul_overflow(__INT_MIN__, -2, &res)) { + return; + } + + clang_analyzer_warnIfReached(); +} + +void test_mul_nooverflow(void) +{ + int res; + + if (__builtin_mul_overflow(10, -2, &res)) { + clang_analyzer_warnIfReached(); + return; + } + + clang_analyzer_dump_int(res); //expected-warning{{-20 S32b}} +} + +void test_nooverflow_diff_types(void) +{ + long res; + + // This is not an overflow, since result type is long. + if (__builtin_add_overflow(__INT_MAX__, 1, &res)) { + clang_analyzer_warnIfReached(); + return; + } + + clang_analyzer_dump_long(res); //expected-warning{{2147483648 S64b}} +} + +void test_uaddll_overflow_contraints(unsigned long a, unsigned long b) +{ + unsigned long long res; + + if (a != 10) + return; + if (b != 10) + return; + + if (__builtin_uaddll_overflow(a, b, &res)) { + clang_analyzer_warnIfReached(); + return; + } +} + +void test_uadd_overflow_contraints(unsigned a, unsigned b) +{ + unsigned res; + + if (a > 5) + return; + if (b != 10) + return; + + if (__builtin_uadd_overflow(a, b, &res)) { + clang_analyzer_warnIfReached(); + return; + } +} diff --git a/clang/test/Analysis/builtin_overflow_notes.c b/clang/test/Analysis/builtin_overflow_notes.c new file mode 100644 index 0000000000000..20f333a4a6cca --- /dev/null +++ b/clang/test/Analysis/builtin_overflow_notes.c @@ -0,0 +1,30 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-output text \ +// RUN: -verify %s + +void test_no_overflow_note(int a, int b) +{ + int res; + + if (__builtin_add_overflow(a, b, &res)) // expected-note {{Assuming no overflow}} + // expected-note@-1 {{Taking false branch}} + return; + + if (res) { // expected-note {{Assuming 'res' is not equal to 0}} + // expected-note@-1 {{Taking true branch}} + int *ptr = 0; // expected-note {{'ptr' initialized to a null pointer value}} + int var = *(int *) ptr; //expected-warning {{Dereference of null pointer}} + //expected-note@-1 {{Dereference of null pointer}} + } +} + +void test_overflow_note(int a, int b) +{ + int res; // expected-note{{'res' declared without an initial value}} + + if (__builtin_add_overflow(a, b, &res)) { // expected-note {{Assuming overflow}} + // expected-note@-1 {{Taking true branch}} + int var = res; // expected-warning{{Assigned value is garbage or undefined}} + // expected-note@-1 {{Assigned value is garbage or undefined}} + return; + } +} diff --git a/clang/test/Analysis/out-of-bounds-diagnostics.c b/clang/test/Analysis/out-of-bounds-diagnostics.c index 8ecad7036c331..65bc28f58276f 100644 --- a/clang/test/Analysis/out-of-bounds-diagnostics.c +++ b/clang/test/Analysis/out-of-bounds-diagnostics.c @@ -278,6 +278,21 @@ int *mallocRegion(void) { return mem; } +int *custom_calloc(size_t a, size_t b) { + size_t res; + + return __builtin_mul_overflow(a, b, &res) ? 0 : malloc(res); +} + +int *mallocRegionOverflow(void) { + int *mem = (int*)custom_calloc(10, sizeof(int)); + + mem[20] = 10; + // expected-warning@-1 {{Out of bound access to memory after the end of the heap area}} + // expected-note@-2 {{Access of the heap area at index 20, while it holds only 10 'int' elements}} + return mem; +} + int *mallocRegionDeref(void) { int *mem = (int*)malloc(2*sizeof(int)); diff --git a/clang/test/Analysis/ptr-arith.c b/clang/test/Analysis/ptr-arith.c index 020a500629230..b5ccd2c207ead 100644 --- a/clang/test/Analysis/ptr-arith.c +++ b/clang/test/Analysis/ptr-arith.c @@ -42,6 +42,10 @@ domain_port (const char *domain_b, const char *domain_e, void f4(void) { int *p; p = (int*) 0x10000; // expected-warning{{Using a fixed address is not portable because that address will probably not be valid in all environments or platforms}} + + int *p1; + p1 = p; // no warning + long x = 0x10100; x += 10; p = (int*) x; // expected-warning{{Using a fixed address is not portable because that address will probably not be valid in all environments or platforms}} diff --git a/clang/test/Analysis/taint-tester.c b/clang/test/Analysis/taint-tester.c index 479a96c92ecec..66d54eab19f39 100644 --- a/clang/test/Analysis/taint-tester.c +++ b/clang/test/Analysis/taint-tester.c @@ -196,3 +196,19 @@ void noCrashTest(void) { __builtin___memcpy_chk(pointer2, pointer1, 0, 0); // no-crash } } + +void builtin_overflow_test(void) { + int input, input2, res; + + scanf("%d", &input); + scanf("%d", &input2); + + if (__builtin_add_overflow(input, 10, &res)) // expected-warning + {{tainted}} + return; + + if (__builtin_add_overflow(10, input, &res)) // expected-warning + {{tainted}} + return; + + if (__builtin_add_overflow(input2, input, &res)) // expected-warning + {{tainted}} + return; +} 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 9e890204c78bd..752451f3d9749 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 @@ -164,11 +164,12 @@ namespace { #if __cplusplus < 201402L namespace ImplicitConstexprDef { - struct A { + struct A { // #defined-here void f(); // expected-note {{member declaration does not match because it is not const qualified}} }; constexpr void A::f() { } // expected-warning {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const' to avoid a change in behavior}} // expected-error@-1 {{out-of-line definition of 'f' does not match any declaration in 'ImplicitConstexprDef::A'}} + // expected-note@#defined-here {{defined here}} } #endif diff --git a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p8.cpp b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p8.cpp index 0454412229fad..32ad25e2b7054 100644 --- a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p8.cpp +++ b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p8.cpp @@ -1,8 +1,9 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -struct A { }; +struct A { }; // #defined-here A::A (enum { e1 }) {} // expected-error{{cannot be defined in a parameter}} void A::f(enum { e2 }) {} // expected-error{{cannot be defined in a parameter}} enum { e3 } A::g() { } // expected-error{{cannot be defined in the result type}} \ // expected-error{{out-of-line definition}} +// expected-note@#defined-here{{defined here}} diff --git a/clang/test/CXX/dcl/dcl.fct/p17.cpp b/clang/test/CXX/dcl/dcl.fct/p17.cpp index d7487233f5d5c..4a81875cb4f89 100644 --- a/clang/test/CXX/dcl/dcl.fct/p17.cpp +++ b/clang/test/CXX/dcl/dcl.fct/p17.cpp @@ -96,7 +96,7 @@ namespace unconstrained { // expected-error@-1{{no matching}} template - struct S { + struct S { // #defined-here constexpr auto f1(auto x, T t) -> decltype(x + t); template @@ -110,6 +110,7 @@ namespace unconstrained { template constexpr auto S::f2(auto x, U u, T t) -> decltype(x + u + t) { return x + u + t; } // expected-error@-1 {{out-of-line definition of 'f2' does not match any declaration in 'S'}} + // expected-note@#defined-here {{S defined here}} template template diff --git a/clang/test/CXX/drs/cwg22xx.cpp b/clang/test/CXX/drs/cwg22xx.cpp index 797c3ed8546ef..0614d4f34ad5e 100644 --- a/clang/test/CXX/drs/cwg22xx.cpp +++ b/clang/test/CXX/drs/cwg22xx.cpp @@ -102,12 +102,13 @@ namespace MultilevelSpecialization { // default argument -- how far back do we look when determining whether a // parameter was expanded from a pack? // -- zygoloid 2020-06-02 - template struct B { + template struct B { // #cwg2233-B template void f(int i = 0, int (&... arr)[V]); }; template<> template void B::f(int i, int (&arr1)[a], int (&arr2)[b]) {} // since-cxx11-error@-1 {{out-of-line definition of 'f' does not match any declaration in 'cwg2233::MultilevelSpecialization::B'}} + // since-cxx11-note@#cwg2233-B {{defined here}} template<> template<> void B::f<1, 1>(int i, int (&arr1a)[1], int (&arr2a)[1]) {} } diff --git a/clang/test/CXX/drs/cwg27xx.cpp b/clang/test/CXX/drs/cwg27xx.cpp index 581e2af822d55..fb5c8b1d1fbf8 100644 --- a/clang/test/CXX/drs/cwg27xx.cpp +++ b/clang/test/CXX/drs/cwg27xx.cpp @@ -2,9 +2,9 @@ // RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++11 -pedantic-errors -verify=expected %s // RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++14 -pedantic-errors -verify=expected %s // RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -pedantic-errors -verify=expected %s -// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -pedantic-errors -verify=expected %s -// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++23 -pedantic-errors -verify=expected,since-cxx23 %s -// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++2c -pedantic-errors -verify=expected,since-cxx23,since-cxx26 %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -pedantic-errors -verify=expected,since-cxx20 %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++23 -pedantic-errors -verify=expected,since-cxx20,since-cxx23 %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++2c -pedantic-errors -verify=expected,since-cxx20,since-cxx23,since-cxx26 %s #if __cplusplus == 199711L #define static_assert(...) __extension__ _Static_assert(__VA_ARGS__) @@ -29,6 +29,35 @@ namespace std { #endif } // namespace std +namespace cwg2707 { // cwg2707: 20 + +#if __cplusplus >= 202002L + +template struct A { // #cwg2707-A + T value[N]; +}; + +template +A(T...) -> A requires (sizeof...(T) == 2); // #cwg2707-guide-A + +// Brace elision is not allowed for synthesized CTAD guides if the array size +// is value-dependent. +// So this should pick up our explicit deduction guide. +A a = {1, 2}; + +A b = {3, 4, 5}; +// since-cxx20-error@-1 {{no viable constructor or deduction guide}} +// since-cxx20-note@#cwg2707-A {{candidate function template not viable}} +// since-cxx20-note@#cwg2707-A {{implicit deduction guide}} +// since-cxx20-note@#cwg2707-guide-A {{constraints not satisfied}} +// since-cxx20-note@#cwg2707-guide-A {{because 'sizeof...(T) == 2' (3 == 2) evaluated to false}} +// since-cxx20-note@#cwg2707-A {{candidate function template not viable}} +// since-cxx20-note@#cwg2707-A {{implicit deduction guide}} + +#endif + +} // namespace cwg2707 + namespace cwg2718 { // cwg2718: 2.7 struct B {}; struct D; @@ -200,32 +229,3 @@ static_assert(false, f().s); // since-cxx26-error@-1 {{static assertion failed: Hello}} #endif } // namespace cwg2798 - -namespace cwg2707 { // cwg2707: 20 - -#if __cplusplus >= 202002L - -template struct A { - T value[N]; -}; - -template -A(T...) -> A requires (sizeof...(T) == 2); - -// Brace elision is not allowed for synthesized CTAD guides if the array size -// is value-dependent. -// So this should pick up our explicit deduction guide. -A a = {1, 2}; - -A b = {3, 4, 5}; -// expected-error@-1 {{no viable constructor or deduction guide}} \ -// expected-note@-13 {{candidate function template not viable}} \ -// expected-note@-13 {{implicit deduction guide}} \ -// expected-note@-8 {{constraints not satisfied}} \ -// expected-note@-8 {{because 'sizeof...(T) == 2' (3 == 2) evaluated to false}} \ -// expected-note@-13 {{candidate function template not viable}} \ -// expected-note@-13 {{implicit deduction guide}} - -#endif - -} // namespace cwg2707 diff --git a/clang/test/CXX/drs/cwg3xx.cpp b/clang/test/CXX/drs/cwg3xx.cpp index 7157feed3f762..f20054c3701b1 100644 --- a/clang/test/CXX/drs/cwg3xx.cpp +++ b/clang/test/CXX/drs/cwg3xx.cpp @@ -597,9 +597,10 @@ namespace cwg336 { // cwg336: yes void mf2(); }; }; - template<> template class A::B {}; + template<> template class A::B {}; // #cwg336-B template<> template<> template void A::B::mf1(T t) {} // expected-error@-1 {{out-of-line definition of 'mf1' does not match any declaration in 'cwg336::Pre::A::B'}} + // expected-note@#cwg336-B {{defined here}} template template<> void A::B::mf2() {} // expected-error@-1 {{nested name specifier 'A::B::' for declaration does not refer into a class, class template or class template partial specialization}} } @@ -758,7 +759,7 @@ namespace cwg347 { // cwg347: yes void g(); }; - struct derived : base {}; + struct derived : base {}; // #cwg347-derived struct derived::nested {}; // expected-error@-1 {{no struct named 'nested' in 'cwg347::derived'}} @@ -766,8 +767,10 @@ namespace cwg347 { // cwg347: yes // expected-error@-1 {{no member named 'n' in 'cwg347::derived'}} void derived::f() {} // expected-error@-1 {{out-of-line definition of 'f' does not match any declaration in 'cwg347::derived'}} + // expected-note@#cwg347-derived {{defined here}} void derived::g() {} // expected-error@-1 {{out-of-line definition of 'g' does not match any declaration in 'cwg347::derived'}} + // expected-note@#cwg347-derived {{defined here}} } // cwg348: na @@ -1009,18 +1012,20 @@ namespace cwg355 { struct ::cwg355_S s; } // cwg356: na namespace cwg357 { // cwg357: yes - template struct A { + template struct A { // #cwg357-A void f() const; // #cwg357-f }; template void A::f() {} // expected-error@-1 {{out-of-line definition of 'f' does not match any declaration in 'A'}} + // expected-note@#cwg357-A {{defined here}} // expected-note@#cwg357-f {{member declaration does not match because it is const qualified}} - struct B { + struct B { // #cwg357-B template void f(); }; template void B::f() const {} // expected-error@-1 {{out-of-line definition of 'f' does not match any declaration in 'cwg357::B'}} + // expected-note@#cwg357-B {{defined here}} } namespace cwg358 { // cwg358: yes diff --git a/clang/test/CXX/special/class.inhctor/p8.cpp b/clang/test/CXX/special/class.inhctor/p8.cpp index 58c01d2b912d4..dbc357c1a8d4f 100644 --- a/clang/test/CXX/special/class.inhctor/p8.cpp +++ b/clang/test/CXX/special/class.inhctor/p8.cpp @@ -24,9 +24,10 @@ struct C { template constexpr C(T t) : v(t) {} int v; }; -struct D : C { +struct D : C { // #defined-here using C::C; }; static_assert(D(123).v == 123, ""); template constexpr D::D(T t) : C(t) {} // expected-error {{does not match any declaration in 'D'}} + // expected-note@#defined-here {{defined here}} diff --git a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp index 19793fe826372..ce27e6aa83c3b 100644 --- a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp +++ b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp @@ -2,13 +2,13 @@ template struct eval; // expected-note 3{{template is declared here}} -template