8 changes: 8 additions & 0 deletions clang-tools-extra/clangd/ConfigFragment.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONFIGFRAGMENT_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONFIGFRAGMENT_H

#include "Config.h"
#include "ConfigProvider.h"
#include "llvm/Support/SMLoc.h"
#include "llvm/Support/SourceMgr.h"
Expand Down Expand Up @@ -308,6 +309,13 @@ struct Fragment {
/// Whether code completion should include suggestions from scopes that are
/// not visible. The required scope prefix will be inserted.
std::optional<Located<bool>> AllScopes;
/// How to present the argument list between '()' and '<>':
/// valid values are enum Config::ArgumentListsPolicy values:
/// None: Nothing at all
/// OpenDelimiter: only opening delimiter "(" or "<"
/// Delimiters: empty pair of delimiters "()" or "<>"
/// FullPlaceholders: full name of both type and parameter
std::optional<Located<std::string>> ArgumentLists;
};
CompletionBlock Completion;

Expand Down
4 changes: 4 additions & 0 deletions clang-tools-extra/clangd/ConfigYAML.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,10 @@ class Parser {
if (auto AllScopes = boolValue(N, "AllScopes"))
F.AllScopes = *AllScopes;
});
Dict.handle("ArgumentLists", [&](Node &N) {
if (auto ArgumentLists = scalarValue(N, "ArgumentLists"))
F.ArgumentLists = *ArgumentLists;
});
Dict.parse(N);
}

Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/FindSymbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ getWorkspaceSymbols(llvm::StringRef Query, int Limit,
*Req.Limit *= 5;
}
TopN<ScoredSymbolInfo, ScoredSymbolGreater> Top(
Req.Limit ? *Req.Limit : std::numeric_limits<size_t>::max());
Req.Limit.value_or(std::numeric_limits<size_t>::max()));
FuzzyMatcher Filter(Req.Query);

Index->fuzzyFind(Req, [HintPath, &Top, &Filter, AnyScope = Req.AnyScope,
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/index/MemIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ bool MemIndex::fuzzyFind(
trace::Span Tracer("MemIndex fuzzyFind");

TopN<std::pair<float, const Symbol *>> Top(
Req.Limit ? *Req.Limit : std::numeric_limits<size_t>::max());
Req.Limit.value_or(std::numeric_limits<size_t>::max()));
FuzzyMatcher Filter(Req.Query);
bool More = false;
for (const auto &Pair : Index) {
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/index/dex/Dex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req,
return LHS.second > RHS.second;
};
TopN<IDAndScore, decltype(Compare)> Top(
Req.Limit ? *Req.Limit : std::numeric_limits<size_t>::max(), Compare);
Req.Limit.value_or(std::numeric_limits<size_t>::max()), Compare);
for (const auto &IDAndScore : IDAndScores) {
const DocID SymbolDocID = IDAndScore.first;
const auto *Sym = Symbols[SymbolDocID];
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ add_clang_library(clangDaemonTweaks OBJECT
RemoveUsingNamespace.cpp
ScopifyEnum.cpp
SpecialMembers.cpp
SwapBinaryOperands.cpp
SwapIfBranches.cpp

LINK_LIBS
Expand Down
217 changes: 217 additions & 0 deletions clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp
Original file line number Diff line number Diff line change
@@ -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 <string>
#include <utility>

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<Effect> 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<CompoundStmt>(N->ASTNode.get<Stmt>()))
return false;
Op = dyn_cast_or_null<BinaryOperator>(N->ASTNode.get<Stmt>());
// If we hit upon a nonswappable binary operator, ignore and keep going
if (Op && !isOpSwappable(Op->getOpcode())) {
Op = nullptr;
}
}
return Op != nullptr;
}

Expected<Tweak::Effect> 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
18 changes: 13 additions & 5 deletions clang-tools-extra/clangd/tool/ClangdMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,13 +242,13 @@ opt<std::string> FallbackStyle{
init(clang::format::DefaultFallbackStyle),
};

opt<bool> EnableFunctionArgSnippets{
opt<int> EnableFunctionArgSnippets{
"function-arg-placeholders",
cat(Features),
desc("When disabled, completions contain only parentheses for "
"function calls. When enabled, completions also contain "
desc("When disabled (0), completions contain only parentheses for "
"function calls. When enabled (1), completions also contain "
"placeholders for method parameters"),
init(CodeCompleteOptions().EnableFunctionArgSnippets),
init(-1),
};

opt<CodeCompleteOptions::IncludeInsertion> HeaderInsertion{
Expand Down Expand Up @@ -650,6 +650,7 @@ class FlagsConfigProvider : public config::Provider {
std::optional<Config::CDBSearchSpec> CDBSearch;
std::optional<Config::ExternalIndexSpec> IndexSpec;
std::optional<Config::BackgroundPolicy> BGPolicy;
std::optional<Config::ArgumentListsPolicy> ArgumentLists;

// If --compile-commands-dir arg was invoked, check value and override
// default path.
Expand Down Expand Up @@ -694,13 +695,21 @@ class FlagsConfigProvider : public config::Provider {
BGPolicy = Config::BackgroundPolicy::Skip;
}

if (EnableFunctionArgSnippets >= 0) {
ArgumentLists = EnableFunctionArgSnippets
? Config::ArgumentListsPolicy::FullPlaceholders
: Config::ArgumentListsPolicy::Delimiters;
}

Frag = [=](const config::Params &, Config &C) {
if (CDBSearch)
C.CompileFlags.CDBSearch = *CDBSearch;
if (IndexSpec)
C.Index.External = *IndexSpec;
if (BGPolicy)
C.Index.Background = *BGPolicy;
if (ArgumentLists)
C.Completion.ArgumentLists = *ArgumentLists;
if (AllScopesCompletion.getNumOccurrences())
C.Completion.AllScopes = AllScopesCompletion;

Expand Down Expand Up @@ -916,7 +925,6 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var
Opts.CodeComplete.IncludeIndicator.Insert.clear();
Opts.CodeComplete.IncludeIndicator.NoInsert.clear();
}
Opts.CodeComplete.EnableFunctionArgSnippets = EnableFunctionArgSnippets;
Opts.CodeComplete.RunParser = CodeCompletionParse;
Opts.CodeComplete.RankingModel = RankingModel;
// FIXME: If we're using C++20 modules, force the lookup process to load
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 16 additions & 0 deletions clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,22 @@ TEST_F(LSPTest, ClangTidyRename) {
EXPECT_EQ(Params, std::vector{llvm::json::Value(std::move(ExpectedEdit))});
}

TEST_F(LSPTest, ClangTidyCrash_Issue109367) {
// This test requires clang-tidy checks to be linked in.
if (!CLANGD_TIDY_CHECKS)
return;
Opts.ClangTidyProvider = [](tidy::ClangTidyOptions &ClangTidyOpts,
llvm::StringRef) {
ClangTidyOpts.Checks = {"-*,boost-use-ranges"};
};
// Check that registering the boost-use-ranges checker's matchers
// on two different threads does not cause a crash.
auto &Client = start();
Client.didOpen("a.cpp", "");
Client.didOpen("b.cpp", "");
Client.sync();
}

TEST_F(LSPTest, IncomingCalls) {
Annotations Code(R"cpp(
void calle^e(int);
Expand Down
25 changes: 23 additions & 2 deletions clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "ClangdServer.h"
#include "CodeComplete.h"
#include "Compiler.h"
#include "Config.h"
#include "Feature.h"
#include "Matchers.h"
#include "Protocol.h"
Expand Down Expand Up @@ -2595,10 +2596,10 @@ TEST(SignatureHelpTest, DynamicIndexDocumentation) {
ElementsAre(AllOf(sig("foo() -> int"), sigDoc("Member doc"))));
}

TEST(CompletionTest, CompletionFunctionArgsDisabled) {
TEST(CompletionTest, ArgumentListsPolicy) {
CodeCompleteOptions Opts;
Opts.EnableSnippets = true;
Opts.EnableFunctionArgSnippets = false;
Opts.ArgumentLists = Config::ArgumentListsPolicy::Delimiters;

{
auto Results = completions(
Expand Down Expand Up @@ -2670,6 +2671,26 @@ TEST(CompletionTest, CompletionFunctionArgsDisabled) {
EXPECT_THAT(Results.Completions, UnorderedElementsAre(AllOf(
named("FOO"), snippetSuffix("($0)"))));
}
{
Opts.ArgumentLists = Config::ArgumentListsPolicy::None;
auto Results = completions(
R"cpp(
void xfoo(int x, int y);
void f() { xfo^ })cpp",
{}, Opts);
EXPECT_THAT(Results.Completions,
UnorderedElementsAre(AllOf(named("xfoo"), snippetSuffix(""))));
}
{
Opts.ArgumentLists = Config::ArgumentListsPolicy::OpenDelimiter;
auto Results = completions(
R"cpp(
void xfoo(int x, int y);
void f() { xfo^ })cpp",
{}, Opts);
EXPECT_THAT(Results.Completions,
UnorderedElementsAre(AllOf(named("xfoo"), snippetSuffix("("))));
}
}

TEST(CompletionTest, SuggestOverrides) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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
16 changes: 16 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ Code completion
Code actions
^^^^^^^^^^^^

- Added `Swap operands` tweak for certain binary operators.

Signature help
^^^^^^^^^^^^^^

Expand All @@ -97,12 +99,22 @@ 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
<https://clang.llvm.org/docs/analyzer/checkers.html>`_.

- Improved :program:`run-clang-tidy.py` script. Fixed minor shutdown noise
happening on certain platforms when interrupting the script.

New checks
^^^^^^^^^^

- New :doc:`bugprone-bitwise-pointer-cast
<clang-tidy/checks/bugprone/bitwise-pointer-cast>` check.

Warns about code that tries to cast between pointers by means of
``std::bit_cast`` or ``memcpy``.

- New :doc:`bugprone-tagged-union-member-count
<clang-tidy/checks/bugprone/tagged-union-member-count>` check.

Expand Down Expand Up @@ -171,6 +183,10 @@ Changes in existing checks
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-loop-convert
<clang-tidy/checks/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
<clang-tidy/checks/modernize/min-max-use-initializer-list>` check by fixing
a false positive when only an implicit conversion happened inside an
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
.. title:: clang-tidy - bugprone-bitwise-pointer-cast

bugprone-bitwise-pointer-cast
=============================

Warns about code that tries to cast between pointers by means of
``std::bit_cast`` or ``memcpy``.

The motivation is that ``std::bit_cast`` is advertised as the safe alternative
to type punning via ``reinterpret_cast`` in modern C++. However, one should not
blindly replace ``reinterpret_cast`` with ``std::bit_cast``, as follows:

.. code-block:: c++

int x{};
-float y = *reinterpret_cast<float*>(&x);
+float y = *std::bit_cast<float*>(&x);

The drop-in replacement behaves exactly the same as ``reinterpret_cast``, and
Undefined Behavior is still invoked. ``std::bit_cast`` is copying the bytes of
the input pointer, not the pointee, into an output pointer of a different type,
which may violate the strict aliasing rules. However, simply looking at the
code, it looks "safe", because it uses ``std::bit_cast`` which is advertised as
safe.

The solution to safe type punning is to apply ``std::bit_cast`` on value types,
not on pointer types:

.. code-block:: c++

int x{};
float y = std::bit_cast<float>(x);

This way, the bytes of the input object are copied into the output object, which
is much safer. Do note that Undefined Behavior can still occur, if there is no
value of type ``To`` corresponding to the value representation produced.
Compilers may be able to optimize this copy and generate identical assembly to
the original ``reinterpret_cast`` version.

Code before C++20 may backport ``std::bit_cast`` by means of ``memcpy``, or
simply call ``memcpy`` directly, which is equally problematic. This is also
detected by this check:

.. code-block:: c++

int* x{};
float* y{};
std::memcpy(&y, &x, sizeof(x));

Alternatively, if a cast between pointers is truly wanted, ``reinterpret_cast``
should be used, to clearly convey the intent and enable warnings from compilers
and linters, which should be addressed accordingly.
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ Clang-Tidy Checks
:doc:`bugprone-assert-side-effect <bugprone/assert-side-effect>`,
:doc:`bugprone-assignment-in-if-condition <bugprone/assignment-in-if-condition>`,
:doc:`bugprone-bad-signal-to-kill-thread <bugprone/bad-signal-to-kill-thread>`,
:doc:`bugprone-bitwise-pointer-cast <bugprone/bitwise-pointer-cast>`,
:doc:`bugprone-bool-pointer-implicit-conversion <bugprone/bool-pointer-implicit-conversion>`, "Yes"
:doc:`bugprone-branch-clone <bugprone/branch-clone>`,
:doc:`bugprone-casting-through-void <bugprone/casting-through-void>`,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// RUN: %check_clang_tidy -std=c++20 %s bugprone-bitwise-pointer-cast %t

void memcpy(void* to, void* dst, unsigned long long size)
{
// Dummy implementation for the purpose of the test
}

namespace std
{
template <typename To, typename From>
To bit_cast(From from)
{
// Dummy implementation for the purpose of the test
To to{};
return to;
}

using ::memcpy;
}

void pointer2pointer()
{
int x{};
float bad = *std::bit_cast<float*>(&x); // UB, but looks safe due to std::bit_cast
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: do not use 'std::bit_cast' to cast between pointers [bugprone-bitwise-pointer-cast]
float good = std::bit_cast<float>(x); // Well-defined

using IntPtr = int*;
using FloatPtr = float*;
IntPtr x2{};
float bad2 = *std::bit_cast<FloatPtr>(x2);
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: do not use 'std::bit_cast' to cast between pointers [bugprone-bitwise-pointer-cast]
}

void pointer2pointer_memcpy()
{
int x{};
int* px{};
float y{};
float* py{};

memcpy(&py, &px, sizeof(px));
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use 'memcpy' to cast between pointers [bugprone-bitwise-pointer-cast]
std::memcpy(&py, &px, sizeof(px));
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use 'memcpy' to cast between pointers [bugprone-bitwise-pointer-cast]

std::memcpy(&y, &x, sizeof(x));
}

// Pointer-integer conversions are allowed by this check
void int2pointer()
{
unsigned long long addr{};
float* p = std::bit_cast<float*>(addr);
std::memcpy(&p, &addr, sizeof(addr));
}

void pointer2int()
{
float* p{};
auto addr = std::bit_cast<unsigned long long>(p);
std::memcpy(&addr, &p, sizeof(p));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// RUN: %check_clang_tidy %s bugprone-bitwise-pointer-cast %t

void memcpy(void* to, void* dst, unsigned long long size)
{
// Dummy implementation for the purpose of the test
}

namespace std
{
using ::memcpy;
}

void pointer2pointer()
{
int x{};
int* px{};
float y{};
float* py{};

memcpy(&py, &px, sizeof(px));
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use 'memcpy' to cast between pointers [bugprone-bitwise-pointer-cast]
std::memcpy(&py, &px, sizeof(px));
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use 'memcpy' to cast between pointers [bugprone-bitwise-pointer-cast]

std::memcpy(&y, &x, sizeof(x));
}

// Pointer-integer conversions are allowed by this check
void int2pointer()
{
unsigned long long addr{};
float* p{};
std::memcpy(&p, &addr, sizeof(addr));
}

void pointer2int()
{
unsigned long long addr{};
float* p{};
std::memcpy(&addr, &p, sizeof(p));
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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]

44 changes: 20 additions & 24 deletions clang/CodeOwners.rst → clang/Maintainers.rst
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
=================
Clang Code Owners
Clang Maintainers
=================

This file is a list of the
`code owners <https://llvm.org/docs/DeveloperPolicy.html#code-owners>`_ for
`maintainers <https://llvm.org/docs/DeveloperPolicy.html#maintainers>`_ for
Clang.

.. contents::
:depth: 2
:local:

Current Code Owners
===================
The following people are the active code owners for the project. Please reach
Active Maintainers
==================
The following people are the active maintainers for the project. Please reach
out to them for code reviews, questions about their area of expertise, or other
assistance.

All parts of Clang not covered by someone else
----------------------------------------------
Lead Maintainer
---------------
| Aaron Ballman
| aaron\@aaronballman.com (email), aaron.ballman (Phabricator), AaronBallman (GitHub), AaronBallman (Discourse), aaronballman (Discord), AaronBallman (IRC)

Contained Components
--------------------
These code owners are responsible for particular high-level components within
These maintainers are responsible for particular high-level components within
Clang that are typically contained to one area of the compiler.

AST matchers
~~~~~~~~~~~~
| Manuel Klimek
| klimek\@google.com (email), klimek (Phabricator), r4nt (GitHub)
| Aaron Ballman
| aaron\@aaronballman.com (email), aaron.ballman (Phabricator), AaronBallman (GitHub), AaronBallman (Discourse), aaronballman (Discord), AaronBallman (IRC)

Clang LLVM IR generation
Expand Down Expand Up @@ -60,7 +60,7 @@ Analysis & CFG
Experimental new constant interpreter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Timm Bäder
| tbaeder\@redhat.com (email), tbaeder (Phabricator), tbaederr (GitHub), tbaeder (Discourse), tbaeder (Discord)
| tbaeder\@redhat.com (em), tbaeder (Phabricator), tbaederr (GitHub), tbaeder (Discourse), tbaeder (Discord)

Modules & serialization
Expand Down Expand Up @@ -125,14 +125,9 @@ Driver parts not covered by someone else

Tools
-----
These code owners are responsible for user-facing tools under the Clang
These maintainers are responsible for user-facing tools under the Clang
umbrella or components used to support such tools.

Tooling library
~~~~~~~~~~~~~~~
| Manuel Klimek
| klimek\@google.com (email), klimek (Phabricator), r4nt (GitHub)

clang-format
~~~~~~~~~~~~
Expand Down Expand Up @@ -255,19 +250,20 @@ SYCL conformance
| alexey.bader\@intel.com (email), bader (Phabricator), bader (GitHub)

Former Code Owners
==================
The following people have graciously spent time performing code ownership
Inactive Maintainers
====================
The following people have graciously spent time performing maintainership
responsibilities but are no longer active in that role. Thank you for all your
help with the success of the project!

Emeritus owners
---------------
Emeritus Lead Maintainers
-------------------------
| Doug Gregor (dgregor\@apple.com)
| Richard Smith (richard\@metafoo.co.uk)

Former component owners
-----------------------
Inactive component maintainers
------------------------------
| Chandler Carruth (chandlerc\@gmail.com, chandlerc\@google.com) -- CMake, library layering
| Devin Coughlin (dcoughlin\@apple.com) -- Clang static analyzer
| Manuel Klimek (klimek\@google.com (email), klimek (Phabricator), r4nt (GitHub)) -- Tooling, AST matchers
15 changes: 15 additions & 0 deletions clang/cmake/caches/hexagon-unknown-linux-musl-clang-cross.cmake
Original file line number Diff line number Diff line change
@@ -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 "")
15 changes: 15 additions & 0 deletions clang/cmake/caches/hexagon-unknown-linux-musl-clang.cmake
Original file line number Diff line number Diff line change
@@ -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 "")
2 changes: 1 addition & 1 deletion clang/docs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ if (LLVM_ENABLE_SPHINX)
"${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}"

COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/../CodeOwners.rst"
"${CMAKE_CURRENT_SOURCE_DIR}/../Maintainers.rst"
"${CMAKE_CURRENT_BINARY_DIR}"
)

Expand Down
114 changes: 114 additions & 0 deletions clang/docs/ClangFormatStyleOptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,21 @@ the configuration (without a prefix: ``Auto``).
a &= 2;
bbb = 2;

* ``bool AlignFunctionDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether function declarations
are aligned.

.. code-block:: c++

true:
unsigned int f1(void);
void f2(void);
size_t f3(void);

false:
unsigned int f1(void);
void f2(void);
size_t f3(void);

* ``bool AlignFunctionPointers`` Only for ``AlignConsecutiveDeclarations``. Whether function pointers are
aligned.

Expand Down Expand Up @@ -534,6 +549,21 @@ the configuration (without a prefix: ``Auto``).
a &= 2;
bbb = 2;

* ``bool AlignFunctionDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether function declarations
are aligned.

.. code-block:: c++

true:
unsigned int f1(void);
void f2(void);
size_t f3(void);

false:
unsigned int f1(void);
void f2(void);
size_t f3(void);

* ``bool AlignFunctionPointers`` Only for ``AlignConsecutiveDeclarations``. Whether function pointers are
aligned.

Expand Down Expand Up @@ -676,6 +706,21 @@ the configuration (without a prefix: ``Auto``).
a &= 2;
bbb = 2;

* ``bool AlignFunctionDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether function declarations
are aligned.

.. code-block:: c++

true:
unsigned int f1(void);
void f2(void);
size_t f3(void);

false:
unsigned int f1(void);
void f2(void);
size_t f3(void);

* ``bool AlignFunctionPointers`` Only for ``AlignConsecutiveDeclarations``. Whether function pointers are
aligned.

Expand Down Expand Up @@ -819,6 +864,21 @@ the configuration (without a prefix: ``Auto``).
a &= 2;
bbb = 2;

* ``bool AlignFunctionDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether function declarations
are aligned.

.. code-block:: c++

true:
unsigned int f1(void);
void f2(void);
size_t f3(void);

false:
unsigned int f1(void);
void f2(void);
size_t f3(void);

* ``bool AlignFunctionPointers`` Only for ``AlignConsecutiveDeclarations``. Whether function pointers are
aligned.

Expand Down Expand Up @@ -1081,6 +1141,21 @@ the configuration (without a prefix: ``Auto``).
a &= 2;
bbb = 2;

* ``bool AlignFunctionDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether function declarations
are aligned.

.. code-block:: c++

true:
unsigned int f1(void);
void f2(void);
size_t f3(void);

false:
unsigned int f1(void);
void f2(void);
size_t f3(void);

* ``bool AlignFunctionPointers`` Only for ``AlignConsecutiveDeclarations``. Whether function pointers are
aligned.

Expand Down Expand Up @@ -1221,6 +1296,21 @@ the configuration (without a prefix: ``Auto``).
a &= 2;
bbb = 2;

* ``bool AlignFunctionDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether function declarations
are aligned.

.. code-block:: c++

true:
unsigned int f1(void);
void f2(void);
size_t f3(void);

false:
unsigned int f1(void);
void f2(void);
size_t f3(void);

* ``bool AlignFunctionPointers`` Only for ``AlignConsecutiveDeclarations``. Whether function pointers are
aligned.

Expand Down Expand Up @@ -1361,6 +1451,21 @@ the configuration (without a prefix: ``Auto``).
a &= 2;
bbb = 2;

* ``bool AlignFunctionDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether function declarations
are aligned.

.. code-block:: c++

true:
unsigned int f1(void);
void f2(void);
size_t f3(void);

false:
unsigned int f1(void);
void f2(void);
size_t f3(void);

* ``bool AlignFunctionPointers`` Only for ``AlignConsecutiveDeclarations``. Whether function pointers are
aligned.

Expand Down Expand Up @@ -6554,6 +6659,15 @@ the configuration (without a prefix: ``Auto``).
let DAGArgOtherID = (other i32:$other1, i32:$other2);
let DAGArgBang = (!cast<SomeType>("Some") i32:$src1, i32:$src2)

.. _TemplateNames:

**TemplateNames** (``List of Strings``) :versionbadge:`clang-format 20` :ref:`¶ <TemplateNames>`
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:`¶ <TypeNames>`
Expand Down
1 change: 0 additions & 1 deletion clang/docs/CodeOwners.rst

This file was deleted.

1 change: 1 addition & 0 deletions clang/docs/Maintainers.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.. include:: ../Maintainers.rst
4 changes: 2 additions & 2 deletions clang/docs/Multilib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,8 @@ For a more comprehensive example see
# If there is no multilib available for a particular set of flags, and the
# other multilibs are not adequate fallbacks, then you can define a variant
# record with a FatalError key in place of the Dir key.
- FatalError: this multilib collection has no hard-float ABI support
# record with an Error key in place of the Dir key.
- Error: this multilib collection has no hard-float ABI support
Flags: [--target=thumbv7m-none-eabi, -mfloat-abi=hard]
Expand Down
80 changes: 60 additions & 20 deletions clang/docs/RealtimeSanitizer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Introduction
============
RealtimeSanitizer (a.k.a. RTSan) is a real-time safety testing tool for C and C++
projects. RTSan can be used to detect real-time violations, i.e. calls to methods
that are not safe for use in functions with deterministic runtime requirements.
that are not safe for use in functions with deterministic run time requirements.
RTSan considers any function marked with the ``[[clang::nonblocking]]`` attribute
to be a real-time function. If RTSan detects a call to ``malloc``, ``free``,
``pthread_mutex_lock``, or anything else that could have a non-deterministic
Expand Down Expand Up @@ -58,31 +58,71 @@ code.
return 0;
}
# Compile and link
% clang++ -fsanitize=realtime -g example_realtime_violation.cpp
% clang++ -fsanitize=realtime example_realtime_violation.cpp
If a real-time safety violation is detected in a ``[[clang::nonblocking]]``
context, or any function invoked by that function, the program will exit with a
non-zero exit code.

.. code-block:: console
% clang++ -fsanitize=realtime -g example_realtime_violation.cpp
% clang++ -fsanitize=realtime example_realtime_violation.cpp
% ./a.out
Real-time violation: intercepted call to real-time unsafe function `malloc` in real-time context! Stack trace:
#0 0x000102893034 in __rtsan::PrintStackTrace() rtsan_stack.cpp:45
#1 0x000102892e64 in __rtsan::Context::ExpectNotRealtime(char const*) rtsan_context.cpp:78
#2 0x00010289397c in malloc rtsan_interceptors.cpp:286
#3 0x000195bd7bd0 in operator new(unsigned long)+0x1c (libc++abi.dylib:arm64+0x16bd0)
#4 0x5c7f00010230f07c (<unknown module>)
#5 0x00010230f058 in std::__1::__libcpp_allocate[abi:ue170006](unsigned long, unsigned long) new:324
#6 0x00010230effc in std::__1::allocator<float>::allocate[abi:ue170006](unsigned long) allocator.h:114
... snip ...
#10 0x00010230e4bc in std::__1::vector<float, std::__1::allocator<float>>::__append(unsigned long) vector:1162
#11 0x00010230dcdc in std::__1::vector<float, std::__1::allocator<float>>::resize(unsigned long) vector:1981
#12 0x00010230dc28 in violation() main.cpp:5
#13 0x00010230dd64 in main main.cpp:9
#14 0x0001958960dc (<unknown module>)
#15 0x2f557ffffffffffc (<unknown module>)
==76290==ERROR: RealtimeSanitizer: unsafe-library-call
Intercepted call to real-time unsafe function `malloc` in real-time context!
#0 0x000102a7b884 in malloc rtsan_interceptors.cpp:426
#1 0x00019c326bd0 in operator new(unsigned long)+0x1c (libc++abi.dylib:arm64+0x16bd0)
#2 0xa30d0001024f79a8 (<unknown module>)
#3 0x0001024f794c in std::__1::__libcpp_allocate[abi:ne200000](unsigned long, unsigned long)+0x44
#4 0x0001024f78c4 in std::__1::allocator<float>::allocate[abi:ne200000](unsigned long)+0x44
... snip ...
#9 0x0001024f6868 in std::__1::vector<float, std::__1::allocator<float>>::resize(unsigned long)+0x48
#10 0x0001024f67b4 in violation()+0x24
#11 0x0001024f68f0 in main+0x18 (a.out:arm64+0x1000028f0)
#12 0x00019bfe3150 (<unknown module>)
#13 0xed5efffffffffffc (<unknown module>)
Blocking functions
------------------

Calls to system library functions such as ``malloc`` are automatically caught by
RealtimeSanitizer. Real-time programmers may also write their own blocking
(real-time unsafe) functions that they wish RealtimeSanitizer to be aware of.
RealtimeSanitizer will raise an error at run time if any function attributed
with ``[[clang::blocking]]`` is called in a ``[[clang::nonblocking]]`` context.

.. code-block:: console
$ cat example_blocking_violation.cpp
#include <atomic>
#include <thread>
std::atomic<bool> has_permission{false};
int wait_for_permission() [[clang::blocking]] {
while (has_permission.load() == false)
std::this_thread::yield();
return 0;
}
int real_time_function() [[clang::nonblocking]] {
return wait_for_permission();
}
int main() {
return real_time_function();
}
$ clang++ -fsanitize=realtime example_blocking_violation.cpp && ./a.out
==76131==ERROR: RealtimeSanitizer: blocking-call
Call to blocking function `wait_for_permission()` in real-time context!
#0 0x0001000c3db0 in wait_for_permission()+0x10 (a.out:arm64+0x100003db0)
#1 0x0001000c3e3c in real_time_function()+0x10 (a.out:arm64+0x100003e3c)
#2 0x0001000c3e68 in main+0x10 (a.out:arm64+0x100003e68)
#3 0x00019bfe3150 (<unknown module>)
#4 0x5a27fffffffffffc (<unknown module>)
Run-time flags
--------------
Expand Down Expand Up @@ -159,7 +199,7 @@ Disabling

In some circumstances, you may want to suppress error reporting in a specific scope.

In C++, this is achieved via ``__rtsan::ScopedDisabler``. Within the scope where the ``ScopedDisabler`` object is instantiated, all sanitizer error reports are suppressed. This suppression applies to the current scope as well as all invoked functions, including any functions called transitively.
In C++, this is achieved via ``__rtsan::ScopedDisabler``. Within the scope where the ``ScopedDisabler`` object is instantiated, all sanitizer error reports are suppressed. This suppression applies to the current scope as well as all invoked functions, including any functions called transitively.

.. code-block:: c++

Expand All @@ -174,7 +214,7 @@ In C++, this is achieved via ``__rtsan::ScopedDisabler``. Within the scope wher

If RealtimeSanitizer is not enabled at compile time (i.e., the code is not compiled with the ``-fsanitize=realtime`` flag), the ``ScopedDisabler`` is compiled as a no-op.

In C, you can use the ``__rtsan_disable()`` and ``rtsan_enable()`` functions to manually disable and re-enable RealtimeSanitizer checks.
In C, you can use the ``__rtsan_disable()`` and ``rtsan_enable()`` functions to manually disable and re-enable RealtimeSanitizer checks.

.. code-block:: c++

Expand Down
9 changes: 9 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,8 @@ clang-format
------------

- Adds ``BreakBinaryOperations`` option.
- Adds ``TemplateNames`` option.
- Adds ``AlignFunctionDeclarations`` option to ``AlignConsecutiveDeclarations``.

libclang
--------
Expand All @@ -627,11 +629,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 <https://clang.llvm.org/docs/analyzer/checkers.html#unix-mismatcheddeallocator-c-c>`__.

- 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
^^^^^^^^^^^^^^^^^^^

Expand Down
2 changes: 1 addition & 1 deletion clang/docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ Design Documents
.. toctree::
:maxdepth: 1

CodeOwners
Maintainers
InternalsManual
DriverInternals
Multilib
Expand Down
68 changes: 56 additions & 12 deletions clang/include/clang/AST/APValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<LValuePathEntry> 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<LValuePathEntry> 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<const CXXRecordDecl*> 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);
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/AST/ComputeDependence.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
35 changes: 35 additions & 0 deletions clang/include/clang/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/AST/JSONNodeDumper.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
29 changes: 29 additions & 0 deletions clang/include/clang/AST/OpenACCClause.h
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,35 @@ class OpenACCNumGangsClause final
}
};

class OpenACCTileClause final
: public OpenACCClauseWithExprs,
public llvm::TrailingObjects<OpenACCTileClause, Expr *> {
OpenACCTileClause(SourceLocation BeginLoc, SourceLocation LParenLoc,
ArrayRef<Expr *> SizeExprs, SourceLocation EndLoc)
: OpenACCClauseWithExprs(OpenACCClauseKind::Tile, BeginLoc, LParenLoc,
EndLoc) {
std::uninitialized_copy(SizeExprs.begin(), SizeExprs.end(),
getTrailingObjects<Expr *>());
setExprs(MutableArrayRef(getTrailingObjects<Expr *>(), 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<Expr *> SizeExprs,
SourceLocation EndLoc);
llvm::ArrayRef<Expr *> getSizeExprs() {
return OpenACCClauseWithExprs::getExprs();
}

llvm::ArrayRef<Expr *> getSizeExprs() const {
return OpenACCClauseWithExprs::getExprs();
}
};

/// Represents one of a handful of clauses that have a single integer
/// expression.
class OpenACCClauseWithSingleIntExpr : public OpenACCClauseWithExprs {
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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, {})
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/AST/TextNodeDumper.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};
Expand Down
128 changes: 109 additions & 19 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include "llvm/Support/PointerLikeTypeTraits.h"
#include "llvm/Support/TrailingObjects.h"
#include "llvm/Support/type_traits.h"
#include <bitset>
#include <cassert>
#include <cstddef>
#include <cstdint>
Expand Down Expand Up @@ -119,6 +120,8 @@ class EnumDecl;
class Expr;
class ExtQualsTypeCommonBase;
class FunctionDecl;
class FunctionEffectsRef;
class FunctionEffectKindSet;
class FunctionEffectSet;
class IdentifierInfo;
class NamedDecl;
Expand Down Expand Up @@ -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<size_t>(Kind::Last) + 1;

/// Flags describing some behaviors of the effect.
using Flags = unsigned;
Expand All @@ -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.
Expand Down Expand Up @@ -4773,35 +4775,43 @@ class FunctionEffect {
case Kind::Blocking:
case Kind::Allocating:
return 0;
case Kind::None:
break;
}
llvm_unreachable("unknown effect kind");
}

/// 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<FunctionEffect>
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<FunctionEffect> 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;
}
};
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<EndBitPos>;

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<size_t>(K);
}

constexpr static FunctionEffect::Kind posToKind(size_t Pos) {
return static_cast<FunctionEffect::Kind>(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.
///
Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -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.
}];
}
Expand Down
12 changes: 12 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -4739,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];
Expand Down Expand Up @@ -4818,6 +4824,12 @@ def HLSLStep: LangBuiltin<"HLSL_LANG"> {
let Prototype = "void(...)";
}

def HLSLRadians : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_elementwise_radians"];
let Attributes = [NoThrow, Const];
let Prototype = "void(...)";
}

// Builtins for XRay.
def XRayCustomEvent : Builtin {
let Spellings = ["__xray_customevent"];
Expand Down
11 changes: 6 additions & 5 deletions clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;

Expand All @@ -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]>;
Expand Down Expand Up @@ -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">;

Expand Down
86 changes: 71 additions & 15 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -10510,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<
Expand Down Expand Up @@ -10972,19 +10972,68 @@ def warn_imp_cast_drops_unaligned : Warning<
InGroup<DiagGroup<"unaligned-qualifier-implicit-cast">>;

// 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<FunctionEffects>, 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<FunctionEffects>, 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<FunctionEffects>, 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<PerfConstraintImpliesNoexcept>, 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<FunctionEffects>;
InGroup<FunctionEffects>, DefaultIgnore;
def warn_mismatched_func_effect_override : Warning<
"attribute '%0' on overriding function does not match base declaration">,
InGroup<FunctionEffects>;
InGroup<FunctionEffects>, DefaultIgnore;
def warn_mismatched_func_effect_redeclaration : Warning<
"attribute '%0' on function does not match previous declaration">,
InGroup<FunctionEffects>;
InGroup<FunctionEffects>, DefaultIgnore;
def warn_conflicting_func_effects : Warning<
"effects conflict when merging declarations; kept '%0', discarded '%1'">,
InGroup<FunctionEffects>;
InGroup<FunctionEffects>, DefaultIgnore;
def err_func_with_effects_no_prototype : Error<
"'%0' function must have a prototype">;

Expand Down Expand Up @@ -12617,17 +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_invalid_in_collapse_loop
: Error<"%select{OpenACC '%1' construct|while loop|do loop}0 cannot appear "
"in intervening code of a 'loop' with a 'collapse' clause">;
def note_acc_collapse_clause_here
: Note<"active 'collapse' clause defined here">;
def err_acc_collapse_multiple_loops
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 'collapse' clause">;
def err_acc_collapse_insufficient_loops
: Error<"'collapse' clause specifies a loop count greater than the number "
"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">;
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/OpenACCClauses.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/StmtNodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -308,5 +308,8 @@ def OpenACCAssociatedStmtConstruct
def OpenACCComputeConstruct : StmtNode<OpenACCAssociatedStmtConstruct>;
def OpenACCLoopConstruct : StmtNode<OpenACCAssociatedStmtConstruct>;

// OpenACC Additional Expressions.
def OpenACCAsteriskSizeExpr : StmtNode<Expr>;

// HLSL Constructs.
def HLSLOutArgExpr : StmtNode<Expr>;
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/arm_sme.td
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,7 @@ multiclass ZAReadzArray<string vg_num>{
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>]>;
Expand Down
30 changes: 16 additions & 14 deletions clang/include/clang/Basic/arm_sve.td
Original file line number Diff line number Diff line change
Expand Up @@ -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]>;
Expand Down Expand Up @@ -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
}

////////////////////////////////////////////////////////////////////////////////
Expand Down
8 changes: 4 additions & 4 deletions clang/include/clang/Driver/Multilib.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class Multilib {
// Some Multilib objects don't actually represent library directories you can
// select. Instead, they represent failures of multilib selection, of the
// form 'Sorry, we don't have any library compatible with these constraints'.
std::optional<std::string> FatalError;
std::optional<std::string> Error;

public:
/// GCCSuffix, OSSuffix & IncludeSuffix will be appended directly to the
Expand All @@ -63,7 +63,7 @@ class Multilib {
Multilib(StringRef GCCSuffix = {}, StringRef OSSuffix = {},
StringRef IncludeSuffix = {}, const flags_list &Flags = flags_list(),
StringRef ExclusiveGroup = {},
std::optional<StringRef> FatalError = std::nullopt);
std::optional<StringRef> Error = std::nullopt);

/// Get the detected GCC installation path suffix for the multi-arch
/// target variant. Always starts with a '/', unless empty
Expand Down Expand Up @@ -94,9 +94,9 @@ class Multilib {

bool operator==(const Multilib &Other) const;

bool isFatalError() const { return FatalError.has_value(); }
bool isError() const { return Error.has_value(); }

const std::string &getFatalError() const { return FatalError.value(); }
const std::string &getErrorMessage() const { return Error.value(); }
};

raw_ostream &operator<<(raw_ostream &OS, const Multilib &M);
Expand Down
12 changes: 8 additions & 4 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -1195,10 +1195,11 @@ def cxx_isystem : JoinedOrSeparate<["-"], "cxx-isystem">, Group<clang_i_Group>,
def c : Flag<["-"], "c">, Flags<[NoXarchOption]>,
Visibility<[ClangOption, FlangOption]>, Group<Action_Group>,
HelpText<"Only run preprocess, compile, and assemble steps">;
defm convergent_functions : BoolFOption<"convergent-functions",
LangOpts<"ConvergentFunctions">, DefaultFalse,
NegFlag<SetFalse, [], [ClangOption], "Assume all functions may be convergent.">,
PosFlag<SetTrue, [], [ClangOption, CC1Option]>>;
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 {
Expand Down Expand Up @@ -5557,6 +5558,9 @@ def mthumb : Flag<["-"], "mthumb">, Group<m_Group>;
def mtune_EQ : Joined<["-"], "mtune=">, Group<m_Group>,
Visibility<[ClangOption, FlangOption]>,
HelpText<"Only supported on AArch64, PowerPC, RISC-V, SPARC, SystemZ, and X86">;
def multi_lib_config : Joined<["-", "--"], "multi-lib-config=">,
HelpText<"Path to the YAML configuration file to be used for multilib selection">,
MetaVarName<"<file>">;
def multi__module : Flag<["-"], "multi_module">;
def multiply__defined__unused : Separate<["-"], "multiply_defined_unused">;
def multiply__defined : Separate<["-"], "multiply_defined">;
Expand Down
25 changes: 25 additions & 0 deletions clang/include/clang/Format/Format.h
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,20 @@ struct FormatStyle {
/// bbb = 2;
/// \endcode
bool AlignCompound;
/// Only for ``AlignConsecutiveDeclarations``. Whether function declarations
/// are aligned.
/// \code
/// true:
/// unsigned int f1(void);
/// void f2(void);
/// size_t f3(void);
///
/// false:
/// unsigned int f1(void);
/// void f2(void);
/// size_t f3(void);
/// \endcode
bool AlignFunctionDeclarations;
/// Only for ``AlignConsecutiveDeclarations``. Whether function pointers are
/// aligned.
/// \code
Expand Down Expand Up @@ -264,6 +278,7 @@ struct FormatStyle {
return Enabled == R.Enabled && AcrossEmptyLines == R.AcrossEmptyLines &&
AcrossComments == R.AcrossComments &&
AlignCompound == R.AlignCompound &&
AlignFunctionDeclarations == R.AlignFunctionDeclarations &&
AlignFunctionPointers == R.AlignFunctionPointers &&
PadOperators == R.PadOperators;
}
Expand Down Expand Up @@ -4974,6 +4989,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<std::string> TemplateNames;

/// A vector of non-keyword identifiers that should be interpreted as type
/// names.
///
Expand Down Expand Up @@ -5230,6 +5254,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 ==
Expand Down
7 changes: 5 additions & 2 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<Expr *> &SizeExprs);
/// Parses a 'gang-arg-list', used for the 'gang' clause.
bool ParseOpenACCGangArgList(SourceLocation GangLoc);
/// Parses a 'gang-arg', used for the 'gang' clause.
Expand Down
178 changes: 109 additions & 69 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<FunctionEffectDiff> {
/// 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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<FunctionEffectMode>
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
Expand Down Expand Up @@ -15062,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<FunctionEffectMode>
ActOnEffectExpression(Expr *CondExpr, StringRef AttributeName);

private:
/// The implementation of RequireCompleteType
bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T,
Expand Down Expand Up @@ -15092,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<FunctionEffectWithCondition>
Old; // Invalid when 'Kind' is 'Added'.
std::optional<FunctionEffectWithCondition>
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<FunctionEffectDiff> {
/// 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<const Decl *> 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 <class FuncOrBlockDecl>
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
Expand Down
83 changes: 63 additions & 20 deletions clang/include/clang/Sema/SemaOpenACC.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,23 @@ 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
Expand All @@ -59,25 +76,27 @@ class SemaOpenACC : public SemaBase {
/// else it should be 'N' minus the current depth traversed.
std::optional<llvm::APSInt> CurCollapseCount;

/// 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;

/// 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.
LLVM_PREFERRED_TYPE(bool)
unsigned CollapseDepthSatisfied : 1;
} CollapseInfo{nullptr, std::nullopt, /*TopLevelLoopSeen=*/false,
/*CurLevelHasLoopAlready=*/false,
/*CollapseDepthSatisfied=*/true};
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<unsigned> 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.
Expand Down Expand Up @@ -179,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");

Expand Down Expand Up @@ -224,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");

Expand Down Expand Up @@ -335,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()}};
Expand All @@ -343,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)};
Expand Down Expand Up @@ -522,17 +545,26 @@ 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), OldCollapseInfo(SemaRef.CollapseInfo),
: 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
Expand All @@ -541,12 +573,20 @@ class SemaOpenACC : public SemaBase {
bool CollapseDepthSatisified =
PreserveDepth ? SemaRef.CollapseInfo.CollapseDepthSatisfied
: OldCollapseInfo.CollapseDepthSatisfied;
bool TileDepthSatisfied = PreserveDepth
? SemaRef.TileInfo.TileDepthSatisfied
: OldTileInfo.TileDepthSatisfied;
bool CurLevelHasLoopAlready =
PreserveDepth ? SemaRef.CollapseInfo.CurLevelHasLoopAlready
: OldCollapseInfo.CurLevelHasLoopAlready;
PreserveDepth ? SemaRef.LoopInfo.CurLevelHasLoopAlready
: OldLoopInfo.CurLevelHasLoopAlready;

SemaRef.LoopInfo = OldLoopInfo;
SemaRef.CollapseInfo = OldCollapseInfo;
SemaRef.TileInfo = OldTileInfo;

SemaRef.CollapseInfo.CollapseDepthSatisfied = CollapseDepthSatisified;
SemaRef.CollapseInfo.CurLevelHasLoopAlready = CurLevelHasLoopAlready;
SemaRef.TileInfo.TileDepthSatisfied = TileDepthSatisfied;
SemaRef.LoopInfo.CurLevelHasLoopAlready = CurLevelHasLoopAlready;
}
};

Expand All @@ -567,6 +607,9 @@ class SemaOpenACC : public SemaBase {
void SetCollapseInfoBeforeAssociatedStmt(
ArrayRef<const OpenACCClause *> UnInstClauses,
ArrayRef<OpenACCClause *> Clauses);
void SetTileInfoBeforeAssociatedStmt(
ArrayRef<const OpenACCClause *> UnInstClauses,
ArrayRef<OpenACCClause *> Clauses);
~AssociatedStmtRAII();
};
};
Expand Down
8 changes: 7 additions & 1 deletion clang/include/clang/Serialization/ASTBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Serialization/ASTReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,9 @@ class ASTReader
/// Sema tracks these to emit deferred diags.
llvm::SmallSetVector<GlobalDeclID, 4> DeclsToCheckForDeferredDiags;

/// The IDs of all decls with function effects to be checked.
SmallVector<GlobalDeclID> DeclsWithEffectsToVerify;

private:
struct ImportedSubmodule {
serialization::SubmoduleID ID;
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Serialization/ASTWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 0 additions & 2 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/AST/ByteCode/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2128,6 +2128,13 @@ bool Compiler<Emitter>::VisitUnaryExprOrTypeTraitExpr(
return this->emitConst(1, E);
}

if (Kind == UETT_OpenMPRequiredSimdAlign) {
assert(E->isArgumentType());
unsigned Bits = ASTCtx.getOpenMPDefaultSimdAlign(E->getArgumentType());

return this->emitConst(ASTCtx.toCharUnitsFromBits(Bits).getQuantity(), E);
}

return false;
}

Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/ByteCode/Descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<ComplexType>())
Expand Down
Loading