226 changes: 135 additions & 91 deletions clang-tools-extra/clang-tidy/modernize/UseStartsEndsWithCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "UseStartsEndsWithCheck.h"

#include "../utils/ASTUtils.h"
#include "../utils/OptionsUtils.h"
#include "clang/Lex/Lexer.h"

Expand All @@ -16,13 +17,71 @@
using namespace clang::ast_matchers;

namespace clang::tidy::modernize {
struct NotLengthExprForStringNode {
NotLengthExprForStringNode(std::string ID, DynTypedNode Node,
ASTContext *Context)
: ID(std::move(ID)), Node(std::move(Node)), Context(Context) {}
bool operator()(const internal::BoundNodesMap &Nodes) const {
// Match a string literal and an integer size or strlen() call.
if (const auto *StringLiteralNode = Nodes.getNodeAs<StringLiteral>(ID)) {
if (const auto *IntegerLiteralSizeNode = Node.get<IntegerLiteral>()) {
return StringLiteralNode->getLength() !=
IntegerLiteralSizeNode->getValue().getZExtValue();
}

if (const auto *StrlenNode = Node.get<CallExpr>()) {
if (StrlenNode->getDirectCallee()->getName() != "strlen" ||
StrlenNode->getNumArgs() != 1) {
return true;
}

if (const auto *StrlenArgNode = dyn_cast<StringLiteral>(
StrlenNode->getArg(0)->IgnoreParenImpCasts())) {
return StrlenArgNode->getLength() != StringLiteralNode->getLength();
}
}
}

// Match a string variable and a call to length() or size().
if (const auto *ExprNode = Nodes.getNodeAs<Expr>(ID)) {
if (const auto *MemberCallNode = Node.get<CXXMemberCallExpr>()) {
const CXXMethodDecl *MethodDeclNode = MemberCallNode->getMethodDecl();
const StringRef Name = MethodDeclNode->getName();
if (!MethodDeclNode->isConst() || MethodDeclNode->getNumParams() != 0 ||
(Name != "size" && Name != "length")) {
return true;
}

if (const auto *OnNode =
dyn_cast<Expr>(MemberCallNode->getImplicitObjectArgument())) {
return !utils::areStatementsIdentical(OnNode->IgnoreParenImpCasts(),
ExprNode->IgnoreParenImpCasts(),
*Context);
}
}
}

return true;
}

private:
std::string ID;
DynTypedNode Node;
ASTContext *Context;
};

AST_MATCHER_P(Expr, lengthExprForStringNode, std::string, ID) {
return Builder->removeBindings(NotLengthExprForStringNode(
ID, DynTypedNode::create(Node), &(Finder->getASTContext())));
}

UseStartsEndsWithCheck::UseStartsEndsWithCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}

void UseStartsEndsWithCheck::registerMatchers(MatchFinder *Finder) {
const auto ZeroLiteral = integerLiteral(equals(0));

const auto HasStartsWithMethodWithName = [](const std::string &Name) {
return hasMethod(
cxxMethodDecl(hasName(Name), isConst(), parameterCountIs(1))
Expand All @@ -32,119 +91,104 @@ void UseStartsEndsWithCheck::registerMatchers(MatchFinder *Finder) {
anyOf(HasStartsWithMethodWithName("starts_with"),
HasStartsWithMethodWithName("startsWith"),
HasStartsWithMethodWithName("startswith"));
const auto ClassWithStartsWithFunction = cxxRecordDecl(anyOf(
HasStartsWithMethod, hasAnyBase(hasType(hasCanonicalType(hasDeclaration(
cxxRecordDecl(HasStartsWithMethod)))))));
const auto OnClassWithStartsWithFunction =
on(hasType(hasCanonicalType(hasDeclaration(cxxRecordDecl(
anyOf(HasStartsWithMethod,
hasAnyBase(hasType(hasCanonicalType(
hasDeclaration(cxxRecordDecl(HasStartsWithMethod)))))))))));

const auto HasEndsWithMethodWithName = [](const std::string &Name) {
return hasMethod(
cxxMethodDecl(hasName(Name), isConst(), parameterCountIs(1))
.bind("ends_with_fun"));
};
const auto HasEndsWithMethod = anyOf(HasEndsWithMethodWithName("ends_with"),
HasEndsWithMethodWithName("endsWith"),
HasEndsWithMethodWithName("endswith"));
const auto OnClassWithEndsWithFunction =
on(expr(hasType(hasCanonicalType(hasDeclaration(cxxRecordDecl(
anyOf(HasEndsWithMethod,
hasAnyBase(hasType(hasCanonicalType(hasDeclaration(
cxxRecordDecl(HasEndsWithMethod)))))))))))
.bind("haystack"));

// Case 1: X.find(Y) [!=]= 0 -> starts_with.
const auto FindExpr = cxxMemberCallExpr(
// A method call with no second argument or the second argument is zero...
anyOf(argumentCountIs(1), hasArgument(1, ZeroLiteral)),
// ... named find...
callee(cxxMethodDecl(hasName("find")).bind("find_fun")),
// ... on a class with a starts_with function.
on(hasType(
hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction)))),
// Bind search expression.
hasArgument(0, expr().bind("search_expr")));
OnClassWithStartsWithFunction, hasArgument(0, expr().bind("needle")));

// Case 2: X.rfind(Y, 0) [!=]= 0 -> starts_with.
const auto RFindExpr = cxxMemberCallExpr(
// A method call with a second argument of zero...
hasArgument(1, ZeroLiteral),
// ... named rfind...
callee(cxxMethodDecl(hasName("rfind")).bind("find_fun")),
// ... on a class with a starts_with function.
on(hasType(
hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction)))),
// Bind search expression.
hasArgument(0, expr().bind("search_expr")));

// Match a string literal and an integer or strlen() call matching the length.
const auto HasStringLiteralAndLengthArgs = [](const auto StringArgIndex,
const auto LengthArgIndex) {
return allOf(
hasArgument(StringArgIndex, stringLiteral().bind("string_literal_arg")),
hasArgument(LengthArgIndex,
anyOf(integerLiteral().bind("integer_literal_size_arg"),
callExpr(callee(functionDecl(parameterCountIs(1),
hasName("strlen"))),
hasArgument(0, stringLiteral().bind(
"strlen_arg"))))));
};

// Match a string variable and a call to length() or size().
const auto HasStringVariableAndSizeCallArgs = [](const auto StringArgIndex,
const auto LengthArgIndex) {
return allOf(
hasArgument(StringArgIndex, declRefExpr(hasDeclaration(
decl().bind("string_var_decl")))),
hasArgument(LengthArgIndex,
cxxMemberCallExpr(
callee(cxxMethodDecl(isConst(), parameterCountIs(0),
hasAnyName("size", "length"))),
on(declRefExpr(
to(decl(equalsBoundNode("string_var_decl"))))))));
};

// Match either one of the two cases above.
const auto HasStringAndLengthArgs =
[HasStringLiteralAndLengthArgs, HasStringVariableAndSizeCallArgs](
const auto StringArgIndex, const auto LengthArgIndex) {
return anyOf(
HasStringLiteralAndLengthArgs(StringArgIndex, LengthArgIndex),
HasStringVariableAndSizeCallArgs(StringArgIndex, LengthArgIndex));
};
OnClassWithStartsWithFunction, hasArgument(0, expr().bind("needle")));

// Case 3: X.compare(0, LEN(Y), Y) [!=]= 0 -> starts_with.
const auto CompareExpr = cxxMemberCallExpr(
// A method call with three arguments...
argumentCountIs(3),
// ... where the first argument is zero...
hasArgument(0, ZeroLiteral),
// ... named compare...
argumentCountIs(3), hasArgument(0, ZeroLiteral),
callee(cxxMethodDecl(hasName("compare")).bind("find_fun")),
// ... on a class with a starts_with function...
on(hasType(
hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction)))),
// ... where the third argument is some string and the second a length.
HasStringAndLengthArgs(2, 1),
// Bind search expression.
hasArgument(2, expr().bind("search_expr")));
OnClassWithStartsWithFunction, hasArgument(2, expr().bind("needle")),
hasArgument(1, lengthExprForStringNode("needle")));

// Case 4: X.compare(LEN(X) - LEN(Y), LEN(Y), Y) [!=]= 0 -> ends_with.
const auto CompareEndsWithExpr = cxxMemberCallExpr(
argumentCountIs(3),
callee(cxxMethodDecl(hasName("compare")).bind("find_fun")),
OnClassWithEndsWithFunction, hasArgument(2, expr().bind("needle")),
hasArgument(1, lengthExprForStringNode("needle")),
hasArgument(0,
binaryOperator(hasOperatorName("-"),
hasLHS(lengthExprForStringNode("haystack")),
hasRHS(lengthExprForStringNode("needle")))));

// All cases comparing to 0.
Finder->addMatcher(
// Match [=!]= with a zero on one side and (r?)find|compare on the other.
binaryOperator(
hasAnyOperatorName("==", "!="),
hasOperands(cxxMemberCallExpr(anyOf(FindExpr, RFindExpr, CompareExpr))
hasOperands(cxxMemberCallExpr(anyOf(FindExpr, RFindExpr, CompareExpr,
CompareEndsWithExpr))
.bind("find_expr"),
ZeroLiteral))
.bind("expr"),
this);

// Case 5: X.rfind(Y) [!=]= LEN(X) - LEN(Y) -> ends_with.
Finder->addMatcher(
binaryOperator(
hasAnyOperatorName("==", "!="),
hasOperands(
cxxMemberCallExpr(
anyOf(
argumentCountIs(1),
allOf(argumentCountIs(2),
hasArgument(
1,
anyOf(declRefExpr(to(varDecl(hasName("npos")))),
memberExpr(member(hasName("npos"))))))),
callee(cxxMethodDecl(hasName("rfind")).bind("find_fun")),
OnClassWithEndsWithFunction,
hasArgument(0, expr().bind("needle")))
.bind("find_expr"),
binaryOperator(hasOperatorName("-"),
hasLHS(lengthExprForStringNode("haystack")),
hasRHS(lengthExprForStringNode("needle")))))
.bind("expr"),
this);
}

void UseStartsEndsWithCheck::check(const MatchFinder::MatchResult &Result) {
const auto *ComparisonExpr = Result.Nodes.getNodeAs<BinaryOperator>("expr");
const auto *FindExpr = Result.Nodes.getNodeAs<CXXMemberCallExpr>("find_expr");
const auto *FindFun = Result.Nodes.getNodeAs<CXXMethodDecl>("find_fun");
const auto *SearchExpr = Result.Nodes.getNodeAs<Expr>("search_expr");
const auto *SearchExpr = Result.Nodes.getNodeAs<Expr>("needle");
const auto *StartsWithFunction =
Result.Nodes.getNodeAs<CXXMethodDecl>("starts_with_fun");

const auto *StringLiteralArg =
Result.Nodes.getNodeAs<StringLiteral>("string_literal_arg");
const auto *IntegerLiteralSizeArg =
Result.Nodes.getNodeAs<IntegerLiteral>("integer_literal_size_arg");
const auto *StrlenArg = Result.Nodes.getNodeAs<StringLiteral>("strlen_arg");

// Filter out compare cases where the length does not match string literal.
if (StringLiteralArg && IntegerLiteralSizeArg &&
StringLiteralArg->getLength() !=
IntegerLiteralSizeArg->getValue().getZExtValue()) {
return;
}

if (StringLiteralArg && StrlenArg &&
StringLiteralArg->getLength() != StrlenArg->getLength()) {
return;
}
const auto *EndsWithFunction =
Result.Nodes.getNodeAs<CXXMethodDecl>("ends_with_fun");
assert(bool(StartsWithFunction) != bool(EndsWithFunction));
const CXXMethodDecl *ReplacementFunction =
StartsWithFunction ? StartsWithFunction : EndsWithFunction;

if (ComparisonExpr->getBeginLoc().isMacroID()) {
return;
Expand All @@ -154,26 +198,26 @@ void UseStartsEndsWithCheck::check(const MatchFinder::MatchResult &Result) {

auto Diagnostic =
diag(FindExpr->getExprLoc(), "use %0 instead of %1() %select{==|!=}2 0")
<< StartsWithFunction->getName() << FindFun->getName() << Neg;
<< ReplacementFunction->getName() << FindFun->getName() << Neg;

// Remove possible arguments after search expression and ' [!=]= 0' suffix.
// Remove possible arguments after search expression and ' [!=]= .+' suffix.
Diagnostic << FixItHint::CreateReplacement(
CharSourceRange::getTokenRange(
Lexer::getLocForEndOfToken(SearchExpr->getEndLoc(), 0,
*Result.SourceManager, getLangOpts()),
ComparisonExpr->getEndLoc()),
")");

// Remove possible '0 [!=]= ' prefix.
// Remove possible '.+ [!=]= ' prefix.
Diagnostic << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
ComparisonExpr->getBeginLoc(), FindExpr->getBeginLoc()));

// Replace method name by 'starts_with'.
// Replace method name by '(starts|ends)_with'.
// Remove possible arguments before search expression.
Diagnostic << FixItHint::CreateReplacement(
CharSourceRange::getCharRange(FindExpr->getExprLoc(),
SearchExpr->getBeginLoc()),
(StartsWithFunction->getName() + "(").str());
(ReplacementFunction->getName() + "(").str());

// Add possible negation '!'.
if (Neg) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
namespace clang::tidy::modernize {

/// Checks for common roundabout ways to express ``starts_with`` and
/// ``ends_with`` and suggests replacing with ``starts_with`` when the method is
/// ``ends_with`` and suggests replacing with the simpler method when it is
/// available. Notably, this will work with ``std::string`` and
/// ``std::string_view``.
///
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/portability/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ add_clang_library(clangTidyPortabilityModule STATIC
RestrictSystemIncludesCheck.cpp
SIMDIntrinsicsCheck.cpp
StdAllocatorConstCheck.cpp
TemplateVirtualMemberFunctionCheck.cpp

LINK_LIBS
clangTidy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "RestrictSystemIncludesCheck.h"
#include "SIMDIntrinsicsCheck.h"
#include "StdAllocatorConstCheck.h"
#include "TemplateVirtualMemberFunctionCheck.h"

namespace clang::tidy {
namespace portability {
Expand All @@ -25,6 +26,8 @@ class PortabilityModule : public ClangTidyModule {
"portability-simd-intrinsics");
CheckFactories.registerCheck<StdAllocatorConstCheck>(
"portability-std-allocator-const");
CheckFactories.registerCheck<TemplateVirtualMemberFunctionCheck>(
"portability-template-virtual-member-function");
}
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//===--- TemplateVirtualMemberFunctionCheck.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 "TemplateVirtualMemberFunctionCheck.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"

using namespace clang::ast_matchers;

namespace clang::tidy::portability {
namespace {
AST_MATCHER(CXXMethodDecl, isUsed) { return Node.isUsed(); }
} // namespace

void TemplateVirtualMemberFunctionCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
cxxMethodDecl(ofClass(classTemplateSpecializationDecl(
unless(isExplicitTemplateSpecialization()))
.bind("specialization")),
isVirtual(), unless(isUsed()),
unless(cxxDestructorDecl(isDefaulted())))
.bind("method"),
this);
}

void TemplateVirtualMemberFunctionCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *ImplicitSpecialization =
Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("specialization");
const auto *MethodDecl = Result.Nodes.getNodeAs<CXXMethodDecl>("method");

diag(MethodDecl->getLocation(),
"unspecified virtual member function instantiation; the virtual "
"member function is not instantiated but it might be with a "
"different compiler");
diag(ImplicitSpecialization->getPointOfInstantiation(),
"template instantiated here", DiagnosticIDs::Note);
}

} // namespace clang::tidy::portability
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//===--- TemplateVirtualMemberFunctionCheck.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_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H

#include "../ClangTidyCheck.h"

namespace clang::tidy::portability {

/// Upon instantiating a template class, non-virtual member functions don't have
/// to be instantiated unless they are used. Virtual member function
/// instantiation on the other hand is unspecified and depends on the
/// implementation of the compiler. This check intends to find cases when a
/// virtual member function is not instantiated but it might be with a different
/// compiler.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/portability/template-virtual-member-function.html
class TemplateVirtualMemberFunctionCheck : public ClangTidyCheck {
public:
TemplateVirtualMemberFunctionCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
};

} // namespace clang::tidy::portability

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H
Original file line number Diff line number Diff line change
Expand Up @@ -1135,6 +1135,9 @@ StyleKind IdentifierNamingCheck::findStyleKind(
if (isa<TypeAliasDecl>(D) && NamingStyles[SK_TypeAlias])
return SK_TypeAlias;

if (isa<NamespaceAliasDecl>(D) && NamingStyles[SK_Namespace])
return SK_Namespace;

if (const auto *Decl = dyn_cast<NamespaceDecl>(D)) {
if (Decl->isAnonymousNamespace())
return SK_Invalid;
Expand Down
7 changes: 6 additions & 1 deletion clang-tools-extra/clangd/AST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,13 @@ getQualification(ASTContext &Context, const DeclContext *DestContext,
// since we stored inner-most parent first.
std::string Result;
llvm::raw_string_ostream OS(Result);
for (const auto *Parent : llvm::reverse(Parents))
for (const auto *Parent : llvm::reverse(Parents)) {
if (Parent != *Parents.rbegin() && Parent->isDependent() &&
Parent->getAsRecordDecl() &&
Parent->getAsRecordDecl()->getDescribedClassTemplate())
OS << "template ";
Parent->print(OS, Context.getPrintingPolicy());
}
return OS.str();
}

Expand Down
4 changes: 2 additions & 2 deletions clang-tools-extra/clangd/Headers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ class IncludeStructure::RecordHeaders : public PPCallbacks {
IDs.push_back(HID);
}
}
Out->MainFileIncludesBySpelling.try_emplace(Inc.Written)
.first->second.push_back(Out->MainFileIncludes.size() - 1);
Out->MainFileIncludesBySpelling[Inc.Written].push_back(
Out->MainFileIncludes.size() - 1);
}

// Record include graph (not just for main-file includes)
Expand Down
3 changes: 1 addition & 2 deletions clang-tools-extra/clangd/XRefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2282,8 +2282,7 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
elog("incomingCalls failed to convert location: {0}", Loc.takeError());
return;
}
auto It = CallsIn.try_emplace(R.Container, std::vector<Range>{}).first;
It->second.push_back(Loc->range);
CallsIn[R.Container].push_back(Loc->range);

ContainerLookup.IDs.insert(R.Container);
});
Expand Down
50 changes: 44 additions & 6 deletions clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,28 @@ getFunctionSourceAfterReplacements(const FunctionDecl *FD,
SM.getBufferData(SM.getMainFileID()), Replacements);
if (!QualifiedFunc)
return QualifiedFunc.takeError();
return QualifiedFunc->substr(FuncBegin, FuncEnd - FuncBegin + 1);

std::string TemplatePrefix;
if (auto *MD = llvm::dyn_cast<CXXMethodDecl>(FD)) {
for (const CXXRecordDecl *Parent = MD->getParent(); Parent;
Parent =
llvm::dyn_cast_or_null<const CXXRecordDecl>(Parent->getParent())) {
if (const TemplateParameterList *Params =
Parent->getDescribedTemplateParams()) {
std::string S;
llvm::raw_string_ostream Stream(S);
Params->print(Stream, FD->getASTContext());
if (!S.empty())
*S.rbegin() = '\n'; // Replace space with newline
TemplatePrefix.insert(0, S);
}
}
}

auto Source = QualifiedFunc->substr(FuncBegin, FuncEnd - FuncBegin + 1);
if (!TemplatePrefix.empty())
Source.insert(0, TemplatePrefix);
return Source;
}

// Returns replacements to delete tokens with kind `Kind` in the range
Expand Down Expand Up @@ -212,9 +233,13 @@ getFunctionSourceCode(const FunctionDecl *FD, const DeclContext *TargetContext,
}
}
const NamedDecl *ND = Ref.Targets.front();
const std::string Qualifier =
std::string Qualifier =
getQualification(AST, TargetContext,
SM.getLocForStartOfFile(SM.getMainFileID()), ND);
if (ND->getDeclContext()->isDependentContext() &&
llvm::isa<TypeDecl>(ND)) {
Qualifier.insert(0, "typename ");
}
if (auto Err = DeclarationCleanups.add(
tooling::Replacement(SM, Ref.NameLoc, 0, Qualifier)))
Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
Expand Down Expand Up @@ -407,10 +432,23 @@ class DefineOutline : public Tweak {
return !SameFile;
}

// Bail out in templated classes, as it is hard to spell the class name,
// i.e if the template parameter is unnamed.
if (MD->getParent()->isTemplated())
return false;
for (const CXXRecordDecl *Parent = MD->getParent(); Parent;
Parent =
llvm::dyn_cast_or_null<const CXXRecordDecl>(Parent->getParent())) {
if (const TemplateParameterList *Params =
Parent->getDescribedTemplateParams()) {

// Class template member functions must be defined in the
// same file.
SameFile = true;

// Bail out if the template parameter is unnamed.
for (NamedDecl *P : *Params) {
if (!P->getIdentifier())
return false;
}
}
}

// The refactoring is meaningless for unnamed classes and namespaces,
// unless we're outlining in the same file
Expand Down
55 changes: 42 additions & 13 deletions clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ TEST_F(DefineOutlineTest, TriggersOnFunctionDecl) {
F^oo(const Foo&) = delete;
};)cpp");

// Not available within templated classes, as it is hard to spell class name
// out-of-line in such cases.
// Not available within templated classes with unnamed parameters, as it is
// hard to spell class name out-of-line in such cases.
EXPECT_UNAVAILABLE(R"cpp(
template <typename> struct Foo { void fo^o(){} };
)cpp");
Expand Down Expand Up @@ -154,7 +154,6 @@ TEST_F(DefineOutlineTest, FailsWithoutSource) {
}

TEST_F(DefineOutlineTest, ApplyTest) {
llvm::StringMap<std::string> EditedFiles;
ExtraFiles["Test.cpp"] = "";
FileName = "Test.hpp";

Expand Down Expand Up @@ -229,17 +228,18 @@ TEST_F(DefineOutlineTest, ApplyTest) {
// Ctor initializer with attribute.
{
R"cpp(
class Foo {
F^oo(int z) __attribute__((weak)) : bar(2){}
template <typename T> class Foo {
F^oo(T z) __attribute__((weak)) : bar(2){}
int bar;
};)cpp",
R"cpp(
class Foo {
Foo(int z) __attribute__((weak)) ;
template <typename T> class Foo {
Foo(T z) __attribute__((weak)) ;
int bar;
};)cpp",
"Foo::Foo(int z) __attribute__((weak)) : bar(2){}\n",
},
};template <typename T>
Foo<T>::Foo(T z) __attribute__((weak)) : bar(2){}
)cpp",
""},
// Virt specifiers.
{
R"cpp(
Expand Down Expand Up @@ -369,7 +369,31 @@ TEST_F(DefineOutlineTest, ApplyTest) {
};)cpp",
" void A::foo(int) {}\n",
},
// Destrctors
// Complex class template
{
R"cpp(
template <typename T, typename ...U> struct O1 {
template <class V, int A> struct O2 {
enum E { E1, E2 };
struct I {
E f^oo(T, U..., V, E) { return E1; }
};
};
};)cpp",
R"cpp(
template <typename T, typename ...U> struct O1 {
template <class V, int A> struct O2 {
enum E { E1, E2 };
struct I {
E foo(T, U..., V, E) ;
};
};
};template <typename T, typename ...U>
template <class V, int A>
typename O1<T, U...>::template O2<V, A>::E O1<T, U...>::template O2<V, A>::I::foo(T, U..., V, E) { return E1; }
)cpp",
""},
// Destructors
{
"class A { ~A^(){} };",
"class A { ~A(); };",
Expand All @@ -378,9 +402,14 @@ TEST_F(DefineOutlineTest, ApplyTest) {
};
for (const auto &Case : Cases) {
SCOPED_TRACE(Case.Test);
llvm::StringMap<std::string> EditedFiles;
EXPECT_EQ(apply(Case.Test, &EditedFiles), Case.ExpectedHeader);
EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
testPath("Test.cpp"), Case.ExpectedSource)));
if (Case.ExpectedSource.empty()) {
EXPECT_TRUE(EditedFiles.empty());
} else {
EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
testPath("Test.cpp"), Case.ExpectedSource)));
}
}
}

Expand Down
18 changes: 17 additions & 1 deletion clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ Objective-C
Miscellaneous
^^^^^^^^^^^^^

- The DefineOutline tweak now handles member functions of class templates.

Improvements to clang-doc
-------------------------

Expand Down Expand Up @@ -121,6 +123,12 @@ New checks
Gives warnings for tagged unions, where the number of tags is
different from the number of data members inside the union.

- New :doc:`portability-template-virtual-member-function
<clang-tidy/checks/portability/template-virtual-member-function>` check.

Finds cases when an uninstantiated virtual member function in a template class
causes cross-compiler incompatibility.

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

Expand Down Expand Up @@ -150,7 +158,7 @@ Changes in existing checks
- Improved :doc:`bugprone-sizeof-expression
<clang-tidy/checks/bugprone/sizeof-expression>` check to find suspicious
usages of ``sizeof()``, ``alignof()``, and ``offsetof()`` when adding or
subtracting from a pointer.
subtracting from a pointer directly or when used to scale a numeric value.

- Improved :doc:`bugprone-unchecked-optional-access
<clang-tidy/checks/bugprone/unchecked-optional-access>` to support
Expand Down Expand Up @@ -196,6 +204,10 @@ Changes in existing checks
<clang-tidy/checks/modernize/use-nullptr>` check to also recognize
``NULL``/``__null`` (but not ``0``) when used with a templated type.

- Improved :doc:`modernize-use-starts-ends-with
<clang-tidy/checks/modernize/use-starts-ends-with>` check to handle two cases
that can be replaced with ``ends_with``

- Improved :doc:`modernize-use-std-format
<clang-tidy/checks/modernize/use-std-format>` check to support replacing
member function calls too and to only expand macros starting with ``PRI``
Expand Down Expand Up @@ -232,6 +244,10 @@ Changes in existing checks
<clang-tidy/checks/readability/redundant-smartptr-get>` check to
remove `->`, when redundant `get()` is removed.

- Improved :doc:`readability-identifier-naming
<clang-tidy/checks/readability/identifier-naming>` check to
validate ``namespace`` aliases.

Removed checks
^^^^^^^^^^^^^^

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,17 @@ and the result is typically unintended, often out of bounds.
``Ptr + sizeof(T)`` will offset the pointer by ``sizeof(T)`` elements,
effectively exponentiating the scaling factor to the power of 2.

Similarly, multiplying or dividing a numeric value with the ``sizeof`` of an
element or the whole buffer is suspicious, because the dimensional connection
between the numeric value and the actual ``sizeof`` result can not always be
deduced.
While scaling an integer up (multiplying) with ``sizeof`` is likely **always**
an issue, a scaling down (division) is not always inherently dangerous, in case
the developer is aware that the division happens between an appropriate number
of _bytes_ and a ``sizeof`` value.
Turning :option:`WarnOnOffsetDividedBySizeOf` off will restrict the
warnings to the multiplication case.

This case also checks suspicious ``alignof`` and ``offsetof`` usages in
pointer arithmetic, as both return the "size" in bytes and not elements,
potentially resulting in doubly-scaled offsets.
Expand Down Expand Up @@ -299,3 +310,9 @@ Options
``sizeof`` is an expression that produces a pointer (except for a few
idiomatic expressions that are probably intentional and correct).
This detects occurrences of CWE 467. Default is `false`.

.. option:: WarnOnOffsetDividedBySizeOf

When `true`, the check will warn on pointer arithmetic where the
element count is obtained from a division with ``sizeof(...)``,
e.g., ``Ptr + Bytes / sizeof(*T)``. Default is `true`.
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 @@ -348,6 +348,7 @@ Clang-Tidy Checks
:doc:`portability-restrict-system-includes <portability/restrict-system-includes>`, "Yes"
:doc:`portability-simd-intrinsics <portability/simd-intrinsics>`,
:doc:`portability-std-allocator-const <portability/std-allocator-const>`,
:doc:`portability-template-virtual-member-function <portability/template-virtual-member-function>`,
:doc:`readability-avoid-const-params-in-decls <readability/avoid-const-params-in-decls>`, "Yes"
:doc:`readability-avoid-nested-conditional-operator <readability/avoid-nested-conditional-operator>`,
:doc:`readability-avoid-return-with-void-value <readability/avoid-return-with-void-value>`, "Yes"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@ modernize-use-starts-ends-with
==============================

Checks for common roundabout ways to express ``starts_with`` and ``ends_with``
and suggests replacing with ``starts_with`` when the method is available.
Notably, this will work with ``std::string`` and ``std::string_view``.
and suggests replacing with the simpler method when it is available. Notably,
this will work with ``std::string`` and ``std::string_view``.

.. code-block:: c++

std::string s = "...";
if (s.find("prefix") == 0) { /* do something */ }
if (s.rfind("prefix", 0) == 0) { /* do something */ }
if (s.compare(0, strlen("prefix"), "prefix") == 0) { /* do something */ }
if (s.compare(s.size() - strlen("suffix"), strlen("suffix"), "suffix") == 0) {
/* do something */
}
if (s.rfind("suffix") == (s.length() - 6)) {
/* do something */
}
becomes

Expand All @@ -22,3 +28,5 @@ becomes
if (s.starts_with("prefix")) { /* do something */ }
if (s.starts_with("prefix")) { /* do something */ }
if (s.starts_with("prefix")) { /* do something */ }
if (s.ends_with("suffix")) { /* do something */ }
if (s.ends_with("suffix")) { /* do something */ }
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.. title:: clang-tidy - portability-template-virtual-member-function

portability-template-virtual-member-function
============================================

Finds cases when an uninstantiated virtual member function in a template class causes
cross-compiler incompatibility.

Upon instantiating a template class, non-virtual member functions don't have to be
instantiated unless they are used. Virtual member function instantiation on the other hand
is unspecified and depends on the implementation of the compiler.

In the following snippets the virtual member function is not instantiated by GCC and Clang,
but it is instantiated by MSVC, so while the snippet is accepted by the former compilers,
it is rejected by the latter.

.. code:: c++

template<typename T>
struct CrossPlatformError {
virtual ~CrossPlatformError() = default;

static void used() {}

virtual void unused() {
T MSVCError = this;
};
};

int main() {
CrossPlatformError<int>::used();
return 0;
}

Cross-platform projects that need to support MSVC on Windows might see compiler errors
because certain virtual member functions are instantiated, which are not instantiated
by other compilers on other platforms. This check highlights such virtual member functions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ struct basic_string {
constexpr bool starts_with(C ch) const noexcept;
constexpr bool starts_with(const C* s) const;

constexpr bool ends_with(std::basic_string_view<C, T> sv) const noexcept;
constexpr bool ends_with(C ch) const noexcept;
constexpr bool ends_with(const C* s) const;

_Type& operator[](size_type);
const _Type& operator[](size_type) const;

Expand Down Expand Up @@ -108,6 +112,10 @@ struct basic_string_view {
constexpr bool starts_with(C ch) const noexcept;
constexpr bool starts_with(const C* s) const;

constexpr bool ends_with(basic_string_view sv) const noexcept;
constexpr bool ends_with(C ch) const noexcept;
constexpr bool ends_with(const C* s) const;

constexpr int compare(basic_string_view sv) const noexcept;

static constexpr size_t npos = -1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ template <typename T>
class CRTP {
public:
CRTP() = default;
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: public contructor allows the CRTP to be constructed as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: public constructor allows the CRTP to be constructed as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-FIXES: private:{{[[:space:]]*}}CRTP() = default;{{[[:space:]]*}}public:
// CHECK-FIXES: friend T;
};
Expand All @@ -39,7 +39,7 @@ template <typename T>
class CRTP {
public:
CRTP(int) {}
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: public contructor allows the CRTP to be constructed as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: public constructor allows the CRTP to be constructed as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-FIXES: private:{{[[:space:]]*}}CRTP(int) {}{{[[:space:]]*}}public:
// CHECK-FIXES: friend T;
};
Expand All @@ -52,10 +52,10 @@ template <typename T>
class CRTP {
public:
CRTP(int) {}
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: public contructor allows the CRTP to be constructed as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: public constructor allows the CRTP to be constructed as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-FIXES: private:{{[[:space:]]*}}CRTP(int) {}{{[[:space:]]*}}public:
CRTP(float) {}
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: public contructor allows the CRTP to be constructed as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: public constructor allows the CRTP to be constructed as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-FIXES: private:{{[[:space:]]*}}CRTP(float) {}{{[[:space:]]*}}public:

// CHECK-FIXES: friend T;
Expand All @@ -70,13 +70,13 @@ template <typename T>
class CRTP {
protected:
CRTP(int) {}
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: protected contructor allows the CRTP to be inherited from as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: protected constructor allows the CRTP to be inherited from as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-FIXES: private:{{[[:space:]]*}}CRTP(int) {}{{[[:space:]]*}}protected:
CRTP() = default;
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: protected contructor allows the CRTP to be inherited from as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: protected constructor allows the CRTP to be inherited from as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-FIXES: private:{{[[:space:]]*}}CRTP() = default;{{[[:space:]]*}}protected:
CRTP(float) {}
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: protected contructor allows the CRTP to be inherited from as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: protected constructor allows the CRTP to be inherited from as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-FIXES: private:{{[[:space:]]*}}CRTP(float) {}{{[[:space:]]*}}protected:

// CHECK-FIXES: friend T;
Expand All @@ -101,7 +101,7 @@ namespace struct_default_ctor {
template <typename T>
struct CRTP {
CRTP() = default;
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: public contructor allows the CRTP to be constructed as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: public constructor allows the CRTP to be constructed as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-FIXES: private:{{[[:space:]]*}}CRTP() = default;{{[[:space:]]*}}public:
// CHECK-FIXES: friend T;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// RUN: %check_clang_tidy %s bugprone-sizeof-expression %t -- \
// RUN: -config='{CheckOptions: { \
// RUN: bugprone-sizeof-expression.WarnOnOffsetDividedBySizeOf: false \
// RUN: }}'

typedef __SIZE_TYPE__ size_t;

void situational14(int *Buffer, size_t BufferSize) {
int *P = &Buffer[0];
while (P < Buffer + BufferSize / sizeof(*Buffer)) {
// NO-WARNING: This test opted out of "P +- N */ sizeof(...)" warnings.
++P;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -352,21 +352,30 @@ void good13(void) {
int Buffer[BufferSize];

int *P = &Buffer[0];
while (P < (Buffer + sizeof(Buffer) / sizeof(int))) {
while (P < Buffer + sizeof(Buffer) / sizeof(int)) {
// NO-WARNING: Calculating the element count of the buffer here, which is
// safe with this idiom (as long as the types don't change).
++P;
}

while (P < (Buffer + sizeof(Buffer) / sizeof(Buffer[0]))) {
while (P < Buffer + sizeof(Buffer) / sizeof(Buffer[0])) {
// NO-WARNING: Calculating the element count of the buffer here, which is
// safe with this idiom.
++P;
}

while (P < (Buffer + sizeof(Buffer) / sizeof(*P))) {
while (P < Buffer + sizeof(Buffer) / sizeof(*P)) {
// NO-WARNING: Calculating the element count of the buffer here, which is
// safe with this idiom.
++P;
}
}

void situational14(int *Buffer, size_t BufferSize) {
int *P = &Buffer[0];
while (P < Buffer + BufferSize / sizeof(*Buffer)) {
// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator
// CHECK-MESSAGES: :[[@LINE-2]]:21: note: '+' in pointer arithmetic internally scales with 'sizeof(int)' == {{[0-9]+}}
++P;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,62 @@ void test(std::string s, std::string_view sv, sub_string ss, sub_sub_string sss,
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
// CHECK-FIXES: !s.starts_with(sv);

s.compare(s.size() - 6, 6, "suffix") == 0;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: s.ends_with("suffix");

s.compare(s.size() - 6, strlen("abcdef"), "suffix") == 0;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: s.ends_with("suffix");

std::string suffix = "suffix";
s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: s.ends_with(suffix);

s.rfind("suffix") == s.size() - 6;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: s.ends_with("suffix");

s.rfind("suffix") == s.size() - strlen("suffix");
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: s.ends_with("suffix");

s.rfind(suffix) == s.size() - suffix.size();
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: s.ends_with(suffix);

s.rfind(suffix, std::string::npos) == s.size() - suffix.size();
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: s.ends_with(suffix);

s.rfind(suffix) == (s.size() - suffix.size());
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: s.ends_with(suffix);

s.rfind(suffix, s.npos) == (s.size() - suffix.size());
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: s.ends_with(suffix);

s.rfind(suffix, s.npos) == (((s.size()) - (suffix.size())));
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: s.ends_with(suffix);

s.rfind(suffix) != s.size() - suffix.size();
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: !s.ends_with(suffix);

(s.size() - suffix.size()) == s.rfind(suffix);
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: s.ends_with(suffix);

struct S {
std::string s;
} t;
t.s.rfind(suffix) == (t.s.size() - suffix.size());
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: t.s.ends_with(suffix);

// Expressions that don't trigger the check are here.
#define EQ(x, y) ((x) == (y))
EQ(s.find("a"), 0);
Expand All @@ -219,4 +275,5 @@ void test(std::string s, std::string_view sv, sub_string ss, sub_sub_string sss,
STARTS_WITH_COMPARE(s, s) == 0;

s.compare(0, 1, "ab") == 0;
s.rfind(suffix, 1) == s.size() - suffix.size();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// RUN: %check_clang_tidy %s portability-template-virtual-member-function %t
namespace UninstantiatedVirtualMember {
template<typename T>
struct CrossPlatformError {
virtual ~CrossPlatformError() = default;

static void used() {}

// CHECK-MESSAGES: [[#@LINE+1]]:18: warning: unspecified virtual member function instantiation
virtual void unused() {
T MSVCError = this;
};
};

int main() {
// CHECK-MESSAGES: [[#@LINE+1]]:5: note: template instantiated here
CrossPlatformError<int>::used();
return 0;
}
} // namespace UninstantiatedVirtualMember

namespace UninstantiatedVirtualMembers {
template<typename T>
struct CrossPlatformError {
virtual ~CrossPlatformError() = default;

static void used() {}

// CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
// CHECK-MESSAGES: [[#@LINE+13]]:5: note: template instantiated here
virtual void unused() {
T MSVCError = this;
};

// CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
// CHECK-MESSAGES: [[#@LINE+7]]:5: note: template instantiated here
virtual void unused2() {
T MSVCError = this;
};
};

int main() {
CrossPlatformError<int>::used();
return 0;
}
} // namespace UninstantiatedVirtualMembers

namespace UninstantiatedVirtualDestructor {
template<typename T>
struct CrossPlatformError {
// CHECK-MESSAGES: [[#@LINE+2]]:13: warning: unspecified virtual member function instantiation
// CHECK-MESSAGES: [[#@LINE+9]]:5: note: template instantiated here
virtual ~CrossPlatformError() {
T MSVCError = this;
};

static void used() {}
};

int main() {
CrossPlatformError<int>::used();
return 0;
}
} // namespace UninstantiatedVirtualDestructor

namespace MultipleImplicitInstantiations {
template<typename T>
struct CrossPlatformError {
virtual ~CrossPlatformError() = default;

static void used() {}

// CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
// CHECK-MESSAGES: [[#@LINE+7]]:5: note: template instantiated here
virtual void unused() {
T MSVCError = this;
};
};

int main() {
CrossPlatformError<int>::used();
CrossPlatformError<float>::used();
CrossPlatformError<long>::used();
return 0;
}
} // namespace MultipleImplicitInstantiations

namespace SomeImplicitInstantiationError {
template <typename T> struct CrossPlatformError {
virtual ~CrossPlatformError() = default;

static void used() {}

// CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
// CHECK-MESSAGES: [[#@LINE+5]]:5: note: template instantiated here
virtual void unused(){};
};

int main() {
CrossPlatformError<int>::used();
CrossPlatformError<float> NoError;
return 0;
}
} // namespace SomeImplicitInstantiationError

namespace InstantiatedVirtualMemberFunctions {
template<typename T>
struct NoError {
virtual ~NoError() {};
virtual void unused() {};
virtual void unused2() {};
virtual void unused3() {};
};

int main() {
NoError<int> Ne;
return 0;
}
} // namespace InstantiatedVirtualMemberFunctions

namespace UninstantiatedNonVirtualMemberFunctions {
template<typename T>
struct NoError {
static void used() {};
void unused() {};
void unused2() {};
void unused3() {};
};

int main() {
NoError<int>::used();
return 0;
}
} // namespace UninstantiatedNonVirtualMemberFunctions

namespace PartialSpecializationError {
template<typename T, typename U>
struct CrossPlatformError {};

template<typename U>
struct CrossPlatformError<int, U>{
virtual ~CrossPlatformError() = default;

static void used() {}

// CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
// CHECK-MESSAGES: [[#@LINE+7]]:5: note: template instantiated here
virtual void unused() {
U MSVCError = this;
};
};

int main() {
CrossPlatformError<int, float>::used();
return 0;
}
} // namespace PartialSpecializationError

namespace PartialSpecializationNoInstantiation {
template<typename T, typename U>
struct NoInstantiation {};

template<typename U>
struct NoInstantiation<int, U>{
virtual ~NoInstantiation() = default;

static void used() {}

virtual void unused() {
U MSVCError = this;
};
};
} // namespace PartialSpecializationNoInstantiation
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ inline namespace InlineNamespace {
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: invalid case style for inline namespace 'InlineNamespace'
// CHECK-FIXES: {{^}}inline namespace inline_namespace {{{$}}

namespace FOO_ALIAS = FOO_NS;
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: invalid case style for namespace 'FOO_ALIAS' [readability-identifier-naming]
// CHECK-FIXES: {{^}}namespace foo_alias = FOO_NS;{{$}}

SYSTEM_NS::structure g_s1;
// NO warnings or fixes expected as SYSTEM_NS and structure are declared in a header file

Expand Down
13 changes: 13 additions & 0 deletions clang/bindings/python/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,19 @@ if(${LLVM_NATIVE_ARCH} MATCHES "^(AArch64|Hexagon|Sparc|SystemZ)$")
set(RUN_PYTHON_TESTS FALSE)
endif()

# Tests will fail if cross-compiling for a different target, as tests will try
# to use the host Python3_EXECUTABLE and make FFI calls to functions in target
# libraries.
if(CMAKE_CROSSCOMPILING)
# FIXME: Consider a solution that allows better control over these tests in
# a crosscompiling scenario. e.g. registering them with lit to allow them to
# be explicitly skipped via appropriate LIT_ARGS, or adding a mechanism to
# allow specifying a python interpreter compiled for the target that could
# be executed using qemu-user.
message(WARNING "check-clang-python not added to check-all as these tests fail in a cross-build setup")
set(RUN_PYTHON_TESTS FALSE)
endif()

if(RUN_PYTHON_TESTS)
set_property(GLOBAL APPEND PROPERTY
LLVM_ALL_ADDITIONAL_TEST_TARGETS check-clang-python)
Expand Down
2 changes: 2 additions & 0 deletions clang/cmake/caches/Fuchsia-stage2.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ foreach(target armv6m-none-eabi;armv7m-none-eabi;armv8m.main-none-eabi)
set(RUNTIMES_${target}_LIBCXX_CXX_ABI none CACHE STRING "")
set(RUNTIMES_${target}_LIBCXX_ENABLE_SHARED OFF CACHE BOOL "")
set(RUNTIMES_${target}_LIBCXX_ENABLE_STATIC ON CACHE BOOL "")
set(RUNTIMES_${target}_LIBCXX_SHARED_OUTPUT_NAME "c++-shared" CACHE STRING "")
set(RUNTIMES_${target}_LIBCXX_LIBC "llvm-libc" CACHE STRING "")
set(RUNTIMES_${target}_LIBCXX_ENABLE_FILESYSTEM OFF CACHE BOOL "")
set(RUNTIMES_${target}_LIBCXX_ENABLE_RANDOM_DEVICE OFF CACHE BOOL "")
Expand Down Expand Up @@ -396,6 +397,7 @@ foreach(target riscv32-unknown-elf)
set(RUNTIMES_${target}_LIBCXX_CXX_ABI none CACHE STRING "")
set(RUNTIMES_${target}_LIBCXX_ENABLE_SHARED OFF CACHE BOOL "")
set(RUNTIMES_${target}_LIBCXX_ENABLE_STATIC ON CACHE BOOL "")
set(RUNTIMES_${target}_LIBCXX_SHARED_OUTPUT_NAME "c++-shared" CACHE STRING "")
set(RUNTIMES_${target}_LIBCXX_LIBC "llvm-libc" CACHE STRING "")
set(RUNTIMES_${target}_LIBCXX_ENABLE_FILESYSTEM OFF CACHE BOOL "")
set(RUNTIMES_${target}_LIBCXX_ENABLE_RANDOM_DEVICE OFF CACHE BOOL "")
Expand Down
11 changes: 11 additions & 0 deletions clang/cmake/modules/AddClang.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,17 @@ macro(add_clang_library name)
endif()
llvm_add_library(${name} ${LIBTYPE} ${ARG_UNPARSED_ARGUMENTS} ${srcs})

if(MSVC AND NOT CLANG_LINK_CLANG_DYLIB)
# Make sure all consumers also turn off visibility macros so there not trying to dllimport symbols.
target_compile_definitions(${name} PUBLIC CLANG_BUILD_STATIC)
if(TARGET "obj.${name}")
target_compile_definitions("obj.${name}" PUBLIC CLANG_BUILD_STATIC)
endif()
elseif(NOT ARG_SHARED AND NOT ARG_STATIC)
# Clang component libraries linked in to clang-cpp are declared without SHARED or STATIC
target_compile_definitions("obj.${name}" PUBLIC CLANG_EXPORTS)
endif()

set(libs ${name})
if(ARG_SHARED AND ARG_STATIC)
list(APPEND libs ${name}_static)
Expand Down
50 changes: 37 additions & 13 deletions clang/docs/ClangFormatStyleOptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5406,22 +5406,46 @@ the configuration (without a prefix: ``Auto``).

.. _ReflowComments:

**ReflowComments** (``Boolean``) :versionbadge:`clang-format 3.8` :ref:`¶ <ReflowComments>`
If ``true``, clang-format will attempt to re-flow comments. That is it
will touch a comment and *reflow* long comments into new lines, trying to
obey the ``ColumnLimit``.
**ReflowComments** (``ReflowCommentsStyle``) :versionbadge:`clang-format 3.8` :ref:`¶ <ReflowComments>`
Comment reformatting style.

.. code-block:: c++
Possible values:

* ``RCS_Never`` (in configuration: ``Never``)
Leave comments untouched.

.. code-block:: c++

// veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information
/* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information */
/* third veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information
* and a misaligned second line */
* ``RCS_IndentOnly`` (in configuration: ``IndentOnly``)
Only apply indentation rules, moving comments left or right, without
changing formatting inside the comments.

.. code-block:: c++

// veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information
/* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information */
/* third veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information
* and a misaligned second line */
* ``RCS_Always`` (in configuration: ``Always``)
Apply indentation rules and reflow long comments into new lines, trying
to obey the ``ColumnLimit``.

.. code-block:: c++

// veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of
// information
/* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of
* information */
/* third veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of
* information and a misaligned second line */
false:
// veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information
/* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information */

true:
// veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of
// information
/* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of
* information */

.. _RemoveBracesLLVM:

Expand Down
17 changes: 12 additions & 5 deletions clang/docs/ClangPlugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,6 @@ The members of ``ParsedAttrInfo`` that a plugin attribute must define are:
attribute, each of which consists of an attribute syntax and how the
attribute name is spelled for that syntax. If the syntax allows a scope then
the spelling must be "scope::attr" if a scope is present or "::attr" if not.
* ``handleDeclAttribute``, which is the function that applies the attribute to
a declaration. It is responsible for checking that the attribute's arguments
are valid, and typically applies the attribute by adding an ``Attr`` to the
``Decl``. It returns either ``AttributeApplied``, to indicate that the
attribute was successfully applied, or ``AttributeNotApplied`` if it wasn't.

The members of ``ParsedAttrInfo`` that may need to be defined, depending on the
attribute, are:
Expand All @@ -105,6 +100,18 @@ attribute, are:
arguments to the attribute.
* ``diagAppertainsToDecl``, which checks if the attribute has been used on the
right kind of declaration and issues a diagnostic if not.
* ``handleDeclAttribute``, which is the function that applies the attribute to
a declaration. It is responsible for checking that the attribute's arguments
are valid, and typically applies the attribute by adding an ``Attr`` to the
``Decl``. It returns either ``AttributeApplied``, to indicate that the
attribute was successfully applied, or ``AttributeNotApplied`` if it wasn't.
* ``diagAppertainsToStmt``, which checks if the attribute has been used on the
right kind of statement and issues a diagnostic if not.
* ``handleStmtAttribute``, which is the function that applies the attribute to
a statement. It is responsible for checking that the attribute's arguments
are valid, and typically applies the attribute by adding an ``Attr`` to the
``Stmt``. It returns either ``AttributeApplied``, to indicate that the
attribute was successfully applied, or ``AttributeNotApplied`` if it wasn't.
* ``diagLangOpts``, which checks if the attribute is permitted for the current
language mode and issues a diagnostic if not.
* ``existsInTarget``, which checks if the attribute is permitted for the given
Expand Down
23 changes: 23 additions & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5883,3 +5883,26 @@ specify the starting offset to begin embedding from. The resources is treated
as being empty if the specified offset is larger than the number of bytes in
the resource. The offset will be applied *before* any ``limit`` parameters are
applied.
Union and aggregate initialization in C
=======================================
In C23 (N2900), when an object is initialized from initializer ``= {}``, all
elements of arrays, all members of structs, and the first members of unions are
empty-initialized recursively. In addition, all padding bits are initialized to
zero.
Clang guarantees the following behaviors:
* ``1:`` Clang supports initializer ``= {}`` mentioned above in all C
standards.
* ``2:`` When unions are initialized from initializer ``= {}``, bytes outside
of the first members of unions are also initialized to zero.
* ``3:`` When unions, structures and arrays are initialized from initializer
``= { initializer-list }``, all members not explicitly initialized in
the initializer list are empty-initialized recursively. In addition, all
padding bits are initialized to zero.
Currently, the above extension only applies to C source code, not C++.
61 changes: 48 additions & 13 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,20 @@ C++ Specific Potentially Breaking Changes
// Was error, now evaluates to false.
constexpr bool b = f() == g();
- The warning ``-Wdeprecated-literal-operator`` is now on by default, as this is
something that WG21 has shown interest in removing from the language. The
result is that anyone who is compiling with ``-Werror`` should see this
diagnostic. To fix this diagnostic, simply removing the space character from
between the ``operator""`` and the user defined literal name will make the
source no longer deprecated. This is consistent with `CWG2521 <https://cplusplus.github.io/CWG/issues/2521.html>_`.

.. code-block:: c++

// Now diagnoses by default.
unsigned operator"" _udl_name(unsigned long long);
// Fixed version:
unsigned operator""_udl_name(unsigned long long);

ABI Changes in This Version
---------------------------

Expand Down Expand Up @@ -171,13 +185,12 @@ C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^
- Removed the restriction to literal types in constexpr functions in C++23 mode.

- Extend lifetime of temporaries in mem-default-init for P2718R0. Clang now fully
supports `P2718R0 Lifetime extension in range-based for loops <https://wg21.link/P2718R0>`_.

C++20 Feature Support
^^^^^^^^^^^^^^^^^^^^^

C++17 Feature Support
^^^^^^^^^^^^^^^^^^^^^
- The implementation of the relaxed template template argument matching rules is
more complete and reliable, and should provide more accurate diagnostics.

Resolutions to C++ Defect Reports
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -204,8 +217,7 @@ Resolutions to C++ Defect Reports
(`CWG2351: void{} <https://cplusplus.github.io/CWG/issues/2351.html>`_).

- Clang now has improved resolution to CWG2398, allowing class templates to have
default arguments deduced when partial ordering, and better backwards compatibility
in overload resolution.
default arguments deduced when partial ordering.

- Clang now allows comparing unequal object pointers that have been cast to ``void *``
in constant expressions. These comparisons always worked in non-constant expressions.
Expand All @@ -217,6 +229,10 @@ Resolutions to C++ Defect Reports
- Clang now allows trailing requires clause on explicit deduction guides.
(`CWG2707: Deduction guides cannot have a trailing requires-clause <https://cplusplus.github.io/CWG/issues/2707.html>`_).

- Clang now diagnoses a space in the first production of a ``literal-operator-id``
by default.
(`CWG2521: User-defined literals and reserved identifiers <https://cplusplus.github.io/CWG/issues/2521.html>`_).

C Language Changes
------------------

Expand All @@ -234,6 +250,8 @@ Non-comprehensive list of changes in this release
- The floating point comparison builtins (``__builtin_isgreater``,
``__builtin_isgreaterequal``, ``__builtin_isless``, etc.) and
``__builtin_signbit`` can now be used in constant expressions.
- Plugins can now define custom attributes that apply to statements
as well as declarations.

New Compiler Flags
------------------
Expand Down Expand Up @@ -336,10 +354,6 @@ Improvements to Clang's diagnostics

- Clang now diagnoses when the result of a [[nodiscard]] function is discarded after being cast in C. Fixes #GH104391.

- Clang now properly explains the reason a template template argument failed to
match a template template parameter, in terms of the C++17 relaxed matching rules
instead of the old ones.

- Don't emit duplicated dangling diagnostics. (#GH93386).

- Improved diagnostic when trying to befriend a concept. (#GH45182).
Expand Down Expand Up @@ -384,6 +398,10 @@ Improvements to Clang's diagnostics
- The warning for an unsupported type for a named register variable is now phrased ``unsupported type for named register variable``,
instead of ``bad type for named register variable``. This makes it clear that the type is not supported at all, rather than being
suboptimal in some way the error fails to mention (#GH111550).

- Clang now emits a ``-Wdepredcated-literal-operator`` diagnostic, even if the
name was a reserved name, which we improperly allowed to suppress the
diagnostic.

Improvements to Clang's time-trace
----------------------------------
Expand Down Expand Up @@ -449,8 +467,6 @@ Bug Fixes to C++ Support
- Correctly check constraints of explicit instantiations of member functions. (#GH46029)
- When performing partial ordering of function templates, clang now checks that
the deduction was consistent. Fixes (#GH18291).
- Fixes to several issues in partial ordering of template template parameters, which
were documented in the test suite.
- Fixed an assertion failure about a constraint of a friend function template references to a value with greater
template depth than the friend function template. (#GH98258)
- Clang now rebuilds the template parameters of out-of-line declarations and specializations in the context
Expand Down Expand Up @@ -488,9 +504,19 @@ Bug Fixes to C++ Support
in certain friend declarations. (#GH93099)
- Clang now instantiates the correct lambda call operator when a lambda's class type is
merged across modules. (#GH110401)
- Clang now uses the correct set of template argument lists when comparing the constraints of
out-of-line definitions and member templates explicitly specialized for a given implicit instantiation of
a class template. (#GH102320)
- Fix a crash when parsing a pseudo destructor involving an invalid type. (#GH111460)
- Fixed an assertion failure when invoking recovery call expressions with explicit attributes
and undeclared templates. (#GH107047, #GH49093)
and undeclared templates. (#GH107047), (#GH49093)
- Clang no longer crashes when a lambda contains an invalid block declaration that contains an unexpanded
parameter pack. (#GH109148)
- Fixed overload handling for object parameters with top-level cv-qualifiers in explicit member functions (#GH100394)
- Fixed a bug in lambda captures where ``constexpr`` class-type objects were not properly considered ODR-used in
certain situations. (#GH47400), (#GH90896)
- Fix erroneous templated array size calculation leading to crashes in generated code. (#GH41441)
- During the lookup for a base class name, non-type names are ignored. (#GH16855)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -598,6 +624,8 @@ CUDA/HIP Language Changes

CUDA Support
^^^^^^^^^^^^
- Clang now supports CUDA SDK up to 12.6
- Added support for sm_100

AIX Support
^^^^^^^^^^^
Expand All @@ -611,6 +639,8 @@ WebAssembly Support
AVR Support
^^^^^^^^^^^

- Reject C/C++ compilation for avr1 devices which have no SRAM.

DWARF Support in Clang
----------------------

Expand All @@ -633,12 +663,17 @@ AST Matchers

- Fixed a crash when traverse lambda expr with invalid captures. (#GH106444)

- Ensure ``hasName`` matches template specializations across inline namespaces,
making `matchesNodeFullSlow` and `matchesNodeFullFast` consistent.

clang-format
------------

- Adds ``BreakBinaryOperations`` option.
- Adds ``TemplateNames`` option.
- Adds ``AlignFunctionDeclarations`` option to ``AlignConsecutiveDeclarations``.
- Adds ``IndentOnly`` suboption to ``ReflowComments`` to fix the indentation of multi-line comments
without touching their contents, renames ``false`` to ``Never``, and ``true`` to ``Always``.

libclang
--------
Expand Down
48 changes: 48 additions & 0 deletions clang/examples/Attribute/Attribute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,54 @@ struct ExampleAttrInfo : public ParsedAttrInfo {
}
return AttributeApplied;
}

bool diagAppertainsToStmt(Sema &S, const ParsedAttr &Attr,
const Stmt *St) const override {
// This attribute appertains to for loop statements only.
if (!isa<ForStmt>(St)) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type_str)
<< Attr << Attr.isRegularKeywordAttribute() << "for loop statements";
return false;
}
return true;
}

AttrHandling handleStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &Attr,
class Attr *&Result) const override {
// We make some rules here:
// 1. Only accept at most 3 arguments here.
// 2. The first argument must be a string literal if it exists.
if (Attr.getNumArgs() > 3) {
unsigned ID = S.getDiagnostics().getCustomDiagID(
DiagnosticsEngine::Error,
"'example' attribute only accepts at most three arguments");
S.Diag(Attr.getLoc(), ID);
return AttributeNotApplied;
}
// If there are arguments, the first argument should be a string literal.
if (Attr.getNumArgs() > 0) {
auto *Arg0 = Attr.getArgAsExpr(0);
StringLiteral *Literal =
dyn_cast<StringLiteral>(Arg0->IgnoreParenCasts());
if (!Literal) {
unsigned ID = S.getDiagnostics().getCustomDiagID(
DiagnosticsEngine::Error, "first argument to the 'example' "
"attribute must be a string literal");
S.Diag(Attr.getLoc(), ID);
return AttributeNotApplied;
}
SmallVector<Expr *, 16> ArgsBuf;
for (unsigned i = 0; i < Attr.getNumArgs(); i++) {
ArgsBuf.push_back(Attr.getArgAsExpr(i));
}
Result = AnnotateAttr::Create(S.Context, "example", ArgsBuf.data(),
ArgsBuf.size(), Attr.getRange());
} else {
Result = AnnotateAttr::Create(S.Context, "example", nullptr, 0,
Attr.getRange());
}
return AttributeApplied;
}
};

} // namespace
Expand Down
17 changes: 17 additions & 0 deletions clang/include/clang/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,23 @@ class InheritableParamAttr : public InheritableAttr {
}
};

class InheritableParamOrStmtAttr : public InheritableParamAttr {
protected:
InheritableParamOrStmtAttr(ASTContext &Context,
const AttributeCommonInfo &CommonInfo,
attr::Kind AK, bool IsLateParsed,
bool InheritEvenIfAlreadyPresent)
: InheritableParamAttr(Context, CommonInfo, AK, IsLateParsed,
InheritEvenIfAlreadyPresent) {}

public:
// Implement isa/cast/dyncast/etc.
static bool classof(const Attr *A) {
return A->getKind() >= attr::FirstInheritableParamOrStmtAttr &&
A->getKind() <= attr::LastInheritableParamOrStmtAttr;
}
};

class HLSLAnnotationAttr : public InheritableAttr {
protected:
HLSLAnnotationAttr(ASTContext &Context, const AttributeCommonInfo &CommonInfo,
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/AST/ComputeDependence.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ ExprDependence computeDependence(ArrayInitLoopExpr *E);
ExprDependence computeDependence(ImplicitValueInitExpr *E);
ExprDependence computeDependence(InitListExpr *E);
ExprDependence computeDependence(ExtVectorElementExpr *E);
ExprDependence computeDependence(BlockExpr *E);
ExprDependence computeDependence(BlockExpr *E,
bool ContainsUnexpandedParameterPack);
ExprDependence computeDependence(AsTypeExpr *E);
ExprDependence computeDependence(DeclRefExpr *E, const ASTContext &Ctx);
ExprDependence computeDependence(RecoveryExpr *E);
Expand Down
97 changes: 59 additions & 38 deletions clang/include/clang/AST/DeclTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -781,15 +781,11 @@ class RedeclarableTemplateDecl : public TemplateDecl,
EntryType *Entry, void *InsertPos);

struct CommonBase {
CommonBase() : InstantiatedFromMember(nullptr, false) {}
CommonBase() {}

/// The template from which this was most
/// directly instantiated (or null).
///
/// The boolean value indicates whether this template
/// was explicitly specialized.
llvm::PointerIntPair<RedeclarableTemplateDecl*, 1, bool>
InstantiatedFromMember;
RedeclarableTemplateDecl *InstantiatedFromMember = nullptr;

/// If non-null, points to an array of specializations (including
/// partial specializations) known only by their external declaration IDs.
Expand All @@ -809,14 +805,19 @@ class RedeclarableTemplateDecl : public TemplateDecl,
};

/// Pointer to the common data shared by all declarations of this
/// template.
mutable CommonBase *Common = nullptr;
/// template, and a flag indicating if the template is a member
/// specialization.
mutable llvm::PointerIntPair<CommonBase *, 1, bool> Common;

CommonBase *getCommonPtrInternal() const { return Common.getPointer(); }

/// Retrieves the "common" pointer shared by all (re-)declarations of
/// the same template. Calling this routine may implicitly allocate memory
/// for the common pointer.
CommonBase *getCommonPtr() const;

void setCommonPtr(CommonBase *C) const { Common.setPointer(C); }

virtual CommonBase *newCommon(ASTContext &C) const = 0;

// Construct a template decl with name, parameters, and templated element.
Expand Down Expand Up @@ -857,15 +858,22 @@ class RedeclarableTemplateDecl : public TemplateDecl,
/// template<> template<typename T>
/// struct X<int>::Inner { /* ... */ };
/// \endcode
bool isMemberSpecialization() const {
return getCommonPtr()->InstantiatedFromMember.getInt();
bool isMemberSpecialization() const { return Common.getInt(); }

/// Determines whether any redeclaration of this template was
/// a specialization of a member template.
bool hasMemberSpecialization() const {
for (const auto *D : redecls()) {
if (D->isMemberSpecialization())
return true;
}
return false;
}

/// Note that this member template is a specialization.
void setMemberSpecialization() {
assert(getCommonPtr()->InstantiatedFromMember.getPointer() &&
"Only member templates can be member template specializations");
getCommonPtr()->InstantiatedFromMember.setInt(true);
assert(!isMemberSpecialization() && "already a member specialization");
Common.setInt(true);
}

/// Retrieve the member template from which this template was
Expand Down Expand Up @@ -905,12 +913,12 @@ class RedeclarableTemplateDecl : public TemplateDecl,
/// void X<T>::f(T, U);
/// \endcode
RedeclarableTemplateDecl *getInstantiatedFromMemberTemplate() const {
return getCommonPtr()->InstantiatedFromMember.getPointer();
return getCommonPtr()->InstantiatedFromMember;
}

void setInstantiatedFromMemberTemplate(RedeclarableTemplateDecl *TD) {
assert(!getCommonPtr()->InstantiatedFromMember.getPointer());
getCommonPtr()->InstantiatedFromMember.setPointer(TD);
assert(!getCommonPtr()->InstantiatedFromMember);
getCommonPtr()->InstantiatedFromMember = TD;
}

/// Retrieve the "injected" template arguments that correspond to the
Expand Down Expand Up @@ -1989,6 +1997,8 @@ class ClassTemplateSpecializationDecl : public CXXRecordDecl,
/// template arguments have been deduced.
void setInstantiationOf(ClassTemplatePartialSpecializationDecl *PartialSpec,
const TemplateArgumentList *TemplateArgs) {
assert(!isa<ClassTemplatePartialSpecializationDecl>(this) &&
"A partial specialization cannot be instantiated from a template");
assert(!SpecializedTemplate.is<SpecializedPartialSpecialization*>() &&
"Already set to a class template partial specialization!");
auto *PS = new (getASTContext()) SpecializedPartialSpecialization();
Expand All @@ -2000,6 +2010,8 @@ class ClassTemplateSpecializationDecl : public CXXRecordDecl,
/// Note that this class template specialization is an instantiation
/// of the given class template.
void setInstantiationOf(ClassTemplateDecl *TemplDecl) {
assert(!isa<ClassTemplatePartialSpecializationDecl>(this) &&
"A partial specialization cannot be instantiated from a template");
assert(!SpecializedTemplate.is<SpecializedPartialSpecialization*>() &&
"Previously set to a class template partial specialization!");
SpecializedTemplate = TemplDecl;
Expand Down Expand Up @@ -2187,19 +2199,23 @@ class ClassTemplatePartialSpecializationDecl
/// struct X<int>::Inner<T*> { /* ... */ };
/// \endcode
bool isMemberSpecialization() const {
const auto *First =
cast<ClassTemplatePartialSpecializationDecl>(getFirstDecl());
return First->InstantiatedFromMember.getInt();
return InstantiatedFromMember.getInt();
}

/// Note that this member template is a specialization.
void setMemberSpecialization() {
auto *First = cast<ClassTemplatePartialSpecializationDecl>(getFirstDecl());
assert(First->InstantiatedFromMember.getPointer() &&
"Only member templates can be member template specializations");
return First->InstantiatedFromMember.setInt(true);
/// Determines whether any redeclaration of this this class template partial
/// specialization was a specialization of a member partial specialization.
bool hasMemberSpecialization() const {
for (const auto *D : redecls()) {
if (cast<ClassTemplatePartialSpecializationDecl>(D)
->isMemberSpecialization())
return true;
}
return false;
}

/// Note that this member template is a specialization.
void setMemberSpecialization() { return InstantiatedFromMember.setInt(true); }

/// Retrieves the injected specialization type for this partial
/// specialization. This is not the same as the type-decl-type for
/// this partial specialization, which is an InjectedClassNameType.
Expand Down Expand Up @@ -2268,10 +2284,6 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl {
return static_cast<Common *>(RedeclarableTemplateDecl::getCommonPtr());
}

void setCommonPtr(Common *C) {
RedeclarableTemplateDecl::Common = C;
}

public:

friend class ASTDeclReader;
Expand Down Expand Up @@ -2754,6 +2766,8 @@ class VarTemplateSpecializationDecl : public VarDecl,
/// template arguments have been deduced.
void setInstantiationOf(VarTemplatePartialSpecializationDecl *PartialSpec,
const TemplateArgumentList *TemplateArgs) {
assert(!isa<VarTemplatePartialSpecializationDecl>(this) &&
"A partial specialization cannot be instantiated from a template");
assert(!SpecializedTemplate.is<SpecializedPartialSpecialization *>() &&
"Already set to a variable template partial specialization!");
auto *PS = new (getASTContext()) SpecializedPartialSpecialization();
Expand All @@ -2765,6 +2779,8 @@ class VarTemplateSpecializationDecl : public VarDecl,
/// Note that this variable template specialization is an instantiation
/// of the given variable template.
void setInstantiationOf(VarTemplateDecl *TemplDecl) {
assert(!isa<VarTemplatePartialSpecializationDecl>(this) &&
"A partial specialization cannot be instantiated from a template");
assert(!SpecializedTemplate.is<SpecializedPartialSpecialization *>() &&
"Previously set to a variable template partial specialization!");
SpecializedTemplate = TemplDecl;
Expand Down Expand Up @@ -2949,19 +2965,24 @@ class VarTemplatePartialSpecializationDecl
/// U* X<int>::Inner<T*> = (T*)(0) + 1;
/// \endcode
bool isMemberSpecialization() const {
const auto *First =
cast<VarTemplatePartialSpecializationDecl>(getFirstDecl());
return First->InstantiatedFromMember.getInt();
return InstantiatedFromMember.getInt();
}

/// Note that this member template is a specialization.
void setMemberSpecialization() {
auto *First = cast<VarTemplatePartialSpecializationDecl>(getFirstDecl());
assert(First->InstantiatedFromMember.getPointer() &&
"Only member templates can be member template specializations");
return First->InstantiatedFromMember.setInt(true);
/// Determines whether any redeclaration of this this variable template
/// partial specialization was a specialization of a member partial
/// specialization.
bool hasMemberSpecialization() const {
for (const auto *D : redecls()) {
if (cast<VarTemplatePartialSpecializationDecl>(D)
->isMemberSpecialization())
return true;
}
return false;
}

/// Note that this member template is a specialization.
void setMemberSpecialization() { return InstantiatedFromMember.setInt(true); }

SourceRange getSourceRange() const override LLVM_READONLY;

void Profile(llvm::FoldingSetNodeID &ID) const {
Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -6413,9 +6413,9 @@ class BlockExpr : public Expr {
protected:
BlockDecl *TheBlock;
public:
BlockExpr(BlockDecl *BD, QualType ty)
BlockExpr(BlockDecl *BD, QualType ty, bool ContainsUnexpandedParameterPack)
: Expr(BlockExprClass, ty, VK_PRValue, OK_Ordinary), TheBlock(BD) {
setDependence(computeDependence(this));
setDependence(computeDependence(this, ContainsUnexpandedParameterPack));
}

/// Build an empty block expression.
Expand Down
142 changes: 64 additions & 78 deletions clang/include/clang/AST/OpenACCClause.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,84 +119,6 @@ class OpenACCSeqClause : public OpenACCClause {
}
};

// Not yet implemented, but the type name is necessary for 'seq' diagnostics, so
// this provides a basic, do-nothing implementation. We still need to add this
// type to the visitors/etc, as well as get it to take its proper arguments.
class OpenACCGangClause : public OpenACCClause {
protected:
OpenACCGangClause(SourceLocation BeginLoc, SourceLocation EndLoc)
: OpenACCClause(OpenACCClauseKind::Gang, BeginLoc, EndLoc) {
llvm_unreachable("Not yet implemented");
}

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::Gang;
}

static OpenACCGangClause *
Create(const ASTContext &Ctx, SourceLocation BeginLoc, SourceLocation EndLoc);

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

// Not yet implemented, but the type name is necessary for 'seq' diagnostics, so
// this provides a basic, do-nothing implementation. We still need to add this
// type to the visitors/etc, as well as get it to take its proper arguments.
class OpenACCVectorClause : public OpenACCClause {
protected:
OpenACCVectorClause(SourceLocation BeginLoc, SourceLocation EndLoc)
: OpenACCClause(OpenACCClauseKind::Vector, BeginLoc, EndLoc) {
llvm_unreachable("Not yet implemented");
}

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::Gang;
}

static OpenACCVectorClause *
Create(const ASTContext &Ctx, SourceLocation BeginLoc, SourceLocation EndLoc);

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

// Not yet implemented, but the type name is necessary for 'seq' diagnostics, so
// this provides a basic, do-nothing implementation. We still need to add this
// type to the visitors/etc, as well as get it to take its proper arguments.
class OpenACCWorkerClause : public OpenACCClause {
protected:
OpenACCWorkerClause(SourceLocation BeginLoc, SourceLocation EndLoc)
: OpenACCClause(OpenACCClauseKind::Gang, BeginLoc, EndLoc) {
llvm_unreachable("Not yet implemented");
}

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::Gang;
}

static OpenACCWorkerClause *
Create(const ASTContext &Ctx, SourceLocation BeginLoc, SourceLocation EndLoc);

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

/// Represents a clause that has a list of parameters.
class OpenACCClauseWithParams : public OpenACCClause {
/// Location of the '('.
Expand Down Expand Up @@ -535,6 +457,70 @@ class OpenACCClauseWithSingleIntExpr : public OpenACCClauseWithExprs {
Expr *getIntExpr() { return hasIntExpr() ? getExprs()[0] : nullptr; };
};

class OpenACCGangClause final
: public OpenACCClauseWithExprs,
public llvm::TrailingObjects<OpenACCGangClause, Expr *, OpenACCGangKind> {
protected:
OpenACCGangClause(SourceLocation BeginLoc, SourceLocation LParenLoc,
ArrayRef<OpenACCGangKind> GangKinds,
ArrayRef<Expr *> IntExprs, SourceLocation EndLoc);

OpenACCGangKind getGangKind(unsigned I) const {
return getTrailingObjects<OpenACCGangKind>()[I];
}

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::Gang;
}

size_t numTrailingObjects(OverloadToken<Expr *>) const {
return getNumExprs();
}

unsigned getNumExprs() const { return getExprs().size(); }
std::pair<OpenACCGangKind, const Expr *> getExpr(unsigned I) const {
return {getGangKind(I), getExprs()[I]};
}

static OpenACCGangClause *
Create(const ASTContext &Ctx, SourceLocation BeginLoc,
SourceLocation LParenLoc, ArrayRef<OpenACCGangKind> GangKinds,
ArrayRef<Expr *> IntExprs, SourceLocation EndLoc);
};

class OpenACCWorkerClause : public OpenACCClauseWithSingleIntExpr {
protected:
OpenACCWorkerClause(SourceLocation BeginLoc, SourceLocation LParenLoc,
Expr *IntExpr, SourceLocation EndLoc);

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::Worker;
}

static OpenACCWorkerClause *Create(const ASTContext &Ctx,
SourceLocation BeginLoc,
SourceLocation LParenLoc, Expr *IntExpr,
SourceLocation EndLoc);
};

class OpenACCVectorClause : public OpenACCClauseWithSingleIntExpr {
protected:
OpenACCVectorClause(SourceLocation BeginLoc, SourceLocation LParenLoc,
Expr *IntExpr, SourceLocation EndLoc);

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::Vector;
}

static OpenACCVectorClause *Create(const ASTContext &Ctx,
SourceLocation BeginLoc,
SourceLocation LParenLoc, Expr *IntExpr,
SourceLocation EndLoc);
};

class OpenACCNumWorkersClause : public OpenACCClauseWithSingleIntExpr {
OpenACCNumWorkersClause(SourceLocation BeginLoc, SourceLocation LParenLoc,
Expr *IntExpr, SourceLocation EndLoc);
Expand Down
18 changes: 11 additions & 7 deletions clang/include/clang/AST/PrettyPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,17 @@ class PrintingCallbacks {
/// This type is intended to be small and suitable for passing by value.
/// It is very frequently copied.
struct PrintingPolicy {
enum SuppressInlineNamespaceMode : uint8_t { None, Redundant, All };

/// Create a default printing policy for the specified language.
PrintingPolicy(const LangOptions &LO)
: Indentation(2), SuppressSpecifiers(false),
SuppressTagKeyword(LO.CPlusPlus), IncludeTagDefinition(false),
SuppressScope(false), SuppressUnwrittenScope(false),
SuppressInlineNamespace(true), SuppressElaboration(false),
SuppressInitializers(false), ConstantArraySizeAsWritten(false),
AnonymousTagLocations(true), SuppressStrongLifetime(false),
SuppressLifetimeQualifiers(false),
SuppressInlineNamespace(SuppressInlineNamespaceMode::Redundant),
SuppressElaboration(false), SuppressInitializers(false),
ConstantArraySizeAsWritten(false), AnonymousTagLocations(true),
SuppressStrongLifetime(false), SuppressLifetimeQualifiers(false),
SuppressTemplateArgsInCXXConstructors(false),
SuppressDefaultTemplateArgs(true), Bool(LO.Bool),
Nullptr(LO.CPlusPlus11 || LO.C23), NullptrTypeInNamespace(LO.CPlusPlus),
Expand Down Expand Up @@ -141,10 +143,12 @@ struct PrintingPolicy {
unsigned SuppressUnwrittenScope : 1;

/// Suppress printing parts of scope specifiers that correspond
/// to inline namespaces, where the name is unambiguous with the specifier
/// to inline namespaces.
/// If Redudant, where the name is unambiguous with the specifier removed.
/// If All, even if the name is ambiguous with the specifier
/// removed.
LLVM_PREFERRED_TYPE(bool)
unsigned SuppressInlineNamespace : 1;
LLVM_PREFERRED_TYPE(SuppressInlineNamespaceMode)
unsigned SuppressInlineNamespace : 2;

/// Ignore qualifiers and tag keywords as specified by elaborated type sugar,
/// instead letting the underlying type print as normal.
Expand Down
34 changes: 23 additions & 11 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -2662,6 +2662,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
#include "clang/Basic/HLSLIntangibleTypes.def"
bool isHLSLSpecificType() const; // Any HLSL specific type
bool isHLSLIntangibleType() const; // Any HLSL intangible type
bool isHLSLAttributedResourceType() const;

/// Determines if this type, which must satisfy
/// isObjCLifetimeType(), is implicitly __unsafe_unretained rather
Expand Down Expand Up @@ -6270,6 +6271,14 @@ class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode {
: ResourceClass(ResourceClass), IsROV(IsROV), RawBuffer(RawBuffer) {}

Attributes() : Attributes(llvm::dxil::ResourceClass::UAV, false, false) {}

friend bool operator==(const Attributes &LHS, const Attributes &RHS) {
return std::tie(LHS.ResourceClass, LHS.IsROV, LHS.RawBuffer) ==
std::tie(RHS.ResourceClass, RHS.IsROV, RHS.RawBuffer);
}
friend bool operator!=(const Attributes &LHS, const Attributes &RHS) {
return !(LHS == RHS);
}
};

private:
Expand All @@ -6279,20 +6288,21 @@ class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode {
QualType ContainedType;
const Attributes Attrs;

HLSLAttributedResourceType(QualType Canon, QualType Wrapped,
QualType Contained, const Attributes &Attrs)
: Type(HLSLAttributedResource, Canon,
HLSLAttributedResourceType(QualType Wrapped, QualType Contained,
const Attributes &Attrs)
: Type(HLSLAttributedResource, QualType(),
Contained.isNull() ? TypeDependence::None
: Contained->getDependence()),
WrappedType(Wrapped), ContainedType(Contained), Attrs(Attrs) {}

public:
QualType getWrappedType() const { return WrappedType; }
QualType getContainedType() const { return ContainedType; }
bool hasContainedType() const { return !ContainedType.isNull(); }
const Attributes &getAttrs() const { return Attrs; }

bool isSugared() const { return true; }
QualType desugar() const { return getWrappedType(); }
bool isSugared() const { return false; }
QualType desugar() const { return QualType(this, 0); }

void Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, WrappedType, ContainedType, Attrs);
Expand Down Expand Up @@ -8436,17 +8446,19 @@ inline bool Type::isOpenCLSpecificType() const {
}
#include "clang/Basic/HLSLIntangibleTypes.def"

inline bool Type::isHLSLSpecificType() const {
inline bool Type::isHLSLIntangibleType() const {
#define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) is##Id##Type() ||
return
#include "clang/Basic/HLSLIntangibleTypes.def"
false; // end boolean or operation
isHLSLAttributedResourceType();
}

inline bool Type::isHLSLIntangibleType() const {
// All HLSL specific types are currently intangible type as well, but that
// might change in the future.
return isHLSLSpecificType();
inline bool Type::isHLSLSpecificType() const {
return isHLSLIntangibleType() || isa<HLSLAttributedResourceType>(this);
}

inline bool Type::isHLSLAttributedResourceType() const {
return isa<HLSLAttributedResourceType>(this);
}

inline bool Type::isTemplateTypeParmType() const {
Expand Down
7 changes: 6 additions & 1 deletion clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,11 @@ class TargetSpecificAttr<TargetSpec target> {
/// redeclarations, even when it's written on a parameter.
class InheritableParamAttr : InheritableAttr;

/// A attribute that is either a declaration attribute or a statement attribute,
/// and if used as a declaration attribute, is inherited by later
/// redeclarations, even when it's written on a parameter.
class InheritableParamOrStmtAttr : InheritableParamAttr;

/// An attribute which changes the ABI rules for a specific parameter.
class ParameterABIAttr : InheritableParamAttr {
let Subjects = SubjectList<[ParmVar]>;
Expand Down Expand Up @@ -928,7 +933,7 @@ def AnalyzerNoReturn : InheritableAttr {
let Documentation = [Undocumented];
}

def Annotate : InheritableParamAttr {
def Annotate : InheritableParamOrStmtAttr {
let Spellings = [Clang<"annotate">];
let Args = [StringArgument<"Annotation">, VariadicExprArgument<"Args">];
// Ensure that the annotate attribute can be used with
Expand Down
44 changes: 42 additions & 2 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,18 @@ def FminF16F128 : Builtin, F16F128MathTemplate {
let Prototype = "T(T, T)";
}

def FmaximumNumF16F128 : Builtin, F16F128MathTemplate {
let Spellings = ["__builtin_fmaximum_num"];
let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const, Constexpr];
let Prototype = "T(T, T)";
}

def FminimumNumF16F128 : Builtin, F16F128MathTemplate {
let Spellings = ["__builtin_fminimum_num"];
let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const, Constexpr];
let Prototype = "T(T, T)";
}

def Atan2F128 : Builtin {
let Spellings = ["__builtin_atan2f128"];
let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions];
Expand Down Expand Up @@ -1186,13 +1198,13 @@ def AllowRuntimeCheck : Builtin {

def ShuffleVector : Builtin {
let Spellings = ["__builtin_shufflevector"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr];
let Prototype = "void(...)";
}

def ConvertVector : Builtin {
let Spellings = ["__builtin_convertvector"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr];
let Prototype = "void(...)";
}

Expand Down Expand Up @@ -3728,6 +3740,22 @@ def Fmin : FPMathTemplate, LibBuiltin<"math.h"> {
let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
}

def FmaximumNum : FPMathTemplate, LibBuiltin<"math.h"> {
let Spellings = ["fmaximum_num"];
let Attributes = [NoThrow, Const];
let Prototype = "T(T, T)";
let AddBuiltinPrefixedAlias = 1;
let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
}

def FminimumNum : FPMathTemplate, LibBuiltin<"math.h"> {
let Spellings = ["fminimum_num"];
let Attributes = [NoThrow, Const];
let Prototype = "T(T, T)";
let AddBuiltinPrefixedAlias = 1;
let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
}

def Hypot : FPMathTemplate, LibBuiltin<"math.h"> {
let Spellings = ["hypot"];
let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions];
Expand Down Expand Up @@ -4733,6 +4761,12 @@ def HLSLWaveIsFirstLane : LangBuiltin<"HLSL_LANG"> {
let Prototype = "bool()";
}

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

def HLSLClamp : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_elementwise_clamp"];
let Attributes = [NoThrow, Const];
Expand All @@ -4745,6 +4779,12 @@ def HLSLCross: LangBuiltin<"HLSL_LANG"> {
let Prototype = "void(...)";
}

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

def HLSLDotProduct : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_dot"];
let Attributes = [NoThrow, Const];
Expand Down
10 changes: 8 additions & 2 deletions clang/include/clang/Basic/BuiltinsNVPTX.def
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@
#pragma push_macro("SM_89")
#pragma push_macro("SM_90")
#pragma push_macro("SM_90a")
#pragma push_macro("SM_100")
#define SM_100 "sm_100"
#define SM_90a "sm_90a"
#define SM_90 "sm_90|" SM_90a
#define SM_90 "sm_90|" SM_90a "|" SM_100
#define SM_89 "sm_89|" SM_90
#define SM_87 "sm_87|" SM_89
#define SM_86 "sm_86|" SM_87
Expand Down Expand Up @@ -63,7 +65,9 @@
#pragma push_macro("PTX83")
#pragma push_macro("PTX84")
#pragma push_macro("PTX85")
#define PTX85 "ptx85"
#pragma push_macro("PTX86")
#define PTX86 "ptx86"
#define PTX85 "ptx85|" PTX86
#define PTX84 "ptx84|" PTX85
#define PTX83 "ptx83|" PTX84
#define PTX82 "ptx82|" PTX83
Expand Down Expand Up @@ -1086,6 +1090,7 @@ TARGET_BUILTIN(__nvvm_getctarank_shared_cluster, "iv*3", "", AND(SM_90,PTX78))
#pragma pop_macro("SM_89")
#pragma pop_macro("SM_90")
#pragma pop_macro("SM_90a")
#pragma pop_macro("SM_100")
#pragma pop_macro("PTX42")
#pragma pop_macro("PTX60")
#pragma pop_macro("PTX61")
Expand All @@ -1108,3 +1113,4 @@ TARGET_BUILTIN(__nvvm_getctarank_shared_cluster, "iv*3", "", AND(SM_90,PTX78))
#pragma pop_macro("PTX83")
#pragma pop_macro("PTX84")
#pragma pop_macro("PTX85")
#pragma pop_macro("PTX86")
4 changes: 3 additions & 1 deletion clang/include/clang/Basic/Cuda.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ enum class CudaVersion {
CUDA_123,
CUDA_124,
CUDA_125,
CUDA_126,
FULLY_SUPPORTED = CUDA_123,
PARTIALLY_SUPPORTED =
CUDA_125, // Partially supported. Proceed with a warning.
CUDA_126, // Partially supported. Proceed with a warning.
NEW = 10000, // Too new. Issue a warning, but allow using it.
};
const char *CudaVersionToString(CudaVersion V);
Expand Down Expand Up @@ -78,6 +79,7 @@ enum class OffloadArch {
SM_89,
SM_90,
SM_90a,
SM_100,
GFX600,
GFX601,
GFX602,
Expand Down
40 changes: 31 additions & 9 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,12 @@ def ext_designated_init_brace_elision : ExtWarn<
// Declarations.
def ext_plain_complex : ExtWarn<
"plain '_Complex' requires a type specifier; assuming '_Complex double'">;
def ext_imaginary_constant : Extension<
def warn_c23_compat_imaginary_constant : Warning<
"imaginary constants are incompatible with C standards before C2y">,
DefaultIgnore, InGroup<CPre2yCompat>;
def ext_c2y_imaginary_constant : Extension<
"imaginary constants are a C2y extension">, InGroup<C2y>;
def ext_gnu_imaginary_constant : Extension<
"imaginary constants are a GNU extension">, InGroup<GNUImaginaryConstant>;
def ext_integer_complex : Extension<
"complex integer types are a GNU extension">, InGroup<GNUComplexInteger>;
Expand Down Expand Up @@ -439,7 +444,7 @@ def warn_reserved_extern_symbol: Warning<
InGroup<ReservedIdentifier>, DefaultIgnore;
def warn_deprecated_literal_operator_id: Warning<
"identifier %0 preceded by whitespace in a literal operator declaration "
"is deprecated">, InGroup<DeprecatedLiteralOperator>, DefaultIgnore;
"is deprecated">, InGroup<DeprecatedLiteralOperator>;
def warn_reserved_module_name : Warning<
"%0 is a reserved name for a module">, InGroup<ReservedModuleIdentifier>;
def warn_import_implementation_partition_unit_in_interface_unit : Warning<
Expand Down Expand Up @@ -5262,13 +5267,6 @@ def note_template_arg_refers_here_func : Note<
def err_template_arg_template_params_mismatch : Error<
"template template argument has different template parameters than its "
"corresponding template template parameter">;
def note_template_arg_template_params_mismatch : Note<
"template template argument has different template parameters than its "
"corresponding template template parameter">;
def err_non_deduced_mismatch : Error<
"could not match %diff{$ against $|types}0,1">;
def err_inconsistent_deduction : Error<
"conflicting deduction %diff{$ against $|types}0,1 for parameter">;
def err_template_arg_not_integral_or_enumeral : Error<
"non-type template argument of type %0 must have an integral or enumeration"
" type">;
Expand Down Expand Up @@ -9232,6 +9230,8 @@ def err_typecheck_cond_incompatible_operands : Error<
def err_typecheck_expect_scalar_or_vector : Error<
"invalid operand of type %0 where %1 or "
"a vector of such type is required">;
def err_typecheck_expect_any_scalar_or_vector : Error<
"invalid operand of type %0 where a scalar or vector is required">;
def err_typecheck_expect_flt_or_vector : Error<
"invalid operand of type %0 where floating, complex or "
"a vector of such types is required">;
Expand Down Expand Up @@ -10126,6 +10126,8 @@ def note_lambda_capture_initializer : Note<
" via initialization of lambda capture %0}1">;
def note_init_with_default_member_initializer : Note<
"initializing field %0 with default member initializer">;
def note_init_with_default_argument : Note<
"initializing parameter %0 with default argument">;

// Check for initializing a member variable with the address or a reference to
// a constructor parameter.
Expand Down Expand Up @@ -12583,6 +12585,7 @@ def err_acc_duplicate_clause_disallowed
: Error<"OpenACC '%1' clause cannot appear more than once on a '%0' "
"directive">;
def note_acc_previous_clause_here : Note<"previous clause is here">;
def note_acc_previous_expr_here : Note<"previous expression is here">;
def err_acc_branch_in_out_compute_construct
: Error<"invalid %select{branch|return|throw}0 %select{out of|into}1 "
"OpenACC Compute Construct">;
Expand Down Expand Up @@ -12689,6 +12692,25 @@ def err_acc_insufficient_loops
def err_acc_intervening_code
: Error<"inner loops must be tightly nested inside a '%0' clause on "
"a 'loop' construct">;
def err_acc_gang_multiple_elt
: Error<"OpenACC 'gang' clause may have at most one %select{unnamed or "
"'num'|'dim'|'static'}0 argument">;
def err_acc_int_arg_invalid
: Error<"'%1' argument on '%0' clause is not permitted on a%select{n "
"orphaned|||}2 'loop' construct %select{|associated with a "
"'parallel' compute construct|associated with a 'kernels' compute "
"construct|associated with a 'serial' compute construct}2">;
def err_acc_gang_dim_value
: Error<"argument to 'gang' clause dimension must be %select{a constant "
"expression|1, 2, or 3: evaluated to %1}0">;
def err_acc_num_arg_conflict
: Error<"'num' argument to '%0' clause not allowed on a 'loop' construct "
"associated with a 'kernels' construct that has a "
"'%select{num_gangs|num_workers|vector_length}1' "
"clause">;
def err_acc_clause_in_clause_region
: Error<"loop with a '%0' clause may not exist in the region of a '%1' "
"clause%select{| on a 'kernels' compute construct}2">;

// AMDGCN builtins diagnostics
def err_amdgcn_global_load_lds_size_invalid_value : Error<"invalid size value">;
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/OpenACCClauses.def
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ VISIT_CLAUSE(DevicePtr)
VISIT_CLAUSE(DeviceType)
CLAUSE_ALIAS(DType, DeviceType, false)
VISIT_CLAUSE(FirstPrivate)
VISIT_CLAUSE(Gang)
VISIT_CLAUSE(If)
VISIT_CLAUSE(Independent)
VISIT_CLAUSE(NoCreate)
Expand All @@ -53,8 +54,10 @@ VISIT_CLAUSE(Reduction)
VISIT_CLAUSE(Self)
VISIT_CLAUSE(Seq)
VISIT_CLAUSE(Tile)
VISIT_CLAUSE(Vector)
VISIT_CLAUSE(VectorLength)
VISIT_CLAUSE(Wait)
VISIT_CLAUSE(Worker)

#undef VISIT_CLAUSE
#undef CLAUSE_ALIAS
Loading