538 changes: 538 additions & 0 deletions bolt/test/X86/dwarf5-empty-function-ranges.s

Large diffs are not rendered by default.

485 changes: 485 additions & 0 deletions bolt/test/X86/dwarf5-loclist-out-of-order.s

Large diffs are not rendered by default.

6 changes: 2 additions & 4 deletions bolt/test/X86/dwarf5-subprogram-single-gc-ranges.test
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@

# RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf5-subprogram-single-gc-ranges-main.s -o %t1.o
# RUN: %clang %cflags %t1.o -o %t.exe -Wl,-q
# RUN: llvm-bolt %t.exe -o %t.bolt --update-debug-sections &> %t1.txt
# RUN: llvm-dwarfdump --show-form --verbose --debug-info %t.bolt >> %t1.txt
# RUN: llvm-bolt %t.exe -o %t.bolt --update-debug-sections
# RUN: llvm-dwarfdump --show-form --verbose --debug-info %t.bolt > %t1.txt
# RUN: cat %t1.txt | FileCheck --check-prefix=POSTCHECK %s

# This test checks BOLT correctly handles DW_TAG_subprogram with Ranges with single entry, when function was GCed.

# POSTCHECK: BOLT-WARNING: [internal-dwarf-error]: subprogram got GCed by the linker, DW_AT_ranges is used

# POSTCHECK: DW_TAG_subprogram
# POSTCHECK-NEXT: DW_AT_frame_base
# POSTCHECK-NEXT: DW_AT_linkage_name
Expand Down
12 changes: 9 additions & 3 deletions clang-tools-extra/clang-tidy/modernize/UseOverrideCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "UseOverrideCheck.h"
#include "../utils/LexerUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
Expand Down Expand Up @@ -228,9 +229,14 @@ void UseOverrideCheck::check(const MatchFinder::MatchResult &Result) {
if (HasVirtual) {
for (Token Tok : Tokens) {
if (Tok.is(tok::kw_virtual)) {
Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
Tok.getLocation(), Tok.getLocation()));
break;
std::optional<Token> NextToken =
utils::lexer::findNextTokenIncludingComments(
Tok.getEndLoc(), Sources, getLangOpts());
if (NextToken.has_value()) {
Diag << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
Tok.getLocation(), NextToken->getLocation()));
break;
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,14 @@ void RedundantInlineSpecifierCheck::registerMatchers(MatchFinder *Finder) {
this);

if (getLangOpts().CPlusPlus17) {
const auto IsPartOfRecordDecl = hasAncestor(recordDecl());
Finder->addMatcher(
varDecl(isInlineSpecified(),
anyOf(isInternalLinkage(StrictMode),
allOf(isConstexpr(), hasAncestor(recordDecl()))))
varDecl(
isInlineSpecified(),
anyOf(allOf(isInternalLinkage(StrictMode),
unless(allOf(hasInitializer(expr()), IsPartOfRecordDecl,
isStaticStorageClass()))),
allOf(isConstexpr(), IsPartOfRecordDecl)))
.bind("var_decl"),
this);
}
Expand Down
7 changes: 5 additions & 2 deletions clang-tools-extra/clangd/ClangdLSPServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -844,14 +844,17 @@ void ClangdLSPServer::onWorkspaceSymbol(
}

void ClangdLSPServer::onPrepareRename(const TextDocumentPositionParams &Params,
Callback<std::optional<Range>> Reply) {
Callback<PrepareRenameResult> Reply) {
Server->prepareRename(
Params.textDocument.uri.file(), Params.position, /*NewName*/ std::nullopt,
Opts.Rename,
[Reply = std::move(Reply)](llvm::Expected<RenameResult> Result) mutable {
if (!Result)
return Reply(Result.takeError());
return Reply(std::move(Result->Target));
PrepareRenameResult PrepareResult;
PrepareResult.range = Result->Target;
PrepareResult.placeholder = Result->Placeholder;
return Reply(std::move(PrepareResult));
});
}

Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/ClangdLSPServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ class ClangdLSPServer : private ClangdServer::Callbacks,
void onWorkspaceSymbol(const WorkspaceSymbolParams &,
Callback<std::vector<SymbolInformation>>);
void onPrepareRename(const TextDocumentPositionParams &,
Callback<std::optional<Range>>);
Callback<PrepareRenameResult>);
void onRename(const RenameParams &, Callback<WorkspaceEdit>);
void onHover(const TextDocumentPositionParams &,
Callback<std::optional<Hover>>);
Expand Down
9 changes: 9 additions & 0 deletions clang-tools-extra/clangd/Protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1187,6 +1187,15 @@ bool fromJSON(const llvm::json::Value &Params, RenameParams &R,
O.map("position", R.position) && O.map("newName", R.newName);
}

llvm::json::Value toJSON(const PrepareRenameResult &PRR) {
if (PRR.placeholder.empty())
return toJSON(PRR.range);
return llvm::json::Object{
{"range", toJSON(PRR.range)},
{"placeholder", PRR.placeholder},
};
}

llvm::json::Value toJSON(const DocumentHighlight &DH) {
return llvm::json::Object{
{"range", toJSON(DH.range)},
Expand Down
30 changes: 30 additions & 0 deletions clang-tools-extra/clangd/Protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -1436,6 +1436,14 @@ struct RenameParams {
};
bool fromJSON(const llvm::json::Value &, RenameParams &, llvm::json::Path);

struct PrepareRenameResult {
/// Range of the string to rename.
Range range;
/// Placeholder text to use in the editor if non-empty.
std::string placeholder;
};
llvm::json::Value toJSON(const PrepareRenameResult &PRR);

enum class DocumentHighlightKind { Text = 1, Read = 2, Write = 3 };

/// A document highlight is a range inside a text document which deserves
Expand Down Expand Up @@ -1969,6 +1977,28 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &, const ASTNode &);
} // namespace clang

namespace llvm {

template <> struct DenseMapInfo<clang::clangd::Range> {
using Range = clang::clangd::Range;
static inline Range getEmptyKey() {
static clang::clangd::Position Tomb{-1, -1};
static Range R{Tomb, Tomb};
return R;
}
static inline Range getTombstoneKey() {
static clang::clangd::Position Tomb{-2, -2};
static Range R{Tomb, Tomb};
return R;
}
static unsigned getHashValue(const Range &Val) {
return llvm::hash_combine(Val.start.line, Val.start.character, Val.end.line,
Val.end.character);
}
static bool isEqual(const Range &LHS, const Range &RHS) {
return std::tie(LHS.start, LHS.end) == std::tie(RHS.start, RHS.end);
}
};

template <> struct format_provider<clang::clangd::Position> {
static void format(const clang::clangd::Position &Pos, raw_ostream &OS,
StringRef Style) {
Expand Down
11 changes: 8 additions & 3 deletions clang-tools-extra/clangd/index/SymbolCollector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,16 +174,21 @@ bool isSpelled(SourceLocation Loc, const NamedDecl &ND) {
auto Name = ND.getDeclName();
const auto NameKind = Name.getNameKind();
if (NameKind != DeclarationName::Identifier &&
NameKind != DeclarationName::CXXConstructorName)
NameKind != DeclarationName::CXXConstructorName &&
NameKind != DeclarationName::ObjCZeroArgSelector &&
NameKind != DeclarationName::ObjCOneArgSelector &&
NameKind != DeclarationName::ObjCMultiArgSelector)
return false;
const auto &AST = ND.getASTContext();
const auto &SM = AST.getSourceManager();
const auto &LO = AST.getLangOpts();
clang::Token Tok;
if (clang::Lexer::getRawToken(Loc, Tok, SM, LO))
return false;
auto StrName = Name.getAsString();
return clang::Lexer::getSpelling(Tok, SM, LO) == StrName;
auto TokSpelling = clang::Lexer::getSpelling(Tok, SM, LO);
if (const auto *MD = dyn_cast<ObjCMethodDecl>(&ND))
return TokSpelling == MD->getSelector().getNameForSlot(0);
return TokSpelling == Name.getAsString();
}
} // namespace

Expand Down
413 changes: 357 additions & 56 deletions clang-tools-extra/clangd/refactor/Rename.cpp

Large diffs are not rendered by default.

39 changes: 32 additions & 7 deletions clang-tools-extra/clangd/refactor/Rename.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@

#include "Protocol.h"
#include "SourceCode.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LangOptions.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Error.h"
#include <optional>

Expand Down Expand Up @@ -53,13 +55,34 @@ struct RenameInputs {
struct RenameResult {
// The range of the symbol that the user can attempt to rename.
Range Target;
// Placeholder text for the rename operation if non-empty.
std::string Placeholder;
// Rename occurrences for the current main file.
std::vector<Range> LocalChanges;
// Complete edits for the rename, including LocalChanges.
// If the full set of changes is unknown, this field is empty.
FileEdits GlobalChanges;
};

/// Represents a symbol range where the symbol can potentially have multiple
/// tokens.
struct SymbolRange {
/// Ranges for the tokens that make up the symbol's name.
/// Usually a single range, but there can be multiple ranges if the tokens for
/// the symbol are split, e.g. ObjC selectors.
std::vector<Range> Ranges;

SymbolRange(Range R);
SymbolRange(std::vector<Range> Ranges);

/// Returns the first range.
Range range() const;

friend bool operator==(const SymbolRange &LHS, const SymbolRange &RHS);
friend bool operator!=(const SymbolRange &LHS, const SymbolRange &RHS);
friend bool operator<(const SymbolRange &LHS, const SymbolRange &RHS);
};

/// Renames all occurrences of the symbol. The result edits are unformatted.
/// If AllowCrossFile is false, returns an error if rename a symbol that's used
/// in another file (per the index).
Expand All @@ -71,8 +94,8 @@ llvm::Expected<RenameResult> rename(const RenameInputs &RInputs);
/// REQUIRED: Occurrences is sorted and doesn't have duplicated ranges.
llvm::Expected<Edit> buildRenameEdit(llvm::StringRef AbsFilePath,
llvm::StringRef InitialCode,
std::vector<Range> Occurrences,
llvm::StringRef NewName);
std::vector<SymbolRange> Occurrences,
llvm::ArrayRef<llvm::StringRef> NewNames);

/// Adjusts indexed occurrences to match the current state of the file.
///
Expand All @@ -84,25 +107,27 @@ llvm::Expected<Edit> buildRenameEdit(llvm::StringRef AbsFilePath,
/// The API assumes that Indexed contains only named occurrences (each
/// occurrence has the same length).
/// REQUIRED: Indexed is sorted.
std::optional<std::vector<Range>>
std::optional<std::vector<SymbolRange>>
adjustRenameRanges(llvm::StringRef DraftCode, llvm::StringRef Identifier,
std::vector<Range> Indexed, const LangOptions &LangOpts);
std::vector<Range> Indexed, const LangOptions &LangOpts,
std::optional<Selector> Selector);

/// Calculates the lexed occurrences that the given indexed occurrences map to.
/// Returns std::nullopt if we don't find a mapping.
///
/// Exposed for testing only.
///
/// REQUIRED: Indexed and Lexed are sorted.
std::optional<std::vector<Range>> getMappedRanges(ArrayRef<Range> Indexed,
ArrayRef<Range> Lexed);
std::optional<std::vector<SymbolRange>>
getMappedRanges(ArrayRef<Range> Indexed, ArrayRef<SymbolRange> Lexed);
/// Evaluates how good the mapped result is. 0 indicates a perfect match.
///
/// Exposed for testing only.
///
/// REQUIRED: Indexed and Lexed are sorted, Indexed and MappedIndex have the
/// same size.
size_t renameRangeAdjustmentCost(ArrayRef<Range> Indexed, ArrayRef<Range> Lexed,
size_t renameRangeAdjustmentCost(ArrayRef<Range> Indexed,
ArrayRef<SymbolRange> Lexed,
ArrayRef<size_t> MappedIndex);

} // namespace clangd
Expand Down
3 changes: 3 additions & 0 deletions clang-tools-extra/clangd/test/rename.test
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
# CHECK: "id": 1,
# CHECK-NEXT: "jsonrpc": "2.0",
# CHECK-NEXT: "result": {
# CHECK-NEXT: "placeholder": "foo",
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
# CHECK-NEXT: "character": 7,
# CHECK-NEXT: "line": 0
Expand All @@ -18,6 +20,7 @@
# CHECK-NEXT: "character": 4,
# CHECK-NEXT: "line": 0
# CHECK-NEXT: }
# CHECK-NEXT: }
# CHECK-NEXT: }
---
{"jsonrpc":"2.0","id":2,"method":"textDocument/prepareRename","params":{"textDocument":{"uri":"test:///foo.cpp"},"position":{"line":0,"character":2}}}
Expand Down
42 changes: 42 additions & 0 deletions clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -898,6 +898,48 @@ TEST(DiagnosticTest, ClangTidySelfContainedDiags) {
withFix(equalToFix(ExpectedDFix))))));
}

TEST(DiagnosticTest, ClangTidySelfContainedDiagsFormatting) {
Annotations Main(R"cpp(
class Interface {
public:
virtual void Reset1() = 0;
virtual void Reset2() = 0;
};
class A : public Interface {
// This will be marked by clangd to use override instead of virtual
$virtual1[[virtual ]]void $Reset1[[Reset1]]()$override1[[]];
$virtual2[[virtual ]]/**/void $Reset2[[Reset2]]()$override2[[]];
};
)cpp");
TestTU TU = TestTU::withCode(Main.code());
TU.ClangTidyProvider =
addTidyChecks("cppcoreguidelines-explicit-virtual-functions,");
clangd::Fix const ExpectedFix1{
"prefer using 'override' or (rarely) 'final' "
"instead of 'virtual'",
{TextEdit{Main.range("override1"), " override"},
TextEdit{Main.range("virtual1"), ""}},
{}};
clangd::Fix const ExpectedFix2{
"prefer using 'override' or (rarely) 'final' "
"instead of 'virtual'",
{TextEdit{Main.range("override2"), " override"},
TextEdit{Main.range("virtual2"), ""}},
{}};
// Note that in the Fix we expect the "virtual" keyword and the following
// whitespace to be deleted
EXPECT_THAT(TU.build().getDiagnostics(),
ifTidyChecks(UnorderedElementsAre(
AllOf(Diag(Main.range("Reset1"),
"prefer using 'override' or (rarely) 'final' "
"instead of 'virtual'"),
withFix(equalToFix(ExpectedFix1))),
AllOf(Diag(Main.range("Reset2"),
"prefer using 'override' or (rarely) 'final' "
"instead of 'virtual'"),
withFix(equalToFix(ExpectedFix2))))));
}

TEST(DiagnosticsTest, Preprocessor) {
// This looks like a preamble, but there's an #else in the middle!
// Check that:
Expand Down
220 changes: 203 additions & 17 deletions clang-tools-extra/clangd/unittests/RenameTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ std::string expectedResult(Annotations Test, llvm::StringRef NewName) {
return Result;
}

std::vector<SymbolRange> symbolRanges(llvm::ArrayRef<Range> Ranges) {
std::vector<SymbolRange> Result;
for (const auto &R : Ranges)
Result.emplace_back(R);
return Result;
}

TEST(RenameTest, WithinFileRename) {
// For each "^" this test moves cursor to its location and applies renaming
// while checking that all identifiers in [[]] ranges are also renamed.
Expand Down Expand Up @@ -876,6 +883,157 @@ TEST(RenameTest, WithinFileRename) {
}
}

TEST(RenameTest, ObjCWithinFileRename) {
struct TestCase {
/// Annotated source code that should be renamed. Every point (indicated by
/// `^`) will be used as a rename location.
llvm::StringRef Input;
/// The new name that should be given to the rename locaitons.
llvm::StringRef NewName;
/// The expected rename source code or `nullopt` if we expect rename to
/// fail.
std::optional<llvm::StringRef> Expected;
};
TestCase Tests[] = {// Simple rename
{
// Input
R"cpp(
@interface Foo
- (int)performA^ction:(int)action w^ith:(int)value;
@end
@implementation Foo
- (int)performAc^tion:(int)action w^ith:(int)value {
return [self performAction:action with:value];
}
@end
)cpp",
// New name
"performNewAction:by:",
// Expected
R"cpp(
@interface Foo
- (int)performNewAction:(int)action by:(int)value;
@end
@implementation Foo
- (int)performNewAction:(int)action by:(int)value {
return [self performNewAction:action by:value];
}
@end
)cpp",
},
// Rename selector with macro
{
// Input
R"cpp(
#define mySelector - (int)performAction:(int)action with:(int)value
@interface Foo
^mySelector;
@end
@implementation Foo
mySelector {
return [self performAction:action with:value];
}
@end
)cpp",
// New name
"performNewAction:by:",
// Expected error
std::nullopt,
},
// Rename selector in macro definition
{
// Input
R"cpp(
#define mySelector - (int)perform^Action:(int)action with:(int)value
@interface Foo
mySelector;
@end
@implementation Foo
mySelector {
return [self performAction:action with:value];
}
@end
)cpp",
// New name
"performNewAction:by:",
// Expected error
std::nullopt,
},
// Don't rename `@selector`
// `@selector` is not tied to a single selector. Eg. there
// might be multiple
// classes in the codebase that implement that selector.
// It's thus more like
// a string literal and we shouldn't rename it.
{
// Input
R"cpp(
@interface Foo
- (void)performA^ction:(int)action with:(int)value;
@end
@implementation Foo
- (void)performAction:(int)action with:(int)value {
SEL mySelector = @selector(performAction:with:);
}
@end
)cpp",
// New name
"performNewAction:by:",
// Expected
R"cpp(
@interface Foo
- (void)performNewAction:(int)action by:(int)value;
@end
@implementation Foo
- (void)performNewAction:(int)action by:(int)value {
SEL mySelector = @selector(performAction:with:);
}
@end
)cpp",
},
// Fail if rename initiated inside @selector
{
// Input
R"cpp(
@interface Foo
- (void)performAction:(int)action with:(int)value;
@end
@implementation Foo
- (void)performAction:(int)action with:(int)value {
SEL mySelector = @selector(perfo^rmAction:with:);
}
@end
)cpp",
// New name
"performNewAction:by:",
// Expected
std::nullopt,
}};
for (TestCase T : Tests) {
SCOPED_TRACE(T.Input);
Annotations Code(T.Input);
auto TU = TestTU::withCode(Code.code());
TU.ExtraArgs.push_back("-xobjective-c");
auto AST = TU.build();
auto Index = TU.index();
for (const auto &RenamePos : Code.points()) {
auto RenameResult =
rename({RenamePos, T.NewName, AST, testPath(TU.Filename),
getVFSFromAST(AST), Index.get()});
if (std::optional<StringRef> Expected = T.Expected) {
ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError();
ASSERT_EQ(1u, RenameResult->GlobalChanges.size());
EXPECT_EQ(
applyEdits(std::move(RenameResult->GlobalChanges)).front().second,
*Expected);
} else {
ASSERT_FALSE(bool(RenameResult));
consumeError(RenameResult.takeError());
}
}
}
}

TEST(RenameTest, Renameable) {
struct Case {
const char *Code;
Expand Down Expand Up @@ -926,12 +1084,38 @@ TEST(RenameTest, Renameable) {
void f(X x) {x+^+;})cpp",
"no symbol", HeaderFile},

{R"cpp(// disallow rename on non-normal identifiers.
{R"cpp(
@interface Foo {}
-(int) fo^o:(int)x; // Token is an identifier, but declaration name isn't a simple identifier.
- (int)[[fo^o]]:(int)x;
@end
)cpp",
"not a supported kind", HeaderFile},
nullptr, HeaderFile, "newName:"},
{R"cpp(//disallow as : count must match
@interface Foo {}
- (int)fo^o:(int)x;
@end
)cpp",
"invalid name: the chosen name \"MockName\" is not a valid identifier",
HeaderFile},
{R"cpp(
@interface Foo {}
- (int)[[o^ne]]:(int)one two:(int)two;
@end
)cpp",
nullptr, HeaderFile, "a:two:"},
{R"cpp(
@interface Foo {}
- (int)[[o^ne]]:(int)one [[two]]:(int)two;
@end
)cpp",
nullptr, HeaderFile, "a:b:"},
{R"cpp(
@interface Foo {}
- (int)o^ne:(int)one [[two]]:(int)two;
@end
)cpp",
nullptr, HeaderFile, "one:three:"},

{R"cpp(
void foo(int);
void foo(char);
Expand Down Expand Up @@ -1137,7 +1321,7 @@ TEST(RenameTest, Renameable) {
int [[V^ar]];
}
)cpp",
nullptr, !HeaderFile},
nullptr, !HeaderFile},
};

for (const auto& Case : Cases) {
Expand Down Expand Up @@ -1778,16 +1962,16 @@ TEST(CrossFileRenameTests, BuildRenameEdits) {
Annotations Code("[[😂]]");
auto LSPRange = Code.range();
llvm::StringRef FilePath = "/test/TestTU.cpp";
llvm::StringRef NewName = "abc";
auto Edit = buildRenameEdit(FilePath, Code.code(), {LSPRange}, NewName);
llvm::SmallVector<llvm::StringRef, 2> NewNames = {"abc"};
auto Edit = buildRenameEdit(FilePath, Code.code(), {LSPRange}, NewNames);
ASSERT_TRUE(bool(Edit)) << Edit.takeError();
ASSERT_EQ(1UL, Edit->Replacements.size());
EXPECT_EQ(FilePath, Edit->Replacements.begin()->getFilePath());
EXPECT_EQ(4UL, Edit->Replacements.begin()->getLength());

// Test invalid range.
LSPRange.end = {10, 0}; // out of range
Edit = buildRenameEdit(FilePath, Code.code(), {LSPRange}, NewName);
Edit = buildRenameEdit(FilePath, Code.code(), {LSPRange}, NewNames);
EXPECT_FALSE(Edit);
EXPECT_THAT(llvm::toString(Edit.takeError()),
testing::HasSubstr("fail to convert"));
Expand All @@ -1798,10 +1982,11 @@ TEST(CrossFileRenameTests, BuildRenameEdits) {
[[range]]
[[range]]
)cpp");
Edit = buildRenameEdit(FilePath, T.code(), T.ranges(), NewName);
Edit =
buildRenameEdit(FilePath, T.code(), symbolRanges(T.ranges()), NewNames);
ASSERT_TRUE(bool(Edit)) << Edit.takeError();
EXPECT_EQ(applyEdits(FileEdits{{T.code(), std::move(*Edit)}}).front().second,
expectedResult(T, NewName));
expectedResult(T, NewNames[0]));
}

TEST(CrossFileRenameTests, adjustRenameRanges) {
Expand Down Expand Up @@ -1855,8 +2040,9 @@ TEST(CrossFileRenameTests, adjustRenameRanges) {
for (const auto &T : Tests) {
SCOPED_TRACE(T.DraftCode);
Annotations Draft(T.DraftCode);
auto ActualRanges = adjustRenameRanges(
Draft.code(), "x", Annotations(T.IndexedCode).ranges(), LangOpts);
auto ActualRanges = adjustRenameRanges(Draft.code(), "x",
Annotations(T.IndexedCode).ranges(),
LangOpts, std::nullopt);
if (!ActualRanges)
EXPECT_THAT(Draft.ranges(), testing::IsEmpty());
else
Expand Down Expand Up @@ -1970,11 +2156,11 @@ TEST(RangePatchingHeuristic, GetMappedRanges) {
for (const auto &T : Tests) {
SCOPED_TRACE(T.IndexedCode);
auto Lexed = Annotations(T.LexedCode);
auto LexedRanges = Lexed.ranges();
std::vector<Range> ExpectedMatches;
auto LexedRanges = symbolRanges(Lexed.ranges());
std::vector<SymbolRange> ExpectedMatches;
for (auto P : Lexed.points()) {
auto Match = llvm::find_if(LexedRanges, [&P](const Range& R) {
return R.start == P;
auto Match = llvm::find_if(LexedRanges, [&P](const SymbolRange &R) {
return R.range().start == P;
});
ASSERT_NE(Match, LexedRanges.end());
ExpectedMatches.push_back(*Match);
Expand Down Expand Up @@ -2093,8 +2279,8 @@ TEST(CrossFileRenameTests, adjustmentCost) {
std::vector<size_t> MappedIndex;
for (size_t I = 0; I < C.ranges("lex").size(); ++I)
MappedIndex.push_back(I);
EXPECT_EQ(renameRangeAdjustmentCost(C.ranges("idx"), C.ranges("lex"),
MappedIndex),
EXPECT_EQ(renameRangeAdjustmentCost(
C.ranges("idx"), symbolRanges(C.ranges("lex")), MappedIndex),
T.ExpectedCost);
}
}
Expand Down
44 changes: 44 additions & 0 deletions clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ MATCHER(refRange, "") {
MATCHER_P2(OverriddenBy, Subject, Object, "") {
return arg == Relation{Subject.ID, RelationKind::OverriddenBy, Object.ID};
}
MATCHER(isSpelled, "") {
return static_cast<bool>(arg.Kind & RefKind::Spelled);
}
::testing::Matcher<const std::vector<Ref> &>
haveRanges(const std::vector<Range> Ranges) {
return ::testing::UnorderedPointwise(refRange(), Ranges);
Expand Down Expand Up @@ -524,6 +527,47 @@ TEST_F(SymbolCollectorTest, templateArgs) {
forCodeCompletion(false)))));
}

TEST_F(SymbolCollectorTest, ObjCRefs) {
Annotations Header(R"(
@interface Person
- (void)$talk[[talk]];
- (void)$say[[say]]:(id)something;
@end
@interface Person (Category)
- (void)categoryMethod;
- (void)multiArg:(id)a method:(id)b;
@end
)");
Annotations Main(R"(
@implementation Person
- (void)$talk[[talk]] {}
- (void)$say[[say]]:(id)something {}
@end
void fff(Person *p) {
[p $talk[[talk]]];
[p $say[[say]]:0];
[p categoryMethod];
[p multiArg:0 method:0];
}
)");
CollectorOpts.RefFilter = RefKind::All;
CollectorOpts.CollectMainFileRefs = true;
TestFileName = testPath("test.m");
runSymbolCollector(Header.code(), Main.code(),
{"-fblocks", "-xobjective-c++", "-Wno-objc-root-class"});
EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::talk").ID,
haveRanges(Main.ranges("talk")))));
EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
haveRanges(Main.ranges("say")))));
EXPECT_THAT(Refs,
Contains(Pair(findSymbol(Symbols, "Person::categoryMethod").ID,
ElementsAre(isSpelled()))));
EXPECT_THAT(Refs,
Contains(Pair(findSymbol(Symbols, "Person::multiArg:method:").ID,
ElementsAre(isSpelled()))));
}

TEST_F(SymbolCollectorTest, ObjCSymbols) {
const std::string Header = R"(
@interface Person
Expand Down
8 changes: 8 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,14 @@ Changes in existing checks
`AllowStringArrays` option, enabling the exclusion of array types with deduced
length initialized from string literals.

- Improved :doc:`modernize-use-override
<clang-tidy/checks/modernize/use-override>` check to also remove any trailing
whitespace when deleting the ``virtual`` keyword.

- Improved :doc:`readability-redundant-inline-specifier
<clang-tidy/checks/readability/redundant-inline-specifier>` check to properly
emit warnings for static data member with an in-class initializer.

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ struct Base {
virtual void f() = 0;
virtual void f2() const = 0;
virtual void g() = 0;
virtual void g2() = 0;

virtual void j() const;
virtual MustUseResultObject k();
Expand Down Expand Up @@ -126,6 +127,10 @@ struct SimpleCases : public Base {
virtual void t() throw();
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
// CHECK-FIXES: {{^}} void t() throw() override;

virtual /* */ void g2();
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: prefer using 'override' or (rarely) 'final' instead of 'virtual'
// CHECK-FIXES: {{^}} /* */ void g2() override;
};

// CHECK-MESSAGES-NOT: warning:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,17 @@ INLINE_MACRO()

#define INLINE_KW inline
INLINE_KW void fn10() { }

namespace {
class A
{
public:
static inline float test = 3.0F;
static inline double test2 = 3.0;
static inline int test3 = 3;

static inline float test4;
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:10: warning: variable 'test4' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
// CHECK-FIXES-STRICT: static float test4;
};
}
10 changes: 10 additions & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5274,6 +5274,11 @@ Intrinsics Support within Constant Expressions
The following builtin intrinsics can be used in constant expressions:
* ``__builtin_addcb``
* ``__builtin_addcs``
* ``__builtin_addc``
* ``__builtin_addcl``
* ``__builtin_addcll``
* ``__builtin_bitreverse8``
* ``__builtin_bitreverse16``
* ``__builtin_bitreverse32``
Expand Down Expand Up @@ -5320,6 +5325,11 @@ The following builtin intrinsics can be used in constant expressions:
* ``__builtin_rotateright16``
* ``__builtin_rotateright32``
* ``__builtin_rotateright64``
* ``__builtin_subcb``
* ``__builtin_subcs``
* ``__builtin_subc``
* ``__builtin_subcl``
* ``__builtin_subcll``
The following x86-specific intrinsics can be used in constant expressions:
Expand Down
24 changes: 24 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,28 @@ C23 Feature Support
- No longer diagnose use of binary literals as an extension in C23 mode. Fixes
`#72017 <https://github.com/llvm/llvm-project/issues/72017>`_.

- Corrected parsing behavior for the ``alignas`` specifier/qualifier in C23. We
previously handled it as an attribute as in C++, but there are parsing
differences. The behavioral differences are:

.. code-block:: c
struct alignas(8) /* was accepted, now rejected */ S {
char alignas(8) /* was rejected, now accepted */ C;
};
int i alignas(8) /* was accepted, now rejected */ ;
Fixes (`#81472 <https://github.com/llvm/llvm-project/issues/81472>`_).

Non-comprehensive list of changes in this release
-------------------------------------------------

- Added ``__builtin_readsteadycounter`` for reading fixed frequency hardware
counters.

- ``__builtin_addc``, ``__builtin_subc``, and the other sizes of those
builtins are now constexpr and may be used in constant expressions.

New Compiler Flags
------------------

Expand Down Expand Up @@ -305,9 +321,17 @@ DWARF Support in Clang
Floating Point Support in Clang
-------------------------------

Fixed Point Support in Clang
----------------------------

- Support fixed point precision macros according to ``7.18a.3`` of
`ISO/IEC TR 18037:2008 <https://standards.iso.org/ittf/PubliclyAvailableStandards/c051126_ISO_IEC_TR_18037_2008.zip>`_.

AST Matchers
------------

- ``isInStdNamespace`` now supports Decl declared with ``extern "C++"``.

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

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/AST/OperationKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,9 @@ CAST_OPERATION(AddressSpaceConversion)
// Convert an integer initializer to an OpenCL sampler.
CAST_OPERATION(IntToOCLSampler)

// Truncate a vector type by dropping elements from the end (HLSL only).
CAST_OPERATION(HLSLVectorTruncation)

//===- Binary Operations -------------------------------------------------===//
// Operators listed in order of precedence.
// Note that additions to this should also update the StmtVisitor class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ FIXABLE_GADGET(UPCAddressofArraySubscript) // '&DRE[any]' in an Unspecified Poin
FIXABLE_GADGET(UPCStandalonePointer)
FIXABLE_GADGET(UPCPreIncrement) // '++Ptr' in an Unspecified Pointer Context
FIXABLE_GADGET(UUCAddAssign) // 'Ptr += n' in an Unspecified Untyped Context
FIXABLE_GADGET(PointerAssignment)
FIXABLE_GADGET(PtrToPtrAssignment)
FIXABLE_GADGET(CArrayToPtrAssignment)
FIXABLE_GADGET(PointerInit)

#undef FIXABLE_GADGET
Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -4067,14 +4067,14 @@ class MPATemplate : Template<

def Addc : Builtin, MPATemplate {
let Spellings = ["__builtin_addc"];
let Attributes = [NoThrow];
let Attributes = [NoThrow, Constexpr];
// FIXME: Why are these argumentes marked const?
let Prototype = "T(T const, T const, T const, T*)";
}

def Subc : Builtin, MPATemplate {
let Spellings = ["__builtin_subc"];
let Attributes = [NoThrow];
let Attributes = [NoThrow, Constexpr];
// FIXME: Why are these argumentes marked const?
let Prototype = "T(T const, T const, T const, T*)";
}
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticCommonKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,8 @@ def warn_target_unrecognized_env : Warning<
def warn_knl_knm_isa_support_removed : Warning<
"KNL, KNM related Intel Xeon Phi CPU's specific ISA's supports will be removed in LLVM 19.">,
InGroup<DiagGroup<"knl-knm-isa-support-removed">>;
def err_target_unsupported_abi_with_fpu : Error<
"'%0' ABI is not supported with FPU">;

// Source manager
def err_cannot_open_file : Error<"cannot open file '%0': %1">, DefaultFatal;
Expand Down
3 changes: 0 additions & 3 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1355,9 +1355,6 @@ def warn_pragma_acc_ignored
DefaultIgnore;
def err_acc_unexpected_directive
: Error<"unexpected OpenACC directive %select{|'#pragma acc %1'}0">;
def warn_pragma_acc_unimplemented
: Warning<"OpenACC directives not yet implemented, pragma ignored">,
InGroup<SourceUsesOpenACC>;
def err_acc_invalid_directive
: Error<"invalid OpenACC directive %select{%1|'%1 %2'}0">;
def err_acc_invalid_clause : Error<"invalid OpenACC clause %0">;
Expand Down
16 changes: 16 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -11314,6 +11314,8 @@ def err_omp_wrong_dependency_iterator_type : Error<
def err_target_unsupported_type
: Error<"%0 requires %select{|%2 bit size}1 %3 %select{|return }4type support,"
" but target '%5' does not support it">;
def err_target_unsupported_type_for_abi
: Error<"%0 requires %1 type support, but ABI '%2' does not support it">;
def err_omp_lambda_capture_in_declare_target_not_to : Error<
"variable captured in declare target region must appear in a to clause">;
def err_omp_device_type_mismatch : Error<
Expand Down Expand Up @@ -12110,6 +12112,9 @@ def err_hlsl_operator_unsupported : Error<
def err_hlsl_param_qualifier_mismatch :
Error<"conflicting parameter qualifier %0 on parameter %1">;

def warn_hlsl_impcast_vector_truncation : Warning<
"implicit conversion truncates vector: %0 to %1">, InGroup<Conversion>;

// Layout randomization diagnostics.
def err_non_designated_init_used : Error<
"a randomized struct can only be initialized with a designated initializer">;
Expand Down Expand Up @@ -12184,4 +12189,15 @@ def err_wasm_builtin_arg_must_match_table_element_type : Error <
"%ordinal0 argument must match the element type of the WebAssembly table in the %ordinal1 argument">;
def err_wasm_builtin_arg_must_be_integer_type : Error <
"%ordinal0 argument must be an integer">;

// OpenACC diagnostics.
def warn_acc_construct_unimplemented
: Warning<"OpenACC construct '%0' not yet implemented, pragma ignored">,
InGroup<SourceUsesOpenACC>;
def warn_acc_clause_unimplemented
: Warning<"OpenACC clause '%0' not yet implemented, clause ignored">,
InGroup<SourceUsesOpenACC>;
def err_acc_construct_appertainment
: Error<"OpenACC construct '%0' cannot be used here; it can only "
"be used in a statement context">;
} // end of sema component.
4 changes: 2 additions & 2 deletions clang/include/clang/Basic/FileManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,15 +283,15 @@ class FileManager : public RefCountedBase<FileManager> {
bool RequiresNullTerminator = true);
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
getBufferForFile(StringRef Filename, bool isVolatile = false,
bool RequiresNullTerminator = true) {
bool RequiresNullTerminator = true) const {
return getBufferForFileImpl(Filename, /*FileSize=*/-1, isVolatile,
RequiresNullTerminator);
}

private:
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
getBufferForFileImpl(StringRef Filename, int64_t FileSize, bool isVolatile,
bool RequiresNullTerminator);
bool RequiresNullTerminator) const;

public:
/// Get the 'stat' information for the given \p Path.
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Basic/TargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ class TargetInfo : public TransferrableTargetInfo,
bool HasIbm128;
bool HasLongDouble;
bool HasFPReturn;
bool HasFPTypes;
bool HasStrictFP;

unsigned char MaxAtomicPromoteWidth, MaxAtomicInlineWidth;
Expand Down Expand Up @@ -689,6 +690,9 @@ class TargetInfo : public TransferrableTargetInfo,
/// on this target.
virtual bool hasFPReturn() const { return HasFPReturn; }

/// Determine whether floating point types are supported for this target.
virtual bool hasFPTypes() const { return HasFPTypes; }

/// Determine whether constrained floating point is supported on this target.
virtual bool hasStrictFP() const { return HasStrictFP; }

Expand Down Expand Up @@ -1331,6 +1335,10 @@ class TargetInfo : public TransferrableTargetInfo,
return false;
}

/// Make changes to the supported types which depend on both the target
/// features and ABI.
virtual void setSupportedArgTypes() {}

/// Use the specified unit for FP math.
///
/// \return False on error (invalid unit name).
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Driver/ClangOptionDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@ GCC-compatible ``clang`` and ``clang++`` drivers.

}];

string Program = "clang";
string Program = "Clang";
// Note: We *must* use DefaultVis and not ClangOption, since that's
// the name of the actual TableGen record. The alias will not work.
list<string> VisibilityMask = ["DefaultVis"];
list<string> IgnoreFlags = ["HelpHidden", "Unsupported", "Ignored"];
}

#define GENERATING_DOCS
include "Options.td"
61 changes: 46 additions & 15 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,25 @@
// Include the common option parsing interfaces.
include "llvm/Option/OptParser.td"

// When generating documentation, we expect there to be a GlobalDocumentation
// def containing the program name that we are generating documentation for.
// This object should only be used by things that are used in documentation,
// such as the group descriptions.
#ifndef GENERATING_DOCS
// So that this file can still be parsed without such a def, define one if there
// isn't one provided.
def GlobalDocumentation {
// Sensible default in case of mistakes.
string Program = "Clang";
}
#endif

// Use this to generate program specific documentation, for example:
// StringForProgram<"Control how %Program behaves.">.str
class StringForProgram<string _str> {
string str = !subst("%Program", GlobalDocumentation.Program, _str);
}

/////////
// Flags

Expand Down Expand Up @@ -100,14 +119,16 @@ def Action_Group : OptionGroup<"<action group>">, DocName<"Actions">,
// Meta-group for options which are only used for compilation,
// and not linking etc.
def CompileOnly_Group : OptionGroup<"<CompileOnly group>">,
DocName<"Compilation options">, DocBrief<[{
Flags controlling the behavior of Clang during compilation. These flags have
no effect during actions that do not perform compilation.}]>;
DocName<"Compilation options">,
DocBrief<StringForProgram<[{
Flags controlling the behavior of %Program during compilation. These flags have
no effect during actions that do not perform compilation.}]>.str>;

def Preprocessor_Group : OptionGroup<"<Preprocessor group>">,
Group<CompileOnly_Group>,
DocName<"Preprocessor options">, DocBrief<[{
Flags controlling the behavior of the Clang preprocessor.}]>;
DocName<"Preprocessor options">,
DocBrief<StringForProgram<[{
Flags controlling the behavior of the %Program preprocessor.}]>.str>;

def IncludePath_Group : OptionGroup<"<I/i group>">, Group<Preprocessor_Group>,
DocName<"Include path management">,
Expand All @@ -128,9 +149,15 @@ def d_Group : OptionGroup<"<d group>">, Group<Preprocessor_Group>,
Flags allowing the state of the preprocessor to be dumped in various ways.}]>;

def Diag_Group : OptionGroup<"<W/R group>">, Group<CompileOnly_Group>,
DocName<"Diagnostic options">, DocBrief<[{
Flags controlling which warnings, errors, and remarks Clang will generate.
See the :doc:`full list of warning and remark flags <DiagnosticsReference>`.}]>;
DocName<"Diagnostic options">,
DocBrief<!strconcat(StringForProgram<
"Flags controlling which warnings, errors, and remarks %Program will generate. ">.str,
// When in clang link directly to the page.
!cond(!eq(GlobalDocumentation.Program, "Clang"):
"See the :doc:`full list of warning and remark flags <DiagnosticsReference>`.",
// When elsewhere the link will not work.
true:
"See Clang's Diagnostic Reference for a full list of warning and remark flags."))>;

def R_Group : OptionGroup<"<R group>">, Group<Diag_Group>, DocFlatten;
def R_value_Group : OptionGroup<"<R (with value) group>">, Group<R_Group>,
Expand Down Expand Up @@ -3204,10 +3231,10 @@ def fno_experimental_isel : Flag<["-"], "fno-experimental-isel">, Group<f_clang_
def fveclib : Joined<["-"], "fveclib=">, Group<f_Group>,
Visibility<[ClangOption, CC1Option, FlangOption, FC1Option]>,
HelpText<"Use the given vector functions library">,
Values<"Accelerate,libmvec,MASSV,SVML,SLEEF,Darwin_libsystem_m,ArmPL,none">,
Values<"Accelerate,libmvec,MASSV,SVML,SLEEF,Darwin_libsystem_m,ArmPL,AMDLIBM,none">,
NormalizedValuesScope<"llvm::driver::VectorLibrary">,
NormalizedValues<["Accelerate", "LIBMVEC", "MASSV", "SVML", "SLEEF",
"Darwin_libsystem_m", "ArmPL", "NoLibrary"]>,
"Darwin_libsystem_m", "ArmPL", "AMDLIBM", "NoLibrary"]>,
MarshallingInfoEnum<CodeGenOpts<"VecLib">, "NoLibrary">;
def fno_lax_vector_conversions : Flag<["-"], "fno-lax-vector-conversions">, Group<f_Group>,
Alias<flax_vector_conversions_EQ>, AliasArgs<["none"]>;
Expand Down Expand Up @@ -4633,11 +4660,15 @@ def menable_experimental_extensions : Flag<["-"], "menable-experimental-extensio
def mrvv_vector_bits_EQ : Joined<["-"], "mrvv-vector-bits=">, Group<m_Group>,
Visibility<[ClangOption, FlangOption]>,
HelpText<"Specify the size in bits of an RVV vector register">,
DocBrief<"Defaults to the vector length agnostic value of \"scalable\". "
"Accepts power of 2 values between 64 and 65536. Also accepts "
"\"zvl\" to use the value implied by -march/-mcpu. On Clang, value "
"will be reflected in __riscv_v_fixed_vlen preprocessor define "
"(RISC-V only)">;
DocBrief<!strconcat(
"Defaults to the vector length agnostic value of \"scalable\". "
"Accepts power of 2 values between 64 and 65536. Also accepts "
"\"zvl\" to use the value implied by -march/-mcpu.",
!cond(
// Flang does not set the preprocessor define.
!eq(GlobalDocumentation.Program, "Flang") : "",
true: " The value will be reflected in __riscv_v_fixed_vlen preprocessor define"),
" (RISC-V only)")>;

def munaligned_access : Flag<["-"], "munaligned-access">, Group<m_Group>,
HelpText<"Allow memory accesses to be unaligned (AArch32/AArch64/LoongArch/RISC-V only)">;
Expand Down
6 changes: 3 additions & 3 deletions clang/include/clang/Format/Format.h
Original file line number Diff line number Diff line change
Expand Up @@ -1013,7 +1013,7 @@ struct FormatStyle {
/// This option is renamed to ``BreakAfterReturnType``.
/// \version 3.8
/// @deprecated
ReturnTypeBreakingStyle AlwaysBreakAfterReturnType;
// ReturnTypeBreakingStyle AlwaysBreakAfterReturnType;

/// If ``true``, always break before multiline string literals.
///
Expand Down Expand Up @@ -1579,7 +1579,7 @@ struct FormatStyle {

/// The function declaration return type breaking style to use.
/// \version 19
// ReturnTypeBreakingStyle BreakAfterReturnType;
ReturnTypeBreakingStyle BreakAfterReturnType;

/// If ``true``, clang-format will always break after a Json array ``[``
/// otherwise it will scan until the closing ``]`` to determine if it should
Expand Down Expand Up @@ -4824,7 +4824,6 @@ struct FormatStyle {
R.AllowShortIfStatementsOnASingleLine &&
AllowShortLambdasOnASingleLine == R.AllowShortLambdasOnASingleLine &&
AllowShortLoopsOnASingleLine == R.AllowShortLoopsOnASingleLine &&
AlwaysBreakAfterReturnType == R.AlwaysBreakAfterReturnType &&
AlwaysBreakBeforeMultilineStrings ==
R.AlwaysBreakBeforeMultilineStrings &&
AttributeMacros == R.AttributeMacros &&
Expand All @@ -4835,6 +4834,7 @@ struct FormatStyle {
BreakAdjacentStringLiterals == R.BreakAdjacentStringLiterals &&
BreakAfterAttributes == R.BreakAfterAttributes &&
BreakAfterJavaFieldAnnotations == R.BreakAfterJavaFieldAnnotations &&
BreakAfterReturnType == R.BreakAfterReturnType &&
BreakArrays == R.BreakArrays &&
BreakBeforeBinaryOperators == R.BreakBeforeBinaryOperators &&
BreakBeforeBraces == R.BreakBeforeBraces &&
Expand Down
16 changes: 15 additions & 1 deletion clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -3572,7 +3572,21 @@ class Parser : public CodeCompletionHandler {
StmtResult ParseOpenACCDirectiveStmt();

private:
void ParseOpenACCDirective();
/// A struct to hold the information that got parsed by ParseOpenACCDirective,
/// so that the callers of it can use that to construct the appropriate AST
/// nodes.
struct OpenACCDirectiveParseInfo {
OpenACCDirectiveKind DirKind;
SourceLocation StartLoc;
SourceLocation EndLoc;
// TODO OpenACC: Add Clause list here once we have a type for that.
// TODO OpenACC: As we implement support for the Atomic, Routine, Cache, and
// Wait constructs, we likely want to put that information in here as well.
};

/// Parses the OpenACC directive (the entire pragma) including the clause
/// list, but does not produce the main AST node.
OpenACCDirectiveParseInfo ParseOpenACCDirective();
/// Helper that parses an ID Expression based on the language options.
ExprResult ParseOpenACCIDExpression();
/// Parses the variable list for the `cache` construct.
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Parse/RAIIObjectsForParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ namespace clang {

/// This can be used to restore the state early, before the dtor
/// is run.
void restore() { P.OpenMPDirectiveParsing = OldVal; }
void restore() { P.OpenACCDirectiveParsing = OldVal; }

~ParsingOpenACCDirectiveRAII() { restore(); }
};
Expand Down
12 changes: 11 additions & 1 deletion clang/include/clang/Sema/Overload.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ class Sema;
/// Fixed point type conversions according to N1169.
ICK_Fixed_Point_Conversion,

/// HLSL vector truncation.
ICK_HLSL_Vector_Truncation,

/// The number of conversion kinds
ICK_Num_Conversion_Kinds,
};
Expand Down Expand Up @@ -271,6 +274,12 @@ class Sema;
/// pointer-to-member conversion, or boolean conversion.
ImplicitConversionKind Second : 8;

/// Element - Between the second and third conversion a vector or matrix
/// element conversion may occur. If this is not ICK_Identity this
/// conversion is applied element-wise to each element in the vector or
/// matrix.
ImplicitConversionKind Element : 8;

/// Third - The third conversion can be a qualification conversion
/// or a function conversion.
ImplicitConversionKind Third : 8;
Expand Down Expand Up @@ -367,7 +376,8 @@ class Sema;
void setAsIdentityConversion();

bool isIdentityConversion() const {
return Second == ICK_Identity && Third == ICK_Identity;
return Second == ICK_Identity && Element == ICK_Identity &&
Third == ICK_Identity;
}

ImplicitConversionRank getRank() const;
Expand Down
41 changes: 41 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "clang/Basic/DarwinSDKInfo.h"
#include "clang/Basic/ExpressionTraits.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/OpenACCKinds.h"
#include "clang/Basic/OpenCLOptions.h"
#include "clang/Basic/OpenMPKinds.h"
#include "clang/Basic/PragmaKinds.h"
Expand Down Expand Up @@ -12701,6 +12702,46 @@ class Sema final {
OMPClause *ActOnOpenMPXBareClause(SourceLocation StartLoc,
SourceLocation EndLoc);

//===--------------------------------------------------------------------===//
// OpenACC directives and clauses.

/// Called after parsing an OpenACC Clause so that it can be checked.
bool ActOnOpenACCClause(OpenACCClauseKind ClauseKind,
SourceLocation StartLoc);

/// Called after the construct has been parsed, but clauses haven't been
/// parsed. This allows us to diagnose not-implemented, as well as set up any
/// state required for parsing the clauses.
void ActOnOpenACCConstruct(OpenACCDirectiveKind K, SourceLocation StartLoc);

/// Called after the directive, including its clauses, have been parsed and
/// parsing has consumed the 'annot_pragma_openacc_end' token. This DOES
/// happen before any associated declarations or statements have been parsed.
/// This function is only called when we are parsing a 'statement' context.
bool ActOnStartOpenACCStmtDirective(OpenACCDirectiveKind K,
SourceLocation StartLoc);

/// Called after the directive, including its clauses, have been parsed and
/// parsing has consumed the 'annot_pragma_openacc_end' token. This DOES
/// happen before any associated declarations or statements have been parsed.
/// This function is only called when we are parsing a 'Decl' context.
bool ActOnStartOpenACCDeclDirective(OpenACCDirectiveKind K,
SourceLocation StartLoc);
/// Called when we encounter an associated statement for our construct, this
/// should check legality of the statement as it appertains to this Construct.
StmtResult ActOnOpenACCAssociatedStmt(OpenACCDirectiveKind K,
StmtResult AssocStmt);

/// Called after the directive has been completely parsed, including the
/// declaration group or associated statement.
StmtResult ActOnEndOpenACCStmtDirective(OpenACCDirectiveKind K,
SourceLocation StartLoc,
SourceLocation EndLoc,
StmtResult AssocStmt);
/// Called after the directive has been completely parsed, including the
/// declaration group or associated statement.
DeclGroupRef ActOnEndOpenACCDeclDirective();

/// The kind of conversion being performed.
enum CheckedConversionKind {
/// An implicit conversion.
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/DeclBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ bool Decl::isInAnonymousNamespace() const {

bool Decl::isInStdNamespace() const {
const DeclContext *DC = getDeclContext();
return DC && DC->isStdNamespace();
return DC && DC->getNonTransparentContext()->isStdNamespace();
}

bool Decl::isFileContextDecl() const {
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1897,6 +1897,7 @@ bool CastExpr::CastConsistency() const {
case CK_FixedPointToIntegral:
case CK_IntegralToFixedPoint:
case CK_MatrixCast:
case CK_HLSLVectorTruncation:
assert(!getType()->isBooleanType() && "unheralded conversion to bool");
goto CheckNoBasePath;

Expand Down
55 changes: 55 additions & 0 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12696,6 +12696,59 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
return BuiltinOp == Builtin::BI__atomic_always_lock_free ?
Success(0, E) : Error(E);
}
case Builtin::BI__builtin_addcb:
case Builtin::BI__builtin_addcs:
case Builtin::BI__builtin_addc:
case Builtin::BI__builtin_addcl:
case Builtin::BI__builtin_addcll:
case Builtin::BI__builtin_subcb:
case Builtin::BI__builtin_subcs:
case Builtin::BI__builtin_subc:
case Builtin::BI__builtin_subcl:
case Builtin::BI__builtin_subcll: {
LValue CarryOutLValue;
APSInt LHS, RHS, CarryIn, CarryOut, Result;
QualType ResultType = E->getArg(0)->getType();
if (!EvaluateInteger(E->getArg(0), LHS, Info) ||
!EvaluateInteger(E->getArg(1), RHS, Info) ||
!EvaluateInteger(E->getArg(2), CarryIn, Info) ||
!EvaluatePointer(E->getArg(3), CarryOutLValue, Info))
return false;
// Copy the number of bits and sign.
Result = LHS;
CarryOut = LHS;

bool FirstOverflowed = false;
bool SecondOverflowed = false;
switch (BuiltinOp) {
default:
llvm_unreachable("Invalid value for BuiltinOp");
case Builtin::BI__builtin_addcb:
case Builtin::BI__builtin_addcs:
case Builtin::BI__builtin_addc:
case Builtin::BI__builtin_addcl:
case Builtin::BI__builtin_addcll:
Result =
LHS.uadd_ov(RHS, FirstOverflowed).uadd_ov(CarryIn, SecondOverflowed);
break;
case Builtin::BI__builtin_subcb:
case Builtin::BI__builtin_subcs:
case Builtin::BI__builtin_subc:
case Builtin::BI__builtin_subcl:
case Builtin::BI__builtin_subcll:
Result =
LHS.usub_ov(RHS, FirstOverflowed).usub_ov(CarryIn, SecondOverflowed);
break;
}

// It is possible for both overflows to happen but CGBuiltin uses an OR so
// this is consistent.
CarryOut = (uint64_t)(FirstOverflowed | SecondOverflowed);
APValue APV{CarryOut};
if (!handleAssignment(Info, E, CarryOutLValue, ResultType, APV))
return false;
return Success(Result, E);
}
case Builtin::BI__builtin_add_overflow:
case Builtin::BI__builtin_sub_overflow:
case Builtin::BI__builtin_mul_overflow:
Expand Down Expand Up @@ -13927,6 +13980,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) {
case CK_FixedPointCast:
case CK_IntegralToFixedPoint:
case CK_MatrixCast:
case CK_HLSLVectorTruncation:
llvm_unreachable("invalid cast kind for integral value");

case CK_BitCast:
Expand Down Expand Up @@ -14765,6 +14819,7 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) {
case CK_FixedPointToIntegral:
case CK_IntegralToFixedPoint:
case CK_MatrixCast:
case CK_HLSLVectorTruncation:
llvm_unreachable("invalid cast kind for complex value");

case CK_LValueToRValue:
Expand Down
69 changes: 47 additions & 22 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1829,8 +1829,19 @@ bool ByteCodeExprGen<Emitter>::VisitCXXConstructExpr(
return false;
}

if (!this->emitCall(Func, E))
return false;
if (Func->isVariadic()) {
uint32_t VarArgSize = 0;
unsigned NumParams = Func->getNumWrittenParams();
for (unsigned I = NumParams, N = E->getNumArgs(); I != N; ++I) {
VarArgSize +=
align(primSize(classify(E->getArg(I)->getType()).value_or(PT_Ptr)));
}
if (!this->emitCallVar(Func, VarArgSize, E))
return false;
} else {
if (!this->emitCall(Func, 0, E))
return false;
}

// Immediately call the destructor if we have to.
if (DiscardResult) {
Expand Down Expand Up @@ -1863,7 +1874,7 @@ bool ByteCodeExprGen<Emitter>::VisitCXXConstructExpr(
return false;
}

if (!this->emitCall(Func, E))
if (!this->emitCall(Func, 0, E))
return false;
}
return true;
Expand Down Expand Up @@ -2049,7 +2060,7 @@ bool ByteCodeExprGen<Emitter>::VisitCXXInheritedCtorInitExpr(
Offset += align(primSize(PT));
}

return this->emitCall(F, E);
return this->emitCall(F, 0, E);
}

template <class Emitter>
Expand Down Expand Up @@ -2575,7 +2586,7 @@ ByteCodeExprGen<Emitter>::allocateLocal(DeclTy &&Src, bool IsExtended) {
Src, Ty.getTypePtr(), Descriptor::InlineDescMD, Ty.isConstQualified(),
IsTemporary, /*IsMutable=*/false, Init);
if (!D)
return {};
return std::nullopt;

Scope::Local Local = this->createLocal(D);
if (Key)
Expand Down Expand Up @@ -2824,14 +2835,6 @@ bool ByteCodeExprGen<Emitter>::VisitCallExpr(const CallExpr *E) {
const Function *Func = getFunction(FuncDecl);
if (!Func)
return false;
// If the function is being compiled right now, this is a recursive call.
// In that case, the function can't be valid yet, even though it will be
// later.
// If the function is already fully compiled but not constexpr, it was
// found to be faulty earlier on, so bail out.
if (Func->isFullyCompiled() && !Func->isConstexpr())
return false;

assert(HasRVO == Func->hasRVO());

bool HasQualifier = false;
Expand All @@ -2846,20 +2849,38 @@ bool ByteCodeExprGen<Emitter>::VisitCallExpr(const CallExpr *E) {
// and if the function has RVO, we already have the pointer on the stack to
// write the result into.
if (IsVirtual && !HasQualifier) {
if (!this->emitCallVirt(Func, E))
uint32_t VarArgSize = 0;
unsigned NumParams = Func->getNumWrittenParams();
for (unsigned I = NumParams, N = E->getNumArgs(); I != N; ++I)
VarArgSize += align(primSize(classify(E->getArg(I)).value_or(PT_Ptr)));

if (!this->emitCallVirt(Func, VarArgSize, E))
return false;
} else if (Func->isVariadic()) {
uint32_t VarArgSize = 0;
unsigned NumParams = Func->getNumWrittenParams();
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))
return false;
} else {
if (!this->emitCall(Func, E))
if (!this->emitCall(Func, 0, E))
return false;
}
} else {
// Indirect call. Visit the callee, which will leave a FunctionPointer on
// the stack. Cleanup of the returned value if necessary will be done after
// the function call completed.

// Sum the size of all args from the call expr.
uint32_t ArgSize = 0;
for (unsigned I = 0, N = E->getNumArgs(); I != N; ++I)
ArgSize += align(primSize(classify(E->getArg(I)).value_or(PT_Ptr)));

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

if (!this->emitCallPtr(E))
if (!this->emitCallPtr(ArgSize, E))
return false;
}

Expand Down Expand Up @@ -3210,12 +3231,16 @@ bool ByteCodeExprGen<Emitter>::VisitDeclRefExpr(const DeclRefExpr *E) {
return this->emitGetPtrThisField(Offset, E);
}

// Lazily visit global declarations we haven't seen yet.
// This happens in C.
if (!Ctx.getLangOpts().CPlusPlus) {
// Try to lazily visit (or emit dummy pointers for) declarations
// we haven't seen yet.
if (Ctx.getLangOpts().CPlusPlus) {
if (const auto *VD = dyn_cast<VarDecl>(D); VD && VD->isStaticLocal()) {
if (std::optional<unsigned> I = P.getOrCreateDummy(D))
return this->emitGetPtrGlobal(*I, E);
}
} else {
if (const auto *VD = dyn_cast<VarDecl>(D);
VD && VD->hasGlobalStorage() && VD->getAnyInitializer() &&
VD->getType().isConstQualified()) {
VD && VD->getAnyInitializer() && VD->getType().isConstQualified()) {
if (!this->visitVarDecl(VD))
return false;
// Retry.
Expand Down Expand Up @@ -3386,7 +3411,7 @@ bool ByteCodeExprGen<Emitter>::emitRecordDestruction(const Descriptor *Desc) {
assert(DtorFunc->getNumParams() == 1);
if (!this->emitDupPtr(SourceInfo{}))
return false;
if (!this->emitCall(DtorFunc, SourceInfo{}))
if (!this->emitCall(DtorFunc, 0, SourceInfo{}))
return false;
}
}
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/Interp/ByteCodeStmtGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ bool ByteCodeStmtGen<Emitter>::emitLambdaStaticInvokerBody(
return false;
}

if (!this->emitCall(Func, LambdaCallOp))
if (!this->emitCall(Func, 0, LambdaCallOp))
return false;

this->emitCleanup();
Expand Down
19 changes: 6 additions & 13 deletions clang/lib/AST/Interp/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {
assert(Stk.empty());
ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result);

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

if (Res.isInvalid()) {
Stk.clear();
Expand All @@ -58,16 +58,7 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {
Stk.clear();
#endif

// Implicit lvalue-to-rvalue conversion.
if (E->isGLValue()) {
std::optional<APValue> RValueResult = Res.toRValue();
if (!RValueResult) {
return false;
}
Result = *RValueResult;
} else {
Result = Res.toAPValue();
}
Result = Res.toAPValue();

return true;
}
Expand Down Expand Up @@ -120,7 +111,8 @@ bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD,
!Res.checkFullyInitialized(C.getState()))
return false;

// lvalue-to-rvalue conversion.
// lvalue-to-rvalue conversion. We do this manually here so we can
// examine the result above before converting and returning it.
std::optional<APValue> RValueResult = Res.toRValue();
if (!RValueResult)
return false;
Expand Down Expand Up @@ -209,7 +201,8 @@ bool Context::Run(State &Parent, const Function *Func, APValue &Result) {

{
InterpState State(Parent, *P, Stk, *this);
State.Current = new InterpFrame(State, Func, /*Caller=*/nullptr, {});
State.Current = new InterpFrame(State, Func, /*Caller=*/nullptr, CodePtr(),
Func->getArgSize());
if (Interpret(State, Result)) {
assert(Stk.empty());
return true;
Expand Down
22 changes: 19 additions & 3 deletions clang/lib/AST/Interp/EvalEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent,
: 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 =
new InterpFrame(S, /*Func=*/nullptr, /*Caller=*/nullptr, CodePtr());
new InterpFrame(S, /*Func=*/nullptr, /*Caller=*/nullptr, CodePtr(), 0);
}

EvalEmitter::~EvalEmitter() {
Expand All @@ -33,7 +33,9 @@ EvalEmitter::~EvalEmitter() {
}
}

EvaluationResult EvalEmitter::interpretExpr(const Expr *E) {
EvaluationResult EvalEmitter::interpretExpr(const Expr *E,
bool ConvertResultToRValue) {
this->ConvertResultToRValue = ConvertResultToRValue;
EvalResult.setSource(E);

if (!this->visitExpr(E) && EvalResult.empty())
Expand Down Expand Up @@ -119,12 +121,26 @@ template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) {
template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {
if (!isActive())
return true;
EvalResult.setPointer(S.Stk.pop<Pointer>());

const Pointer &Ptr = S.Stk.pop<Pointer>();
// Implicitly convert lvalue to rvalue, if requested.
if (ConvertResultToRValue) {
if (std::optional<APValue> V = Ptr.toRValue(Ctx)) {
EvalResult.setValue(*V);
} else {
return false;
}
} else {
EvalResult.setPointer(Ptr);
}

return true;
}
template <> bool EvalEmitter::emitRet<PT_FnPtr>(const SourceInfo &Info) {
if (!isActive())
return true;
// Function pointers cannot be converted to rvalues.
assert(!ConvertResultToRValue);
EvalResult.setFunctionPointer(S.Stk.pop<FunctionPointer>());
return true;
}
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/AST/Interp/EvalEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ class EvalEmitter : public SourceMapper {
using AddrTy = uintptr_t;
using Local = Scope::Local;

EvaluationResult interpretExpr(const Expr *E);
EvaluationResult interpretExpr(const Expr *E,
bool ConvertResultToRValue = false);
EvaluationResult interpretDecl(const VarDecl *VD);

InterpState &getState() { return S; }
Expand Down Expand Up @@ -86,6 +87,8 @@ class EvalEmitter : public SourceMapper {
InterpState S;
/// Location to write the result to.
EvaluationResult EvalResult;
/// Whether the result should be converted to an RValue.
bool ConvertResultToRValue = false;

/// Temporaries which require storage.
llvm::DenseMap<unsigned, std::unique_ptr<char[]>> Locals;
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/Interp/EvaluationResult.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ class EvaluationResult final {
void setSource(DeclTy D) { Source = D; }

void setValue(const APValue &V) {
// V could still be an LValue.
assert(empty());
assert(!V.isLValue());
Value = std::move(V);
Kind = RValue;
}
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/AST/Interp/Function.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,16 @@ class Function final {

unsigned getNumParams() const { return ParamTypes.size(); }

/// Returns the number of parameter this function takes when it's called,
/// i.e excluding the instance pointer and the RVO pointer.
unsigned getNumWrittenParams() const {
assert(getNumParams() >= (unsigned)(hasThisPointer() + hasRVO()));
return getNumParams() - hasThisPointer() - hasRVO();
}
unsigned getWrittenArgSize() const {
return ArgSize - (align(primSize(PT_Ptr)) * (hasThisPointer() + hasRVO()));
}

unsigned getParamOffset(unsigned ParamIndex) const {
return ParamOffsets[ParamIndex];
}
Expand Down
30 changes: 23 additions & 7 deletions clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,16 +169,27 @@ void cleanupAfterFunctionCall(InterpState &S, CodePtr OpPC) {
// CallExpr we're look for is at the return PC of the current function, i.e.
// in the caller.
// This code path should be executed very rarely.
const auto *CE =
cast<CallExpr>(S.Current->Caller->getExpr(S.Current->getRetPC()));
unsigned FixedParams = CurFunc->getNumParams();
int32_t ArgsToPop = CE->getNumArgs() - FixedParams;
assert(ArgsToPop >= 0);
for (int32_t I = ArgsToPop - 1; I >= 0; --I) {
const Expr *A = CE->getArg(FixedParams + I);
unsigned NumVarArgs;
const Expr *const *Args = nullptr;
unsigned NumArgs = 0;
const Expr *CallSite = S.Current->Caller->getExpr(S.Current->getRetPC());
if (const auto *CE = dyn_cast<CallExpr>(CallSite)) {
Args = CE->getArgs();
NumArgs = CE->getNumArgs();
} else if (const auto *CE = dyn_cast<CXXConstructExpr>(CallSite)) {
Args = CE->getArgs();
NumArgs = CE->getNumArgs();
} else
assert(false && "Can't get arguments from that expression type");

assert(NumArgs >= CurFunc->getNumWrittenParams());
NumVarArgs = NumArgs - CurFunc->getNumWrittenParams();
for (unsigned I = 0; I != NumVarArgs; ++I) {
const Expr *A = Args[NumArgs - 1 - I];
popArg(S, A);
}
}

// And in any case, remove the fixed parameters (the non-variadic ones)
// at the end.
S.Current->popArgs();
Expand Down Expand Up @@ -466,6 +477,11 @@ bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
if (!DiagDecl->isDefined() && S.checkingPotentialConstantExpression())
return false;

// If the declaration is defined _and_ declared 'constexpr', the below
// diagnostic doesn't add anything useful.
if (DiagDecl->isDefined() && DiagDecl->isConstexpr())
return false;

S.FFDiag(Loc, diag::note_constexpr_invalid_function, 1)
<< DiagDecl->isConstexpr() << (bool)CD << DiagDecl;
S.Note(DiagDecl->getLocation(), diag::note_declared_at);
Expand Down
79 changes: 66 additions & 13 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -1915,10 +1915,60 @@ inline bool ArrayDecay(InterpState &S, CodePtr OpPC) {
return false;
}

inline bool Call(InterpState &S, CodePtr OpPC, const Function *Func) {
inline bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func,
uint32_t VarArgSize) {
if (Func->hasThisPointer()) {
size_t ThisOffset =
Func->getArgSize() - (Func->hasRVO() ? primSize(PT_Ptr) : 0);
size_t ArgSize = Func->getArgSize() + VarArgSize;
size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0);
const Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset);

// If the current function is a lambda static invoker and
// the function we're about to call is a lambda call operator,
// skip the CheckInvoke, since the ThisPtr is a null pointer
// anyway.
if (!(S.Current->getFunction() &&
S.Current->getFunction()->isLambdaStaticInvoker() &&
Func->isLambdaCallOperator())) {
if (!CheckInvoke(S, OpPC, ThisPtr))
return false;
}

if (S.checkingPotentialConstantExpression())
return false;
}

if (!CheckCallable(S, OpPC, Func))
return false;

if (!CheckCallDepth(S, OpPC))
return false;

auto NewFrame = std::make_unique<InterpFrame>(S, Func, OpPC, VarArgSize);
InterpFrame *FrameBefore = S.Current;
S.Current = NewFrame.get();

APValue CallResult;
// Note that we cannot assert(CallResult.hasValue()) here since
// Ret() above only sets the APValue if the curent frame doesn't
// have a caller set.
if (Interpret(S, CallResult)) {
NewFrame.release(); // Frame was delete'd already.
assert(S.Current == FrameBefore);
return true;
}

// Interpreting the function failed somehow. Reset to
// previous state.
S.Current = FrameBefore;
return false;

return false;
}
inline bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
uint32_t VarArgSize) {
if (Func->hasThisPointer()) {
size_t ArgSize = Func->getArgSize() + VarArgSize;
size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0);

const Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset);

Expand All @@ -1943,7 +1993,7 @@ inline bool Call(InterpState &S, CodePtr OpPC, const Function *Func) {
if (!CheckCallDepth(S, OpPC))
return false;

auto NewFrame = std::make_unique<InterpFrame>(S, Func, OpPC);
auto NewFrame = std::make_unique<InterpFrame>(S, Func, OpPC, VarArgSize);
InterpFrame *FrameBefore = S.Current;
S.Current = NewFrame.get();

Expand All @@ -1963,11 +2013,12 @@ inline bool Call(InterpState &S, CodePtr OpPC, const Function *Func) {
return false;
}

inline bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func) {
inline bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
uint32_t VarArgSize) {
assert(Func->hasThisPointer());
assert(Func->isVirtual());
size_t ThisOffset =
Func->getArgSize() - (Func->hasRVO() ? primSize(PT_Ptr) : 0);
size_t ArgSize = Func->getArgSize() + VarArgSize;
size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0);
Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset);

const CXXRecordDecl *DynamicDecl =
Expand Down Expand Up @@ -1998,7 +2049,7 @@ inline bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func) {
}
}

return Call(S, OpPC, Func);
return Call(S, OpPC, Func, VarArgSize);
}

inline bool CallBI(InterpState &S, CodePtr &PC, const Function *Func,
Expand All @@ -2016,17 +2067,19 @@ inline bool CallBI(InterpState &S, CodePtr &PC, const Function *Func,
return false;
}

inline bool CallPtr(InterpState &S, CodePtr OpPC) {
inline bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize) {
const FunctionPointer &FuncPtr = S.Stk.pop<FunctionPointer>();

const Function *F = FuncPtr.getFunction();
if (!F || !F->isConstexpr())
return false;
assert(F);

assert(ArgSize >= F->getWrittenArgSize());
uint32_t VarArgSize = ArgSize - F->getWrittenArgSize();

if (F->isVirtual())
return CallVirt(S, OpPC, F);
return CallVirt(S, OpPC, F, VarArgSize);

return Call(S, OpPC, F);
return Call(S, OpPC, F, VarArgSize);
}

inline bool GetFnPtr(InterpState &S, CodePtr OpPC, const Function *Func) {
Expand Down
17 changes: 12 additions & 5 deletions clang/lib/AST/Interp/InterpFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ using namespace clang;
using namespace clang::interp;

InterpFrame::InterpFrame(InterpState &S, const Function *Func,
InterpFrame *Caller, CodePtr RetPC)
InterpFrame *Caller, CodePtr RetPC, unsigned ArgSize)
: Caller(Caller), S(S), Depth(Caller ? Caller->Depth + 1 : 0), Func(Func),
RetPC(RetPC), ArgSize(Func ? Func->getArgSize() : 0),
Args(static_cast<char *>(S.Stk.top())), FrameOffset(S.Stk.size()) {
RetPC(RetPC), ArgSize(ArgSize), Args(static_cast<char *>(S.Stk.top())),
FrameOffset(S.Stk.size()) {
if (!Func)
return;

Expand All @@ -43,8 +43,9 @@ InterpFrame::InterpFrame(InterpState &S, const Function *Func,
}
}

InterpFrame::InterpFrame(InterpState &S, const Function *Func, CodePtr RetPC)
: InterpFrame(S, Func, S.Current, RetPC) {
InterpFrame::InterpFrame(InterpState &S, const Function *Func, CodePtr RetPC,
unsigned VarArgSize)
: InterpFrame(S, Func, S.Current, RetPC, Func->getArgSize() + VarArgSize) {
// As per our calling convention, the this pointer is
// part of the ArgSize.
// If the function has RVO, the RVO pointer is first.
Expand Down Expand Up @@ -228,10 +229,16 @@ SourceInfo InterpFrame::getSource(CodePtr PC) const {
}

const Expr *InterpFrame::getExpr(CodePtr PC) const {
if (Func && (!Func->hasBody() || Func->getDecl()->isImplicit()) && Caller)
return Caller->getExpr(RetPC);

return S.getExpr(Func, PC);
}

SourceLocation InterpFrame::getLocation(CodePtr PC) const {
if (Func && (!Func->hasBody() || Func->getDecl()->isImplicit()) && Caller)
return Caller->getLocation(RetPC);

return S.getLocation(Func, PC);
}

Expand Down
5 changes: 3 additions & 2 deletions clang/lib/AST/Interp/InterpFrame.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@ class InterpFrame final : public Frame {

/// Creates a new frame for a method call.
InterpFrame(InterpState &S, const Function *Func, InterpFrame *Caller,
CodePtr RetPC);
CodePtr RetPC, unsigned ArgSize);

/// Creates a new frame with the values that make sense.
/// I.e., the caller is the current frame of S,
/// the This() pointer is the current Pointer on the top of S's stack,
/// and the RVO pointer is before that.
InterpFrame(InterpState &S, const Function *Func, CodePtr RetPC);
InterpFrame(InterpState &S, const Function *Func, CodePtr RetPC,
unsigned VarArgSize = 0);

/// Destroys the frame, killing all live pointers to stack slots.
~InterpFrame();
Expand Down
11 changes: 8 additions & 3 deletions clang/lib/AST/Interp/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -191,12 +191,12 @@ def NoRet : Opcode {}


def Call : Opcode {
let Args = [ArgFunction];
let Args = [ArgFunction, ArgUint32];
let Types = [];
}

def CallVirt : Opcode {
let Args = [ArgFunction];
let Args = [ArgFunction, ArgUint32];
let Types = [];
}

Expand All @@ -206,7 +206,12 @@ def CallBI : Opcode {
}

def CallPtr : Opcode {
let Args = [];
let Args = [ArgUint32];
let Types = [];
}

def CallVar : Opcode {
let Args = [ArgFunction, ArgUint32];
let Types = [];
}

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/Interp/Source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ SourceRange SourceInfo::getRange() const {
}

const Expr *SourceInfo::asExpr() const {
if (auto *S = Source.dyn_cast<const Stmt *>())
if (const auto *S = Source.dyn_cast<const Stmt *>())
return dyn_cast<Expr>(S);
return nullptr;
}
Expand Down
9 changes: 8 additions & 1 deletion clang/lib/Analysis/FlowSensitive/Transfer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -663,10 +663,17 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
void VisitInitListExpr(const InitListExpr *S) {
QualType Type = S->getType();

if (!Type->isStructureOrClassType()) {
if (Type->isUnionType()) {
if (auto *Val = Env.createValue(Type))
Env.setValue(*S, *Val);
return;
}

if (!Type->isStructureOrClassType()) {
// Until array initialization is implemented, we don't need to care about
// cases where `getNumInits() > 1`.
if (S->getNumInits() == 1)
propagateValueOrStorageLocation(*S->getInit(0), *S, Env);
return;
}

Expand Down
189 changes: 163 additions & 26 deletions clang/lib/Analysis/UnsafeBufferUsage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@
//===----------------------------------------------------------------------===//

#include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/Lexer.h"
Expand Down Expand Up @@ -281,10 +284,13 @@ isInUnspecifiedPointerContext(internal::Matcher<Stmt> InnerMatcher) {
// 4. the operand of a pointer subtraction operation
// (i.e., computing the distance between two pointers); or ...

auto CallArgMatcher =
callExpr(forEachArgumentWithParam(InnerMatcher,
hasPointerType() /* array also decays to pointer type*/),
unless(callee(functionDecl(hasAttr(attr::UnsafeBufferUsage)))));
// clang-format off
auto CallArgMatcher = callExpr(
forEachArgumentWithParamType(
InnerMatcher,
isAnyPointer() /* array also decays to pointer type*/),
unless(callee(
functionDecl(hasAttr(attr::UnsafeBufferUsage)))));

auto CastOperandMatcher =
castExpr(anyOf(hasCastKind(CastKind::CK_PointerToIntegral),
Expand All @@ -306,6 +312,7 @@ isInUnspecifiedPointerContext(internal::Matcher<Stmt> InnerMatcher) {
hasRHS(hasPointerType())),
eachOf(hasLHS(InnerMatcher),
hasRHS(InnerMatcher)));
// clang-format on

return stmt(anyOf(CallArgMatcher, CastOperandMatcher, CompOperandMatcher,
PtrSubtractionMatcher));
Expand Down Expand Up @@ -402,6 +409,39 @@ AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) {
}
return false;
}

AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) {
// FIXME: Proper solution:
// - refactor Sema::CheckArrayAccess
// - split safe/OOB/unknown decision logic from diagnostics emitting code
// - e. g. "Try harder to find a NamedDecl to point at in the note."
// already duplicated
// - call both from Sema and from here

const auto *BaseDRE =
dyn_cast<DeclRefExpr>(Node.getBase()->IgnoreParenImpCasts());
if (!BaseDRE)
return false;
if (!BaseDRE->getDecl())
return false;
const auto *CATy = Finder->getASTContext().getAsConstantArrayType(
BaseDRE->getDecl()->getType());
if (!CATy)
return false;
const APInt ArrSize = CATy->getSize();

if (const auto *IdxLit = dyn_cast<IntegerLiteral>(Node.getIdx())) {
const APInt ArrIdx = IdxLit->getValue();
// FIXME: ArrIdx.isNegative() we could immediately emit an error as that's a
// bug
if (ArrIdx.isNonNegative() &&
ArrIdx.getLimitedValue() < ArrSize.getLimitedValue())
return true;
}

return false;
}

} // namespace clang::ast_matchers

namespace {
Expand Down Expand Up @@ -594,16 +634,16 @@ class ArraySubscriptGadget : public WarningGadget {
}

static Matcher matcher() {
// FIXME: What if the index is integer literal 0? Should this be
// a safe gadget in this case?
// clang-format off
// clang-format off
return stmt(arraySubscriptExpr(
hasBase(ignoringParenImpCasts(
anyOf(hasPointerType(), hasArrayType()))),
unless(hasIndex(
anyOf(integerLiteral(equals(0)), arrayInitIndexExpr())
)))
.bind(ArraySubscrTag));
unless(anyOf(
isSafeArraySubscript(),
hasIndex(
anyOf(integerLiteral(equals(0)), arrayInitIndexExpr())
)
))).bind(ArraySubscrTag));
// clang-format on
}

Expand Down Expand Up @@ -762,21 +802,22 @@ class PointerInitGadget : public FixableGadget {
/// \code
/// p = q;
/// \endcode
class PointerAssignmentGadget : public FixableGadget {
/// where both `p` and `q` are pointers.
class PtrToPtrAssignmentGadget : public FixableGadget {
private:
static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
const DeclRefExpr * PtrLHS; // the LHS pointer expression in `PA`
const DeclRefExpr * PtrRHS; // the RHS pointer expression in `PA`

public:
PointerAssignmentGadget(const MatchFinder::MatchResult &Result)
: FixableGadget(Kind::PointerAssignment),
PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
PtrToPtrAssignmentGadget(const MatchFinder::MatchResult &Result)
: FixableGadget(Kind::PtrToPtrAssignment),
PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}

static bool classof(const Gadget *G) {
return G->getKind() == Kind::PointerAssignment;
return G->getKind() == Kind::PtrToPtrAssignment;
}

static Matcher matcher() {
Expand Down Expand Up @@ -811,6 +852,60 @@ class PointerAssignmentGadget : public FixableGadget {
}
};

/// An assignment expression of the form:
/// \code
/// ptr = array;
/// \endcode
/// where `p` is a pointer and `array` is a constant size array.
class CArrayToPtrAssignmentGadget : public FixableGadget {
private:
static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA`
const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA`

public:
CArrayToPtrAssignmentGadget(const MatchFinder::MatchResult &Result)
: FixableGadget(Kind::CArrayToPtrAssignment),
PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}

static bool classof(const Gadget *G) {
return G->getKind() == Kind::CArrayToPtrAssignment;
}

static Matcher matcher() {
auto PtrAssignExpr = binaryOperator(
allOf(hasOperatorName("="),
hasRHS(ignoringParenImpCasts(
declRefExpr(hasType(hasCanonicalType(constantArrayType())),
toSupportedVariable())
.bind(PointerAssignRHSTag))),
hasLHS(declRefExpr(hasPointerType(), toSupportedVariable())
.bind(PointerAssignLHSTag))));

return stmt(isInUnspecifiedUntypedContext(PtrAssignExpr));
}

virtual std::optional<FixItList>
getFixits(const FixitStrategy &S) const override;

virtual const Stmt *getBaseStmt() const override {
// FIXME: This should be the binary operator, assuming that this method
// makes sense at all on a FixableGadget.
return PtrLHS;
}

virtual DeclUseList getClaimedVarUseSites() const override {
return DeclUseList{PtrLHS, PtrRHS};
}

virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
getStrategyImplications() const override {
return {};
}
};

/// A call of a function or method that performs unchecked buffer operations
/// over one of its pointer parameters.
class UnsafeBufferUsageAttrGadget : public WarningGadget {
Expand Down Expand Up @@ -1434,7 +1529,7 @@ bool clang::internal::anyConflict(const SmallVectorImpl<FixItHint> &FixIts,
}

std::optional<FixItList>
PointerAssignmentGadget::getFixits(const FixitStrategy &S) const {
PtrToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
switch (S.lookup(LeftVD)) {
Expand All @@ -1453,6 +1548,42 @@ PointerAssignmentGadget::getFixits(const FixitStrategy &S) const {
return std::nullopt;
}

/// \returns fixit that adds .data() call after \DRE.
static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
const DeclRefExpr *DRE);

std::optional<FixItList>
CArrayToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
// TLDR: Implementing fixits for non-Wontfix strategy on both LHS and RHS is
// non-trivial.
//
// CArrayToPtrAssignmentGadget doesn't have strategy implications because
// constant size array propagates its bounds. Because of that LHS and RHS are
// addressed by two different fixits.
//
// At the same time FixitStrategy S doesn't reflect what group a fixit belongs
// to and can't be generally relied on in multi-variable Fixables!
//
// E. g. If an instance of this gadget is fixing variable on LHS then the
// variable on RHS is fixed by a different fixit and its strategy for LHS
// fixit is as if Wontfix.
//
// The only exception is Wontfix strategy for a given variable as that is
// valid for any fixit produced for the given input source code.
if (S.lookup(LeftVD) == FixitStrategy::Kind::Span) {
if (S.lookup(RightVD) == FixitStrategy::Kind::Wontfix) {
return FixItList{};
}
} else if (S.lookup(LeftVD) == FixitStrategy::Kind::Wontfix) {
if (S.lookup(RightVD) == FixitStrategy::Kind::Array) {
return createDataFixit(RightVD->getASTContext(), PtrRHS);
}
}
return std::nullopt;
}

std::optional<FixItList>
PointerInitGadget::getFixits(const FixitStrategy &S) const {
const auto *LeftVD = PtrInitLHS;
Expand Down Expand Up @@ -1870,6 +2001,19 @@ PointerDereferenceGadget::getFixits(const FixitStrategy &S) const {
return std::nullopt;
}

static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
const DeclRefExpr *DRE) {
const SourceManager &SM = Ctx.getSourceManager();
// Inserts the .data() after the DRE
std::optional<SourceLocation> EndOfOperand =
getPastLoc(DRE, SM, Ctx.getLangOpts());

if (EndOfOperand)
return FixItList{{FixItHint::CreateInsertion(*EndOfOperand, ".data()")}};

return std::nullopt;
}

// Generates fix-its replacing an expression of the form UPC(DRE) with
// `DRE.data()`
std::optional<FixItList>
Expand All @@ -1878,14 +2022,7 @@ UPCStandalonePointerGadget::getFixits(const FixitStrategy &S) const {
switch (S.lookup(VD)) {
case FixitStrategy::Kind::Array:
case FixitStrategy::Kind::Span: {
ASTContext &Ctx = VD->getASTContext();
SourceManager &SM = Ctx.getSourceManager();
// Inserts the .data() after the DRE
std::optional<SourceLocation> EndOfOperand =
getPastLoc(Node, SM, Ctx.getLangOpts());

if (EndOfOperand)
return FixItList{{FixItHint::CreateInsertion(*EndOfOperand, ".data()")}};
return createDataFixit(VD->getASTContext(), Node);
// FIXME: Points inside a macro expansion.
break;
}
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Basic/FileManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ FileManager::getBufferForFile(FileEntryRef FE, bool isVolatile,
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
FileManager::getBufferForFileImpl(StringRef Filename, int64_t FileSize,
bool isVolatile,
bool RequiresNullTerminator) {
bool RequiresNullTerminator) const {
if (FileSystemOpts.WorkingDir.empty())
return FS->getBufferForFile(Filename, FileSize, RequiresNullTerminator,
isVolatile);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Basic/TargetInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ TargetInfo::TargetInfo(const llvm::Triple &T) : Triple(T) {
HasFullBFloat16 = false;
HasLongDouble = true;
HasFPReturn = true;
HasFPTypes = true;
HasStrictFP = false;
PointerWidth = PointerAlign = 32;
BoolWidth = BoolAlign = 8;
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Basic/Targets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,7 @@ TargetInfo::CreateTargetInfo(DiagnosticsEngine &Diags,
Target->setSupportedOpenCLOpts();
Target->setCommandLineOpenCLOpts();
Target->setMaxAtomicWidth();
Target->setSupportedArgTypes();

if (!Opts->DarwinTargetVariantTriple.empty())
Target->DarwinTargetVariantTriple =
Expand Down
25 changes: 23 additions & 2 deletions clang/lib/Basic/Targets/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//

#include "AArch64.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/TargetBuiltins.h"
#include "clang/Basic/TargetInfo.h"
Expand Down Expand Up @@ -199,13 +200,32 @@ AArch64TargetInfo::AArch64TargetInfo(const llvm::Triple &Triple,
StringRef AArch64TargetInfo::getABI() const { return ABI; }

bool AArch64TargetInfo::setABI(const std::string &Name) {
if (Name != "aapcs" && Name != "darwinpcs")
if (Name != "aapcs" && Name != "aapcs-soft" && Name != "darwinpcs")
return false;

ABI = Name;
return true;
}

void AArch64TargetInfo::setSupportedArgTypes() {
if (!(FPU & FPUMode) && ABI != "aapcs-soft") {
// When a hard-float ABI is used on a target without an FPU, all
// floating-point argument and return types are rejected because they must
// be passed in FP registers.
HasFPTypes = false;
}
}

bool AArch64TargetInfo::validateTarget(DiagnosticsEngine &Diags) const {
if (hasFeature("fp") && ABI == "aapcs-soft") {
// aapcs-soft is not allowed for targets with an FPU, to avoid there being
// two incomatible ABIs.
Diags.Report(diag::err_target_unsupported_abi_with_fpu) << ABI;
return false;
}
return true;
}

bool AArch64TargetInfo::validateBranchProtection(StringRef Spec, StringRef,
BranchProtectionInfo &BPI,
StringRef &Err) const {
Expand Down Expand Up @@ -686,7 +706,8 @@ bool AArch64TargetInfo::hasFeature(StringRef Feature) const {
return llvm::StringSwitch<bool>(Feature)
.Cases("aarch64", "arm64", "arm", true)
.Case("fmv", HasFMV)
.Cases("neon", "fp", "simd", FPU & NeonMode)
.Case("fp", FPU & FPUMode)
.Cases("neon", "simd", FPU & NeonMode)
.Case("jscvt", HasJSCVT)
.Case("fcma", HasFCMA)
.Case("rng", HasRandGen)
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Basic/Targets/AArch64.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public TargetInfo {

StringRef getABI() const override;
bool setABI(const std::string &Name) override;
void setSupportedArgTypes() override;

bool validateBranchProtection(StringRef Spec, StringRef Arch,
BranchProtectionInfo &BPI,
Expand Down Expand Up @@ -199,6 +200,8 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public TargetInfo {
bool hasInt128Type() const override;

bool hasBitIntType() const override { return true; }

bool validateTarget(DiagnosticsEngine &Diags) const override;
};

class LLVM_LIBRARY_VISIBILITY AArch64leTargetInfo : public AArch64TargetInfo {
Expand Down
3 changes: 1 addition & 2 deletions clang/lib/Basic/Targets/AMDGPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,10 +285,9 @@ void AMDGPUTargetInfo::getTargetDefines(const LangOptions &Opts,
: getArchNameR600(GPUKind));

// Sanitize the name of generic targets.
// e.g. gfx10.1-generic -> gfx10_1_generic
// e.g. gfx10-1-generic -> gfx10_1_generic
if (GPUKind >= llvm::AMDGPU::GK_AMDGCN_GENERIC_FIRST &&
GPUKind <= llvm::AMDGPU::GK_AMDGCN_GENERIC_LAST) {
std::replace(CanonName.begin(), CanonName.end(), '.', '_');
std::replace(CanonName.begin(), CanonName.end(), '-', '_');
}

Expand Down
16 changes: 10 additions & 6 deletions clang/lib/CodeGen/BackendConsumer.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class BackendConsumer : public ASTConsumer {
const CodeGenOptions &CodeGenOpts;
const TargetOptions &TargetOpts;
const LangOptions &LangOpts;
const FileManager &FileMgr;
std::unique_ptr<raw_pwrite_stream> AsmOutStream;
ASTContext *Context;
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS;
Expand Down Expand Up @@ -74,8 +75,8 @@ class BackendConsumer : public ASTConsumer {
const HeaderSearchOptions &HeaderSearchOpts,
const PreprocessorOptions &PPOpts,
const CodeGenOptions &CodeGenOpts,
const TargetOptions &TargetOpts,
const LangOptions &LangOpts, const std::string &InFile,
const TargetOptions &TargetOpts, const LangOptions &LangOpts,
const FileManager &FileMgr, const std::string &InFile,
SmallVector<LinkModule, 4> LinkModules,
std::unique_ptr<raw_pwrite_stream> OS, llvm::LLVMContext &C,
CoverageSourceInfo *CoverageInfo = nullptr);
Expand All @@ -88,8 +89,8 @@ class BackendConsumer : public ASTConsumer {
const HeaderSearchOptions &HeaderSearchOpts,
const PreprocessorOptions &PPOpts,
const CodeGenOptions &CodeGenOpts,
const TargetOptions &TargetOpts,
const LangOptions &LangOpts, llvm::Module *Module,
const TargetOptions &TargetOpts, const LangOptions &LangOpts,
const FileManager &FileMgr, llvm::Module *Module,
SmallVector<LinkModule, 4> LinkModules, llvm::LLVMContext &C,
CoverageSourceInfo *CoverageInfo = nullptr);

Expand All @@ -111,10 +112,13 @@ class BackendConsumer : public ASTConsumer {
void AssignInheritanceModel(CXXRecordDecl *RD) override;
void HandleVTable(CXXRecordDecl *RD) override;


// Links each entry in LinkModules into our module. Returns true on error.
// Links each entry in LinkModules into our module. Returns true on error.
bool LinkInModules(llvm::Module *M, bool ShouldLinkFiles = true);

// Load a bitcode module from -mlink-builtin-bitcode option using
// methods from a BackendConsumer instead of CompilerInstance
bool ReloadModules(llvm::Module *M);

/// Get the best possible source location to represent a diagnostic that
/// may have associated debug info.
const FullSourceLoc getBestLocationFromDebugLoc(
Expand Down
4 changes: 0 additions & 4 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5912,8 +5912,6 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
}
}

assert(ArgValue->getType()->canLosslesslyBitCastTo(PTy) &&
"Must be able to losslessly bit cast to param");
// Cast vector type (e.g., v256i32) to x86_amx, this only happen
// in amx intrinsics.
if (PTy->isX86_AMXTy())
Expand Down Expand Up @@ -5943,8 +5941,6 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
}
}

assert(V->getType()->canLosslesslyBitCastTo(RetTy) &&
"Must be able to losslessly bit cast result type");
// Cast x86_amx to vector type (e.g., v256i32), this only happen
// in amx intrinsics.
if (V->getType()->isX86_AMXTy())
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5180,6 +5180,7 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) {
case CK_FixedPointToIntegral:
case CK_IntegralToFixedPoint:
case CK_MatrixCast:
case CK_HLSLVectorTruncation:
return EmitUnsupportedLValue(E, "unexpected cast lvalue");

case CK_Dependent:
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CodeGen/CGExprAgg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,7 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) {
case CK_BuiltinFnToFnPtr:
case CK_ZeroToOCLOpaqueType:
case CK_MatrixCast:
case CK_HLSLVectorTruncation:

case CK_IntToOCLSampler:
case CK_FloatingToFixedPoint:
Expand Down Expand Up @@ -1457,6 +1458,7 @@ static bool castPreservesZero(const CastExpr *CE) {
case CK_MatrixCast:
case CK_NonAtomicToAtomic:
case CK_AtomicToNonAtomic:
case CK_HLSLVectorTruncation:
return true;

case CK_BaseToDerivedMemberPointer:
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGExprComplex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,7 @@ ComplexPairTy ComplexExprEmitter::EmitCast(CastKind CK, Expr *Op,
case CK_FixedPointToIntegral:
case CK_IntegralToFixedPoint:
case CK_MatrixCast:
case CK_HLSLVectorTruncation:
llvm_unreachable("invalid cast kind for complex value");

case CK_FloatingRealToComplex:
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1225,6 +1225,7 @@ class ConstExprEmitter :
case CK_IntegralToFixedPoint:
case CK_ZeroToOCLOpaqueType:
case CK_MatrixCast:
case CK_HLSLVectorTruncation:
return nullptr;
}
llvm_unreachable("Invalid CastKind");
Expand Down
64 changes: 61 additions & 3 deletions clang/lib/CodeGen/CGExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2408,6 +2408,12 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
CE->getExprLoc());

case CK_IntegralCast: {
if (E->getType()->isExtVectorType() && DestTy->isExtVectorType()) {
QualType SrcElTy = E->getType()->castAs<VectorType>()->getElementType();
return Builder.CreateIntCast(Visit(E), ConvertType(DestTy),
SrcElTy->isSignedIntegerOrEnumerationType(),
"conv");
}
ScalarConversionOpts Opts;
if (auto *ICE = dyn_cast<ImplicitCastExpr>(CE)) {
if (!ICE->isPartOfExplicitCast())
Expand All @@ -2416,9 +2422,50 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
return EmitScalarConversion(Visit(E), E->getType(), DestTy,
CE->getExprLoc(), Opts);
}
case CK_IntegralToFloating:
case CK_FloatingToIntegral:
case CK_FloatingCast:
case CK_IntegralToFloating: {
if (E->getType()->isVectorType() && DestTy->isVectorType()) {
// TODO: Support constrained FP intrinsics.
assert(!Builder.getIsFPConstrained() &&
"FP Constrained vector casts not supported yet.");
QualType SrcElTy = E->getType()->castAs<VectorType>()->getElementType();
if (SrcElTy->isSignedIntegerOrEnumerationType())
return Builder.CreateSIToFP(Visit(E), ConvertType(DestTy), "conv");
return Builder.CreateUIToFP(Visit(E), ConvertType(DestTy), "conv");
}
CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, CE);
return EmitScalarConversion(Visit(E), E->getType(), DestTy,
CE->getExprLoc());
}
case CK_FloatingToIntegral: {
if (E->getType()->isVectorType() && DestTy->isVectorType()) {
// TODO: Support constrained FP intrinsics.
assert(!Builder.getIsFPConstrained() &&
"FP Constrained vector casts not supported yet.");
QualType DstElTy = DestTy->castAs<VectorType>()->getElementType();
if (DstElTy->isSignedIntegerOrEnumerationType())
return Builder.CreateFPToSI(Visit(E), ConvertType(DestTy), "conv");
return Builder.CreateFPToUI(Visit(E), ConvertType(DestTy), "conv");
}
CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, CE);
return EmitScalarConversion(Visit(E), E->getType(), DestTy,
CE->getExprLoc());
}
case CK_FloatingCast: {
if (E->getType()->isVectorType() && DestTy->isVectorType()) {
// TODO: Support constrained FP intrinsics.
assert(!Builder.getIsFPConstrained() &&
"FP Constrained vector casts not supported yet.");
QualType SrcElTy = E->getType()->castAs<VectorType>()->getElementType();
QualType DstElTy = DestTy->castAs<VectorType>()->getElementType();
if (DstElTy->castAs<BuiltinType>()->getKind() <
SrcElTy->castAs<BuiltinType>()->getKind())
return Builder.CreateFPTrunc(Visit(E), ConvertType(DestTy), "conv");
return Builder.CreateFPExt(Visit(E), ConvertType(DestTy), "conv");
}
CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, CE);
return EmitScalarConversion(Visit(E), E->getType(), DestTy,
CE->getExprLoc());
}
case CK_FixedPointToFloating:
case CK_FloatingToFixedPoint: {
CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, CE);
Expand Down Expand Up @@ -2468,6 +2515,17 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
case CK_IntToOCLSampler:
return CGF.CGM.createOpenCLIntToSamplerConversion(E, CGF);

case CK_HLSLVectorTruncation: {
assert(DestTy->isVectorType() && "Expected dest type to be vector type");
Value *Vec = Visit(const_cast<Expr *>(E));
SmallVector<int, 16> Mask;
unsigned NumElts = DestTy->castAs<VectorType>()->getNumElements();
for (unsigned I = 0; I != NumElts; ++I)
Mask.push_back(I);

return Builder.CreateShuffleVector(Vec, Mask, "trunc");
}

} // end of switch

llvm_unreachable("unknown scalar cast");
Expand Down
Loading