Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
//===--- UseDesignatedInitializersCheck.cpp - clang-tidy ------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "UseDesignatedInitializersCheck.h"
#include "../utils/DesignatedInitializers.h"
#include "clang/AST/APValue.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/Stmt.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchersMacros.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Lex/Lexer.h"

using namespace clang::ast_matchers;

namespace clang::tidy::modernize {

static constexpr char IgnoreSingleElementAggregatesName[] =
"IgnoreSingleElementAggregates";
static constexpr bool IgnoreSingleElementAggregatesDefault = true;

static constexpr char RestrictToPODTypesName[] = "RestrictToPODTypes";
static constexpr bool RestrictToPODTypesDefault = false;

static constexpr char IgnoreMacrosName[] = "IgnoreMacros";
static constexpr bool IgnoreMacrosDefault = true;

namespace {

struct Designators {

Designators(const InitListExpr *InitList) : InitList(InitList) {
assert(InitList->isSyntacticForm());
};

unsigned size() { return getCached().size(); }

std::optional<llvm::StringRef> operator[](const SourceLocation &Location) {
const auto &Designators = getCached();
const auto Result = Designators.find(Location);
if (Result == Designators.end())
return {};
const llvm::StringRef Designator = Result->getSecond();
return (Designator.front() == '.' ? Designator.substr(1) : Designator)
.trim("\0"); // Trim NULL characters appearing on Windows in the
// name.
}

private:
using LocationToNameMap = llvm::DenseMap<clang::SourceLocation, std::string>;

std::optional<LocationToNameMap> CachedDesignators;
const InitListExpr *InitList;

LocationToNameMap &getCached() {
return CachedDesignators ? *CachedDesignators
: CachedDesignators.emplace(
utils::getUnwrittenDesignators(InitList));
}
};

unsigned getNumberOfDesignated(const InitListExpr *SyntacticInitList) {
return llvm::count_if(*SyntacticInitList, [](auto *InitExpr) {
return isa<DesignatedInitExpr>(InitExpr);
});
}

AST_MATCHER(CXXRecordDecl, isAggregate) { return Node.isAggregate(); }

AST_MATCHER(CXXRecordDecl, isPOD) { return Node.isPOD(); }

AST_MATCHER(InitListExpr, isFullyDesignated) {
if (const InitListExpr *SyntacticForm =
Node.isSyntacticForm() ? &Node : Node.getSyntacticForm())
return getNumberOfDesignated(SyntacticForm) == SyntacticForm->getNumInits();
return true;
}

AST_MATCHER(InitListExpr, hasMoreThanOneElement) {
return Node.getNumInits() > 1;
}

} // namespace

UseDesignatedInitializersCheck::UseDesignatedInitializersCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context), IgnoreSingleElementAggregates(Options.get(
IgnoreSingleElementAggregatesName,
IgnoreSingleElementAggregatesDefault)),
RestrictToPODTypes(
Options.get(RestrictToPODTypesName, RestrictToPODTypesDefault)),
IgnoreMacros(
Options.getLocalOrGlobal(IgnoreMacrosName, IgnoreMacrosDefault)) {}

void UseDesignatedInitializersCheck::registerMatchers(MatchFinder *Finder) {
const auto HasBaseWithFields =
hasAnyBase(hasType(cxxRecordDecl(has(fieldDecl()))));
Finder->addMatcher(
initListExpr(
hasType(cxxRecordDecl(RestrictToPODTypes ? isPOD() : isAggregate(),
unless(HasBaseWithFields))
.bind("type")),
IgnoreSingleElementAggregates ? hasMoreThanOneElement() : anything(),
unless(isFullyDesignated()))
.bind("init"),
this);
}

void UseDesignatedInitializersCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *InitList = Result.Nodes.getNodeAs<InitListExpr>("init");
const auto *Type = Result.Nodes.getNodeAs<CXXRecordDecl>("type");
if (!Type || !InitList)
return;
const auto *SyntacticInitList = InitList->getSyntacticForm();
if (!SyntacticInitList)
return;
Designators Designators{SyntacticInitList};
const unsigned NumberOfDesignated = getNumberOfDesignated(SyntacticInitList);
if (SyntacticInitList->getNumInits() - NumberOfDesignated >
Designators.size())
return;

// If the whole initializer list is un-designated, issue only one warning and
// a single fix-it for the whole expression.
if (0 == NumberOfDesignated) {
if (IgnoreMacros && InitList->getBeginLoc().isMacroID())
return;
{
DiagnosticBuilder Diag =
diag(InitList->getLBraceLoc(),
"use designated initializer list to initialize %0");
Diag << Type << InitList->getSourceRange();
for (const Stmt *InitExpr : *SyntacticInitList) {
const auto Designator = Designators[InitExpr->getBeginLoc()];
if (Designator && !Designator->empty())
Diag << FixItHint::CreateInsertion(InitExpr->getBeginLoc(),
("." + *Designator + "=").str());
}
}
diag(Type->getBeginLoc(), "aggregate type is defined here",
DiagnosticIDs::Note);
return;
}

// In case that only a few elements are un-designated (not all as before), the
// check offers dedicated issues and fix-its for each of them.
for (const auto *InitExpr : *SyntacticInitList) {
if (isa<DesignatedInitExpr>(InitExpr))
continue;
if (IgnoreMacros && InitExpr->getBeginLoc().isMacroID())
continue;
const auto Designator = Designators[InitExpr->getBeginLoc()];
if (!Designator || Designator->empty()) {
// There should always be a designator. If there's unexpectedly none, we
// at least report a generic diagnostic.
diag(InitExpr->getBeginLoc(), "use designated init expression")
<< InitExpr->getSourceRange();
} else {
diag(InitExpr->getBeginLoc(),
"use designated init expression to initialize field '%0'")
<< InitExpr->getSourceRange() << *Designator
<< FixItHint::CreateInsertion(InitExpr->getBeginLoc(),
("." + *Designator + "=").str());
}
}
}

void UseDesignatedInitializersCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, IgnoreSingleElementAggregatesName,
IgnoreSingleElementAggregates);
Options.store(Opts, RestrictToPODTypesName, RestrictToPODTypes);
Options.store(Opts, IgnoreMacrosName, IgnoreMacros);
}

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

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEDESIGNATEDINITIALIZERSCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEDESIGNATEDINITIALIZERSCHECK_H

#include "../ClangTidyCheck.h"

namespace clang::tidy::modernize {

/// Finds initializer lists for aggregate type that could be
/// written as designated initializers instead.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-designated-initializers.html
class UseDesignatedInitializersCheck : public ClangTidyCheck {
public:
UseDesignatedInitializersCheck(StringRef Name, ClangTidyContext *Context);
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;

std::optional<TraversalKind> getCheckTraversalKind() const override {
return TK_IgnoreUnlessSpelledInSource;
}

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

} // namespace clang::tidy::modernize

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEDESIGNATEDINITIALIZERSCHECK_H
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ add_clang_library(clangTidyUtils
Aliasing.cpp
ASTUtils.cpp
DeclRefExprUtils.cpp
DesignatedInitializers.cpp
ExceptionAnalyzer.cpp
ExceptionSpecAnalyzer.cpp
ExprSequence.cpp
Expand Down
195 changes: 195 additions & 0 deletions clang-tools-extra/clang-tidy/utils/DesignatedInitializers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
//===--- DesignatedInitializers.cpp - clang-tidy --------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file provides utilities for designated initializers.
///
//===----------------------------------------------------------------------===//

#include "DesignatedInitializers.h"
#include "clang/AST/DeclCXX.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/ScopeExit.h"

namespace clang::tidy::utils {

namespace {

/// Returns true if Name is reserved, like _Foo or __Vector_base.
static inline bool isReservedName(llvm::StringRef Name) {
// This doesn't catch all cases, but the most common.
return Name.size() >= 2 && Name[0] == '_' &&
(isUppercase(Name[1]) || Name[1] == '_');
}

// Helper class to iterate over the designator names of an aggregate type.
//
// For an array type, yields [0], [1], [2]...
// For aggregate classes, yields null for each base, then .field1, .field2,
// ...
class AggregateDesignatorNames {
public:
AggregateDesignatorNames(QualType T) {
if (!T.isNull()) {
T = T.getCanonicalType();
if (T->isArrayType()) {
IsArray = true;
Valid = true;
return;
}
if (const RecordDecl *RD = T->getAsRecordDecl()) {
Valid = true;
FieldsIt = RD->field_begin();
FieldsEnd = RD->field_end();
if (const auto *CRD = llvm::dyn_cast<CXXRecordDecl>(RD)) {
BasesIt = CRD->bases_begin();
BasesEnd = CRD->bases_end();
Valid = CRD->isAggregate();
}
OneField = Valid && BasesIt == BasesEnd && FieldsIt != FieldsEnd &&
std::next(FieldsIt) == FieldsEnd;
}
}
}
// Returns false if the type was not an aggregate.
operator bool() { return Valid; }
// Advance to the next element in the aggregate.
void next() {
if (IsArray)
++Index;
else if (BasesIt != BasesEnd)
++BasesIt;
else if (FieldsIt != FieldsEnd)
++FieldsIt;
}
// Print the designator to Out.
// Returns false if we could not produce a designator for this element.
bool append(std::string &Out, bool ForSubobject) {
if (IsArray) {
Out.push_back('[');
Out.append(std::to_string(Index));
Out.push_back(']');
return true;
}
if (BasesIt != BasesEnd)
return false; // Bases can't be designated. Should we make one up?
if (FieldsIt != FieldsEnd) {
llvm::StringRef FieldName;
if (const IdentifierInfo *II = FieldsIt->getIdentifier())
FieldName = II->getName();

// For certain objects, their subobjects may be named directly.
if (ForSubobject &&
(FieldsIt->isAnonymousStructOrUnion() ||
// std::array<int,3> x = {1,2,3}. Designators not strictly valid!
(OneField && isReservedName(FieldName))))
return true;

if (!FieldName.empty() && !isReservedName(FieldName)) {
Out.push_back('.');
Out.append(FieldName.begin(), FieldName.end());
return true;
}
return false;
}
return false;
}

private:
bool Valid = false;
bool IsArray = false;
bool OneField = false; // e.g. std::array { T __elements[N]; }
unsigned Index = 0;
CXXRecordDecl::base_class_const_iterator BasesIt;
CXXRecordDecl::base_class_const_iterator BasesEnd;
RecordDecl::field_iterator FieldsIt;
RecordDecl::field_iterator FieldsEnd;
};

// Collect designator labels describing the elements of an init list.
//
// This function contributes the designators of some (sub)object, which is
// represented by the semantic InitListExpr Sem.
// This includes any nested subobjects, but *only* if they are part of the
// same original syntactic init list (due to brace elision). In other words,
// it may descend into subobjects but not written init-lists.
//
// For example: struct Outer { Inner a,b; }; struct Inner { int x, y; }
// Outer o{{1, 2}, 3};
// This function will be called with Sem = { {1, 2}, {3, ImplicitValue} }
// It should generate designators '.a:' and '.b.x:'.
// '.a:' is produced directly without recursing into the written sublist.
// (The written sublist will have a separate collectDesignators() call later).
// Recursion with Prefix='.b' and Sem = {3, ImplicitValue} produces '.b.x:'.
void collectDesignators(const InitListExpr *Sem,
llvm::DenseMap<SourceLocation, std::string> &Out,
const llvm::DenseSet<SourceLocation> &NestedBraces,
std::string &Prefix) {
if (!Sem || Sem->isTransparent())
return;
assert(Sem->isSemanticForm());

// The elements of the semantic form all correspond to direct subobjects of
// the aggregate type. `Fields` iterates over these subobject names.
AggregateDesignatorNames Fields(Sem->getType());
if (!Fields)
return;
for (const Expr *Init : Sem->inits()) {
auto Next = llvm::make_scope_exit([&, Size(Prefix.size())] {
Fields.next(); // Always advance to the next subobject name.
Prefix.resize(Size); // Erase any designator we appended.
});
// Skip for a broken initializer or if it is a "hole" in a subobject that
// was not explicitly initialized.
if (!Init || llvm::isa<ImplicitValueInitExpr>(Init))
continue;

const auto *BraceElidedSubobject = llvm::dyn_cast<InitListExpr>(Init);
if (BraceElidedSubobject &&
NestedBraces.contains(BraceElidedSubobject->getLBraceLoc()))
BraceElidedSubobject = nullptr; // there were braces!

if (!Fields.append(Prefix, BraceElidedSubobject != nullptr))
continue; // no designator available for this subobject
if (BraceElidedSubobject) {
// If the braces were elided, this aggregate subobject is initialized
// inline in the same syntactic list.
// Descend into the semantic list describing the subobject.
// (NestedBraces are still correct, they're from the same syntactic
// list).
collectDesignators(BraceElidedSubobject, Out, NestedBraces, Prefix);
continue;
}
Out.try_emplace(Init->getBeginLoc(), Prefix);
}
}

} // namespace

llvm::DenseMap<SourceLocation, std::string>
getUnwrittenDesignators(const InitListExpr *Syn) {
assert(Syn->isSyntacticForm());

// collectDesignators needs to know which InitListExprs in the semantic tree
// were actually written, but InitListExpr::isExplicit() lies.
// Instead, record where braces of sub-init-lists occur in the syntactic form.
llvm::DenseSet<SourceLocation> NestedBraces;
for (const Expr *Init : Syn->inits())
if (auto *Nested = llvm::dyn_cast<InitListExpr>(Init))
NestedBraces.insert(Nested->getLBraceLoc());

// Traverse the semantic form to find the designators.
// We use their SourceLocation to correlate with the syntactic form later.
llvm::DenseMap<SourceLocation, std::string> Designators;
std::string EmptyPrefix;
collectDesignators(Syn->isSemanticForm() ? Syn : Syn->getSemanticForm(),
Designators, NestedBraces, EmptyPrefix);
return Designators;
}

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

#include "clang/AST/Expr.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/DenseMap.h"

namespace clang::tidy::utils {

/// Get designators describing the elements of a (syntactic) init list.
///
/// Given for example the type
/// \code
/// struct S { int i, j; };
/// \endcode
/// and the definition
/// \code
/// S s{1, 2};
/// \endcode
/// calling `getUnwrittenDesignators` for the initializer list expression
/// `{1, 2}` would produce the map `{loc(1): ".i", loc(2): ".j"}`.
///
/// It does not produce designators for any explicitly-written nested lists,
/// e.g. `{1, .j=2}` would only return `{loc(1): ".i"}`.
///
/// It also considers structs with fields of record types like
/// `struct T { S s; };`. In this case, there would be designators of the
/// form `.s.i` and `.s.j` in the returned map.
llvm::DenseMap<clang::SourceLocation, std::string>
getUnwrittenDesignators(const clang::InitListExpr *Syn);

} // namespace clang::tidy::utils
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ target_link_libraries(clangDaemon
clangIncludeCleaner
clangPseudo
clangTidy
clangTidyUtils

clangdSupport
)
Expand Down
170 changes: 4 additions & 166 deletions clang-tools-extra/clangd/InlayHints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
#include "InlayHints.h"
#include "../clang-tidy/utils/DesignatedInitializers.h"
#include "AST.h"
#include "Config.h"
#include "HeuristicResolver.h"
Expand All @@ -24,7 +25,6 @@
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
Expand All @@ -42,169 +42,6 @@ namespace {
// For now, inlay hints are always anchored at the left or right of their range.
enum class HintSide { Left, Right };

// Helper class to iterate over the designator names of an aggregate type.
//
// For an array type, yields [0], [1], [2]...
// For aggregate classes, yields null for each base, then .field1, .field2, ...
class AggregateDesignatorNames {
public:
AggregateDesignatorNames(QualType T) {
if (!T.isNull()) {
T = T.getCanonicalType();
if (T->isArrayType()) {
IsArray = true;
Valid = true;
return;
}
if (const RecordDecl *RD = T->getAsRecordDecl()) {
Valid = true;
FieldsIt = RD->field_begin();
FieldsEnd = RD->field_end();
if (const auto *CRD = llvm::dyn_cast<CXXRecordDecl>(RD)) {
BasesIt = CRD->bases_begin();
BasesEnd = CRD->bases_end();
Valid = CRD->isAggregate();
}
OneField = Valid && BasesIt == BasesEnd && FieldsIt != FieldsEnd &&
std::next(FieldsIt) == FieldsEnd;
}
}
}
// Returns false if the type was not an aggregate.
operator bool() { return Valid; }
// Advance to the next element in the aggregate.
void next() {
if (IsArray)
++Index;
else if (BasesIt != BasesEnd)
++BasesIt;
else if (FieldsIt != FieldsEnd)
++FieldsIt;
}
// Print the designator to Out.
// Returns false if we could not produce a designator for this element.
bool append(std::string &Out, bool ForSubobject) {
if (IsArray) {
Out.push_back('[');
Out.append(std::to_string(Index));
Out.push_back(']');
return true;
}
if (BasesIt != BasesEnd)
return false; // Bases can't be designated. Should we make one up?
if (FieldsIt != FieldsEnd) {
llvm::StringRef FieldName;
if (const IdentifierInfo *II = FieldsIt->getIdentifier())
FieldName = II->getName();

// For certain objects, their subobjects may be named directly.
if (ForSubobject &&
(FieldsIt->isAnonymousStructOrUnion() ||
// std::array<int,3> x = {1,2,3}. Designators not strictly valid!
(OneField && isReservedName(FieldName))))
return true;

if (!FieldName.empty() && !isReservedName(FieldName)) {
Out.push_back('.');
Out.append(FieldName.begin(), FieldName.end());
return true;
}
return false;
}
return false;
}

private:
bool Valid = false;
bool IsArray = false;
bool OneField = false; // e.g. std::array { T __elements[N]; }
unsigned Index = 0;
CXXRecordDecl::base_class_const_iterator BasesIt;
CXXRecordDecl::base_class_const_iterator BasesEnd;
RecordDecl::field_iterator FieldsIt;
RecordDecl::field_iterator FieldsEnd;
};

// Collect designator labels describing the elements of an init list.
//
// This function contributes the designators of some (sub)object, which is
// represented by the semantic InitListExpr Sem.
// This includes any nested subobjects, but *only* if they are part of the same
// original syntactic init list (due to brace elision).
// In other words, it may descend into subobjects but not written init-lists.
//
// For example: struct Outer { Inner a,b; }; struct Inner { int x, y; }
// Outer o{{1, 2}, 3};
// This function will be called with Sem = { {1, 2}, {3, ImplicitValue} }
// It should generate designators '.a:' and '.b.x:'.
// '.a:' is produced directly without recursing into the written sublist.
// (The written sublist will have a separate collectDesignators() call later).
// Recursion with Prefix='.b' and Sem = {3, ImplicitValue} produces '.b.x:'.
void collectDesignators(const InitListExpr *Sem,
llvm::DenseMap<SourceLocation, std::string> &Out,
const llvm::DenseSet<SourceLocation> &NestedBraces,
std::string &Prefix) {
if (!Sem || Sem->isTransparent())
return;
assert(Sem->isSemanticForm());

// The elements of the semantic form all correspond to direct subobjects of
// the aggregate type. `Fields` iterates over these subobject names.
AggregateDesignatorNames Fields(Sem->getType());
if (!Fields)
return;
for (const Expr *Init : Sem->inits()) {
auto Next = llvm::make_scope_exit([&, Size(Prefix.size())] {
Fields.next(); // Always advance to the next subobject name.
Prefix.resize(Size); // Erase any designator we appended.
});
// Skip for a broken initializer or if it is a "hole" in a subobject that
// was not explicitly initialized.
if (!Init || llvm::isa<ImplicitValueInitExpr>(Init))
continue;

const auto *BraceElidedSubobject = llvm::dyn_cast<InitListExpr>(Init);
if (BraceElidedSubobject &&
NestedBraces.contains(BraceElidedSubobject->getLBraceLoc()))
BraceElidedSubobject = nullptr; // there were braces!

if (!Fields.append(Prefix, BraceElidedSubobject != nullptr))
continue; // no designator available for this subobject
if (BraceElidedSubobject) {
// If the braces were elided, this aggregate subobject is initialized
// inline in the same syntactic list.
// Descend into the semantic list describing the subobject.
// (NestedBraces are still correct, they're from the same syntactic list).
collectDesignators(BraceElidedSubobject, Out, NestedBraces, Prefix);
continue;
}
Out.try_emplace(Init->getBeginLoc(), Prefix);
}
}

// Get designators describing the elements of a (syntactic) init list.
// This does not produce designators for any explicitly-written nested lists.
llvm::DenseMap<SourceLocation, std::string>
getDesignators(const InitListExpr *Syn) {
assert(Syn->isSyntacticForm());

// collectDesignators needs to know which InitListExprs in the semantic tree
// were actually written, but InitListExpr::isExplicit() lies.
// Instead, record where braces of sub-init-lists occur in the syntactic form.
llvm::DenseSet<SourceLocation> NestedBraces;
for (const Expr *Init : Syn->inits())
if (auto *Nested = llvm::dyn_cast<InitListExpr>(Init))
NestedBraces.insert(Nested->getLBraceLoc());

// Traverse the semantic form to find the designators.
// We use their SourceLocation to correlate with the syntactic form later.
llvm::DenseMap<SourceLocation, std::string> Designators;
std::string EmptyPrefix;
collectDesignators(Syn->isSemanticForm() ? Syn : Syn->getSemanticForm(),
Designators, NestedBraces, EmptyPrefix);
return Designators;
}

void stripLeadingUnderscores(StringRef &Name) { Name = Name.ltrim('_'); }

// getDeclForType() returns the decl responsible for Type's spelling.
Expand Down Expand Up @@ -847,14 +684,15 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
// This is the one we will ultimately attach designators to.
// It may have subobject initializers inlined without braces. The *semantic*
// form of the init-list has nested init-lists for these.
// getDesignators will look at the semantic form to determine the labels.
// getUnwrittenDesignators will look at the semantic form to determine the
// labels.
assert(Syn->isSyntacticForm() && "RAV should not visit implicit code!");
if (!Cfg.InlayHints.Designators)
return true;
if (Syn->isIdiomaticZeroInitializer(AST.getLangOpts()))
return true;
llvm::DenseMap<SourceLocation, std::string> Designators =
getDesignators(Syn);
tidy::utils::getUnwrittenDesignators(Syn);
for (const Expr *Init : Syn->inits()) {
if (llvm::isa<DesignatedInitExpr>(Init))
continue;
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/tool/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ clang_target_link_libraries(clangdMain
target_link_libraries(clangdMain
PRIVATE
clangTidy
clangTidyUtils

clangDaemon
clangdRemoteIndex
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ target_link_libraries(ClangdTests
clangIncludeCleaner
clangTesting
clangTidy
clangTidyUtils
clangdSupport
)

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

- New :doc:`modernize-use-designated-initializers
<clang-tidy/checks/modernize/use-designated-initializers>` check.

Finds initializer lists for aggregate types that could be
written as designated initializers instead.

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

Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ Clang-Tidy Checks
:doc:`modernize-use-bool-literals <modernize/use-bool-literals>`, "Yes"
:doc:`modernize-use-constraints <modernize/use-constraints>`, "Yes"
:doc:`modernize-use-default-member-init <modernize/use-default-member-init>`, "Yes"
:doc:`modernize-use-designated-initializers <modernize/use-designated-initializers>`, "Yes"
:doc:`modernize-use-emplace <modernize/use-emplace>`, "Yes"
:doc:`modernize-use-equals-default <modernize/use-equals-default>`, "Yes"
:doc:`modernize-use-equals-delete <modernize/use-equals-delete>`, "Yes"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
.. title:: clang-tidy - modernize-use-designated-initializers

modernize-use-designated-initializers
=====================================

Finds initializer lists for aggregate types which could be written as designated
initializers instead.

With plain initializer lists, it is very easy to introduce bugs when adding new
fields in the middle of a struct or class type. The same confusion might arise
when changing the order of fields.

C++20 supports the designated initializer syntax for aggregate types. By
applying it, we can always be sure that aggregates are constructed correctly,
because every variable being initialized is referenced by its name.

Example:

.. code-block::
struct S { int i, j; };
is an aggregate type that should be initialized as

.. code-block::
S s{.i = 1, .j = 2};
instead of

.. code-block::
S s{1, 2};
which could easily become an issue when ``i`` and ``j`` are swapped in the
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
``-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.

Options
-------

.. option:: IgnoreMacros

The value `false` specifies that components of initializer lists expanded from
macros are not checked. The default value is `true`.

.. option:: IgnoreSingleElementAggregates

The value `false` specifies that even initializers for aggregate types with
only a single element should be checked. The default value is `true`.

.. option:: RestrictToPODTypes

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`.
5 changes: 5 additions & 0 deletions clang-tools-extra/include-cleaner/lib/WalkAST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,11 @@ class ASTWalker : public RecursiveASTVisitor<ASTWalker> {
// Mark declaration from definition as it needs type-checking.
if (FD->isThisDeclarationADefinition())
report(FD->getLocation(), FD);
// Explicit specializaiton/instantiations of a function template requires
// primary template.
if (clang::isTemplateExplicitInstantiationOrSpecialization(
FD->getTemplateSpecializationKind()))
report(FD->getLocation(), FD->getPrimaryTemplate());
return true;
}
bool VisitVarDecl(VarDecl *VD) {
Expand Down
10 changes: 4 additions & 6 deletions clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,13 +229,9 @@ TEST(WalkAST, FunctionTemplates) {
EXPECT_THAT(testWalk("template<typename T> void foo(T) {}",
"template void ^foo<int>(int);"),
ElementsAre());
// FIXME: Report specialized template as used from explicit specializations.
EXPECT_THAT(testWalk("template<typename T> void foo(T);",
EXPECT_THAT(testWalk("template<typename T> void $explicit^foo(T);",
"template<> void ^foo<int>(int);"),
ElementsAre());
EXPECT_THAT(testWalk("template<typename T> void foo(T) {}",
"template<typename T> void ^foo(T*) {}"),
ElementsAre());
ElementsAre(Decl::FunctionTemplate));

// Implicit instantiations references most relevant template.
EXPECT_THAT(testWalk(R"cpp(
Expand Down Expand Up @@ -510,6 +506,8 @@ TEST(WalkAST, Functions) {
// Definition uses declaration, not the other way around.
testWalk("void $explicit^foo();", "void ^foo() {}");
testWalk("void foo() {}", "void ^foo();");
testWalk("template <typename> void $explicit^foo();",
"template <typename> void ^foo() {}");

// Unresolved calls marks all the overloads.
testWalk("void $ambiguous^foo(int); void $ambiguous^foo(char);",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
// RUN: %check_clang_tidy -std=c++17 %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: -- -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: -- -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: -- -config="{CheckOptions: {modernize-use-designated-initializers.IgnoreMacros: false}}" \
// RUN: -- -fno-delayed-template-parsing

struct S1 {};

S1 s11{};
S1 s12 = {};
S1 s13();
S1 s14;

struct S2 { int i, j; };

S2 s21{.i=1, .j =2};

S2 s22 = {1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use designated initializer list to initialize 'S2' [modernize-use-designated-initializers]
// CHECK-MESSAGES: :[[@LINE-6]]:1: note: aggregate type is defined here
// CHECK-MESSAGES-POD: :[[@LINE-3]]:10: warning: use designated initializer list to initialize 'S2' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-8]]:1: note: aggregate type is defined here
// CHECK-FIXES: S2 s22 = {.i=1, .j=2};

S2 s23{1};
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use designated initializer list to initialize 'S2' [modernize-use-designated-initializers]
// CHECK-MESSAGES: :[[@LINE-13]]:1: note: aggregate type is defined here
// CHECK-MESSAGES-POD: :[[@LINE-3]]:7: warning: use designated initializer list to initialize 'S2' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-15]]:1: note: aggregate type is defined here
// CHECK-FIXES: S2 s23{.i=1};

S2 s24{.i = 1};

S2 s25 = {.i=1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use designated init expression to initialize field 'j' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-2]]:17: warning: use designated init expression to initialize field 'j' [modernize-use-designated-initializers]
// CHECK-FIXES: S2 s25 = {.i=1, .j=2};

class S3 {
public:
S2 s2;
double d;
};

S3 s31 = {.s2 = 1, 2, 3.1};
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use designated init expression to initialize field 's2.j' [modernize-use-designated-initializers]
// CHECK-MESSAGES: :[[@LINE-2]]:23: warning: use designated init expression to initialize field 'd' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-3]]:20: warning: use designated init expression to initialize field 's2.j' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-4]]:23: warning: use designated init expression to initialize field 'd' [modernize-use-designated-initializers]
// CHECK-FIXES: S3 s31 = {.s2 = 1, .s2.j=2, .d=3.1};

S3 s32 = {{.i = 1, 2}};
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use designated initializer list to initialize 'S3' [modernize-use-designated-initializers]
// CHECK-MESSAGES: :[[@LINE-15]]:1: note: aggregate type is defined here
// CHECK-MESSAGES: :[[@LINE-3]]:20: warning: use designated init expression to initialize field 'j' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-4]]:10: warning: use designated initializer list to initialize 'S3' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-18]]:1: note: aggregate type is defined here
// CHECK-MESSAGES-POD: :[[@LINE-6]]:20: warning: use designated init expression to initialize field 'j' [modernize-use-designated-initializers]
// CHECK-FIXES: S3 s32 = {.s2={.i = 1, .j=2}};

S3 s33 = {{2}, .d=3.1};
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use designated init expression to initialize field 's2' [modernize-use-designated-initializers]
// CHECK-MESSAGES: :[[@LINE-2]]:11: warning: use designated initializer list to initialize 'S2' [modernize-use-designated-initializers]
// CHECK-MESSAGES: :[[@LINE-50]]:1: note: aggregate type is defined here
// CHECK-MESSAGES-POD: :[[@LINE-4]]:11: warning: use designated init expression to initialize field 's2' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-5]]:11: warning: use designated initializer list to initialize 'S2' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-53]]:1: note: aggregate type is defined here
// CHECK-FIXES: S3 s33 = {.s2={.i=2}, .d=3.1};

struct S4 {
double d;
private: static int i;
};

S4 s41 {2.2};
// CHECK-MESSAGES-SINGLE-ELEMENT: :[[@LINE-1]]:8: warning: use designated initializer list to initialize 'S4' [modernize-use-designated-initializers]
// CHECK-MESSAGES-SINGLE-ELEMENT: :[[@LINE-7]]:1: note: aggregate type is defined here
// CHECK-FIXES-SINGLE-ELEMENT: S4 s41 {.d=2.2};

S4 s42 = {{}};
// CHECK-MESSAGES-SINGLE-ELEMENT: :[[@LINE-1]]:10: warning: use designated initializer list to initialize 'S4' [modernize-use-designated-initializers]
// CHECK-MESSAGES-SINGLE-ELEMENT: :[[@LINE-12]]:1: note: aggregate type is defined here
// CHECK-FIXES-SINGLE-ELEMENT: S4 s42 = {.d={}};

template<typename S> S template1() { return {10, 11}; }

S2 s26 = template1<S2>();

template<typename S> S template2() { return {}; }

S2 s27 = template2<S2>();

struct S5: S2 { int x, y; };

S5 s51 {1, 2, .x = 3, .y = 4};

struct S6 {
int i;
struct { int j; } s;
};

S6 s61 {1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use designated initializer list to initialize 'S6' [modernize-use-designated-initializers]
// CHECK-MESSAGES: :[[@LINE-7]]:1: note: aggregate type is defined here
// CHECK-MESSAGES-POD: :[[@LINE-3]]:8: warning: use designated initializer list to initialize 'S6' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-9]]:1: note: aggregate type is defined here
// CHECK-FIXES: S6 s61 {.i=1, .s.j=2};

struct S7 {
union {
int k;
double d;
} u;
};

S7 s71 {1};
// CHECK-MESSAGES-SINGLE-ELEMENT: :[[@LINE-1]]:8: warning: use designated initializer list to initialize 'S7' [modernize-use-designated-initializers]
// CHECK-MESSAGES-SINGLE-ELEMENT: :[[@LINE-9]]:1: note: aggregate type is defined here
// CHECK-FIXES-SINGLE-ELEMENT: S7 s71 {.u.k=1};

struct S8: S7 { int i; };

S8 s81{1, 2};

struct S9 {
int i, j;
S9 &operator=(S9);
};

S9 s91{1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use designated initializer list to initialize 'S9' [modernize-use-designated-initializers]
// CHECK-MESSAGES: :[[@LINE-7]]:1: note: aggregate type is defined here
// CHECK-FIXES: S9 s91{.i=1, .j=2};

struct S10 { int i = 1, j = 2; };

S10 s101 {1, .j=2};
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use designated init expression to initialize field 'i' [modernize-use-designated-initializers]
// CHECK-FIXES: S10 s101 {.i=1, .j=2};

struct S11 { int i; S10 s10; };

S11 s111 { .i = 1 };
S11 s112 { 1 };
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use designated initializer list to initialize 'S11' [modernize-use-designated-initializers]
// CHECK-MESSAGES: :[[@LINE-5]]:1: note: aggregate type is defined here
// CHECK-FIXES: S11 s112 { .i=1 };

S11 s113 { .i=1, {}};
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use designated init expression to initialize field 's10' [modernize-use-designated-initializers]
// CHECK-FIXES: S11 s113 { .i=1, .s10={}};

S11 s114 { .i=1, .s10={1, .j=2}};
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use designated init expression to initialize field 'i' [modernize-use-designated-initializers]
// CHECK-FIXES: S11 s114 { .i=1, .s10={.i=1, .j=2}};

struct S12 {
int i;
struct { int j; };
};

S12 s121 {1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use designated initializer list to initialize 'S12' [modernize-use-designated-initializers]
// CHECK-MESSAGES: :[[@LINE-7]]:1: note: aggregate type is defined here
// CHECK-MESSAGES-POD: :[[@LINE-3]]:10: warning: use designated initializer list to initialize 'S12' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-9]]:1: note: aggregate type is defined here
// CHECK-FIXES: S12 s121 {.i=1, .j=2};

struct S13 {
union {
int k;
double d;
};
int i;
};

S13 s131 {1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use designated initializer list to initialize 'S13' [modernize-use-designated-initializers]
// CHECK-MESSAGES: :[[@LINE-10]]:1: note: aggregate type is defined here
// CHECK-MESSAGES-POD: :[[@LINE-3]]:10: warning: use designated initializer list to initialize 'S13' [modernize-use-designated-initializers]
// CHECK-MESSAGES-POD: :[[@LINE-12]]:1: note: aggregate type is defined here
// CHECK-FIXES: S13 s131 {.k=1, .i=2};

#define A (3+2)
#define B .j=1

S9 s92 {A, B};
// CHECK-MESSAGES-MACROS: :[[@LINE-1]]:9: warning: use designated init expression to initialize field 'i' [modernize-use-designated-initializers]
// CHECK-MESSAGES-MACROS: :[[@LINE-5]]:11: note: expanded from macro 'A'

#define DECLARE_S93 S9 s93 {1, 2}

DECLARE_S93;
// CHECK-MESSAGES-MACROS: :[[@LINE-1]]:1: warning: use designated initializer list to initialize 'S9' [modernize-use-designated-initializers]
// CHECK-MESSAGES-MACROS: :[[@LINE-4]]:28: note: expanded from macro 'DECLARE_S93'
// CHECK-MESSAGES-MACROS: :[[@LINE-71]]:1: note: aggregate type is defined here
8 changes: 4 additions & 4 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3477,7 +3477,7 @@ builtin, the mangler emits their usual pattern without any special treatment.
-----------------------
``__builtin_popcountg`` returns the number of 1 bits in the argument. The
argument can be of any integer type.
argument can be of any unsigned integer type.
**Syntax**:
Expand All @@ -3489,20 +3489,20 @@ argument can be of any integer type.
.. code-block:: c++
int x = 1;
unsigned int x = 1;
int x_pop = __builtin_popcountg(x);
unsigned long y = 3;
int y_pop = __builtin_popcountg(y);
_BitInt(128) z = 7;
unsigned _BitInt(128) z = 7;
int z_pop = __builtin_popcountg(z);
**Description**:
``__builtin_popcountg`` is meant to be a type-generic alternative to the
``__builtin_popcount{,l,ll}`` builtins, with support for other integer types,
such as ``__int128`` and C23 ``_BitInt(N)``.
such as ``unsigned __int128`` and C23 ``unsigned _BitInt(N)``.
Multiprecision Arithmetic Builtins
----------------------------------
Expand Down
14 changes: 7 additions & 7 deletions clang/docs/LibASTMatchersReference.html
Original file line number Diff line number Diff line change
Expand Up @@ -7049,7 +7049,7 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
</pre></td></tr>


<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXFoldExpr.html">CXXFoldExpr</a>&gt;</td><td class="name" onclick="toggle('hasFoldInit0')"><a name="hasFoldInit0Anchor">hasFoldInit</a></td><td>ast_matchers::Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; InnerMacher</td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXFoldExpr.html">CXXFoldExpr</a>&gt;</td><td class="name" onclick="toggle('hasFoldInit0')"><a name="hasFoldInit0Anchor">hasFoldInit</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; InnerMacher</td></tr>
<tr><td colspan="4" class="doc" id="hasFoldInit0"><pre>Matches the operand that does not contain the parameter pack.

Example matches `(0 + ... + args)` and `(args * ... * 1)`
Expand Down Expand Up @@ -7089,7 +7089,7 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
</pre></td></tr>


<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXFoldExpr.html">CXXFoldExpr</a>&gt;</td><td class="name" onclick="toggle('hasPattern0')"><a name="hasPattern0Anchor">hasPattern</a></td><td>ast_matchers::Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; InnerMacher</td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXFoldExpr.html">CXXFoldExpr</a>&gt;</td><td class="name" onclick="toggle('hasPattern0')"><a name="hasPattern0Anchor">hasPattern</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; InnerMacher</td></tr>
<tr><td colspan="4" class="doc" id="hasPattern0"><pre>Matches the operand that contains the parameter pack.

Example matches `(0 + ... + args)`
Expand Down Expand Up @@ -7859,7 +7859,7 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
</pre></td></tr>


<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ClassTemplateSpecializationDecl.html">ClassTemplateSpecializationDecl</a>&gt;</td><td class="name" onclick="toggle('forEachTemplateArgument0')"><a name="forEachTemplateArgument0Anchor">forEachTemplateArgument</a></td><td>clang::ast_matchers::Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1TemplateArgument.html">TemplateArgument</a>&gt; InnerMatcher</td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ClassTemplateSpecializationDecl.html">ClassTemplateSpecializationDecl</a>&gt;</td><td class="name" onclick="toggle('forEachTemplateArgument0')"><a name="forEachTemplateArgument0Anchor">forEachTemplateArgument</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1TemplateArgument.html">TemplateArgument</a>&gt; InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="forEachTemplateArgument0"><pre>Matches classTemplateSpecialization, templateSpecializationType and
functionDecl nodes where the template argument matches the inner matcher.
This matcher may produce multiple matches.
Expand Down Expand Up @@ -8454,7 +8454,7 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
</pre></td></tr>


<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt;</td><td class="name" onclick="toggle('ignoringElidableConstructorCall0')"><a name="ignoringElidableConstructorCall0Anchor">ignoringElidableConstructorCall</a></td><td>ast_matchers::Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; InnerMatcher</td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt;</td><td class="name" onclick="toggle('ignoringElidableConstructorCall0')"><a name="ignoringElidableConstructorCall0Anchor">ignoringElidableConstructorCall</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="ignoringElidableConstructorCall0"><pre>Matches expressions that match InnerMatcher that are possibly wrapped in an
elidable constructor and other corresponding bookkeeping nodes.

Expand Down Expand Up @@ -8691,7 +8691,7 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
</pre></td></tr>


<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html">FunctionDecl</a>&gt;</td><td class="name" onclick="toggle('forEachTemplateArgument2')"><a name="forEachTemplateArgument2Anchor">forEachTemplateArgument</a></td><td>clang::ast_matchers::Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1TemplateArgument.html">TemplateArgument</a>&gt; InnerMatcher</td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html">FunctionDecl</a>&gt;</td><td class="name" onclick="toggle('forEachTemplateArgument2')"><a name="forEachTemplateArgument2Anchor">forEachTemplateArgument</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1TemplateArgument.html">TemplateArgument</a>&gt; InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="forEachTemplateArgument2"><pre>Matches classTemplateSpecialization, templateSpecializationType and
functionDecl nodes where the template argument matches the inner matcher.
This matcher may produce multiple matches.
Expand Down Expand Up @@ -8959,7 +8959,7 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
</pre></td></tr>


<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1InitListExpr.html">InitListExpr</a>&gt;</td><td class="name" onclick="toggle('hasInit0')"><a name="hasInit0Anchor">hasInit</a></td><td>unsigned N, ast_matchers::Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; InnerMatcher</td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1InitListExpr.html">InitListExpr</a>&gt;</td><td class="name" onclick="toggle('hasInit0')"><a name="hasInit0Anchor">hasInit</a></td><td>unsigned N, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="hasInit0"><pre>Matches the n'th item of an initializer list expression.

Example matches y.
Expand Down Expand Up @@ -10026,7 +10026,7 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
</pre></td></tr>


<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1TemplateSpecializationType.html">TemplateSpecializationType</a>&gt;</td><td class="name" onclick="toggle('forEachTemplateArgument1')"><a name="forEachTemplateArgument1Anchor">forEachTemplateArgument</a></td><td>clang::ast_matchers::Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1TemplateArgument.html">TemplateArgument</a>&gt; InnerMatcher</td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1TemplateSpecializationType.html">TemplateSpecializationType</a>&gt;</td><td class="name" onclick="toggle('forEachTemplateArgument1')"><a name="forEachTemplateArgument1Anchor">forEachTemplateArgument</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1TemplateArgument.html">TemplateArgument</a>&gt; InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="forEachTemplateArgument1"><pre>Matches classTemplateSpecialization, templateSpecializationType and
functionDecl nodes where the template argument matches the inner matcher.
This matcher may produce multiple matches.
Expand Down
28 changes: 28 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@ Improvements to Clang's diagnostics

- Clang now diagnoses declarative nested name specifiers that name alias templates.

- Clang now diagnoses lambda function expressions being implicitly cast to boolean values, under ``-Wpointer-bool-conversion``.
Fixes `#82512 <https://github.com/llvm/llvm-project/issues/82512>`_.

Improvements to Clang's time-trace
----------------------------------

Expand All @@ -215,6 +218,10 @@ Bug Fixes in This Version
for logical operators in C23.
Fixes (`#64356 <https://github.com/llvm/llvm-project/issues/64356>`_).

- Clang no longer produces a false-positive `-Wunused-variable` warning
for variables created through copy initialization having side-effects in C++17 and later.
Fixes (`#79518 <https://github.com/llvm/llvm-project/issues/79518>`_).

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

Expand Down Expand Up @@ -288,6 +295,22 @@ Bug Fixes to C++ Support
templates when determining the primary template of an explicit specialization.
- Fixed a crash in Microsoft compatibility mode where unqualified dependent base class
lookup searches the bases of an incomplete class.
- Fix a crash when an unresolved overload set is encountered on the RHS of a ``.*`` operator.
(`#53815 <https://github.com/llvm/llvm-project/issues/53815>`_)
- In ``__restrict``-qualified member functions, attach ``__restrict`` to the pointer type of
``this`` rather than the pointee type.
Fixes (`#82941 <https://github.com/llvm/llvm-project/issues/82941>`_),
(`#42411 <https://github.com/llvm/llvm-project/issues/42411>`_), and
(`#18121 <https://github.com/llvm/llvm-project/issues/18121>`_).
- Clang now properly reports supported C++11 attributes when using
``__has_cpp_attribute`` and parses attributes with arguments in C++03
(`#82995 <https://github.com/llvm/llvm-project/issues/82995>`_)
- Clang now properly diagnoses missing 'default' template arguments on a variety
of templates. Previously we were diagnosing on any non-function template
instead of only on class, alias, and variable templates, as last updated by
CWG2032.
Fixes (`#83461 <https://github.com/llvm/llvm-project/issues/83461>`_)


Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -323,6 +346,11 @@ Arm and AArch64 Support
improvements for most targets. We have not changed the default behavior for
ARMv6, but may revisit that decision in the future. Users can restore the old
behavior with -m[no-]unaligned-access.
- An alias identifier (rdma) has been added for targeting the AArch64
Architecture Extension which uses Rounding Doubling Multiply Accumulate
instructions (rdm). The identifier is available on the command line as
a feature modifier for -march and -mcpu as well as via target attributes
like ``target_version`` or ``target_clones``.

Android Support
^^^^^^^^^^^^^^^
Expand Down
2 changes: 2 additions & 0 deletions clang/docs/tools/dump_ast_matchers.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ def strip_doxygen(comment):

def unify_arguments(args):
"""Gets rid of anything the user doesn't care about in the argument list."""
args = re.sub(r"clang::ast_matchers::internal::", r"", args)
args = re.sub(r"ast_matchers::internal::", r"", args)
args = re.sub(r"internal::", r"", args)
args = re.sub(r"extern const\s+(.*)&", r"\1 ", args)
args = re.sub(r"&", r" ", args)
Expand Down
13 changes: 12 additions & 1 deletion clang/include/clang/APINotes/APINotesWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

//
// This file defines the \c APINotesWriter class that writes out source
// API notes data providing additional information about source code as
// a separate input, such as the non-nil/nilable annotations for
// method parameters.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_APINOTES_WRITER_H
#define LLVM_CLANG_APINOTES_WRITER_H

Expand All @@ -20,11 +26,16 @@ namespace clang {
class FileEntry;

namespace api_notes {

/// A class that writes API notes data to a binary representation that can be
/// read by the \c APINotesReader.
class APINotesWriter {
class Implementation;
std::unique_ptr<Implementation> Implementation;

public:
/// Create a new API notes writer with the given module name and
/// (optional) source file.
APINotesWriter(llvm::StringRef ModuleName, const FileEntry *SF);
~APINotesWriter();

Expand Down
16 changes: 7 additions & 9 deletions clang/include/clang/ASTMatchers/ASTMatchers.h
Original file line number Diff line number Diff line change
Expand Up @@ -4580,8 +4580,7 @@ AST_POLYMORPHIC_MATCHER_P2(hasArgument,
/// return (args * ... * 1);
/// }
/// \endcode
AST_MATCHER_P(CXXFoldExpr, hasFoldInit, ast_matchers::internal::Matcher<Expr>,
InnerMacher) {
AST_MATCHER_P(CXXFoldExpr, hasFoldInit, internal::Matcher<Expr>, InnerMacher) {
const auto *const Init = Node.getInit();
return Init && InnerMacher.matches(*Init, Finder, Builder);
}
Expand All @@ -4603,8 +4602,7 @@ AST_MATCHER_P(CXXFoldExpr, hasFoldInit, ast_matchers::internal::Matcher<Expr>,
/// return (args * ... * 1);
/// }
/// \endcode
AST_MATCHER_P(CXXFoldExpr, hasPattern, ast_matchers::internal::Matcher<Expr>,
InnerMacher) {
AST_MATCHER_P(CXXFoldExpr, hasPattern, internal::Matcher<Expr>, InnerMacher) {
const Expr *const Pattern = Node.getPattern();
return Pattern && InnerMacher.matches(*Pattern, Finder, Builder);
}
Expand Down Expand Up @@ -4685,8 +4683,8 @@ AST_MATCHER(CXXFoldExpr, isBinaryFold) { return Node.getInit() != nullptr; }
/// \code
/// int x{y}.
/// \endcode
AST_MATCHER_P2(InitListExpr, hasInit, unsigned, N,
ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
AST_MATCHER_P2(InitListExpr, hasInit, unsigned, N, internal::Matcher<Expr>,
InnerMatcher) {
return N < Node.getNumInits() &&
InnerMatcher.matches(*Node.getInit(N), Finder, Builder);
}
Expand Down Expand Up @@ -5309,7 +5307,7 @@ AST_POLYMORPHIC_MATCHER_P(
forEachTemplateArgument,
AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl,
TemplateSpecializationType, FunctionDecl),
clang::ast_matchers::internal::Matcher<TemplateArgument>, InnerMatcher) {
internal::Matcher<TemplateArgument>, InnerMatcher) {
ArrayRef<TemplateArgument> TemplateArgs =
clang::ast_matchers::internal::getTemplateSpecializationArgs(Node);
clang::ast_matchers::internal::BoundNodesTreeBuilder Result;
Expand Down Expand Up @@ -8525,8 +8523,8 @@ AST_MATCHER(FunctionDecl, hasTrailingReturn) {
///
/// ``varDecl(hasInitializer(ignoringElidableConstructorCall(callExpr())))``
/// matches ``H D = G()`` in C++11 through C++17 (and beyond).
AST_MATCHER_P(Expr, ignoringElidableConstructorCall,
ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
AST_MATCHER_P(Expr, ignoringElidableConstructorCall, internal::Matcher<Expr>,
InnerMatcher) {
// E tracks the node that we are examining.
const Expr *E = &Node;
// If present, remove an outer `ExprWithCleanups` corresponding to the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -723,9 +723,12 @@ RecordStorageLocation *getImplicitObjectLocation(const CXXMemberCallExpr &MCE,
RecordStorageLocation *getBaseObjectLocation(const MemberExpr &ME,
const Environment &Env);

/// Returns the fields of `RD` that are initialized by an `InitListExpr`, in the
/// order in which they appear in `InitListExpr::inits()`.
std::vector<FieldDecl *> getFieldsForInitListExpr(const RecordDecl *RD);
/// Returns the fields of a `RecordDecl` that are initialized by an
/// `InitListExpr`, in the order in which they appear in
/// `InitListExpr::inits()`.
/// `Init->getType()` must be a record type.
std::vector<const FieldDecl *>
getFieldsForInitListExpr(const InitListExpr *InitList);

/// Associates a new `RecordValue` with `Loc` and returns the new value.
RecordValue &refreshRecordValue(RecordStorageLocation &Loc, Environment &Env);
Expand Down
14 changes: 13 additions & 1 deletion clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,7 @@ def Popcount : Builtin, BitInt_Long_LongLongTemplate {

def Popcountg : Builtin {
let Spellings = ["__builtin_popcountg"];
let Attributes = [NoThrow, Const];
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Prototype = "int(...)";
}

Expand Down Expand Up @@ -4536,6 +4536,18 @@ def HLSLDotProduct : LangBuiltin<"HLSL_LANG"> {
let Prototype = "void(...)";
}

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

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

// Builtins for XRay.
def XRayCustomEvent : Builtin {
let Spellings = ["__xray_customevent"];
Expand Down
1 change: 0 additions & 1 deletion clang/include/clang/Basic/DiagnosticDriverKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,6 @@ def err_drv_cannot_mix_options : Error<"cannot specify '%1' along with '%0'">;
def err_drv_invalid_object_mode : Error<
"OBJECT_MODE setting %0 is not recognized and is not a valid setting">;

def err_aix_unsupported_tls_model : Error<"TLS model '%0' is not yet supported on AIX">;
def err_roptr_requires_data_sections: Error<"-mxcoff-roptr is supported only with -fdata-sections">;
def err_roptr_cannot_build_shared: Error<"-mxcoff-roptr is not supported with -shared">;

Expand Down
20 changes: 12 additions & 8 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -765,7 +765,7 @@ def err_builtin_redeclare : Error<"cannot redeclare builtin function %0">;
def err_arm_invalid_specialreg : Error<"invalid special register for builtin">;
def err_arm_invalid_coproc : Error<"coprocessor %0 must be configured as "
"%select{GCP|CDE}1">;
def err_invalid_cpu_supports : Error<"invalid cpu feature string for builtin">;
def warn_invalid_cpu_supports : Warning<"invalid cpu feature string for builtin">;
def err_invalid_cpu_is : Error<"invalid cpu name for builtin">;
def err_invalid_cpu_specific_dispatch_value : Error<
"invalid option '%0' for %select{cpu_specific|cpu_dispatch}1">;
Expand Down Expand Up @@ -4127,8 +4127,8 @@ def ext_ms_impcast_fn_obj : ExtWarn<
"Microsoft extension">, InGroup<MicrosoftCast>;

def warn_impcast_pointer_to_bool : Warning<
"address of%select{| function| array}0 '%1' will always evaluate to "
"'true'">,
"address of %select{'%1'|function '%1'|array '%1'|lambda function pointer "
"conversion operator}0 will always evaluate to 'true'">,
InGroup<PointerBoolConversion>;
def warn_cast_nonnull_to_bool : Warning<
"nonnull %select{function call|parameter}0 '%1' will evaluate to "
Expand Down Expand Up @@ -10267,9 +10267,9 @@ def err_sizeless_nonlocal : Error<
"non-local variable with sizeless type %0">;

def err_vec_builtin_non_vector : Error<
"first two arguments to %0 must be vectors">;
"%select{first two|all}1 arguments to %0 must be vectors">;
def err_vec_builtin_incompatible_vector : Error<
"first two arguments to %0 must have the same type">;
"%select{first two|all}1 arguments to %0 must have the same type">;
def err_vsx_builtin_nonconstant_argument : Error<
"argument %0 to %1 must be a 2-bit unsigned literal (i.e. 0, 1, 2 or 3)">;

Expand Down Expand Up @@ -11984,7 +11984,7 @@ def err_builtin_invalid_arg_type: Error <
"signed integer or floating point type|vector type|"
"floating point type|"
"vector of integers|"
"type of integer}1 (was %2)">;
"type of unsigned integer}1 (was %2)">;

def err_builtin_matrix_disabled: Error<
"matrix types extension is disabled. Pass -fenable-matrix to enable it">;
Expand Down Expand Up @@ -12212,6 +12212,10 @@ def err_acc_construct_appertainment
: Error<"OpenACC construct '%0' cannot be used here; it can only "
"be used in a statement context">;
def err_acc_branch_in_out_compute_construct
: Error<"invalid %select{branch|return}0 %select{out of|into}1 OpenACC "
"Compute Construct">;
: Error<"invalid %select{branch|return|throw}0 %select{out of|into}1 "
"OpenACC Compute Construct">;
def note_acc_branch_into_compute_construct
: Note<"invalid branch into OpenACC Compute Construct">;
def note_acc_branch_out_of_compute_construct
: Note<"invalid branch out of OpenACC Compute Construct">;
} // end of sema component.
36 changes: 29 additions & 7 deletions clang/include/clang/Basic/TargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -1369,13 +1369,35 @@ class TargetInfo : public TransferrableTargetInfo,
}

struct BranchProtectionInfo {
LangOptions::SignReturnAddressScopeKind SignReturnAddr =
LangOptions::SignReturnAddressScopeKind::None;
LangOptions::SignReturnAddressKeyKind SignKey =
LangOptions::SignReturnAddressKeyKind::AKey;
bool BranchTargetEnforcement = false;
bool BranchProtectionPAuthLR = false;
bool GuardedControlStack = false;
LangOptions::SignReturnAddressScopeKind SignReturnAddr;
LangOptions::SignReturnAddressKeyKind SignKey;
bool BranchTargetEnforcement;
bool BranchProtectionPAuthLR;
bool GuardedControlStack;

BranchProtectionInfo() = default;

const char *getSignReturnAddrStr() const {
switch (SignReturnAddr) {
case LangOptions::SignReturnAddressScopeKind::None:
return "none";
case LangOptions::SignReturnAddressScopeKind::NonLeaf:
return "non-leaf";
case LangOptions::SignReturnAddressScopeKind::All:
return "all";
}
llvm_unreachable("Unexpected SignReturnAddressScopeKind");
}

const char *getSignKeyStr() const {
switch (SignKey) {
case LangOptions::SignReturnAddressKeyKind::AKey:
return "a_key";
case LangOptions::SignReturnAddressKeyKind::BKey:
return "b_key";
}
llvm_unreachable("Unexpected SignReturnAddressKeyKind");
}
};

/// Determine if the Architecture in this TargetInfo supports branch
Expand Down
5 changes: 3 additions & 2 deletions clang/include/clang/Basic/TargetOSMacros.def
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,19 @@ TARGET_OS(TARGET_OS_UNIX, Triple.isOSNetBSD() ||
TARGET_OS(TARGET_OS_MAC, Triple.isOSDarwin())
TARGET_OS(TARGET_OS_OSX, Triple.isMacOSX())
TARGET_OS(TARGET_OS_IPHONE, Triple.isiOS() || Triple.isTvOS() ||
Triple.isWatchOS())
Triple.isWatchOS() || Triple.isXROS())
// Triple::isiOS() also includes tvOS
TARGET_OS(TARGET_OS_IOS, Triple.getOS() == llvm::Triple::IOS)
TARGET_OS(TARGET_OS_TV, Triple.isTvOS())
TARGET_OS(TARGET_OS_WATCH, Triple.isWatchOS())
TARGET_OS(TARGET_OS_VISION, Triple.isXROS())
TARGET_OS(TARGET_OS_DRIVERKIT, Triple.isDriverKit())
TARGET_OS(TARGET_OS_MACCATALYST, Triple.isMacCatalystEnvironment())
TARGET_OS(TARGET_OS_SIMULATOR, Triple.isSimulatorEnvironment())

// Deprecated Apple target conditionals.
TARGET_OS(TARGET_OS_EMBEDDED, (Triple.isiOS() || Triple.isTvOS() \
|| Triple.isWatchOS()) \
|| Triple.isWatchOS() || Triple.isXROS()) \
&& !Triple.isMacCatalystEnvironment() \
&& !Triple.isSimulatorEnvironment())
TARGET_OS(TARGET_OS_NANO, Triple.isWatchOS())
Expand Down
3 changes: 1 addition & 2 deletions clang/include/clang/Driver/Driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ class Driver {
/// Target and driver mode components extracted from clang executable name.
ParsedClangName ClangNameParts;

/// The path to the installed clang directory, if any.
/// TODO: Remove this in favor of Dir.
std::string InstalledDir;

/// The path to the compiler resource directory.
Expand Down Expand Up @@ -433,7 +433,6 @@ class Driver {
return InstalledDir.c_str();
return Dir.c_str();
}
void setInstalledDir(StringRef Value) { InstalledDir = std::string(Value); }

bool isSaveTempsEnabled() const { return SaveTemps != SaveTempsNone; }
bool isSaveTempsObj() const { return SaveTemps == SaveTempsObj; }
Expand Down
31 changes: 18 additions & 13 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,13 @@ multiclass BoolGOption<string flag_base, KeyPathAndMacro kpm,
Group<g_Group>;
}

multiclass BoolMOption<string flag_base, KeyPathAndMacro kpm,
Default default, FlagDef flag1, FlagDef flag2,
BothFlags both = BothFlags<[]>> {
defm NAME : BoolOption<"m", flag_base, kpm, default, flag1, flag2, both>,
Group<m_Group>;
}

// Works like BoolOption except without marshalling
multiclass BoolOptionWithoutMarshalling<string prefix = "", string spelling_base,
FlagDef flag1_base, FlagDef flag2_base,
Expand Down Expand Up @@ -4600,11 +4607,10 @@ def mretpoline : Flag<["-"], "mretpoline">, Group<m_Group>,
Visibility<[ClangOption, CLOption]>;
def mno_retpoline : Flag<["-"], "mno-retpoline">, Group<m_Group>,
Visibility<[ClangOption, CLOption]>;
defm speculative_load_hardening : BoolOption<"m", "speculative-load-hardening",
defm speculative_load_hardening : BoolMOption<"speculative-load-hardening",
CodeGenOpts<"SpeculativeLoadHardening">, DefaultFalse,
PosFlag<SetTrue, [], [ClangOption, CC1Option]>,
NegFlag<SetFalse>, BothFlags<[], [ClangOption, CLOption]>>,
Group<m_Group>;
NegFlag<SetFalse>, BothFlags<[], [ClangOption, CLOption]>>;
def mlvi_hardening : Flag<["-"], "mlvi-hardening">, Group<m_Group>,
Visibility<[ClangOption, CLOption]>,
HelpText<"Enable all mitigations for Load Value Injection (LVI)">;
Expand Down Expand Up @@ -4821,13 +4827,13 @@ def mexec_model_EQ : Joined<["-"], "mexec-model=">, Group<m_wasm_Features_Driver
"explicitly terminated.">;
} // let Flags = [TargetSpecific]

defm amdgpu_ieee : BoolOption<"m", "amdgpu-ieee",
defm amdgpu_ieee : BoolMOption<"amdgpu-ieee",
CodeGenOpts<"EmitIEEENaNCompliantInsts">, DefaultTrue,
PosFlag<SetTrue, [], [ClangOption], "Sets the IEEE bit in the expected default floating point "
" mode register. Floating point opcodes that support exception flag "
"gathering quiet and propagate signaling NaN inputs per IEEE 754-2008. "
"This option changes the ABI. (AMDGPU only)">,
NegFlag<SetFalse, [], [ClangOption, CC1Option]>>, Group<m_Group>;
NegFlag<SetFalse, [], [ClangOption, CC1Option]>>;

def mcode_object_version_EQ : Joined<["-"], "mcode-object-version=">, Group<m_Group>,
HelpText<"Specify code object ABI version. Defaults to 5. (AMDGPU only)">,
Expand All @@ -4846,14 +4852,14 @@ defm wavefrontsize64 : SimpleMFlag<"wavefrontsize64",
"Specify wavefront size 64", "Specify wavefront size 32",
" mode (AMDGPU only)">;

defm unsafe_fp_atomics : BoolOption<"m", "unsafe-fp-atomics",
defm unsafe_fp_atomics : BoolMOption<"unsafe-fp-atomics",
TargetOpts<"AllowAMDGPUUnsafeFPAtomics">, DefaultFalse,
PosFlag<SetTrue, [], [ClangOption, CC1Option],
"Enable generation of unsafe floating point "
"atomic instructions. May generate more efficient code, but may not "
"respect rounding and denormal modes, and may give incorrect results "
"for certain memory destinations. (AMDGPU only)">,
NegFlag<SetFalse>>, Group<m_Group>;
NegFlag<SetFalse>>;

def faltivec : Flag<["-"], "faltivec">, Group<f_Group>;
def fno_altivec : Flag<["-"], "fno-altivec">, Group<f_Group>;
Expand Down Expand Up @@ -4941,11 +4947,10 @@ def mrop_protect : Flag<["-"], "mrop-protect">,
def mprivileged : Flag<["-"], "mprivileged">,
Group<m_ppc_Features_Group>;

defm regnames : BoolOption<"m", "regnames",
defm regnames : BoolMOption<"regnames",
CodeGenOpts<"PPCUseFullRegisterNames">, DefaultFalse,
PosFlag<SetTrue, [], [ClangOption, CC1Option], "Use full register names when writing assembly output">,
NegFlag<SetFalse, [], [ClangOption], "Use only register numbers when writing assembly output">>,
Group<m_Group>;
NegFlag<SetFalse, [], [ClangOption], "Use only register numbers when writing assembly output">>;
} // let Flags = [TargetSpecific]
def maix_small_local_exec_tls : Flag<["-"], "maix-small-local-exec-tls">,
Group<m_ppc_Features_Group>,
Expand Down Expand Up @@ -4987,10 +4992,10 @@ def mxcoff_build_id_EQ : Joined<["-"], "mxcoff-build-id=">, Group<Link_Group>, M
def mignore_xcoff_visibility : Flag<["-"], "mignore-xcoff-visibility">, Group<m_Group>,
HelpText<"Not emit the visibility attribute for asm in AIX OS or give all symbols 'unspecified' visibility in XCOFF object file">,
Flags<[TargetSpecific]>, Visibility<[ClangOption, CC1Option]>;
defm backchain : BoolOption<"m", "backchain",
defm backchain : BoolMOption<"backchain",
CodeGenOpts<"Backchain">, DefaultFalse,
PosFlag<SetTrue, [], [ClangOption], "Link stack frames through backchain on System Z">,
NegFlag<SetFalse>, BothFlags<[], [ClangOption, CC1Option]>>, Group<m_Group>;
NegFlag<SetFalse>, BothFlags<[], [ClangOption, CC1Option]>>;

def mno_warn_nonportable_cfstrings : Flag<["-"], "mno-warn-nonportable-cfstrings">, Group<m_Group>;
def mno_omit_leaf_frame_pointer : Flag<["-"], "mno-omit-leaf-frame-pointer">, Group<m_Group>;
Expand Down Expand Up @@ -6952,7 +6957,7 @@ def msmall_data_limit : Separate<["-"], "msmall-data-limit">,
def funwind_tables_EQ : Joined<["-"], "funwind-tables=">,
HelpText<"Generate unwinding tables for all functions">,
MarshallingInfoInt<CodeGenOpts<"UnwindTables">>;
defm constructor_aliases : BoolOption<"m", "constructor-aliases",
defm constructor_aliases : BoolMOption<"constructor-aliases",
CodeGenOpts<"CXXCtorDtorAliases">, DefaultFalse,
PosFlag<SetTrue, [], [ClangOption], "Enable">,
NegFlag<SetFalse, [], [ClangOption], "Disable">,
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Frontend/CompilerInstance.h
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,9 @@ class CompilerInstance : public ModuleLoader {
// of the context or else not CompilerInstance specific.
bool ExecuteAction(FrontendAction &Act);

/// At the end of a compilation, print the number of warnings/errors.
void printDiagnosticStats();

/// Load the list of plugins requested in the \c FrontendOptions.
void LoadRequestedPlugins();

Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Frontend/CompilerInvocation.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ class CompilerInvocationBase {
/// @}
};

class CowCompilerInvocation;

/// Helper class for holding the data necessary to invoke the compiler.
///
/// This class is designed to represent an abstract "invocation" of the
Expand All @@ -220,6 +222,9 @@ class CompilerInvocation : public CompilerInvocationBase {
}
~CompilerInvocation() = default;

explicit CompilerInvocation(const CowCompilerInvocation &X);
CompilerInvocation &operator=(const CowCompilerInvocation &X);

/// Const getters.
/// @{
// Note: These need to be pulled in manually. Otherwise, they get hidden by
Expand Down
50 changes: 46 additions & 4 deletions clang/include/clang/InstallAPI/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@
#ifndef LLVM_CLANG_INSTALLAPI_CONTEXT_H
#define LLVM_CLANG_INSTALLAPI_CONTEXT_H

#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/FileManager.h"
#include "clang/InstallAPI/HeaderFile.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/TextAPI/InterfaceFile.h"
#include "llvm/TextAPI/RecordVisitor.h"
#include "llvm/TextAPI/RecordsSlice.h"

namespace clang {
namespace installapi {
class FrontendRecordsSlice;

/// Struct used for generating validating InstallAPI.
/// The attributes captured represent all necessary information
Expand All @@ -24,14 +27,53 @@ struct InstallAPIContext {
/// Library attributes that are typically passed as linker inputs.
llvm::MachO::RecordsSlice::BinaryAttrs BA;

/// Active target triple to parse.
llvm::Triple TargetTriple{};
/// All headers that represent a library.
HeaderSeq InputHeaders;

/// Active language mode to parse in.
Language LangMode = Language::ObjC;

/// Active header access type.
HeaderType Type = HeaderType::Unknown;

/// Active TargetSlice for symbol record collection.
std::shared_ptr<FrontendRecordsSlice> Slice;

/// FileManager for all I/O operations.
FileManager *FM = nullptr;

/// DiagnosticsEngine for all error reporting.
DiagnosticsEngine *Diags = nullptr;

/// File Path of output location.
llvm::StringRef OutputLoc{};

/// What encoding to write output as.
llvm::MachO::FileType FT = llvm::MachO::FileType::TBD_V5;

/// Populate entries of headers that should be included for TextAPI
/// generation.
void addKnownHeader(const HeaderFile &H);

/// Record visited files during frontend actions to determine whether to
/// include their declarations for TextAPI generation.
///
/// \param FE Header that is being parsed.
/// \param PP Preprocesser used for querying how header was imported.
/// \return Access level of header if it should be included for TextAPI
/// generation.
std::optional<HeaderType> findAndRecordFile(const FileEntry *FE,
const Preprocessor &PP);

private:
using HeaderMap = llvm::DenseMap<const FileEntry *, HeaderType>;

// Collection of parsed header files and their access level. If set to
// HeaderType::Unknown, they are not used for TextAPI generation.
HeaderMap KnownFiles;

// Collection of expected header includes and the access level for them.
llvm::DenseMap<StringRef, HeaderType> KnownIncludes;
};

} // namespace installapi
Expand Down
104 changes: 104 additions & 0 deletions clang/include/clang/InstallAPI/Frontend.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
//===- InstallAPI/Frontend.h -----------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// Top level wrappers for InstallAPI frontend operations.
///
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_INSTALLAPI_FRONTEND_H
#define LLVM_CLANG_INSTALLAPI_FRONTEND_H

#include "clang/AST/ASTConsumer.h"
#include "clang/AST/Availability.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/InstallAPI/Context.h"
#include "clang/InstallAPI/Visitor.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/MemoryBuffer.h"

namespace clang {
namespace installapi {

using SymbolFlags = llvm::MachO::SymbolFlags;
using RecordLinkage = llvm::MachO::RecordLinkage;
using GlobalRecord = llvm::MachO::GlobalRecord;
using ObjCInterfaceRecord = llvm::MachO::ObjCInterfaceRecord;

// Represents a collection of frontend records for a library that are tied to a
// darwin target triple.
class FrontendRecordsSlice : public llvm::MachO::RecordsSlice {
public:
FrontendRecordsSlice(const llvm::Triple &T)
: llvm::MachO::RecordsSlice({T}) {}

/// Add non-ObjC global record with attributes from AST.
///
/// \param Name The name of symbol.
/// \param Linkage The linkage of symbol.
/// \param GV The kind of global.
/// \param Avail The availability information tied to the active target
/// triple.
/// \param D The pointer to the declaration from traversing AST.
/// \param Access The intended access level of symbol.
/// \param Flags The flags that describe attributes of the symbol.
/// \return The non-owning pointer to added record in slice.
GlobalRecord *addGlobal(StringRef Name, RecordLinkage Linkage,
GlobalRecord::Kind GV,
const clang::AvailabilityInfo Avail, const Decl *D,
const HeaderType Access,
SymbolFlags Flags = SymbolFlags::None);

/// Add ObjC Class record with attributes from AST.
///
/// \param Name The name of class, not symbol.
/// \param Linkage The linkage of symbol.
/// \param Avail The availability information tied to the active target
/// triple.
/// \param D The pointer to the declaration from traversing AST.
/// \param Access The intended access level of symbol.
/// \param IsEHType Whether declaration has an exception attribute.
/// \return The non-owning pointer to added record in slice.
ObjCInterfaceRecord *addObjCInterface(StringRef Name, RecordLinkage Linkage,
const clang::AvailabilityInfo Avail,
const Decl *D, HeaderType Access,
bool IsEHType);

private:
/// Frontend information captured about records.
struct FrontendAttrs {
const AvailabilityInfo Avail;
const Decl *D;
const HeaderType Access;
};

/// Mapping of records stored in slice to their frontend attributes.
llvm::DenseMap<llvm::MachO::Record *, FrontendAttrs> FrontendRecords;
};

/// Create a buffer that contains all headers to scan
/// for global symbols with.
std::unique_ptr<llvm::MemoryBuffer> createInputBuffer(InstallAPIContext &Ctx);

class InstallAPIAction : public ASTFrontendAction {
public:
explicit InstallAPIAction(InstallAPIContext &Ctx) : Ctx(Ctx) {}

std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
StringRef InFile) override {
return std::make_unique<InstallAPIVisitor>(
CI.getASTContext(), Ctx, CI.getSourceManager(), CI.getPreprocessor());
}

private:
InstallAPIContext &Ctx;
};
} // namespace installapi
} // namespace clang

#endif // LLVM_CLANG_INSTALLAPI_FRONTEND_H
23 changes: 23 additions & 0 deletions clang/include/clang/InstallAPI/HeaderFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include "clang/Basic/LangStandard.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Regex.h"
#include <optional>
#include <string>
Expand All @@ -32,6 +33,20 @@ enum class HeaderType {
Project,
};

inline StringRef getName(const HeaderType T) {
switch (T) {
case HeaderType::Public:
return "Public";
case HeaderType::Private:
return "Private";
case HeaderType::Project:
return "Project";
case HeaderType::Unknown:
return "Unknown";
}
llvm_unreachable("unexpected header type");
}

class HeaderFile {
/// Full input path to header.
std::string FullPath;
Expand All @@ -52,6 +67,14 @@ class HeaderFile {

static llvm::Regex getFrameworkIncludeRule();

HeaderType getType() const { return Type; }
StringRef getIncludeName() const { return IncludeName; }
StringRef getPath() const { return FullPath; }

bool useIncludeName() const {
return Type != HeaderType::Project && !IncludeName.empty();
}

bool operator==(const HeaderFile &Other) const {
return std::tie(Type, FullPath, IncludeName, Language) ==
std::tie(Other.Type, Other.FullPath, Other.IncludeName,
Expand Down
60 changes: 60 additions & 0 deletions clang/include/clang/InstallAPI/Visitor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//===- InstallAPI/Visitor.h -----------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// ASTVisitor Interface for InstallAPI frontend operations.
///
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_INSTALLAPI_VISITOR_H
#define LLVM_CLANG_INSTALLAPI_VISITOR_H

#include "clang/AST/Mangle.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/InstallAPI/Context.h"
#include "llvm/ADT/Twine.h"

namespace clang {
namespace installapi {

/// ASTVisitor for collecting declarations that represent global symbols.
class InstallAPIVisitor final : public ASTConsumer,
public RecursiveASTVisitor<InstallAPIVisitor> {
public:
InstallAPIVisitor(ASTContext &ASTCtx, InstallAPIContext &Ctx,
SourceManager &SrcMgr, Preprocessor &PP)
: Ctx(Ctx), SrcMgr(SrcMgr), PP(PP),
MC(ItaniumMangleContext::create(ASTCtx, ASTCtx.getDiagnostics())),
Layout(ASTCtx.getTargetInfo().getDataLayoutString()) {}
void HandleTranslationUnit(ASTContext &ASTCtx) override;

/// Collect global variables.
bool VisitVarDecl(const VarDecl *D);

/// Collect Objective-C Interface declarations.
/// Every Objective-C class has an interface declaration that lists all the
/// ivars, properties, and methods of the class.
bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D);

private:
std::string getMangledName(const NamedDecl *D) const;
std::string getBackendMangledName(llvm::Twine Name) const;
std::optional<HeaderType> getAccessForDecl(const NamedDecl *D) const;

InstallAPIContext &Ctx;
SourceManager &SrcMgr;
Preprocessor &PP;
std::unique_ptr<clang::ItaniumMangleContext> MC;
StringRef Layout;
};

} // namespace installapi
} // namespace clang

#endif // LLVM_CLANG_INSTALLAPI_VISITOR_H
14 changes: 12 additions & 2 deletions clang/include/clang/Sema/Scope.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ class Scope {
/// ScopeFlags - These are bitfields that are or'd together when creating a
/// scope, which defines the sorts of things the scope contains.
enum ScopeFlags {
// A bitfield value representing no scopes.
NoScope = 0,

/// This indicates that the scope corresponds to a function, which
/// means that labels are set here.
FnScope = 0x01,
Expand Down Expand Up @@ -521,10 +524,17 @@ class Scope {
return getFlags() & Scope::OpenACCComputeConstructScope;
}

bool isInOpenACCComputeConstructScope() const {
/// Determine if this scope (or its parents) are a compute construct. If the
/// argument is provided, the search will stop at any of the specified scopes.
/// Otherwise, it will stop only at the normal 'no longer search' scopes.
bool isInOpenACCComputeConstructScope(ScopeFlags Flags = NoScope) const {
for (const Scope *S = this; S; S = S->getParent()) {
if (S->getFlags() & Scope::OpenACCComputeConstructScope)
if (S->isOpenACCComputeConstructScope())
return true;

if (S->getFlags() & Flags)
return false;

else if (S->getFlags() &
(Scope::FnScope | Scope::ClassScope | Scope::BlockScope |
Scope::TemplateParamScope | Scope::FunctionPrototypeScope |
Expand Down
8 changes: 7 additions & 1 deletion clang/lib/APINotes/APINotesReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

//
// This file implements the \c APINotesReader class that reads source
// API notes data providing additional information about source code as
// a separate input, such as the non-nil/nilable annotations for
// method parameters.
//
//===----------------------------------------------------------------------===//
#include "clang/APINotes/APINotesReader.h"
#include "APINotesFormat.h"
#include "llvm/ADT/Hashing.h"
Expand Down
15 changes: 13 additions & 2 deletions clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2543,8 +2543,19 @@ QualType CXXMethodDecl::getThisType(const FunctionProtoType *FPT,
const CXXRecordDecl *Decl) {
ASTContext &C = Decl->getASTContext();
QualType ObjectTy = ::getThisObjectType(C, FPT, Decl);
return C.getLangOpts().HLSL ? C.getLValueReferenceType(ObjectTy)
: C.getPointerType(ObjectTy);

// Unlike 'const' and 'volatile', a '__restrict' qualifier must be
// attached to the pointer type, not the pointee.
bool Restrict = FPT->getMethodQuals().hasRestrict();
if (Restrict)
ObjectTy.removeLocalRestrict();

ObjectTy = C.getLangOpts().HLSL ? C.getLValueReferenceType(ObjectTy)
: C.getPointerType(ObjectTy);

if (Restrict)
ObjectTy.addRestrict();
return ObjectTy;
}

QualType CXXMethodDecl::getThisType() const {
Expand Down
8 changes: 6 additions & 2 deletions clang/lib/AST/Interp/ByteCodeEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,12 @@ Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
this->LambdaCaptures[Cap.first] = {
Offset, Cap.second->getType()->isReferenceType()};
}
if (LTC)
this->LambdaThisCapture = R->getField(LTC)->Offset;
if (LTC) {
QualType CaptureType = R->getField(LTC)->Decl->getType();
this->LambdaThisCapture = {R->getField(LTC)->Offset,
CaptureType->isReferenceType() ||
CaptureType->isPointerType()};
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/Interp/ByteCodeEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class ByteCodeEmitter {
/// Lambda captures.
llvm::DenseMap<const ValueDecl *, ParamOffset> LambdaCaptures;
/// Offset of the This parameter in a lambda record.
unsigned LambdaThisCapture = 0;
ParamOffset LambdaThisCapture{0, false};
/// Local descriptors.
llvm::SmallVector<SmallVector<Local, 8>, 2> Descriptors;

Expand Down
57 changes: 33 additions & 24 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1220,14 +1220,18 @@ bool ByteCodeExprGen<Emitter>::VisitArrayInitLoopExpr(

template <class Emitter>
bool ByteCodeExprGen<Emitter>::VisitOpaqueValueExpr(const OpaqueValueExpr *E) {
const Expr *SourceExpr = E->getSourceExpr();
if (!SourceExpr)
return false;

if (Initializing)
return this->visitInitializer(E->getSourceExpr());
return this->visitInitializer(SourceExpr);

PrimType SubExprT = classify(E->getSourceExpr()).value_or(PT_Ptr);
PrimType SubExprT = classify(SourceExpr).value_or(PT_Ptr);
if (auto It = OpaqueExprs.find(E); It != OpaqueExprs.end())
return this->emitGetLocal(SubExprT, It->second, E);

if (!this->visit(E->getSourceExpr()))
if (!this->visit(SourceExpr))
return false;

// At this point we either have the evaluated source expression or a pointer
Expand Down Expand Up @@ -2587,7 +2591,10 @@ bool ByteCodeExprGen<Emitter>::visitExpr(const Expr *E) {

if (!this->emitFinishInit(E))
return false;
return this->emitRetValue(E);
// We are destroying the locals AFTER the Ret op.
// The Ret op needs to copy the (alive) values, but the
// destructors may still turn the entire expression invalid.
return this->emitRetValue(E) && RootScope.destroyLocals();
}

return false;
Expand Down Expand Up @@ -2835,7 +2842,8 @@ bool ByteCodeExprGen<Emitter>::VisitCallExpr(const CallExpr *E) {
return false;
} else if (Func->isVariadic()) {
uint32_t VarArgSize = 0;
unsigned NumParams = Func->getNumWrittenParams();
unsigned NumParams =
Func->getNumWrittenParams() + isa<CXXOperatorCallExpr>(E);
for (unsigned I = NumParams, N = E->getNumArgs(); I != N; ++I)
VarArgSize += align(primSize(classify(E->getArg(I)).value_or(PT_Ptr)));
if (!this->emitCallVar(Func, VarArgSize, E))
Expand Down Expand Up @@ -2926,8 +2934,11 @@ bool ByteCodeExprGen<Emitter>::VisitCXXThisExpr(const CXXThisExpr *E) {
if (DiscardResult)
return true;

if (this->LambdaThisCapture > 0)
return this->emitGetThisFieldPtr(this->LambdaThisCapture, E);
if (this->LambdaThisCapture.Offset > 0) {
if (this->LambdaThisCapture.IsPtr)
return this->emitGetThisFieldPtr(this->LambdaThisCapture.Offset, E);
return this->emitGetPtrThisField(this->LambdaThisCapture.Offset, E);
}

return this->emitThis(E);
}
Expand Down Expand Up @@ -3210,19 +3221,16 @@ bool ByteCodeExprGen<Emitter>::VisitDeclRefExpr(const DeclRefExpr *E) {
// we haven't seen yet.
if (Ctx.getLangOpts().CPlusPlus) {
if (const auto *VD = dyn_cast<VarDecl>(D)) {
// Dummy for static locals
if (VD->isStaticLocal()) {
if (std::optional<unsigned> I = P.getOrCreateDummy(D))
return this->emitGetPtrGlobal(*I, E);
return false;
}
// Visit local const variables like normal.
if (VD->isLocalVarDecl() && VD->getType().isConstQualified()) {
if (!this->visitVarDecl(VD))
return false;
// Retry.
return this->VisitDeclRefExpr(E);
}

if (VD->hasExternalStorage())
return this->emitInvalidDeclRef(E, E);
}
} else {
if (const auto *VD = dyn_cast<VarDecl>(D);
Expand All @@ -3232,11 +3240,11 @@ bool ByteCodeExprGen<Emitter>::VisitDeclRefExpr(const DeclRefExpr *E) {
// Retry.
return this->VisitDeclRefExpr(E);
}

if (std::optional<unsigned> I = P.getOrCreateDummy(D))
return this->emitGetPtrGlobal(*I, E);
}

if (std::optional<unsigned> I = P.getOrCreateDummy(D))
return this->emitGetPtrGlobal(*I, E);

return this->emitInvalidDeclRef(E, E);
}

Expand Down Expand Up @@ -3414,14 +3422,15 @@ bool ByteCodeExprGen<Emitter>::emitRecordDestruction(const Record *R) {
// Now emit the destructor and recurse into base classes.
if (const CXXDestructorDecl *Dtor = R->getDestructor();
Dtor && !Dtor->isTrivial()) {
if (const Function *DtorFunc = getFunction(Dtor)) {
assert(DtorFunc->hasThisPointer());
assert(DtorFunc->getNumParams() == 1);
if (!this->emitDupPtr(SourceInfo{}))
return false;
if (!this->emitCall(DtorFunc, 0, SourceInfo{}))
return false;
}
const Function *DtorFunc = getFunction(Dtor);
if (!DtorFunc)
return false;
assert(DtorFunc->hasThisPointer());
assert(DtorFunc->getNumParams() == 1);
if (!this->emitDupPtr(SourceInfo{}))
return false;
if (!this->emitCall(DtorFunc, 0, SourceInfo{}))
return false;
}

for (const Record::Base &Base : llvm::reverse(R->bases())) {
Expand Down
29 changes: 20 additions & 9 deletions clang/lib/AST/Interp/ByteCodeExprGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ template <class Emitter> class VariableScope {
}

virtual void emitDestruction() {}
virtual void emitDestructors() {}
virtual bool emitDestructors() { return true; }
VariableScope *getParent() const { return Parent; }

protected:
Expand All @@ -356,13 +356,18 @@ template <class Emitter> class LocalScope : public VariableScope<Emitter> {
}

/// Overriden to support explicit destruction.
void emitDestruction() override {
void emitDestruction() override { destroyLocals(); }

/// Explicit destruction of local variables.
bool destroyLocals() {
if (!Idx)
return;
this->emitDestructors();
return true;

bool Success = this->emitDestructors();
this->Ctx->emitDestroy(*Idx, SourceInfo{});
removeStoredOpaqueValues();
this->Idx = std::nullopt;
return Success;
}

void addLocal(const Scope::Local &Local) override {
Expand All @@ -374,19 +379,25 @@ template <class Emitter> class LocalScope : public VariableScope<Emitter> {
this->Ctx->Descriptors[*Idx].emplace_back(Local);
}

void emitDestructors() override {
bool emitDestructors() override {
if (!Idx)
return;
return true;
// Emit destructor calls for local variables of record
// type with a destructor.
for (Scope::Local &Local : this->Ctx->Descriptors[*Idx]) {
if (!Local.Desc->isPrimitive() && !Local.Desc->isPrimitiveArray()) {
this->Ctx->emitGetPtrLocal(Local.Offset, SourceInfo{});
this->Ctx->emitDestruction(Local.Desc);
this->Ctx->emitPopPtr(SourceInfo{});
if (!this->Ctx->emitGetPtrLocal(Local.Offset, SourceInfo{}))
return false;

if (!this->Ctx->emitDestruction(Local.Desc))
return false;

if (!this->Ctx->emitPopPtr(SourceInfo{}))
return false;
removeIfStoredOpaqueValue(Local);
}
}
return true;
}

void removeStoredOpaqueValues() {
Expand Down
46 changes: 28 additions & 18 deletions clang/lib/AST/Interp/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) {
}

bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {
assert(Stk.empty());
ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result);
bool Recursing = !Stk.empty();
ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk);

auto Res = C.interpretExpr(E, /*ConvertResultToRValue=*/E->isGLValue());

Expand All @@ -51,42 +51,47 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {
return false;
}

assert(Stk.empty());
if (!Recursing) {
assert(Stk.empty());
#ifndef NDEBUG
// Make sure we don't rely on some value being still alive in
// InterpStack memory.
Stk.clear();
// Make sure we don't rely on some value being still alive in
// InterpStack memory.
Stk.clear();
#endif
}

Result = Res.toAPValue();

return true;
}

bool Context::evaluate(State &Parent, const Expr *E, APValue &Result) {
assert(Stk.empty());
ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result);
bool Recursing = !Stk.empty();
ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk);

auto Res = C.interpretExpr(E);
if (Res.isInvalid()) {
Stk.clear();
return false;
}

assert(Stk.empty());
if (!Recursing) {
assert(Stk.empty());
#ifndef NDEBUG
// Make sure we don't rely on some value being still alive in
// InterpStack memory.
Stk.clear();
// Make sure we don't rely on some value being still alive in
// InterpStack memory.
Stk.clear();
#endif
}

Result = Res.toAPValue();
return true;
}

bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD,
APValue &Result) {
assert(Stk.empty());
ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result);
bool Recursing = !Stk.empty();
ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk);

bool CheckGlobalInitialized =
shouldBeGloballyIndexed(VD) &&
Expand All @@ -97,12 +102,14 @@ bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD,
return false;
}

assert(Stk.empty());
if (!Recursing) {
assert(Stk.empty());
#ifndef NDEBUG
// Make sure we don't rely on some value being still alive in
// InterpStack memory.
Stk.clear();
// Make sure we don't rely on some value being still alive in
// InterpStack memory.
Stk.clear();
#endif
}

Result = Res.toAPValue();
return true;
Expand Down Expand Up @@ -217,6 +224,9 @@ const CXXMethodDecl *
Context::getOverridingFunction(const CXXRecordDecl *DynamicDecl,
const CXXRecordDecl *StaticDecl,
const CXXMethodDecl *InitialFunction) const {
assert(DynamicDecl);
assert(StaticDecl);
assert(InitialFunction);

const CXXRecordDecl *CurRecord = DynamicDecl;
const CXXMethodDecl *FoundFunction = InitialFunction;
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class Context final {

/// Classifies an expression.
std::optional<PrimType> classify(const Expr *E) const {
assert(E);
if (E->isGLValue()) {
if (E->getType()->isFunctionType())
return PT_FnPtr;
Expand Down
7 changes: 5 additions & 2 deletions clang/lib/AST/Interp/EvalEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ using namespace clang;
using namespace clang::interp;

EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent,
InterpStack &Stk, APValue &Result)
InterpStack &Stk)
: Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), EvalResult(&Ctx) {
// Create a dummy frame for the interpreter which does not have locals.
S.Current =
Expand All @@ -38,8 +38,11 @@ EvaluationResult EvalEmitter::interpretExpr(const Expr *E,
this->ConvertResultToRValue = ConvertResultToRValue;
EvalResult.setSource(E);

if (!this->visitExpr(E) && EvalResult.empty())
if (!this->visitExpr(E)) {
// EvalResult may already have a result set, but something failed
// after that (e.g. evaluating destructors).
EvalResult.setInvalid();
}

return std::move(this->EvalResult);
}
Expand Down
5 changes: 2 additions & 3 deletions clang/lib/AST/Interp/EvalEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ class EvalEmitter : public SourceMapper {
InterpState &getState() { return S; }

protected:
EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk,
APValue &Result);
EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk);

virtual ~EvalEmitter();

Expand Down Expand Up @@ -74,7 +73,7 @@ class EvalEmitter : public SourceMapper {
/// Lambda captures.
llvm::DenseMap<const ValueDecl *, ParamOffset> LambdaCaptures;
/// Offset of the This parameter in a lambda record.
unsigned LambdaThisCapture = 0;
ParamOffset LambdaThisCapture{0, false};
/// Local descriptors.
llvm::SmallVector<SmallVector<Local, 8>, 2> Descriptors;

Expand Down
3 changes: 2 additions & 1 deletion clang/lib/AST/Interp/EvaluationResult.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ class EvaluationResult final {
Kind = LValue;
}
void setInvalid() {
assert(empty());
// We are NOT asserting empty() here, since setting it to invalid
// is allowed even if there is already a result.
Kind = Invalid;
}
void setValid() {
Expand Down
30 changes: 21 additions & 9 deletions clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,10 +285,6 @@ static bool CheckConstant(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
return CheckConstant(S, OpPC, Ptr.getDeclDesc());
}

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

bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
CheckSubobjectKind CSK) {
if (!Ptr.isZero())
Expand Down Expand Up @@ -595,10 +591,8 @@ bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result,
return true;
}

/// We aleady know the given DeclRefExpr is invalid for some reason,
/// now figure out why and print appropriate diagnostics.
bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR) {
const ValueDecl *D = DR->getDecl();
static bool diagnoseUnknownDecl(InterpState &S, CodePtr OpPC,
const ValueDecl *D) {
const SourceInfo &E = S.Current->getSource(OpPC);

if (isa<ParmVarDecl>(D)) {
Expand All @@ -621,10 +615,28 @@ bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR) {
return false;
}
}

return false;
}

/// We aleady know the given DeclRefExpr is invalid for some reason,
/// now figure out why and print appropriate diagnostics.
bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR) {
const ValueDecl *D = DR->getDecl();
return diagnoseUnknownDecl(S, OpPC, D);
}

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

const Descriptor *Desc = Ptr.getDeclDesc();
const ValueDecl *D = Desc->asValueDecl();
if (!D)
return false;

return diagnoseUnknownDecl(S, OpPC, D);
}

bool CheckNonNullArgs(InterpState &S, CodePtr OpPC, const Function *F,
const CallExpr *CE, unsigned ArgSize) {
auto Args = llvm::ArrayRef(CE->getArgs(), CE->getNumArgs());
Expand Down
Loading