Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ static constexpr bool RestrictToPODTypesDefault = false;
static constexpr char IgnoreMacrosName[] = "IgnoreMacros";
static constexpr bool IgnoreMacrosDefault = true;

static constexpr char StrictCStandardComplianceName[] =
"StrictCStandardCompliance";
static constexpr bool StrictCStandardComplianceDefault = true;

static constexpr char StrictCppStandardComplianceName[] =
"StrictCppStandardCompliance";
static constexpr bool StrictCppStandardComplianceDefault = true;

namespace {

struct Designators {
Expand Down Expand Up @@ -97,7 +105,12 @@ UseDesignatedInitializersCheck::UseDesignatedInitializersCheck(
RestrictToPODTypes(
Options.get(RestrictToPODTypesName, RestrictToPODTypesDefault)),
IgnoreMacros(
Options.getLocalOrGlobal(IgnoreMacrosName, IgnoreMacrosDefault)) {}
Options.getLocalOrGlobal(IgnoreMacrosName, IgnoreMacrosDefault)),
StrictCStandardCompliance(Options.get(StrictCStandardComplianceName,
StrictCStandardComplianceDefault)),
StrictCppStandardCompliance(
Options.get(StrictCppStandardComplianceName,
StrictCppStandardComplianceDefault)) {}

void UseDesignatedInitializersCheck::registerMatchers(MatchFinder *Finder) {
const auto HasBaseWithFields =
Expand Down Expand Up @@ -179,6 +192,9 @@ void UseDesignatedInitializersCheck::storeOptions(
IgnoreSingleElementAggregates);
Options.store(Opts, RestrictToPODTypesName, RestrictToPODTypes);
Options.store(Opts, IgnoreMacrosName, IgnoreMacros);
Options.store(Opts, StrictCStandardComplianceName, StrictCStandardCompliance);
Options.store(Opts, StrictCppStandardComplianceName,
StrictCppStandardCompliance);
}

} // namespace clang::tidy::modernize
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,19 @@ class UseDesignatedInitializersCheck : public ClangTidyCheck {
return TK_IgnoreUnlessSpelledInSource;
}

bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus20 || LangOpts.C99 ||
(LangOpts.CPlusPlus && !StrictCppStandardCompliance) ||
(!LangOpts.CPlusPlus && !LangOpts.ObjC &&
!StrictCStandardCompliance);
}

private:
bool IgnoreSingleElementAggregates;
bool RestrictToPODTypes;
bool IgnoreMacros;
bool StrictCStandardCompliance;
bool StrictCppStandardCompliance;
};

} // namespace clang::tidy::modernize
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,16 @@ void recordRemoval(const DeclStmt &Stmt, ASTContext &Context,
}
}

AST_MATCHER_FUNCTION_P(StatementMatcher, isConstRefReturningMethodCall,
AST_MATCHER_FUNCTION_P(StatementMatcher,
isRefReturningMethodCallWithConstOverloads,
std::vector<StringRef>, ExcludedContainerTypes) {
// Match method call expressions where the `this` argument is only used as
// const, this will be checked in `check()` part. This returned const
// reference is highly likely to outlive the local const reference of the
// variable being declared. The assumption is that the const reference being
// returned either points to a global static variable or to a member of the
// called object.
// const, this will be checked in `check()` part. This returned reference is
// highly likely to outlive the local const reference of the variable being
// declared. The assumption is that the reference being returned either points
// to a global static variable or to a member of the called object.
const auto MethodDecl =
cxxMethodDecl(returns(hasCanonicalType(matchers::isReferenceToConst())))
cxxMethodDecl(returns(hasCanonicalType(referenceType())))
.bind(MethodDeclId);
const auto ReceiverExpr =
ignoringParenImpCasts(declRefExpr(to(varDecl().bind(ObjectArgId))));
Expand Down Expand Up @@ -121,7 +121,7 @@ AST_MATCHER_FUNCTION_P(StatementMatcher, initializerReturnsReferenceToConst,
declRefExpr(to(varDecl(hasLocalStorage()).bind(OldVarDeclId)));
return expr(
anyOf(isConstRefReturningFunctionCall(),
isConstRefReturningMethodCall(ExcludedContainerTypes),
isRefReturningMethodCallWithConstOverloads(ExcludedContainerTypes),
ignoringImpCasts(OldVarDeclRef),
ignoringImpCasts(unaryOperator(hasOperatorName("&"),
hasUnaryOperand(OldVarDeclRef)))));
Expand Down Expand Up @@ -259,10 +259,11 @@ void UnnecessaryCopyInitialization::registerMatchers(MatchFinder *Finder) {
.bind("blockStmt");
};

Finder->addMatcher(LocalVarCopiedFrom(anyOf(isConstRefReturningFunctionCall(),
isConstRefReturningMethodCall(
ExcludedContainerTypes))),
this);
Finder->addMatcher(
LocalVarCopiedFrom(anyOf(
isConstRefReturningFunctionCall(),
isRefReturningMethodCallWithConstOverloads(ExcludedContainerTypes))),
this);

Finder->addMatcher(LocalVarCopiedFrom(declRefExpr(
to(varDecl(hasLocalStorage()).bind(OldVarDeclId)))),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,9 @@ void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) {
hasParent(callExpr()),
hasSourceExpression(binaryOperator(hasAnyOperatorName("==", "!="))));

auto IsInCompilerGeneratedFunction = hasAncestor(namedDecl(anyOf(
isImplicit(), functionDecl(isDefaulted()), functionTemplateDecl())));

Finder->addMatcher(
traverse(TK_AsIs,
implicitCastExpr(
Expand All @@ -299,7 +302,7 @@ void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) {
// additional parens in replacement.
optionally(hasParent(stmt().bind("parentStmt"))),
unless(isInTemplateInstantiation()),
unless(hasAncestor(functionTemplateDecl())))
unless(IsInCompilerGeneratedFunction))
.bind("implicitCastToBool")),
this);

Expand Down Expand Up @@ -331,7 +334,7 @@ void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) {
anyOf(hasParent(implicitCastExpr().bind("furtherImplicitCast")),
anything()),
unless(isInTemplateInstantiation()),
unless(hasAncestor(functionTemplateDecl())))),
unless(IsInCompilerGeneratedFunction))),
this);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ static void addParantheses(const BinaryOperator *BinOp,
int Precedence1 = getPrecedence(BinOp);
int Precedence2 = getPrecedence(ParentBinOp);

if (ParentBinOp != nullptr && Precedence1 != Precedence2) {
if (ParentBinOp != nullptr && Precedence1 != Precedence2 && Precedence1 > 0 &&
Precedence2 > 0) {
const clang::SourceLocation StartLoc = BinOp->getBeginLoc();
const clang::SourceLocation EndLoc =
clang::Lexer::getLocForEndOfToken(BinOp->getEndLoc(), 0, SM, LangOpts);
Expand Down
168 changes: 161 additions & 7 deletions clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,116 @@ void extractNodesByIdTo(ArrayRef<BoundNodes> Matches, StringRef ID,
Nodes.insert(Match.getNodeAs<Node>(ID));
}

// Returns true if both types refer to the same type,
// ignoring the const-qualifier.
bool isSameTypeIgnoringConst(QualType A, QualType B) {
A = A.getCanonicalType();
B = B.getCanonicalType();
A.addConst();
B.addConst();
return A == B;
}

// Returns true if `D` and `O` have the same parameter types.
bool hasSameParameterTypes(const CXXMethodDecl &D, const CXXMethodDecl &O) {
if (D.getNumParams() != O.getNumParams())
return false;
for (int I = 0, E = D.getNumParams(); I < E; ++I) {
if (!isSameTypeIgnoringConst(D.getParamDecl(I)->getType(),
O.getParamDecl(I)->getType()))
return false;
}
return true;
}

// If `D` has a const-qualified overload with otherwise identical
// ref-qualifiers and parameter types, returns that overload.
const CXXMethodDecl *findConstOverload(const CXXMethodDecl &D) {
assert(!D.isConst());

DeclContext::lookup_result LookupResult =
D.getParent()->lookup(D.getNameInfo().getName());
if (LookupResult.isSingleResult()) {
// No overload.
return nullptr;
}
for (const Decl *Overload : LookupResult) {
const auto *O = dyn_cast<CXXMethodDecl>(Overload);
if (O && !O->isDeleted() && O->isConst() &&
O->getRefQualifier() == D.getRefQualifier() &&
hasSameParameterTypes(D, *O))
return O;
}
return nullptr;
}

// Returns true if both types are pointers or reference to the same type,
// ignoring the const-qualifier.
bool pointsToSameTypeIgnoringConst(QualType A, QualType B) {
assert(A->isPointerType() || A->isReferenceType());
assert(B->isPointerType() || B->isReferenceType());
return isSameTypeIgnoringConst(A->getPointeeType(), B->getPointeeType());
}

// Return true if non-const member function `M` likely does not mutate `*this`.
//
// Note that if the member call selects a method/operator `f` that
// is not const-qualified, then we also consider that the object is
// not mutated if:
// - (A) there is a const-qualified overload `cf` of `f` that has
// the
// same ref-qualifiers;
// - (B) * `f` returns a value, or
// * if `f` returns a `T&`, `cf` returns a `const T&` (up to
// possible aliases such as `reference` and
// `const_reference`), or
// * if `f` returns a `T*`, `cf` returns a `const T*` (up to
// possible aliases).
// - (C) the result of the call is not mutated.
//
// The assumption that `cf` has the same semantics as `f`.
// For example:
// - In `std::vector<T> v; const T t = v[...];`, we consider that
// expression `v[...]` does not mutate `v` as
// `T& std::vector<T>::operator[]` has a const overload
// `const T& std::vector<T>::operator[] const`, and the
// result expression of type `T&` is only used as a `const T&`;
// - In `std::map<K, V> m; V v = m.at(...);`, we consider
// `m.at(...)` to be an immutable access for the same reason.
// However:
// - In `std::map<K, V> m; const V v = m[...];`, We consider that
// `m[...]` mutates `m` as `V& std::map<K, V>::operator[]` does
// not have a const overload.
// - In `std::vector<T> v; T& t = v[...];`, we consider that
// expression `v[...]` mutates `v` as the result is kept as a
// mutable reference.
//
// This function checks (A) ad (B), but the caller should make sure that the
// object is not mutated through the return value.
bool isLikelyShallowConst(const CXXMethodDecl &M) {
assert(!M.isConst());
// The method can mutate our variable.

// (A)
const CXXMethodDecl *ConstOverload = findConstOverload(M);
if (ConstOverload == nullptr) {
return false;
}

// (B)
const QualType CallTy = M.getReturnType().getCanonicalType();
const QualType OverloadTy = ConstOverload->getReturnType().getCanonicalType();
if (CallTy->isReferenceType()) {
return OverloadTy->isReferenceType() &&
pointsToSameTypeIgnoringConst(CallTy, OverloadTy);
}
if (CallTy->isPointerType()) {
return OverloadTy->isPointerType() &&
pointsToSameTypeIgnoringConst(CallTy, OverloadTy);
}
return isSameTypeIgnoringConst(CallTy, OverloadTy);
}

// A matcher that matches DeclRefExprs that are used in ways such that the
// underlying declaration is not modified.
// If the declaration is of pointer type, `Indirections` specifies the level
Expand All @@ -54,16 +164,15 @@ void extractNodesByIdTo(ArrayRef<BoundNodes> Matches, StringRef ID,
// matches (A).
//
AST_MATCHER_P(DeclRefExpr, doesNotMutateObject, int, Indirections) {
// We walk up the parents of the DeclRefExpr recursively until we end up on a
// parent that cannot modify the underlying object. There are a few kinds of
// expressions:
// - Those that cannot be used to mutate the underlying object. We can stop
// We walk up the parents of the DeclRefExpr recursively. There are a few
// kinds of expressions:
// - Those that cannot be used to mutate the underlying variable. We can stop
// recursion there.
// - Those that can be used to mutate the underlying object in analyzable
// - Those that can be used to mutate the underlying variable in analyzable
// ways (such as taking the address or accessing a subobject). We have to
// examine the parents.
// - Those that we don't know how to analyze. In that case we stop there and
// we assume that they can mutate the underlying expression.
// we assume that they can modify the expression.

struct StackEntry {
StackEntry(const Expr *E, int Indirections)
Expand All @@ -90,7 +199,7 @@ AST_MATCHER_P(DeclRefExpr, doesNotMutateObject, int, Indirections) {
assert(Ty->isPointerType());
Ty = Ty->getPointeeType().getCanonicalType();
}
if (Ty.isConstQualified())
if (Ty->isVoidType() || Ty.isConstQualified())
continue;

// Otherwise we have to look at the parents to see how the expression is
Expand Down Expand Up @@ -159,11 +268,56 @@ AST_MATCHER_P(DeclRefExpr, doesNotMutateObject, int, Indirections) {
// The method call cannot mutate our variable.
continue;
}
if (isLikelyShallowConst(*Method)) {
// We still have to check that the object is not modified through
// the method's return value (C).
const auto MemberParents = Ctx.getParents(*Member);
assert(MemberParents.size() == 1);
const auto *Call = MemberParents[0].get<CallExpr>();
// If `o` is an object of class type and `f` is a member function,
// then `o.f` has to be used as part of a call expression.
assert(Call != nullptr && "member function has to be called");
Stack.emplace_back(
Call,
Method->getReturnType().getCanonicalType()->isPointerType()
? 1
: 0);
continue;
}
return false;
}
Stack.emplace_back(Member, 0);
continue;
}
if (const auto *const OpCall = dyn_cast<CXXOperatorCallExpr>(P)) {
// Operator calls have function call syntax. The `*this` parameter
// is the first parameter.
if (OpCall->getNumArgs() == 0 || OpCall->getArg(0) != Entry.E) {
return false;
}
const auto *const Method =
dyn_cast<CXXMethodDecl>(OpCall->getDirectCallee());

if (Method == nullptr) {
// This is not a member operator. Typically, a friend operator. These
// are handled like function calls.
return false;
}

if (Method->isConst() || Method->isStatic()) {
continue;
}
if (isLikelyShallowConst(*Method)) {
// We still have to check that the object is not modified through
// the operator's return value (C).
Stack.emplace_back(
OpCall,
Method->getReturnType().getCanonicalType()->isPointerType() ? 1
: 0);
continue;
}
return false;
}

if (const auto *const Op = dyn_cast<UnaryOperator>(P)) {
switch (Op->getOpcode()) {
Expand Down
11 changes: 9 additions & 2 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,10 @@ Changes in existing checks
check by ignoring ``__func__`` macro in lambda captures, initializers of
default parameters and nested function declarations.

- Improved :doc:`bugprone-multi-level-implicit-pointer-conversion
<clang-tidy/checks/bugprone/multi-level-implicit-pointer-conversion>` check
by ignoring implicit pointer conversions that are part of a cast expression.

- Improved :doc:`bugprone-non-zero-enum-to-bool-conversion
<clang-tidy/checks/bugprone/non-zero-enum-to-bool-conversion>` check by
eliminating false positives resulting from direct usage of bitwise operators
Expand Down Expand Up @@ -372,8 +376,10 @@ Changes in existing checks
- Improved :doc:`performance-unnecessary-copy-initialization
<clang-tidy/checks/performance/unnecessary-copy-initialization>` check by
detecting more cases of constant access. In particular, pointers can be
analyzed, se the check now handles the common patterns
analyzed, so the check now handles the common patterns
`const auto e = (*vector_ptr)[i]` and `const auto e = vector_ptr->at(i);`.
Calls to mutable function where there exists a `const` overload are also
handled.

- Improved :doc:`readability-avoid-return-with-void-value
<clang-tidy/checks/readability/avoid-return-with-void-value>` check by adding
Expand Down Expand Up @@ -408,7 +414,8 @@ Changes in existing checks
valid fix suggestions for ``static_cast`` without a preceding space and
fixed problem with duplicate parentheses in double implicit casts. Corrected
the fix suggestions for C23 and later by using C-style casts instead of
``static_cast``.
``static_cast``. Fixed false positives in C++20 spaceship operator by ignoring
casts in implicit and defaulted functions.

- Improved :doc:`readability-redundant-inline-specifier
<clang-tidy/checks/readability/redundant-inline-specifier>` check to properly
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def get_checkers(checkers_td, checkers_rst):
parent_package_ = package["ParentPackage"]
hidden = (checker["Hidden"] != 0) or (package["Hidden"] != 0)

while parent_package_ != None:
while parent_package_ is not None:
parent_package = table_entries[parent_package_["def"]]
checker_package_prefix = (
parent_package["PackageName"] + "." + checker_package_prefix
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ declaration of ``S``.

Even when compiling in a language version older than C++20, depending on your
compiler, designated initializers are potentially supported. Therefore, the
check is not restricted to C++20 and newer versions. Check out the options
check is by default restricted to C99/C++20 and above. Check out the options
``-Wc99-designator`` to get support for mixed designators in initializer list in
C and ``-Wc++20-designator`` for support of designated initializers in older C++
language modes.
Expand All @@ -60,3 +60,13 @@ Options
The value `true` specifies that only Plain Old Data (POD) types shall be
checked. This makes the check applicable to even older C++ standards. The
default value is `false`.

.. option:: StrictCStandardCompliance

When set to `false`, the check will not restrict itself to C99 and above.
The default value is `true`.

.. option:: StrictCppStandardCompliance

When set to `false`, the check will not restrict itself to C++20 and above.
The default value is `true`.
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,15 @@ void test()

takeSecondLevelVoidPtr(getSecondLevelVoidPtr());
}

namespace PR93959 {
void free(void*);

void test() {
char **p = nullptr;
free(p);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: multilevel pointer conversion from 'char **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
free((void *)p);
free(static_cast<void *>(p));
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// RUN: %check_clang_tidy -std=c++17 %s modernize-use-designated-initializers %t \
// RUN: %check_clang_tidy -std=c++20 %s modernize-use-designated-initializers %t \
// RUN: -- \
// RUN: -- -fno-delayed-template-parsing
// RUN: %check_clang_tidy -check-suffixes=,SINGLE-ELEMENT -std=c++17 %s modernize-use-designated-initializers %t \
// RUN: %check_clang_tidy -check-suffixes=,SINGLE-ELEMENT -std=c++20 %s modernize-use-designated-initializers %t \
// RUN: -- -config="{CheckOptions: {modernize-use-designated-initializers.IgnoreSingleElementAggregates: false}}" \
// RUN: -- -fno-delayed-template-parsing
// RUN: %check_clang_tidy -check-suffixes=POD -std=c++17 %s modernize-use-designated-initializers %t \
// RUN: %check_clang_tidy -check-suffixes=POD -std=c++20 %s modernize-use-designated-initializers %t \
// RUN: -- -config="{CheckOptions: {modernize-use-designated-initializers.RestrictToPODTypes: true}}" \
// RUN: -- -fno-delayed-template-parsing
// RUN: %check_clang_tidy -check-suffixes=,MACROS -std=c++17 %s modernize-use-designated-initializers %t \
// RUN: %check_clang_tidy -check-suffixes=,MACROS -std=c++20 %s modernize-use-designated-initializers %t \
// RUN: -- -config="{CheckOptions: {modernize-use-designated-initializers.IgnoreMacros: false}}" \
// RUN: -- -fno-delayed-template-parsing

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ struct ExpensiveToCopyType {

template <typename T>
struct Container {
using reference = T&;
using const_reference = const T&;

bool empty() const;
const T& operator[](int) const;
const T& operator[](int);
Expand All @@ -42,8 +45,8 @@ struct Container {
void nonConstMethod();
bool constMethod() const;

const T& at(int) const;
T& at(int);
reference at(int) const;
const_reference at(int);

};

Expand Down Expand Up @@ -207,6 +210,28 @@ void PositiveOperatorCallConstValueParam(const Container<ExpensiveToCopyType> C)
VarCopyConstructed.constMethod();
}

void PositiveOperatorValueParam(Container<ExpensiveToCopyType> C) {
const auto AutoAssigned = C[42];
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
// CHECK-FIXES: const auto& AutoAssigned = C[42];
AutoAssigned.constMethod();

const auto AutoCopyConstructed(C[42]);
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
// CHECK-FIXES: const auto& AutoCopyConstructed(C[42]);
AutoCopyConstructed.constMethod();

const ExpensiveToCopyType VarAssigned = C.at(42);
// CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
// CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = C.at(42);
VarAssigned.constMethod();

const ExpensiveToCopyType VarCopyConstructed(C.at(42));
// CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarCopyConstructed'
// CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(C.at(42));
VarCopyConstructed.constMethod();
}

void PositiveOperatorCallConstValueParamAlias(const ExpensiveToCopyContainerAlias C) {
const auto AutoAssigned = C[42];
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// RUN: %check_clang_tidy -std=c++20 %s readability-implicit-bool-conversion %t

namespace std {
struct strong_ordering {
int n;
constexpr operator int() const { return n; }
static const strong_ordering equal, greater, less;
};
constexpr strong_ordering strong_ordering::equal = {0};
constexpr strong_ordering strong_ordering::greater = {1};
constexpr strong_ordering strong_ordering::less = {-1};
} // namespace std

namespace PR93409 {
struct X
{
auto operator<=>(const X&) const = default;
bool m_b;
};

struct Y
{
auto operator<=>(const Y&) const = default;
X m_x;
};

bool compare(const Y& y1, const Y& y2)
{
return y1 == y2 || y1 < y2 || y1 > y2;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,20 @@ void f(){
//CHECK-MESSAGES: :[[@LINE+1]]:13: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
int v = FUN5(0 + 1);
}

namespace PR92516 {
void f(int i) {
int j, k;
for (j = i + 1, k = 0; j < 1; ++j) {}
}

void f2(int i) {
int j;
for (j = i + 1; j < 1; ++j) {}
}

void f3(int i) {
int j;
for (j = i + 1, 2; j < 1; ++j) {}
}
}
48 changes: 43 additions & 5 deletions clang-tools-extra/unittests/clang-tidy/DeclRefExprUtilsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ template <int Indirections> void RunTest(StringRef Snippet) {
StringRef CommonCode = R"(
struct ConstTag{};
struct NonConstTag{};
struct Tag1{};
struct S {
void constMethod() const;
Expand All @@ -59,6 +60,13 @@ template <int Indirections> void RunTest(StringRef Snippet) {
void operator[](int);
void operator[](int) const;
int& at(int);
const int& at(int) const;
const int& at(Tag1);
int& weird_overload();
const double& weird_overload() const;
bool operator==(const S&) const;
int int_member;
Expand Down Expand Up @@ -161,9 +169,11 @@ TEST(ConstReferenceDeclRefExprsTest, ConstRefVar) {
useIntConstRef(/*const*/target.int_member);
useIntPtr(/*const*/target.ptr_member);
useIntConstPtr(&/*const*/target.int_member);
(void)/*const*/target.at(3);
const S& const_target_ref = /*const*/target;
const S* const_target_ptr = &/*const*/target;
(void)/*const*/target.at(3);
}
)");
}
Expand All @@ -187,7 +197,7 @@ TEST(ConstReferenceDeclRefExprsTest, ValueVar) {
/*const*/target.staticMethod();
target.nonConstMethod();
/*const*/target(ConstTag{});
target[42];
/*const*/target[42];
/*const*/target(ConstTag{});
target(NonConstTag{});
useRef(target);
Expand All @@ -211,6 +221,14 @@ TEST(ConstReferenceDeclRefExprsTest, ValueVar) {
const S& const_target_ref = /*const*/target;
const S* const_target_ptr = &/*const*/target;
S* target_ptr = &target;
(void)/*const*/target.at(3);
++target.at(3);
const int civ = /*const*/target.at(3);
const int& cir = /*const*/target.at(3);
int& ir = target.at(3);
target.at(Tag1{});
target.weird_overload();
}
)");
}
Expand All @@ -227,7 +245,7 @@ TEST(ConstReferenceDeclRefExprsTest, RefVar) {
/*const*/target.staticMethod();
target.nonConstMethod();
/*const*/target(ConstTag{});
target[42];
/*const*/target[42];
useConstRef((/*const*/target));
(/*const*/target).constMethod();
(void)(/*const*/target == /*const*/target);
Expand All @@ -249,6 +267,14 @@ TEST(ConstReferenceDeclRefExprsTest, RefVar) {
const S& const_target_ref = /*const*/target;
const S* const_target_ptr = &/*const*/target;
S* target_ptr = &target;
(void)/*const*/target.at(3);
++target.at(3);
const int civ = /*const*/target.at(3);
const int& cir = /*const*/target.at(3);
int& ir = target.at(3);
target.at(Tag1{});
target.weird_overload();
}
)");
}
Expand All @@ -266,8 +292,8 @@ TEST(ConstReferenceDeclRefExprsTest, PtrVar) {
/*const*/target->staticMethod();
target->nonConstMethod();
(*/*const*/target)(ConstTag{});
(*target)[42];
target->operator[](42);
(*/*const*/target)[42];
/*const*/target->operator[](42);
useConstRef((*/*const*/target));
(/*const*/target)->constMethod();
(void)(*/*const*/target == */*const*/target);
Expand All @@ -284,7 +310,15 @@ TEST(ConstReferenceDeclRefExprsTest, PtrVar) {
const S& const_target_ref = */*const*/target;
const S* const_target_ptr = /*const*/target;
S* target_ptr = target; // FIXME: we could chect const usage of `target_ptr`.
S* target_ptr = target; // FIXME: we could chect const usage of `target_ptr`
(void)/*const*/target->at(3);
++target->at(3);
const int civ = /*const*/target->at(3);
const int& cir = /*const*/target->at(3);
int& ir = target->at(3);
target->at(Tag1{});
target->weird_overload();
}
)");
}
Expand Down Expand Up @@ -319,6 +353,10 @@ TEST(ConstReferenceDeclRefExprsTest, ConstPtrVar) {
const S& const_target_ref = */*const*/target;
const S* const_target_ptr = /*const*/target;
(void)/*const*/target->at(3);
const int civ = /*const*/target->at(3);
const int& cir = /*const*/target->at(3);
}
)");
}
Expand Down
45 changes: 31 additions & 14 deletions clang/cmake/caches/CrossWinToARMLinux.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,23 @@
# on Windows platform.
#
# NOTE: the build requires a development ARM Linux root filesystem to use
# proper target platform depended library and header files:
# - create <full-path-to-clang-configs> directory and put the clang configuration
# file named <TOOLCHAIN_TARGET_TRIPLE>.cfg into it.
# - add the `--sysroot=<path-to-develop-arm-linux-root-fs>` argument into
# this configuration file.
# - add other necessary target depended clang arguments there,
# such as '-mcpu=cortex-a78' & etc.
# proper target platform depended library and header files.
#
# The build generates a proper clang configuration file with stored
# --sysroot argument for specified target triple. Also it is possible
# to specify configuration path via CMake arguments, such as
# -DCLANG_CONFIG_FILE_USER_DIR=<full-path-to-clang-configs>
# and/or
# -DCLANG_CONFIG_FILE_SYSTEM_DIR=<full-path-to-clang-configs>
#
# See more details here: https://clang.llvm.org/docs/UsersManual.html#configuration-files
#
# Configure:
# cmake -G Ninja ^
# -DTOOLCHAIN_TARGET_TRIPLE=aarch64-unknown-linux-gnu ^
# -DTOOLCHAIN_TARGET_SYSROOTFS=<path-to-develop-arm-linux-root-fs> ^
# -DTOOLCHAIN_SHARED_LIBS=OFF ^
# -DCMAKE_INSTALL_PREFIX=../install ^
# -DCLANG_CONFIG_FILE_USER_DIR=<full-path-to-clang-configs> ^
# -DCMAKE_CXX_FLAGS="-D__OPTIMIZE__" ^
# -DREMOTE_TEST_HOST="<hostname>" ^
# -DREMOTE_TEST_USER="<ssh_user_name>" ^
Expand Down Expand Up @@ -81,6 +83,20 @@ endif()

message(STATUS "Toolchain target triple: ${TOOLCHAIN_TARGET_TRIPLE}")

if (DEFINED TOOLCHAIN_TARGET_SYSROOTFS)
message(STATUS "Toolchain target sysroot: ${TOOLCHAIN_TARGET_SYSROOTFS}")
# Store the --sysroot argument for the compiler-rt test flags.
set(sysroot_flags --sysroot='${TOOLCHAIN_TARGET_SYSROOTFS}')
# Generate the clang configuration file for the specified target triple
# and store --sysroot in this file.
file(WRITE "${CMAKE_BINARY_DIR}/bin/${TOOLCHAIN_TARGET_TRIPLE}.cfg" ${sysroot_flags})
endif()

# Build the shared libraries for libc++/libc++abi/libunwind.
if (NOT DEFINED TOOLCHAIN_SHARED_LIBS)
set(TOOLCHAIN_SHARED_LIBS OFF)
endif()

if (NOT DEFINED LLVM_TARGETS_TO_BUILD)
if ("${TOOLCHAIN_TARGET_TRIPLE}" MATCHES "^(armv|arm32)+")
set(LLVM_TARGETS_TO_BUILD "ARM" CACHE STRING "")
Expand Down Expand Up @@ -183,20 +199,21 @@ set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_COMPILER_RT_CAN_EXECUTE_TESTS

set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_COMPILER_RT_USE_BUILTINS_LIBRARY ON CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_COMPILER_RT_CXX_LIBRARY libcxx CACHE STRING "")
# Tell Clang to seach C++ headers alongside with the just-built binaries for the C++ compiler-rt tests.
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_COMPILER_RT_TEST_COMPILER_CFLAGS "--stdlib=libc++" CACHE STRING "")

# The compiler-rt tests disable the clang configuration files during the execution by setting CLANG_NO_DEFAULT_CONFIG=1
# and drops out the --sysroot from there. Provide it explicity via the test flags here if target sysroot has been specified.
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_COMPILER_RT_TEST_COMPILER_CFLAGS "--stdlib=libc++ ${sysroot_flags}" CACHE STRING "")

set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBUNWIND_USE_COMPILER_RT ON CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBUNWIND_ENABLE_SHARED OFF CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBUNWIND_ENABLE_SHARED ${TOOLCHAIN_SHARED_LIBS} CACHE BOOL "")

set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXXABI_USE_LLVM_UNWINDER ON CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXXABI_ENABLE_STATIC_UNWINDER ON CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXXABI_USE_COMPILER_RT ON CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXXABI_ENABLE_NEW_DELETE_DEFINITIONS OFF CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXXABI_ENABLE_SHARED OFF CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXXABI_ENABLE_SHARED ${TOOLCHAIN_SHARED_LIBS} CACHE BOOL "")

set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_USE_COMPILER_RT ON CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_ENABLE_SHARED OFF CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_ENABLE_SHARED ${TOOLCHAIN_SHARED_LIBS} CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_ABI_VERSION ${LIBCXX_ABI_VERSION} CACHE STRING "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_CXX_ABI "libcxxabi" CACHE STRING "") #!!!
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_ENABLE_NEW_DELETE_DEFINITIONS ON CACHE BOOL "")
Expand Down
2 changes: 1 addition & 1 deletion clang/examples/PrintFunctionNames/PrintFunctionNames.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class PrintFunctionsConsumer : public ASTConsumer {
*sema.LateParsedTemplateMap.find(FD)->second;
sema.LateTemplateParser(sema.OpaqueParser, LPT);
llvm::errs() << "late-parsed-decl: \"" << FD->getNameAsString() << "\"\n";
}
}
}
};

Expand Down
3 changes: 0 additions & 3 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -3203,9 +3203,6 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// valid feature names.
ParsedTargetAttr filterFunctionTargetAttrs(const TargetAttr *TD) const;

std::vector<std::string>
filterFunctionTargetVersionAttrs(const TargetVersionAttr *TV) const;

void getFunctionFeatureMap(llvm::StringMap<bool> &FeatureMap,
const FunctionDecl *) const;
void getFunctionFeatureMap(llvm::StringMap<bool> &FeatureMap,
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/AST/TemplateBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ class TemplateArgument {
bool IncludeType) const;

/// Debugging aid that dumps the template argument.
void dump(raw_ostream &Out) const;
void dump(raw_ostream &Out, const ASTContext &Context) const;

/// Debugging aid that dumps the template argument to standard error.
void dump() const;
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/AST/TemplateName.h
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ class TemplateName {
Qualified Qual = Qualified::AsWritten) const;

/// Debugging aid that dumps the template name.
void dump(raw_ostream &OS) const;
void dump(raw_ostream &OS, const ASTContext &Context) const;

/// Debugging aid that dumps the template name to standard
/// error.
Expand Down
27 changes: 5 additions & 22 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -4470,37 +4470,20 @@ def HLSLShader : InheritableAttr {
let Subjects = SubjectList<[HLSLEntry]>;
let LangOpts = [HLSL];
let Args = [
EnumArgument<"Type", "ShaderType", /*is_string=*/true,
EnumArgument<"Type", "llvm::Triple::EnvironmentType", /*is_string=*/true,
["pixel", "vertex", "geometry", "hull", "domain", "compute",
"raygeneration", "intersection", "anyhit", "closesthit",
"miss", "callable", "mesh", "amplification"],
["Pixel", "Vertex", "Geometry", "Hull", "Domain", "Compute",
"RayGeneration", "Intersection", "AnyHit", "ClosestHit",
"Miss", "Callable", "Mesh", "Amplification"]>
"Miss", "Callable", "Mesh", "Amplification"],
/*opt=*/0, /*fake=*/0, /*isExternalType=*/1>
];
let Documentation = [HLSLSV_ShaderTypeAttrDocs];
let AdditionalMembers =
[{
static const unsigned ShaderTypeMaxValue = (unsigned)HLSLShaderAttr::Amplification;

static llvm::Triple::EnvironmentType getTypeAsEnvironment(HLSLShaderAttr::ShaderType ShaderType) {
switch (ShaderType) {
case HLSLShaderAttr::Pixel: return llvm::Triple::Pixel;
case HLSLShaderAttr::Vertex: return llvm::Triple::Vertex;
case HLSLShaderAttr::Geometry: return llvm::Triple::Geometry;
case HLSLShaderAttr::Hull: return llvm::Triple::Hull;
case HLSLShaderAttr::Domain: return llvm::Triple::Domain;
case HLSLShaderAttr::Compute: return llvm::Triple::Compute;
case HLSLShaderAttr::RayGeneration: return llvm::Triple::RayGeneration;
case HLSLShaderAttr::Intersection: return llvm::Triple::Intersection;
case HLSLShaderAttr::AnyHit: return llvm::Triple::AnyHit;
case HLSLShaderAttr::ClosestHit: return llvm::Triple::ClosestHit;
case HLSLShaderAttr::Miss: return llvm::Triple::Miss;
case HLSLShaderAttr::Callable: return llvm::Triple::Callable;
case HLSLShaderAttr::Mesh: return llvm::Triple::Mesh;
case HLSLShaderAttr::Amplification: return llvm::Triple::Amplification;
}
llvm_unreachable("unknown enumeration value");
static bool isValidShaderType(llvm::Triple::EnvironmentType ShaderType) {
return ShaderType >= llvm::Triple::Pixel && ShaderType <= llvm::Triple::Amplification;
}
}];
}
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -9015,6 +9015,11 @@ def err_cuda_ovl_target : Error<
"cannot overload %select{__device__|__global__|__host__|__host__ __device__}2 function %3">;
def note_cuda_ovl_candidate_target_mismatch : Note<
"candidate template ignored: target attributes do not match">;
def warn_offload_incompatible_redeclare : Warning<
"target-attribute based function overloads are not supported by NVCC and will be treated as a function redeclaration:"
"new declaration is %select{__device__|__global__|__host__|__host__ __device__}0 function, "
"old declaration is %select{__device__|__global__|__host__|__host__ __device__}1 function">,
InGroup<DiagGroup<"nvcc-compat">>, DefaultIgnore;

def err_cuda_device_builtin_surftex_cls_template : Error<
"illegal device builtin %select{surface|texture}0 reference "
Expand Down
6 changes: 3 additions & 3 deletions clang/include/clang/Sema/SemaHLSL.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class SemaHLSL : public SemaBase {
const AttributeCommonInfo &AL, int X,
int Y, int Z);
HLSLShaderAttr *mergeShaderAttr(Decl *D, const AttributeCommonInfo &AL,
HLSLShaderAttr::ShaderType ShaderType);
llvm::Triple::EnvironmentType ShaderType);
HLSLParamModifierAttr *
mergeParamModifierAttr(Decl *D, const AttributeCommonInfo &AL,
HLSLParamModifierAttr::Spelling Spelling);
Expand All @@ -48,8 +48,8 @@ class SemaHLSL : public SemaBase {
void CheckSemanticAnnotation(FunctionDecl *EntryPoint, const Decl *Param,
const HLSLAnnotationAttr *AnnotationAttr);
void DiagnoseAttrStageMismatch(
const Attr *A, HLSLShaderAttr::ShaderType Stage,
std::initializer_list<HLSLShaderAttr::ShaderType> AllowedStages);
const Attr *A, llvm::Triple::EnvironmentType Stage,
std::initializer_list<llvm::Triple::EnvironmentType> AllowedStages);
void DiagnoseAvailabilityViolations(TranslationUnitDecl *TU);

void handleNumThreadsAttr(Decl *D, const ParsedAttr &AL);
Expand Down
59 changes: 32 additions & 27 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
#include "llvm/Support/MD5.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/AArch64TargetParser.h"
#include "llvm/TargetParser/Triple.h"
#include <algorithm>
#include <cassert>
Expand Down Expand Up @@ -13663,17 +13664,20 @@ QualType ASTContext::getCorrespondingSignedFixedPointType(QualType Ty) const {
}
}

std::vector<std::string> ASTContext::filterFunctionTargetVersionAttrs(
const TargetVersionAttr *TV) const {
assert(TV != nullptr);
llvm::SmallVector<StringRef, 8> Feats;
std::vector<std::string> ResFeats;
TV->getFeatures(Feats);
for (auto &Feature : Feats)
if (Target->validateCpuSupports(Feature.str()))
// Use '?' to mark features that came from TargetVersion.
ResFeats.push_back("?" + Feature.str());
return ResFeats;
// Given a list of FMV features, return a concatenated list of the
// corresponding backend features (which may contain duplicates).
static std::vector<std::string> getFMVBackendFeaturesFor(
const llvm::SmallVectorImpl<StringRef> &FMVFeatStrings) {
std::vector<std::string> BackendFeats;
for (StringRef F : FMVFeatStrings) {
if (auto FMVExt = llvm::AArch64::parseArchExtension(F)) {
SmallVector<StringRef, 8> Feats;
FMVExt->DependentFeatures.split(Feats, ',', -1, false);
for (StringRef F : Feats)
BackendFeats.push_back(F.str());
}
}
return BackendFeats;
}

ParsedTargetAttr
Expand Down Expand Up @@ -13708,10 +13712,12 @@ void ASTContext::getFunctionFeatureMap(llvm::StringMap<bool> &FeatureMap,

// Make a copy of the features as passed on the command line into the
// beginning of the additional features from the function to override.
ParsedAttr.Features.insert(
ParsedAttr.Features.begin(),
Target->getTargetOpts().FeaturesAsWritten.begin(),
Target->getTargetOpts().FeaturesAsWritten.end());
// AArch64 handles command line option features in parseTargetAttr().
if (!Target->getTriple().isAArch64())
ParsedAttr.Features.insert(
ParsedAttr.Features.begin(),
Target->getTargetOpts().FeaturesAsWritten.begin(),
Target->getTargetOpts().FeaturesAsWritten.end());

if (ParsedAttr.CPU != "" && Target->isValidCPUName(ParsedAttr.CPU))
TargetCPU = ParsedAttr.CPU;
Expand All @@ -13732,32 +13738,31 @@ void ASTContext::getFunctionFeatureMap(llvm::StringMap<bool> &FeatureMap,
Target->getTargetOpts().FeaturesAsWritten.end());
Target->initFeatureMap(FeatureMap, getDiagnostics(), TargetCPU, Features);
} else if (const auto *TC = FD->getAttr<TargetClonesAttr>()) {
std::vector<std::string> Features;
if (Target->getTriple().isAArch64()) {
// TargetClones for AArch64
llvm::SmallVector<StringRef, 8> Feats;
TC->getFeatures(Feats, GD.getMultiVersionIndex());
for (StringRef Feat : Feats)
if (Target->validateCpuSupports(Feat.str()))
// Use '?' to mark features that came from AArch64 TargetClones.
Features.push_back("?" + Feat.str());
std::vector<std::string> Features = getFMVBackendFeaturesFor(Feats);
Features.insert(Features.begin(),
Target->getTargetOpts().FeaturesAsWritten.begin(),
Target->getTargetOpts().FeaturesAsWritten.end());
Target->initFeatureMap(FeatureMap, getDiagnostics(), TargetCPU, Features);
} else {
std::vector<std::string> Features;
StringRef VersionStr = TC->getFeatureStr(GD.getMultiVersionIndex());
if (VersionStr.starts_with("arch="))
TargetCPU = VersionStr.drop_front(sizeof("arch=") - 1);
else if (VersionStr != "default")
Features.push_back((StringRef{"+"} + VersionStr).str());
Target->initFeatureMap(FeatureMap, getDiagnostics(), TargetCPU, Features);
}
Target->initFeatureMap(FeatureMap, getDiagnostics(), TargetCPU, Features);
} else if (const auto *TV = FD->getAttr<TargetVersionAttr>()) {
std::vector<std::string> Feats = filterFunctionTargetVersionAttrs(TV);
Feats.insert(Feats.begin(),
Target->getTargetOpts().FeaturesAsWritten.begin(),
Target->getTargetOpts().FeaturesAsWritten.end());
Target->initFeatureMap(FeatureMap, getDiagnostics(), TargetCPU, Feats);
llvm::SmallVector<StringRef, 8> Feats;
TV->getFeatures(Feats);
std::vector<std::string> Features = getFMVBackendFeaturesFor(Feats);
Features.insert(Features.begin(),
Target->getTargetOpts().FeaturesAsWritten.begin(),
Target->getTargetOpts().FeaturesAsWritten.end());
Target->initFeatureMap(FeatureMap, getDiagnostics(), TargetCPU, Features);
} else {
FeatureMap = Target->getTargetOpts().FeatureMap;
}
Expand Down
34 changes: 34 additions & 0 deletions clang/lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,3 +360,37 @@ LLVM_DUMP_METHOD void ConceptReference::dump(raw_ostream &OS) const {
ASTDumper P(OS, Ctx, Ctx.getDiagnostics().getShowColors());
P.Visit(this);
}

//===----------------------------------------------------------------------===//
// TemplateName method implementations
//===----------------------------------------------------------------------===//

// FIXME: These are actually using the TemplateArgument dumper, through
// an implicit conversion. The dump will claim this is a template argument,
// which is misleading.

LLVM_DUMP_METHOD void TemplateName::dump() const {
ASTDumper Dumper(llvm::errs(), /*ShowColors=*/false);
Dumper.Visit(*this);
}

LLVM_DUMP_METHOD void TemplateName::dump(llvm::raw_ostream &OS,
const ASTContext &Context) const {
ASTDumper Dumper(OS, Context, Context.getDiagnostics().getShowColors());
Dumper.Visit(*this);
}

//===----------------------------------------------------------------------===//
// TemplateArgument method implementations
//===----------------------------------------------------------------------===//

LLVM_DUMP_METHOD void TemplateArgument::dump() const {
ASTDumper Dumper(llvm::errs(), /*ShowColors=*/false);
Dumper.Visit(*this);
}

LLVM_DUMP_METHOD void TemplateArgument::dump(llvm::raw_ostream &OS,
const ASTContext &Context) const {
ASTDumper Dumper(OS, Context, Context.getDiagnostics().getShowColors());
Dumper.Visit(*this);
}
2 changes: 2 additions & 0 deletions clang/lib/AST/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,6 @@ add_clang_library(clangAST
omp_gen
ClangDriverOptions
intrinsics_gen
# These generated headers are included transitively.
AArch64TargetParserTableGen
)
27 changes: 21 additions & 6 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15209,11 +15209,21 @@ bool ComplexExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
APFloat &ResI = Result.getComplexFloatImag();
if (LHSReal) {
assert(!RHSReal && "Cannot have two real operands for a complex op!");
ResR = A * C;
ResI = A * D;
ResR = A;
ResI = A;
// ResR = A * C;
// ResI = A * D;
if (!handleFloatFloatBinOp(Info, E, ResR, BO_Mul, C) ||
!handleFloatFloatBinOp(Info, E, ResI, BO_Mul, D))
return false;
} else if (RHSReal) {
ResR = C * A;
ResI = C * B;
// ResR = C * A;
// ResI = C * B;
ResR = C;
ResI = C;
if (!handleFloatFloatBinOp(Info, E, ResR, BO_Mul, A) ||
!handleFloatFloatBinOp(Info, E, ResI, BO_Mul, B))
return false;
} else {
// In the fully general case, we need to handle NaNs and infinities
// robustly.
Expand Down Expand Up @@ -15289,8 +15299,13 @@ bool ComplexExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
APFloat &ResR = Result.getComplexFloatReal();
APFloat &ResI = Result.getComplexFloatImag();
if (RHSReal) {
ResR = A / C;
ResI = B / C;
ResR = A;
ResI = B;
// ResR = A / C;
// ResI = B / C;
if (!handleFloatFloatBinOp(Info, E, ResR, BO_Div, C) ||
!handleFloatFloatBinOp(Info, E, ResI, BO_Div, C))
return false;
} else {
if (LHSReal) {
// No real optimizations we can do here, stub out with zero.
Expand Down
23 changes: 22 additions & 1 deletion clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -325,8 +325,11 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {

assert(isPtrType(*FromT));
assert(isPtrType(*ToT));
if (FromT == ToT)
if (FromT == ToT) {
if (SubExpr->getType()->isVoidPointerType())
return this->visit(SubExpr) && this->emitVoidPtrCast(CE);
return this->delegate(SubExpr);
}

if (!this->visit(SubExpr))
return false;
Expand Down Expand Up @@ -1834,6 +1837,9 @@ bool ByteCodeExprGen<Emitter>::VisitCompoundAssignOperator(
std::optional<PrimType> RT = classify(RHS->getType());
std::optional<PrimType> ResultT = classify(E->getType());

if (!Ctx.getLangOpts().CPlusPlus14)
return this->visit(RHS) && this->visit(LHS) && this->emitError(E);

if (!LT || !RT || !ResultT || !LHSComputationT)
return false;

Expand Down Expand Up @@ -3827,6 +3833,21 @@ bool ByteCodeExprGen<Emitter>::VisitComplexUnaryOperator(
// we sometimes have to do the lvalue-to-rvalue conversion here manually.
return this->emitArrayElemPop(classifyPrim(E->getType()), 1, E);

case UO_Not: // ~x
if (!this->visit(SubExpr))
return false;
// Negate the imaginary component.
if (!this->emitArrayElem(ElemT, 1, E))
return false;
if (!this->emitNeg(ElemT, E))
return false;
if (!this->emitInitElem(ElemT, 1, E))
return false;
return DiscardResult ? this->emitPopPtr(E) : true;

case UO_Extension:
return this->delegate(SubExpr);

default:
return this->emitInvalid(E);
}
Expand Down
30 changes: 14 additions & 16 deletions clang/lib/AST/Interp/EvalEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ void EvalEmitter::cleanup() { S.cleanup(); }
EvaluationResult EvalEmitter::interpretExpr(const Expr *E,
bool ConvertResultToRValue) {
S.setEvalLocation(E->getExprLoc());
this->ConvertResultToRValue = ConvertResultToRValue;
this->ConvertResultToRValue = ConvertResultToRValue && !isa<ConstantExpr>(E);
this->CheckFullyInitialized = isa<ConstantExpr>(E);
EvalResult.setSource(E);

Expand All @@ -56,10 +56,14 @@ EvaluationResult EvalEmitter::interpretExpr(const Expr *E,
EvaluationResult EvalEmitter::interpretDecl(const VarDecl *VD,
bool CheckFullyInitialized) {
this->CheckFullyInitialized = CheckFullyInitialized;
this->ConvertResultToRValue =
VD->getAnyInitializer() &&
(VD->getAnyInitializer()->getType()->isAnyComplexType() ||
VD->getAnyInitializer()->getType()->isVectorType());

if (const Expr *Init = VD->getAnyInitializer()) {
QualType T = VD->getType();
this->ConvertResultToRValue = !Init->isGLValue() && !T->isPointerType() &&
!T->isObjCObjectPointerType();
} else
this->ConvertResultToRValue = false;

EvalResult.setSource(VD);

if (!this->visitDecl(VD) && EvalResult.empty())
Expand Down Expand Up @@ -138,6 +142,10 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {
return true;

const Pointer &Ptr = S.Stk.pop<Pointer>();

if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
return false;

// Implicitly convert lvalue to rvalue, if requested.
if (ConvertResultToRValue) {
if (std::optional<APValue> V = Ptr.toRValue(Ctx)) {
Expand All @@ -146,17 +154,7 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {
return false;
}
} else {
if (CheckFullyInitialized) {
if (!EvalResult.checkFullyInitialized(S, Ptr))
return false;

std::optional<APValue> RValueResult = Ptr.toRValue(Ctx);
if (!RValueResult)
return false;
EvalResult.setValue(*RValueResult);
} else {
EvalResult.setValue(Ptr.toAPValue());
}
EvalResult.setValue(Ptr.toAPValue());
}

return true;
Expand Down
9 changes: 6 additions & 3 deletions clang/lib/AST/Interp/EvaluationResult.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,12 @@ bool EvaluationResult::checkFullyInitialized(InterpState &S,

if (const Record *R = Ptr.getRecord())
return CheckFieldsInitialized(S, InitLoc, Ptr, R);
const auto *CAT =
cast<ConstantArrayType>(Ptr.getType()->getAsArrayTypeUnsafe());
return CheckArrayInitialized(S, InitLoc, Ptr, CAT);

if (const auto *CAT = dyn_cast_if_present<ConstantArrayType>(
Ptr.getType()->getAsArrayTypeUnsafe()))
return CheckArrayInitialized(S, InitLoc, Ptr, CAT);

return true;
}

} // namespace interp
Expand Down
16 changes: 16 additions & 0 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -1937,6 +1937,9 @@ template <PrimType Name, class T = typename PrimConv<Name>::T>
bool CastPointerIntegral(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();

if (Ptr.isDummy())
return false;

const SourceInfo &E = S.Current->getSource(OpPC);
S.CCEDiag(E, diag::note_constexpr_invalid_cast)
<< 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);
Expand All @@ -1949,6 +1952,9 @@ static inline bool CastPointerIntegralAP(InterpState &S, CodePtr OpPC,
uint32_t BitWidth) {
const Pointer &Ptr = S.Stk.pop<Pointer>();

if (Ptr.isDummy())
return false;

const SourceInfo &E = S.Current->getSource(OpPC);
S.CCEDiag(E, diag::note_constexpr_invalid_cast)
<< 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);
Expand All @@ -1962,6 +1968,9 @@ static inline bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC,
uint32_t BitWidth) {
const Pointer &Ptr = S.Stk.pop<Pointer>();

if (Ptr.isDummy())
return false;

const SourceInfo &E = S.Current->getSource(OpPC);
S.CCEDiag(E, diag::note_constexpr_invalid_cast)
<< 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);
Expand All @@ -1971,6 +1980,13 @@ static inline bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC,
return true;
}

static inline bool VoidPtrCast(InterpState &S, CodePtr OpPC) {
const SourceInfo &E = S.Current->getSource(OpPC);
S.CCEDiag(E, diag::note_constexpr_invalid_cast)
<< 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);
return true;
}

//===----------------------------------------------------------------------===//
// Zero, Nullptr
//===----------------------------------------------------------------------===//
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,7 @@ def CastPointerIntegralAPS : Opcode {
let HasGroup = 0;
let Args = [ArgUint32];
}
def VoidPtrCast : Opcode;

def DecayPtr : Opcode {
let Types = [PtrTypeClass, PtrTypeClass];
Expand Down
9 changes: 0 additions & 9 deletions clang/lib/AST/TemplateBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -577,15 +577,6 @@ void TemplateArgument::print(const PrintingPolicy &Policy, raw_ostream &Out,
}
}

void TemplateArgument::dump(raw_ostream &Out) const {
LangOptions LO; // FIXME! see also TemplateName::dump().
LO.CPlusPlus = true;
LO.Bool = true;
print(PrintingPolicy(LO), Out, /*IncludeType*/ true);
}

LLVM_DUMP_METHOD void TemplateArgument::dump() const { dump(llvm::errs()); }

//===----------------------------------------------------------------------===//
// TemplateArgumentLoc Implementation
//===----------------------------------------------------------------------===//
Expand Down
11 changes: 0 additions & 11 deletions clang/lib/AST/TemplateName.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,14 +360,3 @@ const StreamingDiagnostic &clang::operator<<(const StreamingDiagnostic &DB,
OS.flush();
return DB << NameStr;
}

void TemplateName::dump(raw_ostream &OS) const {
LangOptions LO; // FIXME!
LO.CPlusPlus = true;
LO.Bool = true;
print(OS, PrintingPolicy(LO));
}

LLVM_DUMP_METHOD void TemplateName::dump() const {
dump(llvm::errs());
}
109 changes: 36 additions & 73 deletions clang/lib/Basic/Targets/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,6 @@ void AArch64TargetInfo::getTargetDefinesARMV84A(const LangOptions &Opts,
void AArch64TargetInfo::getTargetDefinesARMV85A(const LangOptions &Opts,
MacroBuilder &Builder) const {
Builder.defineMacro("__ARM_FEATURE_FRINT", "1");
Builder.defineMacro("__ARM_FEATURE_BTI", "1");
// Also include the Armv8.4 defines
getTargetDefinesARMV84A(Opts, Builder);
}
Expand Down Expand Up @@ -499,6 +498,9 @@ void AArch64TargetInfo::getTargetDefines(const LangOptions &Opts,
if (HasPAuthLR)
Builder.defineMacro("__ARM_FEATURE_PAUTH_LR", "1");

if (HasBTI)
Builder.defineMacro("__ARM_FEATURE_BTI", "1");

if (HasUnalignedAccess)
Builder.defineMacro("__ARM_FEATURE_UNALIGNED", "1");

Expand Down Expand Up @@ -1050,57 +1052,18 @@ bool AArch64TargetInfo::handleTargetFeatures(std::vector<std::string> &Features,
return true;
}

bool AArch64TargetInfo::initFeatureMap(
llvm::StringMap<bool> &Features, DiagnosticsEngine &Diags, StringRef CPU,
const std::vector<std::string> &FeaturesVec) const {
std::vector<std::string> UpdatedFeaturesVec;
// Parse the CPU and add any implied features.
std::optional<llvm::AArch64::CpuInfo> CpuInfo = llvm::AArch64::parseCpu(CPU);
if (CpuInfo) {
auto Exts = CpuInfo->getImpliedExtensions();
std::vector<StringRef> CPUFeats;
llvm::AArch64::getExtensionFeatures(Exts, CPUFeats);
for (auto F : CPUFeats) {
assert((F[0] == '+' || F[0] == '-') && "Expected +/- in target feature!");
UpdatedFeaturesVec.push_back(F.str());
}
}

// Process target and dependent features. This is done in two loops collecting
// them into UpdatedFeaturesVec: first to add dependent '+'features, second to
// add target '+/-'features that can later disable some of features added on
// the first loop. Function Multi Versioning features begin with '?'.
for (const auto &Feature : FeaturesVec)
if (((Feature[0] == '?' || Feature[0] == '+')) &&
AArch64TargetInfo::doesFeatureAffectCodeGen(Feature.substr(1))) {
StringRef DepFeatures =
AArch64TargetInfo::getFeatureDependencies(Feature.substr(1));
SmallVector<StringRef, 1> AttrFeatures;
DepFeatures.split(AttrFeatures, ",");
for (auto F : AttrFeatures)
UpdatedFeaturesVec.push_back(F.str());
}
for (const auto &Feature : FeaturesVec)
if (Feature[0] != '?') {
std::string UpdatedFeature = Feature;
if (Feature[0] == '+') {
std::optional<llvm::AArch64::ExtensionInfo> Extension =
llvm::AArch64::parseArchExtension(Feature.substr(1));
if (Extension)
UpdatedFeature = Extension->Feature.str();
}
UpdatedFeaturesVec.push_back(UpdatedFeature);
}

return TargetInfo::initFeatureMap(Features, Diags, CPU, UpdatedFeaturesVec);
}

// Parse AArch64 Target attributes, which are a comma separated list of:
// "arch=<arch>" - parsed to features as per -march=..
// "cpu=<cpu>" - parsed to features as per -mcpu=.., with CPU set to <cpu>
// "tune=<cpu>" - TuneCPU set to <cpu>
// "feature", "no-feature" - Add (or remove) feature.
// "+feature", "+nofeature" - Add (or remove) feature.
//
// A feature may correspond to an Extension (anything with a corresponding
// AEK_), in which case an ExtensionSet is used to parse it and expand its
// dependencies. Otherwise the feature is passed through (e.g. +v8.1a,
// +outline-atomics, -fmv, etc). Features coming from the command line are
// already parsed, therefore their dependencies do not need expansion.
ParsedTargetAttr AArch64TargetInfo::parseTargetAttr(StringRef Features) const {
ParsedTargetAttr Ret;
if (Features == "default")
Expand All @@ -1110,23 +1073,26 @@ ParsedTargetAttr AArch64TargetInfo::parseTargetAttr(StringRef Features) const {
bool FoundArch = false;

auto SplitAndAddFeatures = [](StringRef FeatString,
std::vector<std::string> &Features) {
std::vector<std::string> &Features,
llvm::AArch64::ExtensionSet &FeatureBits) {
SmallVector<StringRef, 8> SplitFeatures;
FeatString.split(SplitFeatures, StringRef("+"), -1, false);
for (StringRef Feature : SplitFeatures) {
StringRef FeatureName = llvm::AArch64::getArchExtFeature(Feature);
if (!FeatureName.empty())
Features.push_back(FeatureName.str());
if (FeatureBits.parseModifier(Feature, /* AllowNoDashForm = */ true))
continue;
// Pass through features that are not extensions, e.g. +v8.1a,
// +outline-atomics, -fmv, etc.
if (Feature.starts_with("no"))
Features.push_back("-" + Feature.drop_front(2).str());
else
// Pushing the original feature string to give a sema error later on
// when they get checked.
if (Feature.starts_with("no"))
Features.push_back("-" + Feature.drop_front(2).str());
else
Features.push_back("+" + Feature.str());
Features.push_back("+" + Feature.str());
}
};

llvm::AArch64::ExtensionSet FeatureBits;
// Reconstruct the bitset from the command line option features.
FeatureBits.reconstructFromParsedFeatures(getTargetOpts().FeaturesAsWritten);

for (auto &Feature : AttrFeatures) {
Feature = Feature.trim();
if (Feature.starts_with("fpmath="))
Expand All @@ -1149,9 +1115,9 @@ ParsedTargetAttr AArch64TargetInfo::parseTargetAttr(StringRef Features) const {
// Ret.Features.
if (!AI)
continue;
Ret.Features.push_back(AI->ArchFeature.str());
FeatureBits.addArchDefaults(*AI);
// Add any extra features, after the +
SplitAndAddFeatures(Split.second, Ret.Features);
SplitAndAddFeatures(Split.second, Ret.Features, FeatureBits);
} else if (Feature.starts_with("cpu=")) {
if (!Ret.CPU.empty())
Ret.Duplicate = "cpu=";
Expand All @@ -1161,33 +1127,30 @@ ParsedTargetAttr AArch64TargetInfo::parseTargetAttr(StringRef Features) const {
std::pair<StringRef, StringRef> Split =
Feature.split("=").second.trim().split("+");
Ret.CPU = Split.first;
SplitAndAddFeatures(Split.second, Ret.Features);
if (auto CpuInfo = llvm::AArch64::parseCpu(Ret.CPU)) {
FeatureBits.addCPUDefaults(*CpuInfo);
SplitAndAddFeatures(Split.second, Ret.Features, FeatureBits);
}
}
} else if (Feature.starts_with("tune=")) {
if (!Ret.Tune.empty())
Ret.Duplicate = "tune=";
else
Ret.Tune = Feature.split("=").second.trim();
} else if (Feature.starts_with("+")) {
SplitAndAddFeatures(Feature, Ret.Features);
} else if (Feature.starts_with("no-")) {
StringRef FeatureName =
llvm::AArch64::getArchExtFeature(Feature.split("-").second);
if (!FeatureName.empty())
Ret.Features.push_back("-" + FeatureName.drop_front(1).str());
else
Ret.Features.push_back("-" + Feature.split("-").second.str());
SplitAndAddFeatures(Feature, Ret.Features, FeatureBits);
} else {
// Try parsing the string to the internal target feature name. If it is
// invalid, add the original string (which could already be an internal
// name). These should be checked later by isValidFeatureName.
StringRef FeatureName = llvm::AArch64::getArchExtFeature(Feature);
if (!FeatureName.empty())
Ret.Features.push_back(FeatureName.str());
if (FeatureBits.parseModifier(Feature, /* AllowNoDashForm = */ true))
continue;
// Pass through features that are not extensions, e.g. +v8.1a,
// +outline-atomics, -fmv, etc.
if (Feature.starts_with("no-"))
Ret.Features.push_back("-" + Feature.drop_front(3).str());
else
Ret.Features.push_back("+" + Feature.str());
}
}
FeatureBits.toLLVMFeatureList(Ret.Features);
return Ret;
}

Expand Down
4 changes: 0 additions & 4 deletions clang/lib/Basic/Targets/AArch64.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,6 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public TargetInfo {
unsigned multiVersionSortPriority(StringRef Name) const override;
unsigned multiVersionFeatureCost() const override;

bool
initFeatureMap(llvm::StringMap<bool> &Features, DiagnosticsEngine &Diags,
StringRef CPU,
const std::vector<std::string> &FeaturesVec) const override;
bool useFP16ConversionIntrinsics() const override {
return false;
}
Expand Down
32 changes: 10 additions & 22 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5766,28 +5766,16 @@ void CGDebugInfo::EmitPseudoVariable(CGBuilderTy &Builder,
// it is loaded upon use, so we identify such pattern here.
if (llvm::LoadInst *Load = dyn_cast<llvm::LoadInst>(Value)) {
llvm::Value *Var = Load->getPointerOperand();
if (llvm::Metadata *MDValue = llvm::ValueAsMetadata::getIfExists(Var)) {
if (llvm::Value *DbgValue = llvm::MetadataAsValue::getIfExists(
CGM.getLLVMContext(), MDValue)) {
for (llvm::User *U : DbgValue->users()) {
if (llvm::CallInst *DbgDeclare = dyn_cast<llvm::CallInst>(U)) {
if (DbgDeclare->getCalledFunction()->getIntrinsicID() ==
llvm::Intrinsic::dbg_declare &&
DbgDeclare->getArgOperand(0) == DbgValue) {
// There can be implicit type cast applied on a variable if it is
// an opaque ptr, in this case its debug info may not match the
// actual type of object being used as in the next instruction, so
// we will need to emit a pseudo variable for type-casted value.
llvm::DILocalVariable *MDNode = cast<llvm::DILocalVariable>(
cast<llvm::MetadataAsValue>(DbgDeclare->getOperand(1))
->getMetadata());
if (MDNode->getType() == Type)
return;
}
}
}
}
}
// There can be implicit type cast applied on a variable if it is an opaque
// ptr, in this case its debug info may not match the actual type of object
// being used as in the next instruction, so we will need to emit a pseudo
// variable for type-casted value.
auto DeclareTypeMatches = [&](auto *DbgDeclare) {
return DbgDeclare->getVariable()->getType() == Type;
};
if (any_of(llvm::findDbgDeclares(Var), DeclareTypeMatches) ||
any_of(llvm::findDVRDeclares(Var), DeclareTypeMatches))
return;
}

// Find the correct location to insert a sequence of instructions to
Expand Down
29 changes: 9 additions & 20 deletions clang/lib/CodeGen/CGExprAgg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -513,15 +513,6 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType,

QualType elementType =
CGF.getContext().getAsArrayType(ArrayQTy)->getElementType();

// DestPtr is an array*. Construct an elementType* by drilling
// down a level.
llvm::Value *zero = llvm::ConstantInt::get(CGF.SizeTy, 0);
llvm::Value *indices[] = { zero, zero };
llvm::Value *begin = Builder.CreateInBoundsGEP(DestPtr.getElementType(),
DestPtr.emitRawPointer(CGF),
indices, "arrayinit.begin");

CharUnits elementSize = CGF.getContext().getTypeSizeInChars(elementType);
CharUnits elementAlign =
DestPtr.getAlignment().alignmentOfArrayElement(elementSize);
Expand Down Expand Up @@ -562,6 +553,7 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType,
Address endOfInit = Address::invalid();
CodeGenFunction::CleanupDeactivationScope deactivation(CGF);

llvm::Value *begin = DestPtr.emitRawPointer(CGF);
if (dtorKind) {
CodeGenFunction::AllocaTrackerRAII allocaTracker(CGF);
// In principle we could tell the cleanup where we are more
Expand All @@ -585,19 +577,13 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType,

llvm::Value *one = llvm::ConstantInt::get(CGF.SizeTy, 1);

// The 'current element to initialize'. The invariants on this
// variable are complicated. Essentially, after each iteration of
// the loop, it points to the last initialized element, except
// that it points to the beginning of the array before any
// elements have been initialized.
llvm::Value *element = begin;

// Emit the explicit initializers.
for (uint64_t i = 0; i != NumInitElements; ++i) {
// Advance to the next element.
llvm::Value *element = begin;
if (i > 0) {
element = Builder.CreateInBoundsGEP(
llvmElementType, element, one, "arrayinit.element");
element = Builder.CreateInBoundsGEP(llvmElementType, begin,
llvm::ConstantInt::get(CGF.SizeTy, i),
"arrayinit.element");

// Tell the cleanup that it needs to destroy up to this
// element. TODO: some of these stores can be trivially
Expand All @@ -624,9 +610,12 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType,
// do { *array++ = filler; } while (array != end);

// Advance to the start of the rest of the array.
llvm::Value *element = begin;
if (NumInitElements) {
element = Builder.CreateInBoundsGEP(
llvmElementType, element, one, "arrayinit.start");
llvmElementType, element,
llvm::ConstantInt::get(CGF.SizeTy, NumInitElements),
"arrayinit.start");
if (endOfInit.isValid()) Builder.CreateStore(element, endOfInit);
}

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CodeGen/CGHLSLRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ void clang::CodeGen::CGHLSLRuntime::setHLSLEntryAttributes(
assert(ShaderAttr && "All entry functions must have a HLSLShaderAttr");
const StringRef ShaderAttrKindStr = "hlsl.shader";
Fn->addFnAttr(ShaderAttrKindStr,
ShaderAttr->ConvertShaderTypeToStr(ShaderAttr->getType()));
llvm::Triple::getEnvironmentTypeName(ShaderAttr->getType()));
if (HLSLNumThreadsAttr *NumThreadsAttr = FD->getAttr<HLSLNumThreadsAttr>()) {
const StringRef NumThreadsKindStr = "hlsl.numthreads";
std::string NumThreadsStr =
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CodeGen/CGStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,8 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) {
CGM.ErrorUnsupported(S, "OpenMP dispatch directive");
break;
case Stmt::OMPScopeDirectiveClass:
llvm_unreachable("scope not supported with FE outlining");
CGM.ErrorUnsupported(S, "scope with FE outlining");
break;
case Stmt::OMPMaskedDirectiveClass:
EmitOMPMaskedDirective(cast<OMPMaskedDirective>(*S));
break;
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Format/ContinuationIndenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1257,6 +1257,11 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) {
}
return CurrentState.Indent;
}
if (Current.is(TT_TrailingReturnArrow) &&
Previous.isOneOf(tok::kw_noexcept, tok::kw_mutable, tok::kw_constexpr,
tok::kw_consteval, tok::kw_static, TT_AttributeSquare)) {
return ContinuationIndent;
}
if ((Current.isOneOf(tok::r_brace, tok::r_square) ||
(Current.is(tok::greater) && (Style.isProto() || Style.isTableGen()))) &&
State.Stack.size() > 1) {
Expand Down
41 changes: 25 additions & 16 deletions clang/lib/Sema/SemaCUDA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1018,24 +1018,33 @@ void SemaCUDA::checkTargetOverload(FunctionDecl *NewFD,
// HD/global functions "exist" in some sense on both the host and device, so
// should have the same implementation on both sides.
if (NewTarget != OldTarget &&
((NewTarget == CUDAFunctionTarget::HostDevice &&
!(getLangOpts().OffloadImplicitHostDeviceTemplates &&
isImplicitHostDeviceFunction(NewFD) &&
OldTarget == CUDAFunctionTarget::Device)) ||
(OldTarget == CUDAFunctionTarget::HostDevice &&
!(getLangOpts().OffloadImplicitHostDeviceTemplates &&
isImplicitHostDeviceFunction(OldFD) &&
NewTarget == CUDAFunctionTarget::Device)) ||
(NewTarget == CUDAFunctionTarget::Global) ||
(OldTarget == CUDAFunctionTarget::Global)) &&
!SemaRef.IsOverload(NewFD, OldFD, /* UseMemberUsingDeclRules = */ false,
/* ConsiderCudaAttrs = */ false)) {
Diag(NewFD->getLocation(), diag::err_cuda_ovl_target)
<< llvm::to_underlying(NewTarget) << NewFD->getDeclName()
<< llvm::to_underlying(OldTarget) << OldFD;
Diag(OldFD->getLocation(), diag::note_previous_declaration);
NewFD->setInvalidDecl();
break;
if ((NewTarget == CUDAFunctionTarget::HostDevice &&
!(getLangOpts().OffloadImplicitHostDeviceTemplates &&
isImplicitHostDeviceFunction(NewFD) &&
OldTarget == CUDAFunctionTarget::Device)) ||
(OldTarget == CUDAFunctionTarget::HostDevice &&
!(getLangOpts().OffloadImplicitHostDeviceTemplates &&
isImplicitHostDeviceFunction(OldFD) &&
NewTarget == CUDAFunctionTarget::Device)) ||
(NewTarget == CUDAFunctionTarget::Global) ||
(OldTarget == CUDAFunctionTarget::Global)) {
Diag(NewFD->getLocation(), diag::err_cuda_ovl_target)
<< llvm::to_underlying(NewTarget) << NewFD->getDeclName()
<< llvm::to_underlying(OldTarget) << OldFD;
Diag(OldFD->getLocation(), diag::note_previous_declaration);
NewFD->setInvalidDecl();
break;
}
if ((NewTarget == CUDAFunctionTarget::Host &&
OldTarget == CUDAFunctionTarget::Device) ||
(NewTarget == CUDAFunctionTarget::Device &&
OldTarget == CUDAFunctionTarget::Host)) {
Diag(NewFD->getLocation(), diag::warn_offload_incompatible_redeclare)
<< llvm::to_underlying(NewTarget) << llvm::to_underlying(OldTarget);
Diag(OldFD->getLocation(), diag::note_previous_declaration);
}
}
}
}
Expand Down
36 changes: 24 additions & 12 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1806,6 +1806,7 @@ static unsigned getRecordDiagFromTagKind(TagTypeKind Tag) {
static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
Stmt *Body,
Sema::CheckConstexprKind Kind);
static bool CheckConstexprMissingReturn(Sema &SemaRef, const FunctionDecl *Dcl);

// Check whether a function declaration satisfies the requirements of a
// constexpr function definition or a constexpr constructor definition. If so,
Expand Down Expand Up @@ -2411,20 +2412,9 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
}
} else {
if (ReturnStmts.empty()) {
// C++1y doesn't require constexpr functions to contain a 'return'
// statement. We still do, unless the return type might be void, because
// otherwise if there's no return statement, the function cannot
// be used in a core constant expression.
bool OK = SemaRef.getLangOpts().CPlusPlus14 &&
(Dcl->getReturnType()->isVoidType() ||
Dcl->getReturnType()->isDependentType());
switch (Kind) {
case Sema::CheckConstexprKind::Diagnose:
SemaRef.Diag(Dcl->getLocation(),
OK ? diag::warn_cxx11_compat_constexpr_body_no_return
: diag::err_constexpr_body_no_return)
<< Dcl->isConsteval();
if (!OK)
if (!CheckConstexprMissingReturn(SemaRef, Dcl))
return false;
break;

Expand Down Expand Up @@ -2494,6 +2484,28 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
return true;
}

static bool CheckConstexprMissingReturn(Sema &SemaRef,
const FunctionDecl *Dcl) {
bool IsVoidOrDependentType = Dcl->getReturnType()->isVoidType() ||
Dcl->getReturnType()->isDependentType();
// Skip emitting a missing return error diagnostic for non-void functions
// since C++23 no longer mandates constexpr functions to yield constant
// expressions.
if (SemaRef.getLangOpts().CPlusPlus23 && !IsVoidOrDependentType)
return true;

// C++14 doesn't require constexpr functions to contain a 'return'
// statement. We still do, unless the return type might be void, because
// otherwise if there's no return statement, the function cannot
// be used in a core constant expression.
bool OK = SemaRef.getLangOpts().CPlusPlus14 && IsVoidOrDependentType;
SemaRef.Diag(Dcl->getLocation(),
OK ? diag::warn_cxx11_compat_constexpr_body_no_return
: diag::err_constexpr_body_no_return)
<< Dcl->isConsteval();
return OK;
}

bool Sema::CheckImmediateEscalatingFunctionDefinition(
FunctionDecl *FD, const sema::FunctionScopeInfo *FSI) {
if (!getLangOpts().CPlusPlus20 || !FD->isImmediateEscalating())
Expand Down
109 changes: 60 additions & 49 deletions clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ HLSLNumThreadsAttr *SemaHLSL::mergeNumThreadsAttr(Decl *D,

HLSLShaderAttr *
SemaHLSL::mergeShaderAttr(Decl *D, const AttributeCommonInfo &AL,
HLSLShaderAttr::ShaderType ShaderType) {
llvm::Triple::EnvironmentType ShaderType) {
if (HLSLShaderAttr *NT = D->getAttr<HLSLShaderAttr>()) {
if (NT->getType() != ShaderType) {
Diag(NT->getLocation(), diag::err_hlsl_attribute_param_mismatch) << AL;
Expand Down Expand Up @@ -184,25 +184,24 @@ void SemaHLSL::ActOnTopLevelFunction(FunctionDecl *FD) {
if (FD->getName() != TargetInfo.getTargetOpts().HLSLEntry)
return;

StringRef Env = TargetInfo.getTriple().getEnvironmentName();
HLSLShaderAttr::ShaderType ShaderType;
if (HLSLShaderAttr::ConvertStrToShaderType(Env, ShaderType)) {
llvm::Triple::EnvironmentType Env = TargetInfo.getTriple().getEnvironment();
if (HLSLShaderAttr::isValidShaderType(Env) && Env != llvm::Triple::Library) {
if (const auto *Shader = FD->getAttr<HLSLShaderAttr>()) {
// The entry point is already annotated - check that it matches the
// triple.
if (Shader->getType() != ShaderType) {
if (Shader->getType() != Env) {
Diag(Shader->getLocation(), diag::err_hlsl_entry_shader_attr_mismatch)
<< Shader;
FD->setInvalidDecl();
}
} else {
// Implicitly add the shader attribute if the entry function isn't
// explicitly annotated.
FD->addAttr(HLSLShaderAttr::CreateImplicit(getASTContext(), ShaderType,
FD->addAttr(HLSLShaderAttr::CreateImplicit(getASTContext(), Env,
FD->getBeginLoc()));
}
} else {
switch (TargetInfo.getTriple().getEnvironment()) {
switch (Env) {
case llvm::Triple::UnknownEnvironment:
case llvm::Triple::Library:
break;
Expand All @@ -215,38 +214,40 @@ void SemaHLSL::ActOnTopLevelFunction(FunctionDecl *FD) {
void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) {
const auto *ShaderAttr = FD->getAttr<HLSLShaderAttr>();
assert(ShaderAttr && "Entry point has no shader attribute");
HLSLShaderAttr::ShaderType ST = ShaderAttr->getType();
llvm::Triple::EnvironmentType ST = ShaderAttr->getType();

switch (ST) {
case HLSLShaderAttr::Pixel:
case HLSLShaderAttr::Vertex:
case HLSLShaderAttr::Geometry:
case HLSLShaderAttr::Hull:
case HLSLShaderAttr::Domain:
case HLSLShaderAttr::RayGeneration:
case HLSLShaderAttr::Intersection:
case HLSLShaderAttr::AnyHit:
case HLSLShaderAttr::ClosestHit:
case HLSLShaderAttr::Miss:
case HLSLShaderAttr::Callable:
case llvm::Triple::Pixel:
case llvm::Triple::Vertex:
case llvm::Triple::Geometry:
case llvm::Triple::Hull:
case llvm::Triple::Domain:
case llvm::Triple::RayGeneration:
case llvm::Triple::Intersection:
case llvm::Triple::AnyHit:
case llvm::Triple::ClosestHit:
case llvm::Triple::Miss:
case llvm::Triple::Callable:
if (const auto *NT = FD->getAttr<HLSLNumThreadsAttr>()) {
DiagnoseAttrStageMismatch(NT, ST,
{HLSLShaderAttr::Compute,
HLSLShaderAttr::Amplification,
HLSLShaderAttr::Mesh});
{llvm::Triple::Compute,
llvm::Triple::Amplification,
llvm::Triple::Mesh});
FD->setInvalidDecl();
}
break;

case HLSLShaderAttr::Compute:
case HLSLShaderAttr::Amplification:
case HLSLShaderAttr::Mesh:
case llvm::Triple::Compute:
case llvm::Triple::Amplification:
case llvm::Triple::Mesh:
if (!FD->hasAttr<HLSLNumThreadsAttr>()) {
Diag(FD->getLocation(), diag::err_hlsl_missing_numthreads)
<< HLSLShaderAttr::ConvertShaderTypeToStr(ST);
<< llvm::Triple::getEnvironmentTypeName(ST);
FD->setInvalidDecl();
}
break;
default:
llvm_unreachable("Unhandled environment in triple");
}

for (ParmVarDecl *Param : FD->parameters()) {
Expand All @@ -268,31 +269,31 @@ void SemaHLSL::CheckSemanticAnnotation(
const HLSLAnnotationAttr *AnnotationAttr) {
auto *ShaderAttr = EntryPoint->getAttr<HLSLShaderAttr>();
assert(ShaderAttr && "Entry point has no shader attribute");
HLSLShaderAttr::ShaderType ST = ShaderAttr->getType();
llvm::Triple::EnvironmentType ST = ShaderAttr->getType();

switch (AnnotationAttr->getKind()) {
case attr::HLSLSV_DispatchThreadID:
case attr::HLSLSV_GroupIndex:
if (ST == HLSLShaderAttr::Compute)
if (ST == llvm::Triple::Compute)
return;
DiagnoseAttrStageMismatch(AnnotationAttr, ST, {HLSLShaderAttr::Compute});
DiagnoseAttrStageMismatch(AnnotationAttr, ST, {llvm::Triple::Compute});
break;
default:
llvm_unreachable("Unknown HLSLAnnotationAttr");
}
}

void SemaHLSL::DiagnoseAttrStageMismatch(
const Attr *A, HLSLShaderAttr::ShaderType Stage,
std::initializer_list<HLSLShaderAttr::ShaderType> AllowedStages) {
const Attr *A, llvm::Triple::EnvironmentType Stage,
std::initializer_list<llvm::Triple::EnvironmentType> AllowedStages) {
SmallVector<StringRef, 8> StageStrings;
llvm::transform(AllowedStages, std::back_inserter(StageStrings),
[](HLSLShaderAttr::ShaderType ST) {
[](llvm::Triple::EnvironmentType ST) {
return StringRef(
HLSLShaderAttr::ConvertShaderTypeToStr(ST));
HLSLShaderAttr::ConvertEnvironmentTypeToStr(ST));
});
Diag(A->getLoc(), diag::err_hlsl_attr_unsupported_in_stage)
<< A << HLSLShaderAttr::ConvertShaderTypeToStr(Stage)
<< A << llvm::Triple::getEnvironmentTypeName(Stage)
<< (AllowedStages.size() != 1) << join(StageStrings, ", ");
}

Expand Down Expand Up @@ -430,8 +431,8 @@ void SemaHLSL::handleShaderAttr(Decl *D, const ParsedAttr &AL) {
if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc))
return;

HLSLShaderAttr::ShaderType ShaderType;
if (!HLSLShaderAttr::ConvertStrToShaderType(Str, ShaderType)) {
llvm::Triple::EnvironmentType ShaderType;
if (!HLSLShaderAttr::ConvertStrToEnvironmentType(Str, ShaderType)) {
Diag(AL.getLoc(), diag::warn_attribute_type_not_supported)
<< AL << Str << ArgLoc;
return;
Expand Down Expand Up @@ -549,16 +550,22 @@ class DiagnoseHLSLAvailability
//
// Maps FunctionDecl to an unsigned number that represents the set of shader
// environments the function has been scanned for.
// Since HLSLShaderAttr::ShaderType enum is generated from Attr.td and is
// defined without any assigned values, it is guaranteed to be numbered
// sequentially from 0 up and we can use it to 'index' individual bits
// in the set.
// The llvm::Triple::EnvironmentType enum values for shader stages guaranteed
// to be numbered from llvm::Triple::Pixel to llvm::Triple::Amplification
// (verified by static_asserts in Triple.cpp), we can use it to index
// individual bits in the set, as long as we shift the values to start with 0
// by subtracting the value of llvm::Triple::Pixel first.
//
// The N'th bit in the set will be set if the function has been scanned
// in shader environment whose ShaderType integer value equals N.
// in shader environment whose llvm::Triple::EnvironmentType integer value
// equals (llvm::Triple::Pixel + N).
//
// For example, if a function has been scanned in compute and pixel stage
// environment, the value will be 0x21 (100001 binary) because
// (int)HLSLShaderAttr::ShaderType::Pixel == 1 and
// (int)HLSLShaderAttr::ShaderType::Compute == 5.
// environment, the value will be 0x21 (100001 binary) because:
//
// (int)(llvm::Triple::Pixel - llvm::Triple::Pixel) == 0
// (int)(llvm::Triple::Compute - llvm::Triple::Pixel) == 5
//
// A FunctionDecl is mapped to 0 (or not included in the map) if it has not
// been scanned in any environment.
llvm::DenseMap<const FunctionDecl *, unsigned> ScannedDecls;
Expand All @@ -574,12 +581,16 @@ class DiagnoseHLSLAvailability
bool ReportOnlyShaderStageIssues;

// Helper methods for dealing with current stage context / environment
void SetShaderStageContext(HLSLShaderAttr::ShaderType ShaderType) {
void SetShaderStageContext(llvm::Triple::EnvironmentType ShaderType) {
static_assert(sizeof(unsigned) >= 4);
assert((unsigned)ShaderType < 31); // 31 is reserved for "unknown"

CurrentShaderEnvironment = HLSLShaderAttr::getTypeAsEnvironment(ShaderType);
CurrentShaderStageBit = (1 << ShaderType);
assert(HLSLShaderAttr::isValidShaderType(ShaderType));
assert((unsigned)(ShaderType - llvm::Triple::Pixel) < 31 &&
"ShaderType is too big for this bitmap"); // 31 is reserved for
// "unknown"

unsigned bitmapIndex = ShaderType - llvm::Triple::Pixel;
CurrentShaderEnvironment = ShaderType;
CurrentShaderStageBit = (1 << bitmapIndex);
}

void SetUnknownShaderStageContext() {
Expand Down
25 changes: 12 additions & 13 deletions clang/lib/Sema/SemaOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6198,18 +6198,17 @@ class TeamsLoopChecker final : public ConstStmtVisitor<TeamsLoopChecker> {
// unless the assume-no-nested-parallelism flag has been specified.
// OpenMP API runtime library calls do not inhibit parallel loop
// translation, regardless of the assume-no-nested-parallelism.
if (C) {
bool IsOpenMPAPI = false;
auto *FD = dyn_cast_or_null<FunctionDecl>(C->getCalleeDecl());
if (FD) {
std::string Name = FD->getNameInfo().getAsString();
IsOpenMPAPI = Name.find("omp_") == 0;
}
TeamsLoopCanBeParallelFor =
IsOpenMPAPI || SemaRef.getLangOpts().OpenMPNoNestedParallelism;
if (!TeamsLoopCanBeParallelFor)
return;
}
bool IsOpenMPAPI = false;
auto *FD = dyn_cast_or_null<FunctionDecl>(C->getCalleeDecl());
if (FD) {
std::string Name = FD->getNameInfo().getAsString();
IsOpenMPAPI = Name.find("omp_") == 0;
}
TeamsLoopCanBeParallelFor =
IsOpenMPAPI || SemaRef.getLangOpts().OpenMPNoNestedParallelism;
if (!TeamsLoopCanBeParallelFor)
return;

for (const Stmt *Child : C->children())
if (Child)
Visit(Child);
Expand Down Expand Up @@ -24331,7 +24330,7 @@ SemaOpenMP::ActOnOpenMPHasDeviceAddrClause(ArrayRef<Expr *> VarList,

OMPClause *SemaOpenMP::ActOnOpenMPAllocateClause(
Expr *Allocator, ArrayRef<Expr *> VarList, SourceLocation StartLoc,
SourceLocation ColonLoc, SourceLocation LParenLoc, SourceLocation EndLoc) {
SourceLocation LParenLoc, SourceLocation ColonLoc, SourceLocation EndLoc) {
if (Allocator) {
// OpenMP [2.11.4 allocate Clause, Description]
// allocator is an expression of omp_allocator_handle_t type.
Expand Down
111 changes: 96 additions & 15 deletions clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
#include "llvm/ADT/StringRef.h"

using namespace clang;
Expand All @@ -26,16 +27,88 @@ namespace {
class PointerSubChecker
: public Checker< check::PreStmt<BinaryOperator> > {
const BugType BT{this, "Pointer subtraction"};
const llvm::StringLiteral Msg_MemRegionDifferent =
"Subtraction of two pointers that do not point into the same array "
"is undefined behavior.";
const llvm::StringLiteral Msg_LargeArrayIndex =
"Using an array index greater than the array size at pointer subtraction "
"is undefined behavior.";
const llvm::StringLiteral Msg_NegativeArrayIndex =
"Using a negative array index at pointer subtraction "
"is undefined behavior.";
const llvm::StringLiteral Msg_BadVarIndex =
"Indexing the address of a variable with other than 1 at this place "
"is undefined behavior.";

bool checkArrayBounds(CheckerContext &C, const Expr *E,
const ElementRegion *ElemReg,
const MemRegion *Reg) const;
void reportBug(CheckerContext &C, const Expr *E,
const llvm::StringLiteral &Msg) const;

public:
void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
};
}

bool PointerSubChecker::checkArrayBounds(CheckerContext &C, const Expr *E,
const ElementRegion *ElemReg,
const MemRegion *Reg) const {
if (!ElemReg)
return true;

ProgramStateRef State = C.getState();
const MemRegion *SuperReg = ElemReg->getSuperRegion();
SValBuilder &SVB = C.getSValBuilder();

if (SuperReg == Reg) {
if (const llvm::APSInt *I = SVB.getKnownValue(State, ElemReg->getIndex());
I && (!I->isOne() && !I->isZero()))
reportBug(C, E, Msg_BadVarIndex);
return false;
}

DefinedOrUnknownSVal ElemCount =
getDynamicElementCount(State, SuperReg, SVB, ElemReg->getElementType());
auto IndexTooLarge = SVB.evalBinOp(C.getState(), BO_GT, ElemReg->getIndex(),
ElemCount, SVB.getConditionType())
.getAs<DefinedOrUnknownSVal>();
if (IndexTooLarge) {
ProgramStateRef S1, S2;
std::tie(S1, S2) = C.getState()->assume(*IndexTooLarge);
if (S1 && !S2) {
reportBug(C, E, Msg_LargeArrayIndex);
return false;
}
}
auto IndexTooSmall = SVB.evalBinOp(State, BO_LT, ElemReg->getIndex(),
SVB.makeZeroVal(SVB.getArrayIndexType()),
SVB.getConditionType())
.getAs<DefinedOrUnknownSVal>();
if (IndexTooSmall) {
ProgramStateRef S1, S2;
std::tie(S1, S2) = State->assume(*IndexTooSmall);
if (S1 && !S2) {
reportBug(C, E, Msg_NegativeArrayIndex);
return false;
}
}
return true;
}

void PointerSubChecker::reportBug(CheckerContext &C, const Expr *E,
const llvm::StringLiteral &Msg) const {
if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
R->addRange(E->getSourceRange());
C.emitReport(std::move(R));
}
}

void PointerSubChecker::checkPreStmt(const BinaryOperator *B,
CheckerContext &C) const {
// When doing pointer subtraction, if the two pointers do not point to the
// same memory chunk, emit a warning.
// same array, emit a warning.
if (B->getOpcode() != BO_Sub)
return;

Expand All @@ -44,28 +117,36 @@ void PointerSubChecker::checkPreStmt(const BinaryOperator *B,

const MemRegion *LR = LV.getAsRegion();
const MemRegion *RR = RV.getAsRegion();

if (!(LR && RR))
if (!LR || !RR)
return;

const MemRegion *BaseLR = LR->getBaseRegion();
const MemRegion *BaseRR = RR->getBaseRegion();
// Allow subtraction of identical pointers.
if (LR == RR)
return;

if (BaseLR == BaseRR)
// No warning if one operand is unknown.
if (isa<SymbolicRegion>(LR) || isa<SymbolicRegion>(RR))
return;

// Allow arithmetic on different symbolic regions.
if (isa<SymbolicRegion>(BaseLR) || isa<SymbolicRegion>(BaseRR))
const auto *ElemLR = dyn_cast<ElementRegion>(LR);
const auto *ElemRR = dyn_cast<ElementRegion>(RR);

if (!checkArrayBounds(C, B->getLHS(), ElemLR, RR))
return;
if (!checkArrayBounds(C, B->getRHS(), ElemRR, LR))
return;

if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
constexpr llvm::StringLiteral Msg =
"Subtraction of two pointers that do not point to the same memory "
"chunk may cause incorrect result.";
auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
R->addRange(B->getSourceRange());
C.emitReport(std::move(R));
if (ElemLR && ElemRR) {
const MemRegion *SuperLR = ElemLR->getSuperRegion();
const MemRegion *SuperRR = ElemRR->getSuperRegion();
if (SuperLR == SuperRR)
return;
// Allow arithmetic on different symbolic regions.
if (isa<SymbolicRegion>(SuperLR) || isa<SymbolicRegion>(SuperRR))
return;
}

reportBug(C, B, Msg_MemRegionDifferent);
}

void ento::registerPointerSubChecker(CheckerManager &mgr) {
Expand Down
3 changes: 1 addition & 2 deletions clang/test/AST/Interp/builtin-align-cxx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,7 @@ static_assert(__builtin_align_down(&align32array[7], 4) == &align32array[4], "")
static_assert(__builtin_align_down(&align32array[8], 4) == &align32array[8], "");

// Achieving the same thing using casts to uintptr_t is not allowed:
static_assert((char *)((__UINTPTR_TYPE__)&align32array[7] & ~3) == &align32array[4], ""); // both-error{{not an integral constant expression}} \
// expected-note {{cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression}}
static_assert((char *)((__UINTPTR_TYPE__)&align32array[7] & ~3) == &align32array[4], ""); // both-error{{not an integral constant expression}}

static_assert(__builtin_align_down(&align32array[1], 4) == &align32array[0], "");
static_assert(__builtin_align_down(&align32array[1], 64) == &align32array[0], ""); // both-error{{not an integral constant expression}}
Expand Down
6 changes: 2 additions & 4 deletions clang/test/AST/Interp/c.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,10 @@ _Static_assert((&a - 100) != 0, ""); // pedantic-ref-warning {{is a GNU extensio
// pedantic-ref-note {{-100 of non-array}} \
// pedantic-expected-note {{-100 of non-array}}
/// extern variable of a composite type.
/// FIXME: The 'this conversion is not allowed' note is missing in the new interpreter.
extern struct Test50S Test50;
_Static_assert(&Test50 != (void*)0, ""); // all-warning {{always true}} \
// pedantic-ref-warning {{is a GNU extension}} \
// pedantic-ref-note {{this conversion is not allowed in a constant expression}} \
// pedantic-expected-warning {{is a GNU extension}}
// pedantic-warning {{is a GNU extension}} \
// pedantic-note {{this conversion is not allowed in a constant expression}}

struct y {int x,y;};
int a2[(intptr_t)&((struct y*)0)->y]; // all-warning {{folded to constant array}}
Expand Down
5 changes: 5 additions & 0 deletions clang/test/AST/Interp/complex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ static_assert(__imag(Doubles[2]) == 0.0, "");
static_assert(__real(Doubles[3]) == 0.0, "");
static_assert(__imag(Doubles[3]) == 0.0, "");

static_assert(~(0.5 + 1.5j) == (0.5 + -1.5j), "");

static_assert(__extension__ __imag(A) == 0, "");
static_assert(__imag(__extension__ A) == 0, "");

void func(void) {
__complex__ int arr;
_Complex int result;
Expand Down
9 changes: 1 addition & 8 deletions clang/test/AST/Interp/const-eval.c
Original file line number Diff line number Diff line change
Expand Up @@ -140,15 +140,8 @@ EVAL_EXPR(47, &x < &x + 1 ? 1 : -1)
EVAL_EXPR(48, &x != &x - 1 ? 1 : -1)
EVAL_EXPR(49, &x < &x - 100 ? 1 : -1) // ref-error {{not an integer constant expression}}

/// FIXME: Rejecting this is correct, BUT when converting the innermost pointer
/// to an integer, we do not preserve the information where it came from. So when we later
/// create a pointer from it, it also doesn't have that information, which means
/// hasSameBase() for those two pointers will return false. And in those cases, we emit
/// the diagnostic:
/// comparison between '&Test50' and '&(631578)' has unspecified value
extern struct Test50S Test50;
EVAL_EXPR(50, &Test50 < (struct Test50S*)((unsigned long)&Test50 + 10)) // both-error {{not an integer constant expression}} \
// expected-note {{comparison between}}
EVAL_EXPR(50, &Test50 < (struct Test50S*)((unsigned long)&Test50 + 10)) // both-error {{not an integer constant expression}}

EVAL_EXPR(51, 0 != (float)1e99)

Expand Down
4 changes: 4 additions & 0 deletions clang/test/Analysis/casts.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,9 @@ void multiDimensionalArrayPointerCasts(void) {
clang_analyzer_eval(y1 == y2); // expected-warning{{TRUE}}

// FIXME: should be FALSE (i.e. equal pointers).
// FIXME: pointer subtraction warning might be incorrect
clang_analyzer_eval(y1 - y2); // expected-warning{{UNKNOWN}}
// expected-warning@-1{{Subtraction of two pointers that do not point into the same array is undefined behavior}}
// FIXME: should be TRUE (i.e. same symbol).
clang_analyzer_eval(*y1 == *y2); // expected-warning{{UNKNOWN}}

Expand All @@ -147,7 +149,9 @@ void multiDimensionalArrayPointerCasts(void) {
clang_analyzer_eval(y1 == y3); // expected-warning{{TRUE}}

// FIXME: should be FALSE (i.e. equal pointers).
// FIXME: pointer subtraction warning might be incorrect
clang_analyzer_eval(y1 - y3); // expected-warning{{UNKNOWN}}
// expected-warning@-1{{Subtraction of two pointers that do not point into the same array is undefined behavior}}
// FIXME: should be TRUE (i.e. same symbol).
clang_analyzer_eval(*y1 == *y3); // expected-warning{{UNKNOWN}}

Expand Down
120 changes: 120 additions & 0 deletions clang/test/Analysis/pointer-sub.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.PointerSub -verify %s

void f1(void) {
int x, y, z[10];
int d = &y - &x; // expected-warning{{Subtraction of two pointers that do not point into the same array is undefined behavior}}
d = z - &y; // expected-warning{{Subtraction of two pointers that do not point into the same array is undefined behavior}}
d = &x - &x; // no-warning (subtraction of any two identical pointers is allowed)
d = (long *)&x - (long *)&x;
d = (&x + 1) - &x; // no-warning ('&x' is like a single-element array)
d = &x - (&x + 1); // no-warning
d = (&x + 0) - &x; // no-warning
d = (&x - 1) - &x; // expected-warning{{Indexing the address of a variable with other than 1 at this place is undefined behavior}}
d = (&x + 2) - &x; // expected-warning{{Indexing the address of a variable with other than 1 at this place is undefined behavior}}

d = (z + 9) - z; // no-warning (pointers to same array)
d = (z + 10) - z; // no-warning (pointer to "one after the end")
d = (z + 11) - z; // expected-warning{{Using an array index greater than the array size at pointer subtraction is undefined behavior}}
d = (z - 1) - z; // expected-warning{{Using a negative array index at pointer subtraction is undefined behavior}}
}

void f2(void) {
int a[10], b[10], c;
int *p = &a[2];
int *q = &a[8];
int d = q - p; // no-warning (pointers into the same array)

q = &b[3];
d = q - p; // expected-warning{{Subtraction of two pointers that}}

q = a + 10;
d = q - p; // no warning (use of pointer to one after the end is allowed)
q = a + 11;
d = q - a; // expected-warning{{Using an array index greater than the array size at pointer subtraction is undefined behavior}}

d = &a[4] - a; // no-warning
d = &a[2] - p; // no-warning
d = &c - p; // expected-warning{{Subtraction of two pointers that}}

d = (int *)((char *)(&a[4]) + sizeof(int)) - &a[4]; // no-warning (pointers into the same array data)
d = (int *)((char *)(&a[4]) + 1) - &a[4]; // expected-warning{{Subtraction of two pointers that}}
}

void f3(void) {
int a[3][4];
int d;

d = &(a[2]) - &(a[1]);
d = a[2] - a[1]; // expected-warning{{Subtraction of two pointers that}}
d = a[1] - a[1];
d = &(a[1][2]) - &(a[1][0]);
d = &(a[1][2]) - &(a[0][0]); // expected-warning{{Subtraction of two pointers that}}

d = (int *)((char *)(&a[2][2]) + sizeof(int)) - &a[2][2]; // expected-warning{{Subtraction of two pointers that}}
d = (int *)((char *)(&a[2][2]) + 1) - &a[2][2]; // expected-warning{{Subtraction of two pointers that}}
d = (int (*)[4])((char *)&a[2] + sizeof(int (*)[4])) - &a[2]; // expected-warning{{Subtraction of two pointers that}}
d = (int (*)[4])((char *)&a[2] + 1) - &a[2]; // expected-warning{{Subtraction of two pointers that}}
}

void f4(void) {
int n = 4, m = 3;
int a[n][m];
int (*p)[m] = a; // p == &a[0]
p += 1; // p == &a[1]

// FIXME: This is a known problem with -Wpointer-arith (https://github.com/llvm/llvm-project/issues/28328)
int d = p - a; // d == 1 // expected-warning{{subtraction of pointers to type 'int[m]' of zero size has undefined behavior}}

// FIXME: This is a known problem with -Wpointer-arith (https://github.com/llvm/llvm-project/issues/28328)
d = &(a[2]) - &(a[1]); // expected-warning{{subtraction of pointers to type 'int[m]' of zero size has undefined behavior}}

d = a[2] - a[1]; // expected-warning{{Subtraction of two pointers that}}
}

typedef struct {
int a;
int b;
int c[10];
int d[10];
} S;

void f5(void) {
S s;
int y;
int d;

d = &s.b - &s.a; // expected-warning{{Subtraction of two pointers that}}
d = &s.c[0] - &s.a; // expected-warning{{Subtraction of two pointers that}}
d = &s.b - &y; // expected-warning{{Subtraction of two pointers that}}
d = &s.c[3] - &s.c[2];
d = &s.d[3] - &s.c[2]; // expected-warning{{Subtraction of two pointers that}}
d = s.d - s.c; // expected-warning{{Subtraction of two pointers that}}

S sa[10];
d = &sa[2] - &sa[1];
d = &sa[2].a - &sa[1].b; // expected-warning{{Subtraction of two pointers that}}
}

void f6(void) {
long long l;
char *a1 = (char *)&l;
int d = a1[3] - l;

long long la1[3];
long long la2[3];
char *pla1 = (char *)la1;
char *pla2 = (char *)la2;
d = pla1[1] - pla1[0];
d = (long long *)&pla1[1] - &l; // expected-warning{{Subtraction of two pointers that}}
d = &pla2[3] - &pla1[3]; // expected-warning{{Subtraction of two pointers that}}
}

void f7(int *p) {
int a[10];
int d = &a[10] - p; // no-warning ('p' is unknown, even if it cannot point into 'a')
}

void f8(int n) {
int a[10];
int d = a[n] - a[0];
}
14 changes: 2 additions & 12 deletions clang/test/Analysis/ptr-arith.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.FixedAddr,alpha.core.PointerArithm,alpha.core.PointerSub,debug.ExprInspection -Wno-pointer-to-int-cast -verify -triple x86_64-apple-darwin9 -Wno-tautological-pointer-compare -analyzer-config eagerly-assume=false %s
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.FixedAddr,alpha.core.PointerArithm,alpha.core.PointerSub,debug.ExprInspection -Wno-pointer-to-int-cast -verify -triple i686-apple-darwin9 -Wno-tautological-pointer-compare -analyzer-config eagerly-assume=false %s
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.FixedAddr,alpha.core.PointerArithm,debug.ExprInspection -Wno-pointer-to-int-cast -verify -triple x86_64-apple-darwin9 -Wno-tautological-pointer-compare -analyzer-config eagerly-assume=false %s
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.FixedAddr,alpha.core.PointerArithm,debug.ExprInspection -Wno-pointer-to-int-cast -verify -triple i686-apple-darwin9 -Wno-tautological-pointer-compare -analyzer-config eagerly-assume=false %s

void clang_analyzer_eval(int);
void clang_analyzer_dump(int);
Expand Down Expand Up @@ -35,16 +35,6 @@ domain_port (const char *domain_b, const char *domain_e,
return port;
}

void f3(void) {
int x, y;
int d = &y - &x; // expected-warning{{Subtraction of two pointers that do not point to the same memory chunk may cause incorrect result}}

int a[10];
int *p = &a[2];
int *q = &a[8];
d = q-p; // no-warning
}

void f4(void) {
int *p;
p = (int*) 0x10000; // expected-warning{{Using a fixed address is not portable because that address will probably not be valid in all environments or platforms}}
Expand Down
Loading