Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
#include "UniqueptrDeleteReleaseCheck.h"
#include "UppercaseLiteralSuffixCheck.h"
#include "UseAnyOfAllOfCheck.h"
#include "UseStdMinMaxCheck.h"

namespace clang::tidy {
namespace readability {
Expand Down Expand Up @@ -163,6 +164,8 @@ class ReadabilityModule : public ClangTidyModule {
"readability-uppercase-literal-suffix");
CheckFactories.registerCheck<UseAnyOfAllOfCheck>(
"readability-use-anyofallof");
CheckFactories.registerCheck<UseStdMinMaxCheck>(
"readability-use-std-min-max");
}
};

Expand Down
188 changes: 188 additions & 0 deletions clang-tools-extra/clang-tidy/readability/UseStdMinMaxCheck.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
//===--- UseStdMinMaxCheck.cpp - clang-tidy -------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "UseStdMinMaxCheck.h"
#include "../utils/ASTUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Preprocessor.h"

using namespace clang::ast_matchers;

namespace clang::tidy::readability {

namespace {

// Ignore if statements that are inside macros.
AST_MATCHER(IfStmt, isIfInMacro) {
return Node.getIfLoc().isMacroID() || Node.getEndLoc().isMacroID();
}

} // namespace

static const llvm::StringRef AlgorithmHeader("<algorithm>");

static bool minCondition(const BinaryOperator::Opcode Op, const Expr *CondLhs,
const Expr *CondRhs, const Expr *AssignLhs,
const Expr *AssignRhs, const ASTContext &Context) {
if ((Op == BO_LT || Op == BO_LE) &&
(tidy::utils::areStatementsIdentical(CondLhs, AssignRhs, Context) &&
tidy::utils::areStatementsIdentical(CondRhs, AssignLhs, Context)))
return true;

if ((Op == BO_GT || Op == BO_GE) &&
(tidy::utils::areStatementsIdentical(CondLhs, AssignLhs, Context) &&
tidy::utils::areStatementsIdentical(CondRhs, AssignRhs, Context)))
return true;

return false;
}

static bool maxCondition(const BinaryOperator::Opcode Op, const Expr *CondLhs,
const Expr *CondRhs, const Expr *AssignLhs,
const Expr *AssignRhs, const ASTContext &Context) {
if ((Op == BO_LT || Op == BO_LE) &&
(tidy::utils::areStatementsIdentical(CondLhs, AssignLhs, Context) &&
tidy::utils::areStatementsIdentical(CondRhs, AssignRhs, Context)))
return true;

if ((Op == BO_GT || Op == BO_GE) &&
(tidy::utils::areStatementsIdentical(CondLhs, AssignRhs, Context) &&
tidy::utils::areStatementsIdentical(CondRhs, AssignLhs, Context)))
return true;

return false;
}

QualType getNonTemplateAlias(QualType QT) {
while (true) {
// cast to a TypedefType
if (const TypedefType *TT = dyn_cast<TypedefType>(QT)) {
// check if the typedef is a template and if it is dependent
if (!TT->getDecl()->getDescribedTemplate() &&
!TT->getDecl()->getDeclContext()->isDependentContext())
return QT;
QT = TT->getDecl()->getUnderlyingType();
}
// cast to elaborated type
else if (const ElaboratedType *ET = dyn_cast<ElaboratedType>(QT)) {
QT = ET->getNamedType();
} else {
break;
}
}
return QT;
}

static std::string createReplacement(const Expr *CondLhs, const Expr *CondRhs,
const Expr *AssignLhs,
const SourceManager &Source,
const LangOptions &LO,
StringRef FunctionName,
const BinaryOperator *BO) {
const llvm::StringRef CondLhsStr = Lexer::getSourceText(
Source.getExpansionRange(CondLhs->getSourceRange()), Source, LO);
const llvm::StringRef CondRhsStr = Lexer::getSourceText(
Source.getExpansionRange(CondRhs->getSourceRange()), Source, LO);
const llvm::StringRef AssignLhsStr = Lexer::getSourceText(
Source.getExpansionRange(AssignLhs->getSourceRange()), Source, LO);

clang::QualType GlobalImplicitCastType;
clang::QualType LhsType = CondLhs->getType()
.getCanonicalType()
.getNonReferenceType()
.getUnqualifiedType();
clang::QualType RhsType = CondRhs->getType()
.getCanonicalType()
.getNonReferenceType()
.getUnqualifiedType();
if (LhsType != RhsType) {
GlobalImplicitCastType = getNonTemplateAlias(BO->getLHS()->getType());
}

return (AssignLhsStr + " = " + FunctionName +
(!GlobalImplicitCastType.isNull()
? "<" + GlobalImplicitCastType.getAsString() + ">("
: "(") +
CondLhsStr + ", " + CondRhsStr + ");")
.str();
}

UseStdMinMaxCheck::UseStdMinMaxCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM),
areDiagsSelfContained()) {}

void UseStdMinMaxCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
}

void UseStdMinMaxCheck::registerMatchers(MatchFinder *Finder) {
auto AssignOperator =
binaryOperator(hasOperatorName("="),
hasLHS(expr(unless(isTypeDependent())).bind("AssignLhs")),
hasRHS(expr(unless(isTypeDependent())).bind("AssignRhs")));
auto BinaryOperator =
binaryOperator(hasAnyOperatorName("<", ">", "<=", ">="),
hasLHS(expr(unless(isTypeDependent())).bind("CondLhs")),
hasRHS(expr(unless(isTypeDependent())).bind("CondRhs")))
.bind("binaryOp");
Finder->addMatcher(
ifStmt(stmt().bind("if"), unless(isIfInMacro()),
unless(hasElse(stmt())), // Ensure `if` has no `else`
hasCondition(BinaryOperator),
hasThen(
anyOf(stmt(AssignOperator),
compoundStmt(statementCountIs(1), has(AssignOperator)))),
hasParent(stmt(unless(ifStmt(hasElse(
equalsBoundNode("if"))))))), // Ensure `if` has no `else if`
this);
}

void UseStdMinMaxCheck::registerPPCallbacks(const SourceManager &SM,
Preprocessor *PP,
Preprocessor *ModuleExpanderPP) {
IncludeInserter.registerPreprocessor(PP);
}

void UseStdMinMaxCheck::check(const MatchFinder::MatchResult &Result) {
const auto *If = Result.Nodes.getNodeAs<IfStmt>("if");
const clang::LangOptions &LO = Result.Context->getLangOpts();
const auto *CondLhs = Result.Nodes.getNodeAs<Expr>("CondLhs");
const auto *CondRhs = Result.Nodes.getNodeAs<Expr>("CondRhs");
const auto *AssignLhs = Result.Nodes.getNodeAs<Expr>("AssignLhs");
const auto *AssignRhs = Result.Nodes.getNodeAs<Expr>("AssignRhs");
const auto *BinaryOp = Result.Nodes.getNodeAs<BinaryOperator>("binaryOp");
const clang::BinaryOperatorKind BinaryOpcode = BinaryOp->getOpcode();
const SourceLocation IfLocation = If->getIfLoc();
const SourceLocation ThenLocation = If->getEndLoc();

auto ReplaceAndDiagnose = [&](const llvm::StringRef FunctionName) {
const SourceManager &Source = *Result.SourceManager;
diag(IfLocation, "use `%0` instead of `%1`")
<< FunctionName << BinaryOp->getOpcodeStr()
<< FixItHint::CreateReplacement(
SourceRange(IfLocation, Lexer::getLocForEndOfToken(
ThenLocation, 0, Source, LO)),
createReplacement(CondLhs, CondRhs, AssignLhs, Source, LO,
FunctionName, BinaryOp))
<< IncludeInserter.createIncludeInsertion(
Source.getFileID(If->getBeginLoc()), AlgorithmHeader);
};

if (minCondition(BinaryOpcode, CondLhs, CondRhs, AssignLhs, AssignRhs,
(*Result.Context))) {
ReplaceAndDiagnose("std::min");
} else if (maxCondition(BinaryOpcode, CondLhs, CondRhs, AssignLhs, AssignRhs,
(*Result.Context))) {
ReplaceAndDiagnose("std::max");
}
}

} // namespace clang::tidy::readability
42 changes: 42 additions & 0 deletions clang-tools-extra/clang-tidy/readability/UseStdMinMaxCheck.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//===--- UseStdMinMaxCheck.h - clang-tidy -----------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USESTDMINMAXCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USESTDMINMAXCHECK_H

#include "../ClangTidyCheck.h"
#include "../utils/IncludeInserter.h"

namespace clang::tidy::readability {

/// Replaces certain conditional statements with equivalent calls to
/// ``std::min`` or ``std::max``.
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/readability/UseStdMinMax.html
class UseStdMinMaxCheck : public ClangTidyCheck {
public:
UseStdMinMaxCheck(StringRef Name, ClangTidyContext *Context);
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
Preprocessor *ModuleExpanderPP) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
std::optional<TraversalKind> getCheckTraversalKind() const override {
return TK_IgnoreUnlessSpelledInSource;
}

private:
utils::IncludeInserter IncludeInserter;
};

} // namespace clang::tidy::readability

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USESTDMINMAXCHECK_H
6 changes: 6 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ Improvements to clang-tidy
New checks
^^^^^^^^^^

- New :doc:`readability-use-std-min-max
<clang-tidy/checks/readability/use-std-min-max>` check.

Replaces certain conditional statements with equivalent calls to
``std::min`` or ``std::max``.

New check aliases
^^^^^^^^^^^^^^^^^

Expand Down
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 @@ -385,6 +385,7 @@ Clang-Tidy Checks
:doc:`readability-uniqueptr-delete-release <readability/uniqueptr-delete-release>`, "Yes"
:doc:`readability-uppercase-literal-suffix <readability/uppercase-literal-suffix>`, "Yes"
:doc:`readability-use-anyofallof <readability/use-anyofallof>`,
:doc:`readability-use-std-min-max <readability/use-std-min-max>`, "Yes"
:doc:`zircon-temporary-objects <zircon/temporary-objects>`,


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.. title:: clang-tidy - readability-use-std-min-max

readability-use-std-min-max
===========================

Replaces certain conditional statements with equivalent calls to
``std::min`` or ``std::max``.
Note: This may impact performance in critical code due to potential
additional stores compared to the original if statement.

Before:

.. code-block:: c++

void foo() {
int a = 2, b = 3;
if (a < b)
a = b;
}


After:

.. code-block:: c++

void foo() {
int a = 2, b = 3;
a = std::max(a, b);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
// RUN: %check_clang_tidy -std=c++11-or-later %s readability-use-std-min-max %t -- -- -fno-delayed-template-parsing
#define MY_MACRO_MIN(a, b) ((a) < (b) ? (a) : (b))

constexpr int myConstexprMin(int a, int b) {
return a < b ? a : b;
}

constexpr int myConstexprMax(int a, int b) {
return a > b ? a : b;
}

#define MY_IF_MACRO(condition, statement) \
if (condition) { \
statement \
}

class MyClass {
public:
int member1;
int member2;
};

template<typename T>

void foo(T value7) {
int value1,value2,value3;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: value1 = std::max(value1, value2);
if (value1 < value2)
value1 = value2;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: value2 = std::min(value1, value2);
if (value1 < value2)
value2 = value1;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `>` [readability-use-std-min-max]
// CHECK-FIXES: value2 = std::min(value2, value1);
if (value2 > value1)
value2 = value1;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `>` [readability-use-std-min-max]
// CHECK-FIXES: value1 = std::max(value2, value1);
if (value2 > value1)
value1 = value2;

// No suggestion needed here
if (value1 == value2)
value1 = value2;

// CHECK-MESSAGES: :[[@LINE+3]]:3: warning: use `std::max` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: value1 = std::max<int>(value1, value4);
short value4;
if(value1<value4)
value1=value4;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: value3 = std::min(value1+value2, value3);
if(value1+value2<value3)
value3 = value1+value2;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: value1 = std::max(value1, myConstexprMin(value2, value3));
if (value1 < myConstexprMin(value2, value3))
value1 = myConstexprMin(value2, value3);

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `>` [readability-use-std-min-max]
// CHECK-FIXES: value1 = std::min(value1, myConstexprMax(value2, value3));
if (value1 > myConstexprMax(value2, value3))
value1 = myConstexprMax(value2, value3);

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `<=` [readability-use-std-min-max]
// CHECK-FIXES: value2 = std::min(value1, value2);
if (value1 <= value2)
value2 = value1;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `<=` [readability-use-std-min-max]
// CHECK-FIXES: value1 = std::max(value1, value2);
if (value1 <= value2)
value1 = value2;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `>=` [readability-use-std-min-max]
// CHECK-FIXES: value1 = std::max(value2, value1);
if (value2 >= value1)
value1 = value2;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `>=` [readability-use-std-min-max]
// CHECK-FIXES: value2 = std::min(value2, value1);
if (value2 >= value1)
value2 = value1;

// CHECK-MESSAGES: :[[@LINE+3]]:3: warning: use `std::max` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: obj.member1 = std::max(obj.member1, obj.member2);
MyClass obj;
if (obj.member1 < obj.member2)
obj.member1 = obj.member2;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: obj.member2 = std::min(obj.member1, obj.member2);
if (obj.member1 < obj.member2)
obj.member2 = obj.member1;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `>` [readability-use-std-min-max]
// CHECK-FIXES: obj.member2 = std::min(obj.member2, obj.member1);
if (obj.member2 > obj.member1)
obj.member2 = obj.member1;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `>` [readability-use-std-min-max]
// CHECK-FIXES: obj.member1 = std::max(obj.member2, obj.member1);
if (obj.member2 > obj.member1)
obj.member1 = obj.member2;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: obj.member1 = std::max<int>(obj.member1, value4);
if (obj.member1 < value4)
obj.member1 = value4;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: value3 = std::min(obj.member1 + value2, value3);
if (obj.member1 + value2 < value3)
value3 = obj.member1 + value2;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `<=` [readability-use-std-min-max]
// CHECK-FIXES: obj.member2 = std::min(value1, obj.member2);
if (value1 <= obj.member2)
obj.member2 = value1;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `<=` [readability-use-std-min-max]
// CHECK-FIXES: value1 = std::max(value1, obj.member2);
if (value1 <= obj.member2)
value1 = obj.member2;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `>=` [readability-use-std-min-max]
// CHECK-FIXES: value1 = std::max(obj.member2, value1);
if (obj.member2 >= value1)
value1 = obj.member2;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `>=` [readability-use-std-min-max]
// CHECK-FIXES: obj.member2 = std::min(obj.member2, value1);
if (obj.member2 >= value1)
obj.member2 = value1;

// No suggestion needed here
if (MY_MACRO_MIN(value1, value2) < value3)
value3 = MY_MACRO_MIN(value1, value2);

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: value4 = std::max<int>(value4, value2);
if (value4 < value2){
value4 = value2;
}

// No suggestion needed here
if(value1 < value2)
value2 = value1;
else
value2 = value3;

// No suggestion needed here
if(value1<value2){
value2 = value1;
}
else{
value2 = value3;
}

// No suggestion needed here
if(value1<value2){
value2 = value1;
int res = value1 + value2;
}

// No suggestion needed here
MY_IF_MACRO(value1 < value2, value1 = value2;)

// No suggestion needed here
if(value1<value2){
value1 = value2;
}
else if(value1>value2){
value2 = value1;
}

// CHECK-MESSAGES: :[[@LINE+3]]:5: warning: use `std::max` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: value1 = std::max(value1, value3);
if(value1 == value2){
if(value1<value3)
value1 = value3;
}

// CHECK-MESSAGES: :[[@LINE+5]]:7: warning: use `std::max` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: value1 = std::max<int>(value1, value4);
if(value1 == value2){
if(value2 == value3){
value3+=1;
if(value1<value4){
value1 = value4;
}
}
else if(value3>value2){
value2 = value3;
}
}

// CHECK-MESSAGES: :[[@LINE+4]]:3: warning: use `std::min` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: value6 = std::min<unsigned int>(value5, value6);
unsigned int value5;
unsigned char value6;
if(value5<value6){
value6 = value5;
}

//No suggestion needed here
if(value7<value6){
value6 = value7;
}

//CHECK-MESSAGES: :[[@LINE+3]]:3: warning: use `std::min` instead of `<` [readability-use-std-min-max]
//CHECK-FIXES: value1 = std::min(value8, value1);
const int value8 = 5;
if(value8<value1)
value1 = value8;

//CHECK-MESSAGES: :[[@LINE+3]]:3: warning: use `std::min` instead of `<` [readability-use-std-min-max]
//CHECK-FIXES: value1 = std::min(value9, value1);
volatile int value9 = 6;
if(value9<value1)
value1 = value9;
}

using my_size = unsigned long long;

template<typename T>
struct MyVector
{
using size_type = my_size;
size_type size() const;
};

void testVectorSizeType() {
MyVector<int> v;
unsigned int value;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `>` [readability-use-std-min-max]
// CHECK-FIXES: value = std::max<my_size>(v.size(), value);
if (v.size() > value)
value = v.size();

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: value = std::max<my_size>(value, v.size());
if (value < v.size())
value = v.size();
}
31 changes: 31 additions & 0 deletions clang/docs/ClangFormatStyleOptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1664,6 +1664,18 @@ the configuration (without a prefix: ``Auto``).

Possible values:

* ``BTDS_Leave`` (in configuration: ``Leave``)
Do not change the line breaking before the declaration.

.. code-block:: c++

template <typename T>
T foo() {
}
template <typename T> T foo(int aaaaaaaaaaaaaaaaaaaaa,
int bbbbbbbbbbbbbbbbbbbbb) {
}

* ``BTDS_No`` (in configuration: ``No``)
Do not force break before declaration.
``PenaltyBreakTemplateDeclaration`` is taken into account.
Expand Down Expand Up @@ -4142,6 +4154,25 @@ the configuration (without a prefix: ``Auto``).
A(z); -> z;
A(a, b); // will not be expanded.

.. _MainIncludeChar:

**MainIncludeChar** (``MainIncludeCharDiscriminator``) :versionbadge:`clang-format 18` :ref:`¶ <MainIncludeChar>`
When guessing whether a #include is the "main" include, only the include
directives that use the specified character are considered.

Possible values:

* ``MICD_Quote`` (in configuration: ``Quote``)
Main include uses quotes: ``#include "foo.hpp"`` (the default).

* ``MICD_AngleBracket`` (in configuration: ``AngleBracket``)
Main include uses angle brackets: ``#include <foo.hpp>``.

* ``MICD_Any`` (in configuration: ``Any``)
Main include uses either quotes or angle brackets.



.. _MaxEmptyLinesToKeep:

**MaxEmptyLinesToKeep** (``Unsigned``) :versionbadge:`clang-format 3.7` :ref:`¶ <MaxEmptyLinesToKeep>`
Expand Down
18 changes: 18 additions & 0 deletions clang/docs/ClangLinkerWrapper.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,30 @@ only for the linker wrapper will be forwarded to the wrapped linker job.
--pass-remarks=<value> Pass remarks for LTO
--print-wrapped-module Print the wrapped module's IR for testing
--ptxas-arg=<value> Argument to pass to the 'ptxas' invocation
--relocatable Link device code to create a relocatable offloading application
--save-temps Save intermediate results
--sysroot<value> Set the system root
--verbose Verbose output from tools
--v Display the version number and exit
-- The separator for the wrapped linker arguments
Relocatable Linking
===================

The ``clang-linker-wrapper`` handles linking embedded device code and then
registering it with the appropriate runtime. Normally, this is only done when
the executable is created so other files containing device code can be linked
together. This can be somewhat problematic for users who wish to ship static
libraries that contain offloading code to users without a compatible offloading
toolchain.

When using a relocatable link with ``-r``, the ``clang-linker-wrapper`` will
perform the device linking and registration eagerly. This will remove the
embedded device code and register it correctly with the runtime. Semantically,
this is similar to creating a shared library object. If standard relocatable
linking is desired, simply do not run the binaries through the
``clang-linker-wrapper``. This will simply append the embedded device code so
that it can be linked later.

Example
=======
Expand Down
34 changes: 34 additions & 0 deletions clang/docs/OffloadingDesign.rst
Original file line number Diff line number Diff line change
Expand Up @@ -470,3 +470,37 @@ We can see the steps created by clang to generate the offloading code using the
# "nvptx64-nvidia-cuda" - "NVPTX::Assembler", inputs: ["/tmp/zaxpy-07f434.s"], output: "/tmp/zaxpy-0af7b7.o"
# "x86_64-unknown-linux-gnu" - "clang", inputs: ["/tmp/zaxpy-e6a41b.bc", "/tmp/zaxpy-0af7b7.o"], output: "/tmp/zaxpy-416cad.o"
# "x86_64-unknown-linux-gnu" - "Offload::Linker", inputs: ["/tmp/zaxpy-416cad.o"], output: "a.out"
Relocatable Linking
-------------------

The offloading compilation pipeline normally will defer the final device linking
and runtime registration until the ``clang-linker-wrapper`` is run to create the
executable. This is the standard behaviour when compiling for OpenMP offloading
or CUDA and HIP in ``-fgpu-rdc`` mode. However, there are some cases where the
user may wish to perform this device handling prematurely. This is described in
the :doc:`linker wrapper documentation<ClangLinkerWrapper>`.

Effectively, this allows the user to handle offloading specific linking ahead of
time when shipping objects or static libraries. This can be thought of as
performing a standard ``-fno-gpu-rdc`` compilation on a subset of object files.
This can be useful to reduce link time, prevent users from interacting with the
library's device code, or for shipping libraries to incompatible compilers.

Normally, if a relocatable link is done using ``clang -r`` it will simply merge
the ``.llvm.offloading`` sections which will then be linked later when the
executable is created. However, if the ``-r`` flag is used with the offloading
toolchain, it will perform the device linking and registration phases and then
merge the registration code into the final relocatable object file.

The following example shows how using the relocatable link with the offloading
pipeline can create a static library with offloading code that can be
redistributed without requiring any additional handling.

.. code-block:: console
$ clang++ -fopenmp -fopenmp-targets=nvptx64 foo.cpp -c
$ clang++ -lomptarget.devicertl --offload-link -r foo.o -o merged.o
$ llvm-ar rcs libfoo.a merged.o
# g++ app.cpp -L. -lfoo
9 changes: 9 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ Bug Fixes in This Version
- Clang now accepts qualified partial/explicit specializations of variable templates that
are not nominable in the lookup context of the specialization.

- Clang now doesn't produce false-positive warning `-Wconstant-logical-operand`
for logical operators in C23.
Fixes (`#64356 <https://github.com/llvm/llvm-project/issues/64356>`_).

Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down Expand Up @@ -197,10 +201,15 @@ Bug Fixes to C++ Support
Fixes (`#67976 <https://github.com/llvm/llvm-project/issues/67976>`_)
- Fix crash and diagnostic with const qualified member operator new.
Fixes (`#79748 <https://github.com/llvm/llvm-project/issues/79748>`_)
- Fixed a crash where substituting into a requires-expression that involves parameter packs
during the equivalence determination of two constraint expressions.
(`#72557 <https://github.com/llvm/llvm-project/issues/72557>`_)
- Fix a crash when specializing an out-of-line member function with a default
parameter where we did an incorrect specialization of the initialization of
the default parameter.
Fixes (`#68490 <https://github.com/llvm/llvm-project/issues/68490>`_)
- Fix a crash when trying to call a varargs function that also has an explicit object parameter.
Fixes (`#80971 ICE when explicit object parameter be a function parameter pack`)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -2613,6 +2613,9 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
/// Return true if this is a fixed point or integer type.
bool isFixedPointOrIntegerType() const;

/// Return true if this can be converted to (or from) a fixed point type.
bool isConvertibleToFixedPointType() const;

/// Return true if this is a saturated fixed point type according to
/// ISO/IEC JTC1 SC22 WG14 N1169. This type can be signed or unsigned.
bool isSaturatedFixedPointType() const;
Expand Down Expand Up @@ -7493,6 +7496,10 @@ inline bool Type::isFixedPointOrIntegerType() const {
return isFixedPointType() || isIntegerType();
}

inline bool Type::isConvertibleToFixedPointType() const {
return isRealFloatingType() || isFixedPointOrIntegerType();
}

inline bool Type::isSaturatedFixedPointType() const {
if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType)) {
return BT->getKind() >= BuiltinType::SatShortAccum &&
Expand Down
26 changes: 24 additions & 2 deletions clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
#include "clang/AST/DeclBase.h"
#include "clang/AST/Expr.h"
#include "clang/AST/Type.h"
#include "clang/Analysis/FlowSensitive/ControlFlowContext.h"
#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
#include "clang/Analysis/FlowSensitive/Formula.h"
Expand All @@ -31,7 +30,6 @@
#include "llvm/ADT/MapVector.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorHandling.h"
#include <memory>
#include <type_traits>
#include <utility>

Expand Down Expand Up @@ -81,6 +79,8 @@ class Environment {
return ComparisonResult::Unknown;
}

/// DEPRECATED. Override `join` and/or `widen`, instead.
///
/// Modifies `MergedVal` to approximate both `Val1` and `Val2`. This could
/// be a strict lattice join or a more general widening operation.
///
Expand All @@ -105,6 +105,28 @@ class Environment {
return true;
}

/// Modifies `JoinedVal` to approximate both `Val1` and `Val2`. This should
/// obey the properties of a lattice join.
///
/// `Env1` and `Env2` can be used to query child values and path condition
/// implications of `Val1` and `Val2` respectively.
///
/// Requirements:
///
/// `Val1` and `Val2` must be distinct.
///
/// `Val1`, `Val2`, and `JoinedVal` must model values of type `Type`.
///
/// `Val1` and `Val2` must be assigned to the same storage location in
/// `Env1` and `Env2` respectively.
virtual void join(QualType Type, const Value &Val1, const Environment &Env1,
const Value &Val2, const Environment &Env2,
Value &JoinedVal, Environment &JoinedEnv) {
[[maybe_unused]] bool ShouldKeep =
merge(Type, Val1, Env1, Val2, Env2, JoinedVal, JoinedEnv);
assert(ShouldKeep && "dropping merged value is unsupported");
}

/// This function may widen the current value -- replace it with an
/// approximation that can reach a fixed point more quickly than iterated
/// application of the transfer function alone. The previous value is
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/BuiltinsAMDGPU.def
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ BUILTIN(__builtin_amdgcn_s_dcache_inv, "v", "n")
BUILTIN(__builtin_amdgcn_buffer_wbinvl1, "v", "n")
BUILTIN(__builtin_amdgcn_fence, "vUicC*", "n")
BUILTIN(__builtin_amdgcn_groupstaticsize, "Ui", "n")
BUILTIN(__builtin_amdgcn_wavefrontsize, "Ui", "nc")

BUILTIN(__builtin_amdgcn_atomic_inc32, "UZiUZiD*UZiUicC*", "n")
BUILTIN(__builtin_amdgcn_atomic_inc64, "UWiUWiD*UWiUicC*", "n")
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/DiagnosticCommonKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def warn_pragma_debug_unexpected_argument : Warning<
def warn_fp_nan_inf_when_disabled : Warning<
"use of %select{infinity|NaN}0%select{| via a macro}1 is undefined behavior "
"due to the currently enabled floating-point options">,
InGroup<DiagGroup<"nan-infinity-disabled">>;
InGroup<DiagGroup<"nan-infinity-disabled", [], NanInfDisabledDocs>>;
}

// Parse && Sema
Expand Down
9 changes: 9 additions & 0 deletions clang/include/clang/Basic/DiagnosticDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,12 @@ program by treating all string literals as having type ``const char *``
instead of ``char *``. This can cause unexpected behaviors with type-sensitive
constructs like ``_Generic``.
}];

defvar NanInfDisabledDocs = [{
This warning is enabled when source code using the macros ``INFINITY`` or ``NAN``
is compiled with floating-point options preventing these two values. This can
lead to undefined behavior. Check the order of command line arguments that modify
this behavior, such as ``-ffast-math``, ``-fhonor-infinities``, and
``-fhonor-nans`` (etc), as well as ``#pragma`` directives if this diagnostic is
generated unexpectedly.
}];
6 changes: 3 additions & 3 deletions clang/include/clang/Basic/DiagnosticFrontendKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,10 @@ def err_verify_inconsistent_diags : Error<
"'%0' diagnostics %select{expected|seen}1 but not %select{seen|expected}1: "
"%2">;
def err_verify_invalid_no_diags : Error<
"%select{expected|'expected-no-diagnostics'}0 directive cannot follow "
"%select{'expected-no-diagnostics' directive|other expected directives}0">;
"%select{expected|'%0-no-diagnostics'}1 directive cannot follow "
"%select{'%0-no-diagnostics' directive|other expected directives}1">;
def err_verify_no_directives : Error<
"no expected directives found: consider use of 'expected-no-diagnostics'">;
"no expected directives found: consider use of '%0-no-diagnostics'">;
def err_verify_nonconst_addrspace : Error<
"qualifier 'const' is needed for variables in address space '%0'">;

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -2982,6 +2982,9 @@ def err_trailing_requires_clause_on_deduction_guide : Error<
"deduction guide cannot have a requires clause">;
def err_constrained_non_templated_function
: Error<"non-templated function cannot have a requires clause">;
def err_non_temp_spec_requires_clause : Error<
"%select{explicit|friend}0 specialization cannot have a trailing requires clause "
"unless it declares a function template">;
def err_reference_to_function_with_unsatisfied_constraints : Error<
"invalid reference to function %0: constraints not satisfied">;
def err_requires_expr_local_parameter_default_argument : Error<
Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/Basic/arm_neon.td
Original file line number Diff line number Diff line change
Expand Up @@ -1354,9 +1354,9 @@ let isScalarNarrowShift = 1 in {
// Signed/Unsigned Saturating Rounded Shift Right Narrow (Immediate)
def SCALAR_SQRSHRN_N: SInst<"vqrshrn_n", "(1<)1I", "SsSiSlSUsSUiSUl">;
// Signed Saturating Shift Right Unsigned Narrow (Immediate)
def SCALAR_SQSHRUN_N: SInst<"vqshrun_n", "(1<)1I", "SsSiSl">;
def SCALAR_SQSHRUN_N: SInst<"vqshrun_n", "(1<U)1I", "SsSiSl">;
// Signed Saturating Rounded Shift Right Unsigned Narrow (Immediate)
def SCALAR_SQRSHRUN_N: SInst<"vqrshrun_n", "(1<)1I", "SsSiSl">;
def SCALAR_SQRSHRUN_N: SInst<"vqrshrun_n", "(1<U)1I", "SsSiSl">;
}

////////////////////////////////////////////////////////////////////////////////
Expand Down
28 changes: 16 additions & 12 deletions clang/include/clang/Basic/arm_sme.td
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,15 @@ defm SVLD1_ZA32 : ZALoad<"za32", "i", "aarch64_sme_ld1w", [ImmCheck<0, ImmCheck0
defm SVLD1_ZA64 : ZALoad<"za64", "l", "aarch64_sme_ld1d", [ImmCheck<0, ImmCheck0_7>]>;
defm SVLD1_ZA128 : ZALoad<"za128", "q", "aarch64_sme_ld1q", [ImmCheck<0, ImmCheck0_15>]>;

let TargetGuard = "sme" in {
def SVLDR_VNUM_ZA : MInst<"svldr_vnum_za", "vmQl", "",
[IsOverloadNone, IsStreamingCompatible, IsInOutZA],
MemEltTyDefault, "aarch64_sme_ldr">;

def SVLDR_ZA : MInst<"svldr_za", "vmQ", "",
[IsOverloadNone, IsStreamingCompatible, IsInOutZA],
MemEltTyDefault, "aarch64_sme_ldr", []>;
}

////////////////////////////////////////////////////////////////////////////////
// Stores
Expand Down Expand Up @@ -81,13 +83,15 @@ defm SVST1_ZA32 : ZAStore<"za32", "i", "aarch64_sme_st1w", [ImmCheck<0, ImmCheck
defm SVST1_ZA64 : ZAStore<"za64", "l", "aarch64_sme_st1d", [ImmCheck<0, ImmCheck0_7>]>;
defm SVST1_ZA128 : ZAStore<"za128", "q", "aarch64_sme_st1q", [ImmCheck<0, ImmCheck0_15>]>;

let TargetGuard = "sme" in {
def SVSTR_VNUM_ZA : MInst<"svstr_vnum_za", "vm%l", "",
[IsOverloadNone, IsStreamingCompatible, IsInZA],
MemEltTyDefault, "aarch64_sme_str">;

def SVSTR_ZA : MInst<"svstr_za", "vm%", "",
[IsOverloadNone, IsStreamingCompatible, IsInZA],
MemEltTyDefault, "aarch64_sme_str", []>;
}

////////////////////////////////////////////////////////////////////////////////
// Read horizontal/vertical ZA slices
Expand Down Expand Up @@ -277,22 +281,22 @@ multiclass ZAAddSub<string n_suffix> {

def NAME # _ZA32_VG1x2_I32 : Inst<"sv" # n_suffix # "_za32[_{d}]_vg1x2", "vm2", "iUif", MergeNone, "aarch64_sme_" # n_suffix # "_za32_vg1x2", [IsStreaming, IsInOutZA], []>;
def NAME # _ZA32_VG1X4_I32 : Inst<"sv" # n_suffix # "_za32[_{d}]_vg1x4", "vm4", "iUif", MergeNone, "aarch64_sme_" # n_suffix # "_za32_vg1x4", [IsStreaming, IsInOutZA], []>;
}

let TargetGuard = "sme-i16i64" in {
def NAME # _WRITE_SINGLE_ZA64_VG1X2_I64 : Inst<"sv" # n_suffix # "_write[_single]_za64[_{d}]_vg1x2", "vm2d", "lUl", MergeNone, "aarch64_sme_" # n_suffix # "_write_single_za_vg1x2", [IsStreaming, IsInOutZA], []>;
def NAME # _WRITE_SINGLE_ZA64_VG1X4_I64 : Inst<"sv" # n_suffix # "_write[_single]_za64[_{d}]_vg1x4", "vm4d", "lUl", MergeNone, "aarch64_sme_" # n_suffix # "_write_single_za_vg1x4", [IsStreaming, IsInOutZA], []>;
let TargetGuard = "sme2,sme-i16i64" in {
def NAME # _WRITE_SINGLE_ZA64_VG1X2_I64 : Inst<"sv" # n_suffix # "_write[_single]_za64[_{d}]_vg1x2", "vm2d", "lUl", MergeNone, "aarch64_sme_" # n_suffix # "_write_single_za_vg1x2", [IsStreaming, IsInOutZA], []>;
def NAME # _WRITE_SINGLE_ZA64_VG1X4_I64 : Inst<"sv" # n_suffix # "_write[_single]_za64[_{d}]_vg1x4", "vm4d", "lUl", MergeNone, "aarch64_sme_" # n_suffix # "_write_single_za_vg1x4", [IsStreaming, IsInOutZA], []>;

def NAME # _WRITE_ZA64_VG1x2_I64 : Inst<"sv" # n_suffix # "_write_za64[_{d}]_vg1x2", "vm22", "lUl", MergeNone, "aarch64_sme_" # n_suffix # "_write_za_vg1x2", [IsStreaming, IsInOutZA], []>;
def NAME # _WRITE_ZA64_VG1x4_I64 : Inst<"sv" # n_suffix # "_write_za64[_{d}]_vg1x4", "vm44", "lUl", MergeNone, "aarch64_sme_" # n_suffix # "_write_za_vg1x4", [IsStreaming, IsInOutZA], []>;
def NAME # _WRITE_ZA64_VG1x2_I64 : Inst<"sv" # n_suffix # "_write_za64[_{d}]_vg1x2", "vm22", "lUl", MergeNone, "aarch64_sme_" # n_suffix # "_write_za_vg1x2", [IsStreaming, IsInOutZA], []>;
def NAME # _WRITE_ZA64_VG1x4_I64 : Inst<"sv" # n_suffix # "_write_za64[_{d}]_vg1x4", "vm44", "lUl", MergeNone, "aarch64_sme_" # n_suffix # "_write_za_vg1x4", [IsStreaming, IsInOutZA], []>;

def NAME # _ZA64_VG1X2_I64 : Inst<"sv" # n_suffix # "_za64[_{d}]_vg1x2", "vm2", "lUl", MergeNone, "aarch64_sme_" # n_suffix # "_za64_vg1x2", [IsStreaming, IsInOutZA], []>;
def NAME # _ZA64_VG1X4_I64 : Inst<"sv" # n_suffix # "_za64[_{d}]_vg1x4", "vm4", "lUl", MergeNone, "aarch64_sme_" # n_suffix # "_za64_vg1x4", [IsStreaming, IsInOutZA], []>;
}
def NAME # _ZA64_VG1X2_I64 : Inst<"sv" # n_suffix # "_za64[_{d}]_vg1x2", "vm2", "lUl", MergeNone, "aarch64_sme_" # n_suffix # "_za64_vg1x2", [IsStreaming, IsInOutZA], []>;
def NAME # _ZA64_VG1X4_I64 : Inst<"sv" # n_suffix # "_za64[_{d}]_vg1x4", "vm4", "lUl", MergeNone, "aarch64_sme_" # n_suffix # "_za64_vg1x4", [IsStreaming, IsInOutZA], []>;
}

let TargetGuard = "sme-f64f64" in {
def NAME # _ZA64_VG1X2_F64 : Inst<"sv" # n_suffix # "_za64[_{d}]_vg1x2", "vm2", "d", MergeNone, "aarch64_sme_" # n_suffix # "_za64_vg1x2", [IsStreaming, IsInOutZA], []>;
def NAME # _ZA64_VG1X4_F64 : Inst<"sv" # n_suffix # "_za64[_{d}]_vg1x4", "vm4", "d", MergeNone, "aarch64_sme_" # n_suffix # "_za64_vg1x4", [IsStreaming, IsInOutZA], []>;
}
let TargetGuard = "sme2,sme-f64f64" in {
def NAME # _ZA64_VG1X2_F64 : Inst<"sv" # n_suffix # "_za64[_{d}]_vg1x2", "vm2", "d", MergeNone, "aarch64_sme_" # n_suffix # "_za64_vg1x2", [IsStreaming, IsInOutZA], []>;
def NAME # _ZA64_VG1X4_F64 : Inst<"sv" # n_suffix # "_za64[_{d}]_vg1x4", "vm4", "d", MergeNone, "aarch64_sme_" # n_suffix # "_za64_vg1x4", [IsStreaming, IsInOutZA], []>;
}
}

Expand Down
11 changes: 11 additions & 0 deletions clang/include/clang/Format/Format.h
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,16 @@ struct FormatStyle {

/// Different ways to break after the template declaration.
enum BreakTemplateDeclarationsStyle : int8_t {
/// Do not change the line breaking before the declaration.
/// \code
/// template <typename T>
/// T foo() {
/// }
/// template <typename T> T foo(int aaaaaaaaaaaaaaaaaaaaa,
/// int bbbbbbbbbbbbbbbbbbbbb) {
/// }
/// \endcode
BTDS_Leave,
/// Do not force break before declaration.
/// ``PenaltyBreakTemplateDeclaration`` is taken into account.
/// \code
Expand Down Expand Up @@ -4846,6 +4856,7 @@ struct FormatStyle {
R.IncludeStyle.IncludeIsMainRegex &&
IncludeStyle.IncludeIsMainSourceRegex ==
R.IncludeStyle.IncludeIsMainSourceRegex &&
IncludeStyle.MainIncludeChar == R.IncludeStyle.MainIncludeChar &&
IndentAccessModifiers == R.IndentAccessModifiers &&
IndentCaseBlocks == R.IndentCaseBlocks &&
IndentCaseLabels == R.IndentCaseLabels &&
Expand Down
11 changes: 6 additions & 5 deletions clang/include/clang/Lex/Preprocessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -2838,7 +2838,8 @@ class Preprocessor {
return AnnotationInfos.find(II)->second;
}

void emitMacroExpansionWarnings(const Token &Identifier) const {
void emitMacroExpansionWarnings(const Token &Identifier,
bool IsIfnDef = false) const {
IdentifierInfo *Info = Identifier.getIdentifierInfo();
if (Info->isDeprecatedMacro())
emitMacroDeprecationWarning(Identifier);
Expand All @@ -2847,12 +2848,12 @@ class Preprocessor {
!SourceMgr.isInMainFile(Identifier.getLocation()))
emitRestrictExpansionWarning(Identifier);

if (Info->getName() == "INFINITY")
if (getLangOpts().NoHonorInfs)
if (!IsIfnDef) {
if (Info->getName() == "INFINITY" && getLangOpts().NoHonorInfs)
emitRestrictInfNaNWarning(Identifier, 0);
if (Info->getName() == "NAN")
if (getLangOpts().NoHonorNaNs)
if (Info->getName() == "NAN" && getLangOpts().NoHonorNaNs)
emitRestrictInfNaNWarning(Identifier, 1);
}
}

static void processPathForFileMacro(SmallVectorImpl<char> &Path,
Expand Down
23 changes: 23 additions & 0 deletions clang/include/clang/Tooling/Inclusions/IncludeStyle.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,21 @@ struct IncludeStyle {
/// before any other include.
/// \version 10
std::string IncludeIsMainSourceRegex;

/// Character to consider in the include directives for the main header.
enum MainIncludeCharDiscriminator : int8_t {
/// Main include uses quotes: ``#include "foo.hpp"`` (the default).
MICD_Quote,
/// Main include uses angle brackets: ``#include <foo.hpp>``.
MICD_AngleBracket,
/// Main include uses either quotes or angle brackets.
MICD_Any
};

/// When guessing whether a #include is the "main" include, only the include
/// directives that use the specified character are considered.
/// \version 18
MainIncludeCharDiscriminator MainIncludeChar;
};

} // namespace tooling
Expand All @@ -174,6 +189,14 @@ struct ScalarEnumerationTraits<
enumeration(IO &IO, clang::tooling::IncludeStyle::IncludeBlocksStyle &Value);
};

template <>
struct ScalarEnumerationTraits<
clang::tooling::IncludeStyle::MainIncludeCharDiscriminator> {
static void enumeration(
IO &IO,
clang::tooling::IncludeStyle::MainIncludeCharDiscriminator &Value);
};

} // namespace yaml
} // namespace llvm

Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11427,6 +11427,10 @@ class FixedPointExprEvaluator
return true;
}

bool ZeroInitialization(const Expr *E) {
return Success(0, E);
}

//===--------------------------------------------------------------------===//
// Visitor Methods
//===--------------------------------------------------------------------===//
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/AST/Interp/ByteCodeEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "Opcode.h"
#include "Program.h"
#include "clang/AST/ASTLambda.h"
#include "clang/AST/Attr.h"
#include "clang/AST/DeclCXX.h"
#include "clang/Basic/Builtins.h"
#include <type_traits>
Expand Down Expand Up @@ -116,7 +117,8 @@ Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl))
IsEligibleForCompilation = MD->isLambdaStaticInvoker();
if (!IsEligibleForCompilation)
IsEligibleForCompilation = FuncDecl->isConstexpr();
IsEligibleForCompilation =
FuncDecl->isConstexpr() || FuncDecl->hasAttr<MSConstexprAttr>();

// Compile the function body.
if (!IsEligibleForCompilation || !visitFunc(FuncDecl)) {
Expand Down
72 changes: 65 additions & 7 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
// Special case for C++'s three-way/spaceship operator <=>, which
// returns a std::{strong,weak,partial}_ordering (which is a class, so doesn't
// have a PrimType).
if (!T) {
if (!T && Ctx.getLangOpts().CPlusPlus) {
if (DiscardResult)
return true;
const ComparisonCategoryInfo *CmpInfo =
Expand Down Expand Up @@ -820,6 +820,19 @@ bool ByteCodeExprGen<Emitter>::VisitImplicitValueInitExpr(const ImplicitValueIni
return true;
}

if (QT->isAnyComplexType()) {
assert(Initializing);
QualType ElemQT = QT->getAs<ComplexType>()->getElementType();
PrimType ElemT = classifyPrim(ElemQT);
for (unsigned I = 0; I < 2; ++I) {
if (!this->visitZeroInitializer(ElemT, ElemQT, E))
return false;
if (!this->emitInitElem(ElemT, I, E))
return false;
}
return true;
}

return false;
}

Expand Down Expand Up @@ -850,6 +863,10 @@ bool ByteCodeExprGen<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
assert(E->getType()->isRecordType());
const Record *R = getRecord(E->getType());

if (Inits.size() == 1 && E->getType() == Inits[0]->getType()) {
return this->visitInitializer(Inits[0]);
}

unsigned InitIndex = 0;
for (const Expr *Init : Inits) {
if (!this->emitDupPtr(E))
Expand Down Expand Up @@ -1030,14 +1047,17 @@ bool ByteCodeExprGen<Emitter>::VisitSubstNonTypeTemplateParmExpr(

template <class Emitter>
bool ByteCodeExprGen<Emitter>::VisitConstantExpr(const ConstantExpr *E) {
// Try to emit the APValue directly, without visiting the subexpr.
// This will only fail if we can't emit the APValue, so won't emit any
// diagnostics or any double values.
std::optional<PrimType> T = classify(E->getType());
if (T && E->hasAPValueResult() &&
this->visitAPValue(E->getAPValueResult(), *T, E))
return true;
if (T && E->hasAPValueResult()) {
// Try to emit the APValue directly, without visiting the subexpr.
// This will only fail if we can't emit the APValue, so won't emit any
// diagnostics or any double values.
if (DiscardResult)
return true;

if (this->visitAPValue(E->getAPValueResult(), *T, E))
return true;
}
return this->delegate(E->getSubExpr());
}

Expand Down Expand Up @@ -1069,6 +1089,12 @@ bool ByteCodeExprGen<Emitter>::VisitUnaryExprOrTypeTraitExpr(

if (Kind == UETT_SizeOf) {
QualType ArgType = E->getTypeOfArgument();

// C++ [expr.sizeof]p2: "When applied to a reference or a reference type,
// the result is the size of the referenced type."
if (const auto *Ref = ArgType->getAs<ReferenceType>())
ArgType = Ref->getPointeeType();

CharUnits Size;
if (ArgType->isVoidType() || ArgType->isFunctionType())
Size = CharUnits::One();
Expand Down Expand Up @@ -3006,21 +3032,53 @@ bool ByteCodeExprGen<Emitter>::VisitDeclRefExpr(const DeclRefExpr *E) {
// pointer to the actual value) instead of a pointer to the pointer to the
// value.
bool IsReference = D->getType()->isReferenceType();
// Complex values are copied in the AST via a simply assignment or
// ltor cast. But we represent them as two-element arrays, which means
// we pass them around as pointers. So, to assignm from them, we will
// have to copy both (primitive) elements instead.
bool IsComplex = D->getType()->isAnyComplexType();

// Check for local/global variables and parameters.
if (auto It = Locals.find(D); It != Locals.end()) {
const unsigned Offset = It->second.Offset;
// FIXME: Fix the code duplication here with the code in the global case.
if (Initializing && IsComplex) {
PrimType ElemT = classifyComplexElementType(D->getType());
for (unsigned I = 0; I != 2; ++I) {
if (!this->emitGetPtrLocal(Offset, E))
return false;
if (!this->emitArrayElemPop(ElemT, I, E))
return false;
if (!this->emitInitElem(ElemT, I, E))
return false;
}
return true;
}

if (IsReference)
return this->emitGetLocal(PT_Ptr, Offset, E);
return this->emitGetPtrLocal(Offset, E);
} else if (auto GlobalIndex = P.getGlobal(D)) {
if (Initializing && IsComplex) {
PrimType ElemT = classifyComplexElementType(D->getType());
for (unsigned I = 0; I != 2; ++I) {
if (!this->emitGetPtrGlobal(*GlobalIndex, E))
return false;
if (!this->emitArrayElemPop(ElemT, I, E))
return false;
if (!this->emitInitElem(ElemT, I, E))
return false;
}
return true;
}

if (IsReference)
return this->emitGetGlobalPtr(*GlobalIndex, E);

return this->emitGetPtrGlobal(*GlobalIndex, E);
} else if (const auto *PVD = dyn_cast<ParmVarDecl>(D)) {
if (auto It = this->Params.find(PVD); It != this->Params.end()) {
// FIXME: _Complex initializing case?
if (IsReference || !It->second.IsPtr)
return this->emitGetParamPtr(It->second.Offset, E);

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/Interp/ByteCodeStmtGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ bool ByteCodeStmtGen<Emitter>::visitCompoundStmt(
template <class Emitter>
bool ByteCodeStmtGen<Emitter>::visitDeclStmt(const DeclStmt *DS) {
for (auto *D : DS->decls()) {
if (isa<StaticAssertDecl, TagDecl, TypedefNameDecl>(D))
if (isa<StaticAssertDecl, TagDecl, TypedefNameDecl, UsingEnumDecl>(D))
continue;

const auto *VD = dyn_cast<VarDecl>(D);
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Interp/Descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ static void dtorArrayTy(Block *, std::byte *Ptr, const Descriptor *D) {
template <typename T>
static void moveArrayTy(Block *, const std::byte *Src, std::byte *Dst,
const Descriptor *D) {
// FIXME: Need to copy the InitMap?
Src += sizeof(InitMapPtr);
Dst += sizeof(InitMapPtr);
for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) {
const auto *SrcPtr = &reinterpret_cast<const T *>(Src)[I];
auto *DstPtr = &reinterpret_cast<T *>(Dst)[I];
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ static bool CheckConstant(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
}

bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
return !Ptr.isZero() && !Ptr.isDummy();
return !Ptr.isDummy();
}

bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
Expand Down
38 changes: 26 additions & 12 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -1855,13 +1855,38 @@ inline bool ArrayElemPtr(InterpState &S, CodePtr OpPC) {
const T &Offset = S.Stk.pop<T>();
const Pointer &Ptr = S.Stk.peek<Pointer>();

if (!CheckDummy(S, OpPC, Ptr))
return false;

if (!OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr))
return false;

return NarrowPtr(S, OpPC);
}

template <PrimType Name, class T = typename PrimConv<Name>::T>
inline bool ArrayElemPtrPop(InterpState &S, CodePtr OpPC) {
const T &Offset = S.Stk.pop<T>();
const Pointer &Ptr = S.Stk.pop<Pointer>();

if (!CheckDummy(S, OpPC, Ptr))
return false;

if (!OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr))
return false;

return NarrowPtr(S, OpPC);
}

/// Just takes a pointer and checks if its' an incomplete
template <PrimType Name, class T = typename PrimConv<Name>::T>
inline bool ArrayElemPop(InterpState &S, CodePtr OpPC, uint32_t Index) {
const Pointer &Ptr = S.Stk.pop<Pointer>();

S.Stk.push<T>(Ptr.atIndex(Index).deref<T>());
return true;
}

/// Just takes a pointer and checks if it's an incomplete
/// array type.
inline bool ArrayDecay(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
Expand All @@ -1877,17 +1902,6 @@ inline bool ArrayDecay(InterpState &S, CodePtr OpPC) {
return false;
}

template <PrimType Name, class T = typename PrimConv<Name>::T>
inline bool ArrayElemPtrPop(InterpState &S, CodePtr OpPC) {
const T &Offset = S.Stk.pop<T>();
const Pointer &Ptr = S.Stk.pop<Pointer>();

if (!OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr))
return false;

return NarrowPtr(S, OpPC);
}

inline bool Call(InterpState &S, CodePtr OpPC, const Function *Func) {
if (Func->hasThisPointer()) {
size_t ThisOffset =
Expand Down
18 changes: 18 additions & 0 deletions clang/lib/AST/Interp/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,19 @@ static bool interp__builtin_move(InterpState &S, CodePtr OpPC,
return Func->getDecl()->isConstexpr();
}

static bool interp__builtin_eh_return_data_regno(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType());
APSInt Arg = peekToAPSInt(S.Stk, ArgT);

int Result =
S.getCtx().getTargetInfo().getEHDataRegisterNumber(Arg.getZExtValue());
pushInt(S, Result);
return true;
}

bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
const CallExpr *Call) {
InterpFrame *Frame = S.Current;
Expand Down Expand Up @@ -869,6 +882,11 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
return false;
break;

case Builtin::BI__builtin_eh_return_data_regno:
if (!interp__builtin_eh_return_data_regno(S, OpPC, Frame, F, Call))
return false;
break;

default:
return false;
}
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/Interp/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,12 @@ def ExpandPtr : Opcode;
def ArrayElemPtr : AluOpcode;
def ArrayElemPtrPop : AluOpcode;

def ArrayElemPop : Opcode {
let Args = [ArgUint32];
let Types = [AllTypeClass];
let HasGroup = 1;
}

//===----------------------------------------------------------------------===//
// Direct field accessors
//===----------------------------------------------------------------------===//
Expand Down
8 changes: 6 additions & 2 deletions clang/lib/AST/Interp/Pointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,8 +339,12 @@ class Pointer {
}
/// Checks if a structure is a base class.
bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; }
/// Checks if the pointer pointers to a dummy value.
bool isDummy() const { return getDeclDesc()->isDummy(); }
/// Checks if the pointer points to a dummy value.
bool isDummy() const {
if (!Pointee)
return false;
return getDeclDesc()->isDummy();
}

/// Checks if an object or a subfield is mutable.
bool isConst() const {
Expand Down
59 changes: 28 additions & 31 deletions clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,15 @@ static bool compareDistinctValues(QualType Type, Value &Val1,
llvm_unreachable("All cases covered in switch");
}

/// Attempts to merge distinct values `Val1` and `Val2` in `Env1` and `Env2`,
/// respectively, of the same type `Type`. Merging generally produces a single
/// Attempts to join distinct values `Val1` and `Val2` in `Env1` and `Env2`,
/// respectively, of the same type `Type`. Joining generally produces a single
/// value that (soundly) approximates the two inputs, although the actual
/// meaning depends on `Model`.
static Value *mergeDistinctValues(QualType Type, Value &Val1,
const Environment &Env1, Value &Val2,
const Environment &Env2,
Environment &MergedEnv,
Environment::ValueModel &Model) {
static Value *joinDistinctValues(QualType Type, Value &Val1,
const Environment &Env1, Value &Val2,
const Environment &Env2,
Environment &JoinedEnv,
Environment::ValueModel &Model) {
// Join distinct boolean values preserving information about the constraints
// in the respective path conditions.
if (isa<BoolValue>(&Val1) && isa<BoolValue>(&Val2)) {
Expand All @@ -113,42 +113,39 @@ static Value *mergeDistinctValues(QualType Type, Value &Val1,
// ```
auto &Expr1 = cast<BoolValue>(Val1).formula();
auto &Expr2 = cast<BoolValue>(Val2).formula();
auto &A = MergedEnv.arena();
auto &MergedVal = A.makeAtomRef(A.makeAtom());
MergedEnv.assume(
auto &A = JoinedEnv.arena();
auto &JoinedVal = A.makeAtomRef(A.makeAtom());
JoinedEnv.assume(
A.makeOr(A.makeAnd(A.makeAtomRef(Env1.getFlowConditionToken()),
A.makeEquals(MergedVal, Expr1)),
A.makeEquals(JoinedVal, Expr1)),
A.makeAnd(A.makeAtomRef(Env2.getFlowConditionToken()),
A.makeEquals(MergedVal, Expr2))));
return &A.makeBoolValue(MergedVal);
A.makeEquals(JoinedVal, Expr2))));
return &A.makeBoolValue(JoinedVal);
}

Value *MergedVal = nullptr;
Value *JoinedVal = nullptr;
if (auto *RecordVal1 = dyn_cast<RecordValue>(&Val1)) {
auto *RecordVal2 = cast<RecordValue>(&Val2);

if (&RecordVal1->getLoc() == &RecordVal2->getLoc())
// `RecordVal1` and `RecordVal2` may have different properties associated
// with them. Create a new `RecordValue` with the same location but
// without any properties so that we soundly approximate both values. If a
// particular analysis needs to merge properties, it should do so in
// `DataflowAnalysis::merge()`.
MergedVal = &MergedEnv.create<RecordValue>(RecordVal1->getLoc());
// particular analysis needs to join properties, it should do so in
// `DataflowAnalysis::join()`.
JoinedVal = &JoinedEnv.create<RecordValue>(RecordVal1->getLoc());
else
// If the locations for the two records are different, need to create a
// completely new value.
MergedVal = MergedEnv.createValue(Type);
JoinedVal = JoinedEnv.createValue(Type);
} else {
MergedVal = MergedEnv.createValue(Type);
JoinedVal = JoinedEnv.createValue(Type);
}

// FIXME: Consider destroying `MergedValue` immediately if `ValueModel::merge`
// returns false to avoid storing unneeded values in `DACtx`.
if (MergedVal)
if (Model.merge(Type, Val1, Env1, Val2, Env2, *MergedVal, MergedEnv))
return MergedVal;
if (JoinedVal)
Model.join(Type, Val1, Env1, Val2, Env2, *JoinedVal, JoinedEnv);

return nullptr;
return JoinedVal;
}

// When widening does not change `Current`, return value will equal `&Prev`.
Expand Down Expand Up @@ -240,9 +237,9 @@ joinLocToVal(const llvm::MapVector<const StorageLocation *, Value *> &LocToVal,
continue;
}

if (Value *MergedVal = mergeDistinctValues(
if (Value *JoinedVal = joinDistinctValues(
Loc->getType(), *Val, Env1, *It->second, Env2, JoinedEnv, Model)) {
Result.insert({Loc, MergedVal});
Result.insert({Loc, JoinedVal});
}
}

Expand Down Expand Up @@ -657,10 +654,10 @@ Environment Environment::join(const Environment &EnvA, const Environment &EnvB,
// cast.
auto *Func = dyn_cast<FunctionDecl>(EnvA.CallStack.back());
assert(Func != nullptr);
if (Value *MergedVal =
mergeDistinctValues(Func->getReturnType(), *EnvA.ReturnVal, EnvA,
*EnvB.ReturnVal, EnvB, JoinedEnv, Model))
JoinedEnv.ReturnVal = MergedVal;
if (Value *JoinedVal =
joinDistinctValues(Func->getReturnType(), *EnvA.ReturnVal, EnvA,
*EnvB.ReturnVal, EnvB, JoinedEnv, Model))
JoinedEnv.ReturnVal = JoinedVal;
}

if (EnvA.ReturnLoc == EnvB.ReturnLoc)
Expand Down
37 changes: 22 additions & 15 deletions clang/lib/Analysis/UnsafeBufferUsage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2870,19 +2870,6 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
#endif
it = FixablesForAllVars.byVar.erase(it);
} else if (Tracker.hasUnclaimedUses(it->first)) {
#ifndef NDEBUG
auto AllUnclaimed = Tracker.getUnclaimedUses(it->first);
for (auto UnclaimedDRE : AllUnclaimed) {
std::string UnclaimedUseTrace =
getDREAncestorString(UnclaimedDRE, D->getASTContext());

Handler.addDebugNoteForVar(
it->first, UnclaimedDRE->getBeginLoc(),
("failed to produce fixit for '" + it->first->getNameAsString() +
"' : has an unclaimed use\nThe unclaimed DRE trace: " +
UnclaimedUseTrace));
}
#endif
it = FixablesForAllVars.byVar.erase(it);
} else if (it->first->isInitCapture()) {
#ifndef NDEBUG
Expand All @@ -2892,10 +2879,30 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
"' : init capture"));
#endif
it = FixablesForAllVars.byVar.erase(it);
}else {
++it;
} else {
++it;
}
}

#ifndef NDEBUG
for (const auto &it : UnsafeOps.byVar) {
const VarDecl *const UnsafeVD = it.first;
auto UnclaimedDREs = Tracker.getUnclaimedUses(UnsafeVD);
if (UnclaimedDREs.empty())
continue;
const auto UnfixedVDName = UnsafeVD->getNameAsString();
for (const clang::DeclRefExpr *UnclaimedDRE : UnclaimedDREs) {
std::string UnclaimedUseTrace =
getDREAncestorString(UnclaimedDRE, D->getASTContext());

Handler.addDebugNoteForVar(
UnsafeVD, UnclaimedDRE->getBeginLoc(),
("failed to produce fixit for '" + UnfixedVDName +
"' : has an unclaimed use\nThe unclaimed DRE trace: " +
UnclaimedUseTrace));
}
}
#endif

// Fixpoint iteration for pointer assignments
using DepMapTy = DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>;
Expand Down
22 changes: 15 additions & 7 deletions clang/lib/CodeGen/CGExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,7 @@ class ScalarExprEmitter
void EmitUndefinedBehaviorIntegerDivAndRemCheck(const BinOpInfo &Ops,
llvm::Value *Zero,bool isDiv);
// Common helper for getting how wide LHS of shift is.
static Value *GetWidthMinusOneValue(Value* LHS,Value* RHS);
static Value *GetMaximumShiftAmount(Value *LHS, Value *RHS);

// Used for shifting constraints for OpenCL, do mask for powers of 2, URem for
// non powers of two.
Expand Down Expand Up @@ -4115,13 +4115,21 @@ Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) {
return Builder.CreateExactSDiv(diffInChars, divisor, "sub.ptr.div");
}

Value *ScalarExprEmitter::GetWidthMinusOneValue(Value* LHS,Value* RHS) {
Value *ScalarExprEmitter::GetMaximumShiftAmount(Value *LHS, Value *RHS) {
llvm::IntegerType *Ty;
if (llvm::VectorType *VT = dyn_cast<llvm::VectorType>(LHS->getType()))
Ty = cast<llvm::IntegerType>(VT->getElementType());
else
Ty = cast<llvm::IntegerType>(LHS->getType());
return llvm::ConstantInt::get(RHS->getType(), Ty->getBitWidth() - 1);
// For a given type of LHS the maximum shift amount is width(LHS)-1, however
// it can occur that width(LHS)-1 > range(RHS). Since there is no check for
// this in ConstantInt::get, this results in the value getting truncated.
// Constrain the return value to be max(RHS) in this case.
llvm::Type *RHSTy = RHS->getType();
llvm::APInt RHSMax = llvm::APInt::getMaxValue(RHSTy->getScalarSizeInBits());
if (RHSMax.ult(Ty->getBitWidth()))
return llvm::ConstantInt::get(RHSTy, RHSMax);
return llvm::ConstantInt::get(RHSTy, Ty->getBitWidth() - 1);
}

Value *ScalarExprEmitter::ConstrainShiftValue(Value *LHS, Value *RHS,
Expand All @@ -4133,7 +4141,7 @@ Value *ScalarExprEmitter::ConstrainShiftValue(Value *LHS, Value *RHS,
Ty = cast<llvm::IntegerType>(LHS->getType());

if (llvm::isPowerOf2_64(Ty->getBitWidth()))
return Builder.CreateAnd(RHS, GetWidthMinusOneValue(LHS, RHS), Name);
return Builder.CreateAnd(RHS, GetMaximumShiftAmount(LHS, RHS), Name);

return Builder.CreateURem(
RHS, llvm::ConstantInt::get(RHS->getType(), Ty->getBitWidth()), Name);
Expand Down Expand Up @@ -4166,7 +4174,7 @@ Value *ScalarExprEmitter::EmitShl(const BinOpInfo &Ops) {
isa<llvm::IntegerType>(Ops.LHS->getType())) {
CodeGenFunction::SanitizerScope SanScope(&CGF);
SmallVector<std::pair<Value *, SanitizerMask>, 2> Checks;
llvm::Value *WidthMinusOne = GetWidthMinusOneValue(Ops.LHS, Ops.RHS);
llvm::Value *WidthMinusOne = GetMaximumShiftAmount(Ops.LHS, Ops.RHS);
llvm::Value *ValidExponent = Builder.CreateICmpULE(Ops.RHS, WidthMinusOne);

if (SanitizeExponent) {
Expand All @@ -4184,7 +4192,7 @@ Value *ScalarExprEmitter::EmitShl(const BinOpInfo &Ops) {
Builder.CreateCondBr(ValidExponent, CheckShiftBase, Cont);
llvm::Value *PromotedWidthMinusOne =
(RHS == Ops.RHS) ? WidthMinusOne
: GetWidthMinusOneValue(Ops.LHS, RHS);
: GetMaximumShiftAmount(Ops.LHS, RHS);
CGF.EmitBlock(CheckShiftBase);
llvm::Value *BitsShiftedOff = Builder.CreateLShr(
Ops.LHS, Builder.CreateSub(PromotedWidthMinusOne, RHS, "shl.zeros",
Expand Down Expand Up @@ -4235,7 +4243,7 @@ Value *ScalarExprEmitter::EmitShr(const BinOpInfo &Ops) {
isa<llvm::IntegerType>(Ops.LHS->getType())) {
CodeGenFunction::SanitizerScope SanScope(&CGF);
llvm::Value *Valid =
Builder.CreateICmpULE(RHS, GetWidthMinusOneValue(Ops.LHS, RHS));
Builder.CreateICmpULE(Ops.RHS, GetMaximumShiftAmount(Ops.LHS, Ops.RHS));
EmitBinOpCheck(std::make_pair(Valid, SanitizerKind::ShiftExponent), Ops);
}

Expand Down
20 changes: 11 additions & 9 deletions clang/lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1443,16 +1443,18 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
const ToolChain &TC = getToolChain(
*UArgs, computeTargetTriple(*this, TargetTriple, *UArgs));

// Check if the environment version is valid.
// Check if the environment version is valid except wasm case.
llvm::Triple Triple = TC.getTriple();
StringRef TripleVersionName = Triple.getEnvironmentVersionString();
StringRef TripleObjectFormat =
Triple.getObjectFormatTypeName(Triple.getObjectFormat());
if (Triple.getEnvironmentVersion().empty() && TripleVersionName != "" &&
TripleVersionName != TripleObjectFormat) {
Diags.Report(diag::err_drv_triple_version_invalid)
<< TripleVersionName << TC.getTripleString();
ContainsError = true;
if (!Triple.isWasm()) {
StringRef TripleVersionName = Triple.getEnvironmentVersionString();
StringRef TripleObjectFormat =
Triple.getObjectFormatTypeName(Triple.getObjectFormat());
if (Triple.getEnvironmentVersion().empty() && TripleVersionName != "" &&
TripleVersionName != TripleObjectFormat) {
Diags.Report(diag::err_drv_triple_version_invalid)
<< TripleVersionName << TC.getTripleString();
ContainsError = true;
}
}

// Report warning when arm64EC option is overridden by specified target
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5972,6 +5972,14 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
<< A->getAsString(Args) << A->getValue();
else
A->render(Args, CmdArgs);
} else if (Triple.isAArch64() && Triple.isOSBinFormatELF()) {
// "all" is not supported on AArch64 since branch relaxation creates new
// basic blocks for some cross-section branches.
if (Val != "labels" && Val != "none" && !Val.starts_with("list="))
D.Diag(diag::err_drv_invalid_value)
<< A->getAsString(Args) << A->getValue();
else
A->render(Args, CmdArgs);
} else if (Triple.isNVPTX()) {
// Do not pass the option to the GPU compilation. We still want it enabled
// for the host-side compilation, so seeing it here is not an error.
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Format/BreakableToken.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -449,11 +449,11 @@ const FormatToken &BreakableComment::tokenAt(unsigned LineIndex) const {

static bool mayReflowContent(StringRef Content) {
Content = Content.trim(Blanks);
// Lines starting with '@' commonly have special meaning.
// Lines starting with '@' or '\' commonly have special meaning.
// Lines starting with '-', '-#', '+' or '*' are bulleted/numbered lists.
bool hasSpecialMeaningPrefix = false;
for (StringRef Prefix :
{"@", "TODO", "FIXME", "XXX", "-# ", "- ", "+ ", "* "}) {
{"@", "\\", "TODO", "FIXME", "XXX", "-# ", "- ", "+ ", "* "}) {
if (Content.starts_with(Prefix)) {
hasSpecialMeaningPrefix = true;
break;
Expand Down
12 changes: 9 additions & 3 deletions clang/lib/Format/ContinuationIndenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,10 @@ bool ContinuationIndenter::mustBreak(const LineState &State) {
return true;
}
}
return Style.AlwaysBreakTemplateDeclarations != FormatStyle::BTDS_No;
return Style.AlwaysBreakTemplateDeclarations != FormatStyle::BTDS_No &&
(Style.AlwaysBreakTemplateDeclarations !=
FormatStyle::BTDS_Leave ||
Current.NewlinesBefore > 0);
}
if (Previous.is(TT_FunctionAnnotationRParen) &&
State.Line->Type != LT_PreprocessorDirective) {
Expand Down Expand Up @@ -1702,8 +1705,11 @@ void ContinuationIndenter::moveStatePastFakeLParens(LineState &State,
// Special case for generic selection expressions, its comma-separated
// expressions are not aligned to the opening paren like regular calls, but
// rather continuation-indented relative to the _Generic keyword.
if (Previous && Previous->endsSequence(tok::l_paren, tok::kw__Generic))
NewParenState.Indent = CurrentState.LastSpace;
if (Previous && Previous->endsSequence(tok::l_paren, tok::kw__Generic) &&
State.Stack.size() > 1) {
NewParenState.Indent = State.Stack[State.Stack.size() - 2].Indent +
Style.ContinuationIndentWidth;
}

if ((shouldUnindentNextOperator(Current) ||
(Previous &&
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Format/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ template <>
struct ScalarEnumerationTraits<FormatStyle::BreakTemplateDeclarationsStyle> {
static void enumeration(IO &IO,
FormatStyle::BreakTemplateDeclarationsStyle &Value) {
IO.enumCase(Value, "Leave", FormatStyle::BTDS_Leave);
IO.enumCase(Value, "No", FormatStyle::BTDS_No);
IO.enumCase(Value, "MultiLine", FormatStyle::BTDS_MultiLine);
IO.enumCase(Value, "Yes", FormatStyle::BTDS_Yes);
Expand Down Expand Up @@ -1018,6 +1019,7 @@ template <> struct MappingTraits<FormatStyle> {
IO.mapOptional("MacroBlockBegin", Style.MacroBlockBegin);
IO.mapOptional("MacroBlockEnd", Style.MacroBlockEnd);
IO.mapOptional("Macros", Style.Macros);
IO.mapOptional("MainIncludeChar", Style.IncludeStyle.MainIncludeChar);
IO.mapOptional("MaxEmptyLinesToKeep", Style.MaxEmptyLinesToKeep);
IO.mapOptional("NamespaceIndentation", Style.NamespaceIndentation);
IO.mapOptional("NamespaceMacros", Style.NamespaceMacros);
Expand Down Expand Up @@ -1496,6 +1498,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
{".*", 1, 0, false}};
LLVMStyle.IncludeStyle.IncludeIsMainRegex = "(Test)?$";
LLVMStyle.IncludeStyle.IncludeBlocks = tooling::IncludeStyle::IBS_Preserve;
LLVMStyle.IncludeStyle.MainIncludeChar = tooling::IncludeStyle::MICD_Quote;
LLVMStyle.IndentAccessModifiers = false;
LLVMStyle.IndentCaseLabels = false;
LLVMStyle.IndentCaseBlocks = false;
Expand Down
10 changes: 8 additions & 2 deletions clang/lib/Format/TokenAnnotator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5184,7 +5184,9 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line,
// concept ...
if (Right.is(tok::kw_concept))
return Style.BreakBeforeConceptDeclarations == FormatStyle::BBCDS_Always;
return Style.AlwaysBreakTemplateDeclarations == FormatStyle::BTDS_Yes;
return Style.AlwaysBreakTemplateDeclarations == FormatStyle::BTDS_Yes ||
(Style.AlwaysBreakTemplateDeclarations == FormatStyle::BTDS_Leave &&
Right.NewlinesBefore > 0);
}
if (Left.ClosesRequiresClause && Right.isNot(tok::semi)) {
switch (Style.RequiresClausePosition) {
Expand Down Expand Up @@ -5617,7 +5619,11 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line,
return Style.BreakBeforeConceptDeclarations != FormatStyle::BBCDS_Never;
if (Right.is(TT_RequiresClause))
return true;
if (Left.ClosesTemplateDeclaration || Left.is(TT_FunctionAnnotationRParen))
if (Left.ClosesTemplateDeclaration) {
return Style.AlwaysBreakTemplateDeclarations != FormatStyle::BTDS_Leave ||
Right.NewlinesBefore > 0;
}
if (Left.is(TT_FunctionAnnotationRParen))
return true;
if (Left.ClosesRequiresClause)
return true;
Expand Down
13 changes: 10 additions & 3 deletions clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,12 @@ class VerifyDiagnosticConsumer::MarkerTracker {
}
};

static std::string DetailedErrorString(const DiagnosticsEngine &Diags) {
if (Diags.getDiagnosticOptions().VerifyPrefixes.empty())
return "expected";
return *Diags.getDiagnosticOptions().VerifyPrefixes.begin();
}

/// ParseDirective - Go through the comment and see if it indicates expected
/// diagnostics. If so, then put them in the appropriate directive list.
///
Expand Down Expand Up @@ -478,14 +484,14 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
if (NoDiag) {
if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives)
Diags.Report(Pos, diag::err_verify_invalid_no_diags)
<< /*IsExpectedNoDiagnostics=*/true;
<< DetailedErrorString(Diags) << /*IsExpectedNoDiagnostics=*/true;
else
Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics;
continue;
}
if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) {
Diags.Report(Pos, diag::err_verify_invalid_no_diags)
<< /*IsExpectedNoDiagnostics=*/false;
<< DetailedErrorString(Diags) << /*IsExpectedNoDiagnostics=*/false;
continue;
}
Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives;
Expand Down Expand Up @@ -1104,7 +1110,8 @@ void VerifyDiagnosticConsumer::CheckDiagnostics() {
// Produce an error if no expected-* directives could be found in the
// source file(s) processed.
if (Status == HasNoDirectives) {
Diags.Report(diag::err_verify_no_directives).setForceEmit();
Diags.Report(diag::err_verify_no_directives).setForceEmit()
<< DetailedErrorString(Diags);
++NumErrors;
Status = HasNoDirectivesReported;
}
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/Headers/cpuid.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,9 @@
#define bit_AMXINT8 0x02000000

/* Features in %eax for leaf 7 sub-leaf 1 */
#define bit_SHA512 0x00000001
#define bit_SM3 0x00000002
#define bit_SM4 0x00000004
#define bit_RAOINT 0x00000008
#define bit_AVXVNNI 0x00000010
#define bit_AVX512BF16 0x00000020
Expand All @@ -211,7 +214,11 @@
/* Features in %edx for leaf 7 sub-leaf 1 */
#define bit_AVXVNNIINT8 0x00000010
#define bit_AVXNECONVERT 0x00000020
#define bit_AMXCOMPLEX 0x00000100
#define bit_AVXVNNIINT16 0x00000400
#define bit_PREFETCHI 0x00004000
#define bit_USERMSR 0x00008000
#define bit_AVX10 0x00080000

/* Features in %eax for leaf 13 sub-leaf 1 */
#define bit_XSAVEOPT 0x00000001
Expand Down Expand Up @@ -244,6 +251,9 @@
#define bit_RDPRU 0x00000010
#define bit_WBNOINVD 0x00000200

/* Features in %ebx for leaf 0x24 */
#define bit_AVX10_256 0x00020000
#define bit_AVX10_512 0x00040000

#if __i386__
#define __cpuid(__leaf, __eax, __ebx, __ecx, __edx) \
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Lex/PPDirectives.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3288,7 +3288,7 @@ void Preprocessor::HandleIfdefDirective(Token &Result,
return;
}

emitMacroExpansionWarnings(MacroNameTok);
emitMacroExpansionWarnings(MacroNameTok, /*IsIfnDef=*/true);

// Check to see if this is the last token on the #if[n]def line.
CheckEndOfDirective(isIfndef ? "ifndef" : "ifdef");
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Lex/PPExpressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,9 @@ static bool EvaluateDefined(PPValue &Result, Token &PeekTok, DefinedTracker &DT,
Result.Val.setIsUnsigned(false); // Result is signed intmax_t.
DT.IncludedUndefinedIds = !Macro;

PP.emitMacroExpansionWarnings(PeekTok);
PP.emitMacroExpansionWarnings(
PeekTok,
(II->getName() == "INFINITY" || II->getName() == "NAN") ? true : false);

// If there is a macro, mark it used.
if (Result.Val != 0 && ValueLive)
Expand Down
103 changes: 54 additions & 49 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10440,6 +10440,60 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
diag::ext_operator_new_delete_declared_inline)
<< NewFD->getDeclName();

if (Expr *TRC = NewFD->getTrailingRequiresClause()) {
// C++20 [dcl.decl.general]p4:
// The optional requires-clause in an init-declarator or
// member-declarator shall be present only if the declarator declares a
// templated function.
//
// C++20 [temp.pre]p8:
// An entity is templated if it is
// - a template,
// - an entity defined or created in a templated entity,
// - a member of a templated entity,
// - an enumerator for an enumeration that is a templated entity, or
// - the closure type of a lambda-expression appearing in the
// declaration of a templated entity.
//
// [Note 6: A local class, a local or block variable, or a friend
// function defined in a templated entity is a templated entity.
// — end note]
//
// A templated function is a function template or a function that is
// templated. A templated class is a class template or a class that is
// templated. A templated variable is a variable template or a variable
// that is templated.
if (!FunctionTemplate) {
if (isFunctionTemplateSpecialization || isMemberSpecialization) {
// C++ [temp.expl.spec]p8 (proposed resolution for CWG2847):
// An explicit specialization shall not have a trailing
// requires-clause unless it declares a function template.
//
// Since a friend function template specialization cannot be
// definition, and since a non-template friend declaration with a
// trailing requires-clause must be a definition, we diagnose
// friend function template specializations with trailing
// requires-clauses on the same path as explicit specializations
// even though they aren't necessarily prohibited by the same
// language rule.
Diag(TRC->getBeginLoc(), diag::err_non_temp_spec_requires_clause)
<< isFriend;
} else if (isFriend && NewFD->isTemplated() &&
!D.isFunctionDefinition()) {
// C++ [temp.friend]p9:
// A non-template friend declaration with a requires-clause shall be
// a definition.
Diag(NewFD->getBeginLoc(),
diag::err_non_temp_friend_decl_with_requires_clause_must_be_def);
NewFD->setInvalidDecl();
} else if (!NewFD->isTemplated() ||
!(isa<CXXMethodDecl>(NewFD) || D.isFunctionDefinition())) {
Diag(TRC->getBeginLoc(),
diag::err_constrained_non_templated_function);
}
}
}

// We do not add HD attributes to specializations here because
// they may have different constexpr-ness compared to their
// templates and, after maybeAddCUDAHostDeviceAttrs() is applied,
Expand Down Expand Up @@ -12063,55 +12117,6 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
checkThisInStaticMemberFunctionType(Method);
}

if (Expr *TRC = NewFD->getTrailingRequiresClause()) {
// C++20: dcl.decl.general p4:
// The optional requires-clause ([temp.pre]) in an init-declarator or
// member-declarator shall be present only if the declarator declares a
// templated function ([dcl.fct]).
//
// [temp.pre]/8:
// An entity is templated if it is
// - a template,
// - an entity defined ([basic.def]) or created ([class.temporary]) in a
// templated entity,
// - a member of a templated entity,
// - an enumerator for an enumeration that is a templated entity, or
// - the closure type of a lambda-expression ([expr.prim.lambda.closure])
// appearing in the declaration of a templated entity. [Note 6: A local
// class, a local or block variable, or a friend function defined in a
// templated entity is a templated entity. — end note]
//
// A templated function is a function template or a function that is
// templated. A templated class is a class template or a class that is
// templated. A templated variable is a variable template or a variable
// that is templated.

bool IsTemplate = NewFD->getDescribedFunctionTemplate();
bool IsFriend = NewFD->getFriendObjectKind();
if (!IsTemplate && // -a template
// defined... in a templated entity
!(DeclIsDefn && NewFD->isTemplated()) &&
// a member of a templated entity
!(isa<CXXMethodDecl>(NewFD) && NewFD->isTemplated()) &&
// Don't complain about instantiations, they've already had these
// rules + others enforced.
!NewFD->isTemplateInstantiation() &&
// If the function violates [temp.friend]p9 because it is missing
// a definition, and adding a definition would make it templated,
// then let that error take precedence.
!(!DeclIsDefn && IsFriend && NewFD->isTemplated())) {
Diag(TRC->getBeginLoc(), diag::err_constrained_non_templated_function);
} else if (!DeclIsDefn && !IsTemplate && IsFriend &&
!NewFD->isTemplateInstantiation()) {
// C++ [temp.friend]p9:
// A non-template friend declaration with a requires-clause shall be a
// definition.
Diag(NewFD->getBeginLoc(),
diag::err_non_temp_friend_decl_with_requires_clause_must_be_def);
NewFD->setInvalidDecl();
}
}

if (CXXConversionDecl *Conversion = dyn_cast<CXXConversionDecl>(NewFD))
ActOnConversionDeclarator(Conversion);

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14073,7 +14073,7 @@ inline QualType Sema::CheckLogicalOperands(ExprResult &LHS, ExprResult &RHS,
Expr::EvalResult EVResult;
if (RHS.get()->EvaluateAsInt(EVResult, Context)) {
llvm::APSInt Result = EVResult.Val.getInt();
if ((getLangOpts().Bool && !RHS.get()->getType()->isBooleanType() &&
if ((getLangOpts().CPlusPlus && !RHS.get()->getType()->isBooleanType() &&
!RHS.get()->getExprLoc().isMacroID()) ||
(Result != 0 && Result != 1)) {
Diag(Loc, diag::warn_logical_instead_of_bitwise)
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Sema/SemaOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21124,6 +21124,8 @@ Sema::ActOnOpenMPDependClause(const OMPDependClause::DependDataTy &Data,
ExprTy = ATy->getElementType();
else
ExprTy = BaseType->getPointeeType();
if (BaseType.isNull() || ExprTy.isNull())
return nullptr;
ExprTy = ExprTy.getNonReferenceType();
const Expr *Length = OASE->getLength();
Expr::EvalResult Result;
Expand Down
9 changes: 6 additions & 3 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2177,7 +2177,10 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
From->isIntegerConstantExpr(S.getASTContext())) {
SCS.Second = ICK_Compatible_Conversion;
FromType = ToType;
} else if (ToType->isFixedPointType() || FromType->isFixedPointType()) {
} else if ((ToType->isFixedPointType() &&
FromType->isConvertibleToFixedPointType()) ||
(FromType->isFixedPointType() &&
ToType->isConvertibleToFixedPointType())) {
SCS.Second = ICK_Fixed_Point_Conversion;
FromType = ToType;
} else {
Expand Down Expand Up @@ -7719,8 +7722,8 @@ bool Sema::CheckNonDependentConversions(
unsigned Offset =
Method && Method->hasCXXExplicitFunctionObjectParameter() ? 1 : 0;

for (unsigned I = 0, N = std::min(ParamTypes.size(), Args.size()); I != N;
++I) {
for (unsigned I = 0, N = std::min(ParamTypes.size() - Offset, Args.size());
I != N; ++I) {
QualType ParamType = ParamTypes[I + Offset];
if (!ParamType->isDependentType()) {
unsigned ConvIdx;
Expand Down
32 changes: 30 additions & 2 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,10 +241,38 @@ Response HandleFunctionTemplateDecl(const FunctionTemplateDecl *FTD,

while (const Type *Ty = NNS ? NNS->getAsType() : nullptr) {
if (NNS->isInstantiationDependent()) {
if (const auto *TSTy = Ty->getAs<TemplateSpecializationType>())
if (const auto *TSTy = Ty->getAs<TemplateSpecializationType>()) {
ArrayRef<TemplateArgument> Arguments = TSTy->template_arguments();
// Prefer template arguments from the injected-class-type if possible.
// For example,
// ```cpp
// template <class... Pack> struct S {
// template <class T> void foo();
// };
// template <class... Pack> template <class T>
// ^^^^^^^^^^^^^ InjectedTemplateArgs
// They're of kind TemplateArgument::Pack, not of
// TemplateArgument::Type.
// void S<Pack...>::foo() {}
// ^^^^^^^
// TSTy->template_arguments() (which are of PackExpansionType)
// ```
// This meets the contract in
// TreeTransform::TryExpandParameterPacks that the template arguments
// for unexpanded parameters should be of a Pack kind.
if (TSTy->isCurrentInstantiation()) {
auto *RD = TSTy->getCanonicalTypeInternal()->getAsCXXRecordDecl();
if (ClassTemplateDecl *CTD = RD->getDescribedClassTemplate())
Arguments = CTD->getInjectedTemplateArgs();
else if (auto *Specialization =
dyn_cast<ClassTemplateSpecializationDecl>(RD))
Arguments =
Specialization->getTemplateInstantiationArgs().asArray();
}
Result.addOuterTemplateArguments(
const_cast<FunctionTemplateDecl *>(FTD), TSTy->template_arguments(),
const_cast<FunctionTemplateDecl *>(FTD), Arguments,
/*Final=*/false);
}
}

NNS = NNS->getPrefix();
Expand Down
15 changes: 9 additions & 6 deletions clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,16 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
}
if (auto *call = dyn_cast<CallExpr>(E)) {
if (auto *memberCall = dyn_cast<CXXMemberCallExpr>(call)) {
std::optional<bool> IsGetterOfRefCt = isGetterOfRefCounted(memberCall->getMethodDecl());
if (IsGetterOfRefCt && *IsGetterOfRefCt) {
E = memberCall->getImplicitObjectArgument();
if (StopAtFirstRefCountedObj) {
return {E, true};
if (auto *decl = memberCall->getMethodDecl()) {
std::optional<bool> IsGetterOfRefCt =
isGetterOfRefCounted(memberCall->getMethodDecl());
if (IsGetterOfRefCt && *IsGetterOfRefCt) {
E = memberCall->getImplicitObjectArgument();
if (StopAtFirstRefCountedObj) {
return {E, true};
}
continue;
}
continue;
}
}

Expand Down
14 changes: 12 additions & 2 deletions clang/lib/Tooling/Inclusions/HeaderIncludes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,18 @@ int IncludeCategoryManager::getSortIncludePriority(StringRef IncludeName,
return Ret;
}
bool IncludeCategoryManager::isMainHeader(StringRef IncludeName) const {
if (!IncludeName.starts_with("\""))
return false;
switch (Style.MainIncludeChar) {
case IncludeStyle::MICD_Quote:
if (!IncludeName.starts_with("\""))
return false;
break;
case IncludeStyle::MICD_AngleBracket:
if (!IncludeName.starts_with("<"))
return false;
break;
case IncludeStyle::MICD_Any:
break;
}

IncludeName =
IncludeName.drop_front(1).drop_back(1); // remove the surrounding "" or <>
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Tooling/Inclusions/IncludeStyle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,12 @@ void ScalarEnumerationTraits<IncludeStyle::IncludeBlocksStyle>::enumeration(
IO.enumCase(Value, "Regroup", IncludeStyle::IBS_Regroup);
}

void ScalarEnumerationTraits<IncludeStyle::MainIncludeCharDiscriminator>::
enumeration(IO &IO, IncludeStyle::MainIncludeCharDiscriminator &Value) {
IO.enumCase(Value, "Quote", IncludeStyle::MICD_Quote);
IO.enumCase(Value, "AngleBracket", IncludeStyle::MICD_AngleBracket);
IO.enumCase(Value, "Any", IncludeStyle::MICD_Any);
}

} // namespace yaml
} // namespace llvm
13 changes: 13 additions & 0 deletions clang/test/AST/Interp/builtin-functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -363,3 +363,16 @@ namespace ffs {
char ffs6[__builtin_ffsl(0x10L) == 5 ? 1 : -1];
char ffs7[__builtin_ffsll(0x100LL) == 9 ? 1 : -1];
}

namespace EhReturnDataRegno {
void test11(int X) {
switch (X) {
case __builtin_eh_return_data_regno(0): // constant foldable.
break;
}

__builtin_eh_return_data_regno(X); // expected-error {{argument to '__builtin_eh_return_data_regno' must be a constant integer}} \
// ref-error {{argument to '__builtin_eh_return_data_regno' must be a constant integer}}

}
}
28 changes: 28 additions & 0 deletions clang/test/AST/Interp/complex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,16 @@ static_assert(__imag(I3) == 0, "");
// constexpr _Complex _BitInt(8) A = 0;// = {4};


constexpr _Complex double Doubles[4] = {{1.0, 2.0}};
static_assert(__real(Doubles[0]) == 1.0, "");
static_assert(__imag(Doubles[0]) == 2.0, "");
static_assert(__real(Doubles[1]) == 0.0, "");
static_assert(__imag(Doubles[1]) == 0.0, "");
static_assert(__real(Doubles[2]) == 0.0, "");
static_assert(__imag(Doubles[2]) == 0.0, "");
static_assert(__real(Doubles[3]) == 0.0, "");
static_assert(__imag(Doubles[3]) == 0.0, "");

void func(void) {
__complex__ int arr;
_Complex int result;
Expand Down Expand Up @@ -194,3 +204,21 @@ namespace ZeroInit {

constexpr int ignored = (fcomplex(), 0);
}

namespace DeclRefCopy {
constexpr _Complex int ComplexInt = 42 + 24i;

constexpr _Complex int B = ComplexInt;
constexpr _Complex int ArrayOfComplexInt[4] = {ComplexInt, ComplexInt, ComplexInt, ComplexInt};
static_assert(__real(ArrayOfComplexInt[0]) == 42, "");
static_assert(__imag(ArrayOfComplexInt[0]) == 24, "");
static_assert(__real(ArrayOfComplexInt[3]) == 42, "");
static_assert(__imag(ArrayOfComplexInt[3]) == 24, "");

constexpr int localComplexArray() {
_Complex int A = 42 + 24i;
_Complex int ArrayOfComplexInt[4] = {A, A, A, A};
return __real(ArrayOfComplexInt[0]) + __imag(ArrayOfComplexInt[3]);
}
static_assert(localComplexArray() == (24 + 42), "");
}
13 changes: 13 additions & 0 deletions clang/test/AST/Interp/cxx20.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -752,3 +752,16 @@ namespace TryCatch {
}
static_assert(foo() == 11);
}

namespace IgnoredConstantExpr {
consteval int immediate() { return 0;}
struct ReferenceToNestedMembers {
int m;
int a = ((void)immediate(), m);
int b = ((void)immediate(), this->m);
};
struct ReferenceToNestedMembersTest {
void* m = nullptr;
ReferenceToNestedMembers j{0};
} test_reference_to_nested_members;
}
Loading