34 changes: 34 additions & 0 deletions clang-tools-extra/clangd/index/Merge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,40 @@ bool MergedIndex::refs(const RefsRequest &Req,
return More || StaticHadMore;
}

bool MergedIndex::containedRefs(
const ContainedRefsRequest &Req,
llvm::function_ref<void(const ContainedRefsResult &)> Callback) const {
trace::Span Tracer("MergedIndex refersTo");
bool More = false;
uint32_t Remaining = Req.Limit.value_or(std::numeric_limits<uint32_t>::max());
// We don't want duplicated refs from the static/dynamic indexes,
// and we can't reliably deduplicate them because offsets may differ slightly.
// We consider the dynamic index authoritative and report all its refs,
// and only report static index refs from other files.
More |= Dynamic->containedRefs(Req, [&](const auto &O) {
Callback(O);
assert(Remaining != 0);
--Remaining;
});
if (Remaining == 0 && More)
return More;
auto DynamicContainsFile = Dynamic->indexedFiles();
// We return less than Req.Limit if static index returns more refs for dirty
// files.
bool StaticHadMore = Static->containedRefs(Req, [&](const auto &O) {
if ((DynamicContainsFile(O.Location.FileURI) & IndexContents::References) !=
IndexContents::None)
return; // ignore refs that have been seen from dynamic index.
if (Remaining == 0) {
More = true;
return;
}
--Remaining;
Callback(O);
});
return More || StaticHadMore;
}

llvm::unique_function<IndexContents(llvm::StringRef) const>
MergedIndex::indexedFiles() const {
return [DynamicContainsFile{Dynamic->indexedFiles()},
Expand Down
3 changes: 3 additions & 0 deletions clang-tools-extra/clangd/index/Merge.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ class MergedIndex : public SymbolIndex {
llvm::function_ref<void(const Symbol &)>) const override;
bool refs(const RefsRequest &,
llvm::function_ref<void(const Ref &)>) const override;
bool containedRefs(
const ContainedRefsRequest &,
llvm::function_ref<void(const ContainedRefsResult &)>) const override;
void relations(const RelationsRequest &,
llvm::function_ref<void(const SymbolID &, const Symbol &)>)
const override;
Expand Down
13 changes: 13 additions & 0 deletions clang-tools-extra/clangd/index/ProjectAware.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ class ProjectAwareIndex : public SymbolIndex {
/// Query all indexes while prioritizing the associated one (if any).
bool refs(const RefsRequest &Req,
llvm::function_ref<void(const Ref &)> Callback) const override;
/// Query all indexes while prioritizing the associated one (if any).
bool containedRefs(const ContainedRefsRequest &Req,
llvm::function_ref<void(const ContainedRefsResult &)>
Callback) const override;

/// Queries only the associates index when Req.RestrictForCodeCompletion is
/// set, otherwise queries all.
Expand Down Expand Up @@ -94,6 +98,15 @@ bool ProjectAwareIndex::refs(
return false;
}

bool ProjectAwareIndex::containedRefs(
const ContainedRefsRequest &Req,
llvm::function_ref<void(const ContainedRefsResult &)> Callback) const {
trace::Span Tracer("ProjectAwareIndex::refersTo");
if (auto *Idx = getIndex())
return Idx->containedRefs(Req, Callback);
return false;
}

bool ProjectAwareIndex::fuzzyFind(
const FuzzyFindRequest &Req,
llvm::function_ref<void(const Symbol &)> Callback) const {
Expand Down
3 changes: 3 additions & 0 deletions clang-tools-extra/clangd/index/Ref.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ enum class RefKind : uint8_t {
// ^ this references Foo, but does not explicitly spell out its name
// };
Spelled = 1 << 3,
// A reference which is a call. Used as a filter for which references
// to store in data structures used for computing outgoing calls.
Call = 1 << 4,
All = Declaration | Definition | Reference | Spelled,
};

Expand Down
14 changes: 8 additions & 6 deletions clang-tools-extra/clangd/index/Serialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ readCompileCommand(Reader CmdReader, llvm::ArrayRef<llvm::StringRef> Strings) {
// The current versioning scheme is simple - non-current versions are rejected.
// If you make a breaking change, bump this version number to invalidate stored
// data. Later we may want to support some backward compatibility.
constexpr static uint32_t Version = 19;
constexpr static uint32_t Version = 20;

llvm::Expected<IndexFileIn> readRIFF(llvm::StringRef Data,
SymbolOrigin Origin) {
Expand Down Expand Up @@ -704,7 +704,8 @@ llvm::Expected<IndexFileIn> readIndexFile(llvm::StringRef Data,
}

std::unique_ptr<SymbolIndex> loadIndex(llvm::StringRef SymbolFilename,
SymbolOrigin Origin, bool UseDex) {
SymbolOrigin Origin, bool UseDex,
bool SupportContainedRefs) {
trace::Span OverallTracer("LoadIndex");
auto Buffer = llvm::MemoryBuffer::getFile(SymbolFilename);
if (!Buffer) {
Expand Down Expand Up @@ -735,10 +736,11 @@ std::unique_ptr<SymbolIndex> loadIndex(llvm::StringRef SymbolFilename,
size_t NumRelations = Relations.size();

trace::Span Tracer("BuildIndex");
auto Index = UseDex ? dex::Dex::build(std::move(Symbols), std::move(Refs),
std::move(Relations))
: MemIndex::build(std::move(Symbols), std::move(Refs),
std::move(Relations));
auto Index = UseDex
? dex::Dex::build(std::move(Symbols), std::move(Refs),
std::move(Relations), SupportContainedRefs)
: MemIndex::build(std::move(Symbols), std::move(Refs),
std::move(Relations));
vlog("Loaded {0} from {1} with estimated memory usage {2} bytes\n"
" - number of symbols: {3}\n"
" - number of refs: {4}\n"
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/clangd/index/Serialization.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ std::string toYAML(const Ref &);
// Build an in-memory static index from an index file.
// The size should be relatively small, so data can be managed in memory.
std::unique_ptr<SymbolIndex> loadIndex(llvm::StringRef Filename,
SymbolOrigin Origin, bool UseDex = true);
SymbolOrigin Origin, bool UseDex,
bool SupportContainedRefs);

} // namespace clangd
} // namespace clang
Expand Down
20 changes: 17 additions & 3 deletions clang-tools-extra/clangd/index/SymbolCollector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "clang-include-cleaner/Record.h"
#include "clang-include-cleaner/Types.h"
#include "index/CanonicalIncludes.h"
#include "index/Ref.h"
#include "index/Relation.h"
#include "index/Symbol.h"
#include "index/SymbolID.h"
Expand Down Expand Up @@ -660,7 +661,7 @@ bool SymbolCollector::handleDeclOccurrence(
auto FileLoc = SM.getFileLoc(Loc);
auto FID = SM.getFileID(FileLoc);
if (Opts.RefsInHeaders || FID == SM.getMainFileID()) {
addRef(ID, SymbolRef{FileLoc, FID, Roles,
addRef(ID, SymbolRef{FileLoc, FID, Roles, index::getSymbolInfo(ND).Kind,
getRefContainer(ASTNode.Parent, Opts),
isSpelled(FileLoc, *ND)});
}
Expand Down Expand Up @@ -774,8 +775,10 @@ bool SymbolCollector::handleMacroOccurrence(const IdentifierInfo *Name,
// FIXME: Populate container information for macro references.
// FIXME: All MacroRefs are marked as Spelled now, but this should be
// checked.
addRef(ID, SymbolRef{Loc, SM.getFileID(Loc), Roles, /*Container=*/nullptr,
/*Spelled=*/true});
addRef(ID,
SymbolRef{Loc, SM.getFileID(Loc), Roles, index::SymbolKind::Macro,
/*Container=*/nullptr,
/*Spelled=*/true});
}

// Collect symbols.
Expand Down Expand Up @@ -1166,6 +1169,14 @@ bool SymbolCollector::shouldIndexFile(FileID FID) {
return I.first->second;
}

static bool refIsCall(index::SymbolKind Kind) {
using SK = index::SymbolKind;
return Kind == SK::Function || Kind == SK::InstanceMethod ||
Kind == SK::ClassMethod || Kind == SK::StaticMethod ||
Kind == SK::Constructor || Kind == SK::Destructor ||
Kind == SK::ConversionFunction;
}

void SymbolCollector::addRef(SymbolID ID, const SymbolRef &SR) {
const auto &SM = ASTCtx->getSourceManager();
// FIXME: use the result to filter out references.
Expand All @@ -1177,6 +1188,9 @@ void SymbolCollector::addRef(SymbolID ID, const SymbolRef &SR) {
R.Location.End = Range.second;
R.Location.FileURI = HeaderFileURIs->toURI(*FE).c_str();
R.Kind = toRefKind(SR.Roles, SR.Spelled);
if (refIsCall(SR.Kind)) {
R.Kind |= RefKind::Call;
}
R.Container = getSymbolIDCached(SR.Container);
Refs.insert(ID, R);
}
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/index/SymbolCollector.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ class SymbolCollector : public index::IndexDataConsumer {
SourceLocation Loc;
FileID FID;
index::SymbolRoleSet Roles;
index::SymbolKind Kind;
const Decl *Container;
bool Spelled;
};
Expand Down
52 changes: 49 additions & 3 deletions clang-tools-extra/clangd/index/dex/Dex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,14 @@ namespace clangd {
namespace dex {

std::unique_ptr<SymbolIndex> Dex::build(SymbolSlab Symbols, RefSlab Refs,
RelationSlab Rels) {
RelationSlab Rels,
bool SupportContainedRefs) {
auto Size = Symbols.bytes() + Refs.bytes();
// There is no need to include "Rels" in Data because the relations are self-
// contained, without references into a backing store.
auto Data = std::make_pair(std::move(Symbols), std::move(Refs));
return std::make_unique<Dex>(Data.first, Data.second, Rels, std::move(Data),
Size);
Size, SupportContainedRefs);
}

namespace {
Expand Down Expand Up @@ -120,7 +121,7 @@ class IndexBuilder {

} // namespace

void Dex::buildIndex() {
void Dex::buildIndex(bool SupportContainedRefs) {
this->Corpus = dex::Corpus(Symbols.size());
std::vector<std::pair<float, const Symbol *>> ScoredSymbols(Symbols.size());

Expand All @@ -147,6 +148,20 @@ void Dex::buildIndex() {
for (DocID SymbolRank = 0; SymbolRank < Symbols.size(); ++SymbolRank)
Builder.add(*Symbols[SymbolRank], SymbolRank);
InvertedIndex = std::move(Builder).build();

// If the containedRefs() operation is supported, build the RevRefs
// data structure used to implement it.
if (!SupportContainedRefs)
return;
for (const auto &[ID, RefList] : Refs)
for (const auto &R : RefList)
if ((R.Kind & ContainedRefsRequest::SupportedRefKinds) !=
RefKind::Unknown)
RevRefs.emplace_back(R, ID);
// Sort by container ID so we can use binary search for lookup.
llvm::sort(RevRefs, [](const RevRef &A, const RevRef &B) {
return A.ref().Container < B.ref().Container;
});
}

std::unique_ptr<Iterator> Dex::iterator(const Token &Tok) const {
Expand Down Expand Up @@ -314,6 +329,36 @@ bool Dex::refs(const RefsRequest &Req,
return false; // We reported all refs.
}

llvm::iterator_range<std::vector<Dex::RevRef>::const_iterator>
Dex::lookupRevRefs(const SymbolID &Container) const {
// equal_range() requires an element of the same type as the elements of the
// range, so construct a dummy RevRef with the container of interest.
Ref QueryRef;
QueryRef.Container = Container;
RevRef Query(QueryRef, SymbolID{});

auto ItPair = std::equal_range(RevRefs.cbegin(), RevRefs.cend(), Query,
[](const RevRef &A, const RevRef &B) {
return A.ref().Container < B.ref().Container;
});
return {ItPair.first, ItPair.second};
}

bool Dex::containedRefs(
const ContainedRefsRequest &Req,
llvm::function_ref<void(const ContainedRefsResult &)> Callback) const {
trace::Span Tracer("Dex reversed refs");
uint32_t Remaining = Req.Limit.value_or(std::numeric_limits<uint32_t>::max());
for (const auto &Rev : lookupRevRefs(Req.ID)) {
// RevRefs are already filtered to ContainedRefsRequest::SupportedRefKinds
if (Remaining == 0)
return true; // More refs were available.
--Remaining;
Callback(Rev.containedRefsResult());
}
return false; // We reported all refs.
}

void Dex::relations(
const RelationsRequest &Req,
llvm::function_ref<void(const SymbolID &, const Symbol &)> Callback) const {
Expand Down Expand Up @@ -350,6 +395,7 @@ size_t Dex::estimateMemoryUsage() const {
for (const auto &TokenToPostingList : InvertedIndex)
Bytes += TokenToPostingList.second.bytes();
Bytes += Refs.getMemorySize();
Bytes += RevRefs.size() * sizeof(RevRef);
Bytes += Relations.getMemorySize();
return Bytes + BackingDataSize;
}
Expand Down
39 changes: 31 additions & 8 deletions clang-tools-extra/clangd/index/dex/Dex.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ class Dex : public SymbolIndex {
public:
// All data must outlive this index.
template <typename SymbolRange, typename RefsRange, typename RelationsRange>
Dex(SymbolRange &&Symbols, RefsRange &&Refs, RelationsRange &&Relations)
Dex(SymbolRange &&Symbols, RefsRange &&Refs, RelationsRange &&Relations,
bool SupportContainedRefs)
: Corpus(0) {
for (auto &&Sym : Symbols)
this->Symbols.push_back(&Sym);
Expand All @@ -46,15 +47,15 @@ class Dex : public SymbolIndex {
this->Relations[std::make_pair(Rel.Subject,
static_cast<uint8_t>(Rel.Predicate))]
.push_back(Rel.Object);
buildIndex();
buildIndex(SupportContainedRefs);
}
// Symbols and Refs are owned by BackingData, Index takes ownership.
template <typename SymbolRange, typename RefsRange, typename RelationsRange,
typename Payload>
Dex(SymbolRange &&Symbols, RefsRange &&Refs, RelationsRange &&Relations,
Payload &&BackingData, size_t BackingDataSize)
Payload &&BackingData, size_t BackingDataSize, bool SupportContainedRefs)
: Dex(std::forward<SymbolRange>(Symbols), std::forward<RefsRange>(Refs),
std::forward<RelationsRange>(Relations)) {
std::forward<RelationsRange>(Relations), SupportContainedRefs) {
KeepAlive = std::shared_ptr<void>(
std::make_shared<Payload>(std::move(BackingData)), nullptr);
this->BackingDataSize = BackingDataSize;
Expand All @@ -64,16 +65,18 @@ class Dex : public SymbolIndex {
typename FileRange, typename Payload>
Dex(SymbolRange &&Symbols, RefsRange &&Refs, RelationsRange &&Relations,
FileRange &&Files, IndexContents IdxContents, Payload &&BackingData,
size_t BackingDataSize)
size_t BackingDataSize, bool SupportContainedRefs)
: Dex(std::forward<SymbolRange>(Symbols), std::forward<RefsRange>(Refs),
std::forward<RelationsRange>(Relations),
std::forward<Payload>(BackingData), BackingDataSize) {
std::forward<Payload>(BackingData), BackingDataSize,
SupportContainedRefs) {
this->Files = std::forward<FileRange>(Files);
this->IdxContents = IdxContents;
}

/// Builds an index from slabs. The index takes ownership of the slab.
static std::unique_ptr<SymbolIndex> build(SymbolSlab, RefSlab, RelationSlab);
static std::unique_ptr<SymbolIndex> build(SymbolSlab, RefSlab, RelationSlab,
bool SupportContainedRefs);

bool
fuzzyFind(const FuzzyFindRequest &Req,
Expand All @@ -85,6 +88,10 @@ class Dex : public SymbolIndex {
bool refs(const RefsRequest &Req,
llvm::function_ref<void(const Ref &)> Callback) const override;

bool containedRefs(const ContainedRefsRequest &Req,
llvm::function_ref<void(const ContainedRefsResult &)>
Callback) const override;

void relations(const RelationsRequest &Req,
llvm::function_ref<void(const SymbolID &, const Symbol &)>
Callback) const override;
Expand All @@ -95,7 +102,22 @@ class Dex : public SymbolIndex {
size_t estimateMemoryUsage() const override;

private:
void buildIndex();
class RevRef {
const Ref *Reference;
SymbolID Target;

public:
RevRef(const Ref &Reference, SymbolID Target)
: Reference(&Reference), Target(Target) {}
const Ref &ref() const { return *Reference; }
ContainedRefsResult containedRefsResult() const {
return {ref().Location, ref().Kind, Target};
}
};

void buildIndex(bool EnableOutgoingCalls);
llvm::iterator_range<std::vector<RevRef>::const_iterator>
lookupRevRefs(const SymbolID &Container) const;
std::unique_ptr<Iterator> iterator(const Token &Tok) const;
std::unique_ptr<Iterator>
createFileProximityIterator(llvm::ArrayRef<std::string> ProximityPaths) const;
Expand All @@ -116,6 +138,7 @@ class Dex : public SymbolIndex {
llvm::DenseMap<Token, PostingList> InvertedIndex;
dex::Corpus Corpus;
llvm::DenseMap<SymbolID, llvm::ArrayRef<Ref>> Refs;
std::vector<RevRef> RevRefs; // sorted by container ID
static_assert(sizeof(RelationKind) == sizeof(uint8_t),
"RelationKind should be of same size as a uint8_t");
llvm::DenseMap<std::pair<SymbolID, uint8_t>, std::vector<SymbolID>> Relations;
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/clangd/index/dex/dexp/Dexp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,8 @@ std::unique_ptr<SymbolIndex> openIndex(llvm::StringRef Index) {
return Index.starts_with("remote:")
? remote::getClient(Index.drop_front(strlen("remote:")),
ProjectRoot)
: loadIndex(Index, SymbolOrigin::Static, /*UseDex=*/true);
: loadIndex(Index, SymbolOrigin::Static, /*UseDex=*/true,
/*SupportContainedRefs=*/true);
}

bool runCommand(std::string Request, const SymbolIndex &Index) {
Expand Down
7 changes: 7 additions & 0 deletions clang-tools-extra/clangd/index/remote/Client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,13 @@ class IndexClient : public clangd::SymbolIndex {
return streamRPC(Request, &remote::v1::SymbolIndex::Stub::Refs, Callback);
}

bool containedRefs(const clangd::ContainedRefsRequest &Request,
llvm::function_ref<void(const ContainedRefsResult &)>
Callback) const override {
return streamRPC(Request, &remote::v1::SymbolIndex::Stub::ContainedRefs,
Callback);
}

void
relations(const clangd::RelationsRequest &Request,
llvm::function_ref<void(const SymbolID &, const clangd::Symbol &)>
Expand Down
18 changes: 18 additions & 0 deletions clang-tools-extra/clangd/index/remote/Index.proto
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,21 @@ message Relation {
optional string subject_id = 1;
optional Symbol object = 2;
}

message ContainedRefsRequest {
required string id = 1;
optional uint32 limit = 2;
}

message ContainedRefsReply {
oneof kind {
ContainedRef stream_result = 1;
FinalResult final_result = 2;
}
}

message ContainedRef {
required SymbolLocation location = 1;
required uint32 kind = 2;
required string symbol = 3;
}
2 changes: 2 additions & 0 deletions clang-tools-extra/clangd/index/remote/Service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,7 @@ service SymbolIndex {

rpc Refs(RefsRequest) returns (stream RefsReply) {}

rpc ContainedRefs(ContainedRefsRequest) returns (stream ContainedRefsReply) {}

rpc Relations(RelationsRequest) returns (stream RelationsReply) {}
}
48 changes: 48 additions & 0 deletions clang-tools-extra/clangd/index/remote/marshalling/Marshalling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,18 @@ Marshaller::fromProtobuf(const RefsRequest *Message) {
return Req;
}

llvm::Expected<clangd::ContainedRefsRequest>
Marshaller::fromProtobuf(const ContainedRefsRequest *Message) {
clangd::ContainedRefsRequest Req;
auto ID = SymbolID::fromStr(Message->id());
if (!ID)
return ID.takeError();
Req.ID = *ID;
if (Message->has_limit())
Req.Limit = Message->limit();
return Req;
}

llvm::Expected<clangd::RelationsRequest>
Marshaller::fromProtobuf(const RelationsRequest *Message) {
clangd::RelationsRequest Req;
Expand Down Expand Up @@ -192,6 +204,21 @@ llvm::Expected<clangd::Ref> Marshaller::fromProtobuf(const Ref &Message) {
return Result;
}

llvm::Expected<clangd::ContainedRefsResult>
Marshaller::fromProtobuf(const ContainedRef &Message) {
clangd::ContainedRefsResult Result;
auto Location = fromProtobuf(Message.location());
if (!Location)
return Location.takeError();
Result.Location = *Location;
Result.Kind = static_cast<RefKind>(Message.kind());
auto Symbol = SymbolID::fromStr(Message.symbol());
if (!Symbol)
return Symbol.takeError();
Result.Symbol = *Symbol;
return Result;
}

llvm::Expected<std::pair<clangd::SymbolID, clangd::Symbol>>
Marshaller::fromProtobuf(const Relation &Message) {
auto SubjectID = SymbolID::fromStr(Message.subject_id());
Expand Down Expand Up @@ -244,6 +271,15 @@ RefsRequest Marshaller::toProtobuf(const clangd::RefsRequest &From) {
return RPCRequest;
}

ContainedRefsRequest
Marshaller::toProtobuf(const clangd::ContainedRefsRequest &From) {
ContainedRefsRequest RPCRequest;
RPCRequest.set_id(From.ID.str());
if (From.Limit)
RPCRequest.set_limit(*From.Limit);
return RPCRequest;
}

RelationsRequest Marshaller::toProtobuf(const clangd::RelationsRequest &From) {
RelationsRequest RPCRequest;
for (const auto &ID : From.Subjects)
Expand Down Expand Up @@ -299,6 +335,18 @@ llvm::Expected<Ref> Marshaller::toProtobuf(const clangd::Ref &From) {
return Result;
}

llvm::Expected<ContainedRef>
Marshaller::toProtobuf(const clangd::ContainedRefsResult &From) {
ContainedRef Result;
auto Location = toProtobuf(From.Location);
if (!Location)
return Location.takeError();
*Result.mutable_location() = *Location;
Result.set_kind(static_cast<uint32_t>(From.Kind));
*Result.mutable_symbol() = From.Symbol.str();
return Result;
}

llvm::Expected<Relation> Marshaller::toProtobuf(const clangd::SymbolID &Subject,
const clangd::Symbol &Object) {
Relation Result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ class Marshaller {

llvm::Expected<clangd::Symbol> fromProtobuf(const Symbol &Message);
llvm::Expected<clangd::Ref> fromProtobuf(const Ref &Message);
llvm::Expected<clangd::ContainedRefsResult>
fromProtobuf(const ContainedRef &Message);
llvm::Expected<std::pair<clangd::SymbolID, clangd::Symbol>>
fromProtobuf(const Relation &Message);

Expand All @@ -48,6 +50,8 @@ class Marshaller {
llvm::Expected<clangd::FuzzyFindRequest>
fromProtobuf(const FuzzyFindRequest *Message);
llvm::Expected<clangd::RefsRequest> fromProtobuf(const RefsRequest *Message);
llvm::Expected<clangd::ContainedRefsRequest>
fromProtobuf(const ContainedRefsRequest *Message);
llvm::Expected<clangd::RelationsRequest>
fromProtobuf(const RelationsRequest *Message);

Expand All @@ -58,10 +62,13 @@ class Marshaller {
LookupRequest toProtobuf(const clangd::LookupRequest &From);
FuzzyFindRequest toProtobuf(const clangd::FuzzyFindRequest &From);
RefsRequest toProtobuf(const clangd::RefsRequest &From);
ContainedRefsRequest toProtobuf(const clangd::ContainedRefsRequest &From);
RelationsRequest toProtobuf(const clangd::RelationsRequest &From);

llvm::Expected<Symbol> toProtobuf(const clangd::Symbol &From);
llvm::Expected<Ref> toProtobuf(const clangd::Ref &From);
llvm::Expected<ContainedRef>
toProtobuf(const clangd::ContainedRefsResult &From);
llvm::Expected<Relation> toProtobuf(const clangd::SymbolID &Subject,
const clangd::Symbol &Object);

Expand Down
55 changes: 52 additions & 3 deletions clang-tools-extra/clangd/index/remote/server/Server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,53 @@ class RemoteIndexServer final : public v1::SymbolIndex::Service {
return grpc::Status::OK;
}

grpc::Status
ContainedRefs(grpc::ServerContext *Context,
const ContainedRefsRequest *Request,
grpc::ServerWriter<ContainedRefsReply> *Reply) override {
auto StartTime = stopwatch::now();
WithContextValue WithRequestContext(CurrentRequest, Context);
logRequest(*Request);
trace::Span Tracer("ContainedRefsRequest");
auto Req = ProtobufMarshaller->fromProtobuf(Request);
if (!Req) {
elog("Can not parse ContainedRefsRequest from protobuf: {0}",
Req.takeError());
return grpc::Status::CANCELLED;
}
if (!Req->Limit || *Req->Limit > LimitResults) {
log("[public] Limiting result size for ContainedRefs request from {0} to "
"{1}.",
Req->Limit, LimitResults);
Req->Limit = LimitResults;
}
unsigned Sent = 0;
unsigned FailedToSend = 0;
bool HasMore =
Index.containedRefs(*Req, [&](const clangd::ContainedRefsResult &Item) {
auto SerializedItem = ProtobufMarshaller->toProtobuf(Item);
if (!SerializedItem) {
elog("Unable to convert ContainedRefsResult to protobuf: {0}",
SerializedItem.takeError());
++FailedToSend;
return;
}
ContainedRefsReply NextMessage;
*NextMessage.mutable_stream_result() = *SerializedItem;
logResponse(NextMessage);
Reply->Write(NextMessage);
++Sent;
});
ContainedRefsReply LastMessage;
LastMessage.mutable_final_result()->set_has_more(HasMore);
logResponse(LastMessage);
Reply->Write(LastMessage);
SPAN_ATTACH(Tracer, "Sent", Sent);
SPAN_ATTACH(Tracer, "Failed to send", FailedToSend);
logRequestSummary("v1/ContainedRefs", Sent, StartTime);
return grpc::Status::OK;
}

grpc::Status Relations(grpc::ServerContext *Context,
const RelationsRequest *Request,
grpc::ServerWriter<RelationsReply> *Reply) override {
Expand Down Expand Up @@ -396,7 +443,8 @@ void hotReload(clangd::SwapIndex &Index, llvm::StringRef IndexPath,
LastStatus.getLastModificationTime(), Status->getLastModificationTime());
LastStatus = *Status;
std::unique_ptr<clang::clangd::SymbolIndex> NewIndex =
loadIndex(IndexPath, SymbolOrigin::Static);
loadIndex(IndexPath, SymbolOrigin::Static, /*UseDex=*/true,
/*SupportContainedRefs=*/true);
if (!NewIndex) {
elog("Failed to load new index. Old index will be served.");
return;
Expand Down Expand Up @@ -532,8 +580,9 @@ int main(int argc, char *argv[]) {
return Status.getError().value();
}

auto SymIndex =
clang::clangd::loadIndex(IndexPath, clang::clangd::SymbolOrigin::Static);
auto SymIndex = clang::clangd::loadIndex(
IndexPath, clang::clangd::SymbolOrigin::Static, /*UseDex=*/true,
/*SupportContainedRefs=*/true);
if (!SymIndex) {
llvm::errs() << "Failed to open the index.\n";
return -1;
Expand Down
Binary file not shown.
2 changes: 2 additions & 0 deletions clang-tools-extra/clangd/test/type-hierarchy-ext.test
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# CHECK-NEXT: "data": {
# CHECK-NEXT: "symbolID": "A6576FE083F2949A"
# CHECK-NEXT: },
# CHECK-NEXT: "detail": "Child3",
# CHECK-NEXT: "kind": 23,
# CHECK-NEXT: "name": "Child3",
# CHECK-NEXT: "range": {
Expand Down Expand Up @@ -153,6 +154,7 @@
# CHECK-NEXT: "data": {
# CHECK-NEXT: "symbolID": "5705B382DFC77CBC"
# CHECK-NEXT: },
# CHECK-NEXT: "detail": "Child4",
# CHECK-NEXT: "kind": 23,
# CHECK-NEXT: "name": "Child4",
# CHECK-NEXT: "range": {
Expand Down
2 changes: 2 additions & 0 deletions clang-tools-extra/clangd/test/type-hierarchy.test
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
# CHECK-NEXT: ],
# CHECK-NEXT: "symbolID": "ECDC0C46D75120F4"
# CHECK-NEXT: },
# CHECK-NEXT: "detail": "Child1",
# CHECK-NEXT: "kind": 23,
# CHECK-NEXT: "name": "Child1",
# CHECK-NEXT: "range": {
Expand Down Expand Up @@ -112,6 +113,7 @@
# CHECK-NEXT: ],
# CHECK-NEXT: "symbolID": "A6576FE083F2949A"
# CHECK-NEXT: },
# CHECK-NEXT: "detail": "Child3",
# CHECK-NEXT: "kind": 23,
# CHECK-NEXT: "name": "Child3",
# CHECK-NEXT: "range": {
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/tool/Check.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ class Checker {
unsigned ErrCount = 0;

Checker(llvm::StringRef File, const ClangdLSPServer::Options &Opts)
: File(File), Opts(Opts) {}
: File(File), Opts(Opts), Index(/*SupportContainedRefs=*/true) {}

// Read compilation database and choose a compile command for the file.
bool buildCommand(const ThreadsafeFS &TFS) {
Expand Down
14 changes: 10 additions & 4 deletions clang-tools-extra/clangd/tool/ClangdMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,7 @@ const char TestScheme::TestDir[] = "/clangd-test";

std::unique_ptr<SymbolIndex>
loadExternalIndex(const Config::ExternalIndexSpec &External,
AsyncTaskRunner *Tasks) {
AsyncTaskRunner *Tasks, bool SupportContainedRefs) {
static const trace::Metric RemoteIndexUsed("used_remote_index",
trace::Metric::Value, "address");
switch (External.Kind) {
Expand All @@ -620,8 +620,9 @@ loadExternalIndex(const Config::ExternalIndexSpec &External,
External.Location);
auto NewIndex = std::make_unique<SwapIndex>(std::make_unique<MemIndex>());
auto IndexLoadTask = [File = External.Location,
PlaceHolder = NewIndex.get()] {
if (auto Idx = loadIndex(File, SymbolOrigin::Static, /*UseDex=*/true))
PlaceHolder = NewIndex.get(), SupportContainedRefs] {
if (auto Idx = loadIndex(File, SymbolOrigin::Static, /*UseDex=*/true,
SupportContainedRefs))
PlaceHolder->reset(std::move(Idx));
};
if (Tasks) {
Expand Down Expand Up @@ -909,7 +910,12 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var
Opts.BackgroundIndexPriority = BackgroundIndexPriority;
Opts.ReferencesLimit = ReferencesLimit;
Opts.Rename.LimitFiles = RenameFileLimit;
auto PAI = createProjectAwareIndex(loadExternalIndex, Sync);
auto PAI = createProjectAwareIndex(
[SupportContainedRefs = Opts.EnableOutgoingCalls](
const Config::ExternalIndexSpec &External, AsyncTaskRunner *Tasks) {
return loadExternalIndex(External, Tasks, SupportContainedRefs);
},
Sync);
Opts.StaticIndex = PAI.get();
Opts.AsyncThreadsCount = WorkerThreadsCount;
Opts.MemoryCleanup = getMemoryCleanupFunction();
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,8 @@ TEST_F(BackgroundIndexTest, Reindex) {
class BackgroundIndexRebuilderTest : public testing::Test {
protected:
BackgroundIndexRebuilderTest()
: Source(IndexContents::All), Target(std::make_unique<MemIndex>()),
: Source(IndexContents::All, /*SupportContainedRefs=*/true),
Target(std::make_unique<MemIndex>()),
Rebuilder(&Target, &Source, /*Threads=*/10) {
// Prepare FileSymbols with TestSymbol in it, for checkRebuild.
TestSymbol.ID = SymbolID("foo");
Expand Down
277 changes: 204 additions & 73 deletions clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1703,6 +1703,12 @@ class IndexRequestCollector : public SymbolIndex {
return false;
}

bool containedRefs(
const ContainedRefsRequest &,
llvm::function_ref<void(const ContainedRefsResult &)>) const override {
return false;
}

void relations(const RelationsRequest &,
llvm::function_ref<void(const SymbolID &, const Symbol &)>)
const override {}
Expand Down
46 changes: 24 additions & 22 deletions clang-tools-extra/clangd/unittests/DexTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ TEST(DexSearchTokens, SymbolPath) {

TEST(Dex, Lookup) {
auto I = Dex::build(generateSymbols({"ns::abc", "ns::xyz"}), RefSlab(),
RelationSlab());
RelationSlab(), true);
EXPECT_THAT(lookup(*I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc"));
EXPECT_THAT(lookup(*I, {SymbolID("ns::abc"), SymbolID("ns::xyz")}),
UnorderedElementsAre("ns::abc", "ns::xyz"));
Expand All @@ -489,7 +489,7 @@ TEST(Dex, FuzzyFind) {
auto Index =
Dex::build(generateSymbols({"ns::ABC", "ns::BCD", "::ABC",
"ns::nested::ABC", "other::ABC", "other::A"}),
RefSlab(), RelationSlab());
RefSlab(), RelationSlab(), true);
FuzzyFindRequest Req;
Req.Query = "ABC";
Req.Scopes = {"ns::"};
Expand All @@ -511,7 +511,8 @@ TEST(Dex, FuzzyFind) {
}

TEST(DexTest, DexLimitedNumMatches) {
auto I = Dex::build(generateNumSymbols(0, 100), RefSlab(), RelationSlab());
auto I =
Dex::build(generateNumSymbols(0, 100), RefSlab(), RelationSlab(), true);
FuzzyFindRequest Req;
Req.Query = "5";
Req.AnyScope = true;
Expand All @@ -526,7 +527,7 @@ TEST(DexTest, DexLimitedNumMatches) {
TEST(DexTest, FuzzyMatch) {
auto I = Dex::build(
generateSymbols({"LaughingOutLoud", "LionPopulation", "LittleOldLady"}),
RefSlab(), RelationSlab());
RefSlab(), RelationSlab(), true);
FuzzyFindRequest Req;
Req.Query = "lol";
Req.AnyScope = true;
Expand All @@ -537,7 +538,7 @@ TEST(DexTest, FuzzyMatch) {

TEST(DexTest, ShortQuery) {
auto I = Dex::build(generateSymbols({"_OneTwoFourSix"}), RefSlab(),
RelationSlab());
RelationSlab(), true);
FuzzyFindRequest Req;
Req.AnyScope = true;
bool Incomplete;
Expand Down Expand Up @@ -580,7 +581,7 @@ TEST(DexTest, ShortQuery) {

TEST(DexTest, MatchQualifiedNamesWithoutSpecificScope) {
auto I = Dex::build(generateSymbols({"a::y1", "b::y2", "y3"}), RefSlab(),
RelationSlab());
RelationSlab(), true);
FuzzyFindRequest Req;
Req.AnyScope = true;
Req.Query = "y";
Expand All @@ -589,7 +590,7 @@ TEST(DexTest, MatchQualifiedNamesWithoutSpecificScope) {

TEST(DexTest, MatchQualifiedNamesWithGlobalScope) {
auto I = Dex::build(generateSymbols({"a::y1", "b::y2", "y3"}), RefSlab(),
RelationSlab());
RelationSlab(), true);
FuzzyFindRequest Req;
Req.Query = "y";
Req.Scopes = {""};
Expand All @@ -599,7 +600,7 @@ TEST(DexTest, MatchQualifiedNamesWithGlobalScope) {
TEST(DexTest, MatchQualifiedNamesWithOneScope) {
auto I =
Dex::build(generateSymbols({"a::y1", "a::y2", "a::x", "b::y2", "y3"}),
RefSlab(), RelationSlab());
RefSlab(), RelationSlab(), true);
FuzzyFindRequest Req;
Req.Query = "y";
Req.Scopes = {"a::"};
Expand All @@ -609,7 +610,7 @@ TEST(DexTest, MatchQualifiedNamesWithOneScope) {
TEST(DexTest, MatchQualifiedNamesWithMultipleScopes) {
auto I =
Dex::build(generateSymbols({"a::y1", "a::y2", "a::x", "b::y3", "y3"}),
RefSlab(), RelationSlab());
RefSlab(), RelationSlab(), true);
FuzzyFindRequest Req;
Req.Query = "y";
Req.Scopes = {"a::", "b::"};
Expand All @@ -618,7 +619,7 @@ TEST(DexTest, MatchQualifiedNamesWithMultipleScopes) {

TEST(DexTest, NoMatchNestedScopes) {
auto I = Dex::build(generateSymbols({"a::y1", "a::b::y2"}), RefSlab(),
RelationSlab());
RelationSlab(), true);
FuzzyFindRequest Req;
Req.Query = "y";
Req.Scopes = {"a::"};
Expand All @@ -627,7 +628,7 @@ TEST(DexTest, NoMatchNestedScopes) {

TEST(DexTest, WildcardScope) {
auto I = Dex::build(generateSymbols({"a::y1", "a::b::y2", "c::y3"}),
RefSlab(), RelationSlab());
RefSlab(), RelationSlab(), true);
FuzzyFindRequest Req;
Req.AnyScope = true;
Req.Query = "y";
Expand All @@ -638,7 +639,7 @@ TEST(DexTest, WildcardScope) {

TEST(DexTest, IgnoreCases) {
auto I = Dex::build(generateSymbols({"ns::ABC", "ns::abc"}), RefSlab(),
RelationSlab());
RelationSlab(), true);
FuzzyFindRequest Req;
Req.Query = "AB";
Req.Scopes = {"ns::"};
Expand All @@ -648,15 +649,15 @@ TEST(DexTest, IgnoreCases) {
TEST(DexTest, UnknownPostingList) {
// Regression test: we used to ignore unknown scopes and accept any symbol.
auto I = Dex::build(generateSymbols({"ns::ABC", "ns::abc"}), RefSlab(),
RelationSlab());
RelationSlab(), true);
FuzzyFindRequest Req;
Req.Scopes = {"ns2::"};
EXPECT_THAT(match(*I, Req), UnorderedElementsAre());
}

TEST(DexTest, Lookup) {
auto I = Dex::build(generateSymbols({"ns::abc", "ns::xyz"}), RefSlab(),
RelationSlab());
RelationSlab(), true);
EXPECT_THAT(lookup(*I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc"));
EXPECT_THAT(lookup(*I, {SymbolID("ns::abc"), SymbolID("ns::xyz")}),
UnorderedElementsAre("ns::abc", "ns::xyz"));
Expand All @@ -671,7 +672,7 @@ TEST(DexTest, SymbolIndexOptionsFilter) {
CodeCompletionSymbol.Flags = Symbol::SymbolFlag::IndexedForCodeCompletion;
NonCodeCompletionSymbol.Flags = Symbol::SymbolFlag::None;
std::vector<Symbol> Symbols{CodeCompletionSymbol, NonCodeCompletionSymbol};
Dex I(Symbols, RefSlab(), RelationSlab());
Dex I(Symbols, RefSlab(), RelationSlab(), true);
FuzzyFindRequest Req;
Req.AnyScope = true;
Req.RestrictForCodeCompletion = false;
Expand All @@ -687,7 +688,7 @@ TEST(DexTest, ProximityPathsBoosting) {
CloseSymbol.CanonicalDeclaration.FileURI = "unittest:///a/b/c/d/e/f/file.h";

std::vector<Symbol> Symbols{CloseSymbol, RootSymbol};
Dex I(Symbols, RefSlab(), RelationSlab());
Dex I(Symbols, RefSlab(), RelationSlab(), true);

FuzzyFindRequest Req;
Req.AnyScope = true;
Expand Down Expand Up @@ -726,15 +727,15 @@ TEST(DexTests, Refs) {
Req.Filter = RefKind::Declaration | RefKind::Definition;

std::vector<std::string> Files;
EXPECT_FALSE(Dex(std::vector<Symbol>{Foo, Bar}, Refs, RelationSlab())
EXPECT_FALSE(Dex(std::vector<Symbol>{Foo, Bar}, Refs, RelationSlab(), true)
.refs(Req, [&](const Ref &R) {
Files.push_back(R.Location.FileURI);
}));
EXPECT_THAT(Files, UnorderedElementsAre("foo.h", "foo.cc"));

Req.Limit = 1;
Files.clear();
EXPECT_TRUE(Dex(std::vector<Symbol>{Foo, Bar}, Refs, RelationSlab())
EXPECT_TRUE(Dex(std::vector<Symbol>{Foo, Bar}, Refs, RelationSlab(), true)
.refs(Req, [&](const Ref &R) {
Files.push_back(R.Location.FileURI);
}));
Expand All @@ -751,7 +752,7 @@ TEST(DexTests, Relations) {
std::vector<Relation> Relations{{Parent.ID, RelationKind::BaseOf, Child1.ID},
{Parent.ID, RelationKind::BaseOf, Child2.ID}};

Dex I{Symbols, RefSlab(), Relations};
Dex I{Symbols, RefSlab(), Relations, true};

std::vector<SymbolID> Results;
RelationsRequest Req;
Expand All @@ -770,7 +771,7 @@ TEST(DexIndex, IndexedFiles) {
auto Data = std::make_pair(std::move(Symbols), std::move(Refs));
llvm::StringSet<> Files = {"unittest:///foo.cc", "unittest:///bar.cc"};
Dex I(std::move(Data.first), std::move(Data.second), RelationSlab(),
std::move(Files), IndexContents::All, std::move(Data), Size);
std::move(Files), IndexContents::All, std::move(Data), Size, true);
auto ContainsFile = I.indexedFiles();
EXPECT_EQ(ContainsFile("unittest:///foo.cc"), IndexContents::All);
EXPECT_EQ(ContainsFile("unittest:///bar.cc"), IndexContents::All);
Expand All @@ -784,7 +785,7 @@ TEST(DexTest, PreferredTypesBoosting) {
Sym2.Type = "T2";

std::vector<Symbol> Symbols{Sym1, Sym2};
Dex I(Symbols, RefSlab(), RelationSlab());
Dex I(Symbols, RefSlab(), RelationSlab(), true);

FuzzyFindRequest Req;
Req.AnyScope = true;
Expand Down Expand Up @@ -820,7 +821,8 @@ TEST(DexTest, TemplateSpecialization) {
index::SymbolProperty::TemplatePartialSpecialization);
B.insert(S);

auto I = dex::Dex::build(std::move(B).build(), RefSlab(), RelationSlab());
auto I =
dex::Dex::build(std::move(B).build(), RefSlab(), RelationSlab(), true);
FuzzyFindRequest Req;
Req.AnyScope = true;

Expand Down
52 changes: 26 additions & 26 deletions clang-tools-extra/clangd/unittests/FileIndexTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ std::unique_ptr<RelationSlab> relSlab(llvm::ArrayRef<const Relation> Rels) {
}

TEST(FileSymbolsTest, UpdateAndGet) {
FileSymbols FS(IndexContents::All);
FileSymbols FS(IndexContents::All, true);
EXPECT_THAT(runFuzzyFind(*FS.buildIndex(IndexType::Light), ""), IsEmpty());

FS.update("f1", numSlab(1, 3), refSlab(SymbolID("1"), "f1.cc"), nullptr,
Expand All @@ -116,7 +116,7 @@ TEST(FileSymbolsTest, UpdateAndGet) {
}

TEST(FileSymbolsTest, Overlap) {
FileSymbols FS(IndexContents::All);
FileSymbols FS(IndexContents::All, true);
FS.update("f1", numSlab(1, 3), nullptr, nullptr, false);
FS.update("f2", numSlab(3, 5), nullptr, nullptr, false);
for (auto Type : {IndexType::Light, IndexType::Heavy})
Expand All @@ -126,7 +126,7 @@ TEST(FileSymbolsTest, Overlap) {
}

TEST(FileSymbolsTest, MergeOverlap) {
FileSymbols FS(IndexContents::All);
FileSymbols FS(IndexContents::All, true);
auto OneSymboSlab = [](Symbol Sym) {
SymbolSlab::Builder S;
S.insert(Sym);
Expand All @@ -147,7 +147,7 @@ TEST(FileSymbolsTest, MergeOverlap) {
}

TEST(FileSymbolsTest, SnapshotAliveAfterRemove) {
FileSymbols FS(IndexContents::All);
FileSymbols FS(IndexContents::All, true);

SymbolID ID("1");
FS.update("f1", numSlab(1, 3), refSlab(ID, "f1.cc"), nullptr, false);
Expand Down Expand Up @@ -180,14 +180,14 @@ void update(FileIndex &M, llvm::StringRef Basename, llvm::StringRef Code) {
}

TEST(FileIndexTest, CustomizedURIScheme) {
FileIndex M;
FileIndex M(true);
update(M, "f", "class string {};");

EXPECT_THAT(runFuzzyFind(M, ""), ElementsAre(declURI("unittest:///f.h")));
}

TEST(FileIndexTest, IndexAST) {
FileIndex M;
FileIndex M(true);
update(M, "f1", "namespace ns { void f() {} class X {}; }");

FuzzyFindRequest Req;
Expand All @@ -198,7 +198,7 @@ TEST(FileIndexTest, IndexAST) {
}

TEST(FileIndexTest, NoLocal) {
FileIndex M;
FileIndex M(true);
update(M, "f1", "namespace ns { void f() { int local = 0; } class X {}; }");

EXPECT_THAT(
Expand All @@ -207,7 +207,7 @@ TEST(FileIndexTest, NoLocal) {
}

TEST(FileIndexTest, IndexMultiASTAndDeduplicate) {
FileIndex M;
FileIndex M(true);
update(M, "f1", "namespace ns { void f() {} class X {}; }");
update(M, "f2", "namespace ns { void ff() {} class X {}; }");

Expand All @@ -219,7 +219,7 @@ TEST(FileIndexTest, IndexMultiASTAndDeduplicate) {
}

TEST(FileIndexTest, ClassMembers) {
FileIndex M;
FileIndex M(true);
update(M, "f1", "class X { static int m1; int m2; static void f(); };");

EXPECT_THAT(runFuzzyFind(M, ""),
Expand All @@ -228,7 +228,7 @@ TEST(FileIndexTest, ClassMembers) {
}

TEST(FileIndexTest, IncludeCollected) {
FileIndex M;
FileIndex M(true);
update(
M, "f",
"// IWYU pragma: private, include <the/good/header.h>\nclass string {};");
Expand All @@ -240,7 +240,7 @@ TEST(FileIndexTest, IncludeCollected) {
}

TEST(FileIndexTest, IWYUPragmaExport) {
FileIndex M;
FileIndex M(true);

TestTU File;
File.Code = R"cpp(#pragma once
Expand Down Expand Up @@ -286,7 +286,7 @@ template <class Ty, class Arg>
vector<Ty> make_vector(Arg A) {}
)cpp";

FileIndex M;
FileIndex M(true);
update(M, "f", Source);

auto Symbols = runFuzzyFind(M, "");
Expand Down Expand Up @@ -334,7 +334,7 @@ TEST(FileIndexTest, RebuildWithPreamble) {
IgnoreDiagnostics IgnoreDiags;
auto CI = buildCompilerInvocation(PI, IgnoreDiags);

FileIndex Index;
FileIndex Index(true);
bool IndexUpdated = false;
buildPreamble(
FooCpp, *CI, PI,
Expand Down Expand Up @@ -374,7 +374,7 @@ TEST(FileIndexTest, Refs) {
RefsRequest Request;
Request.IDs = {Foo.ID};

FileIndex Index;
FileIndex Index(true);
// Add test.cc
TestTU Test;
Test.HeaderCode = HeaderCode;
Expand Down Expand Up @@ -409,7 +409,7 @@ TEST(FileIndexTest, MacroRefs) {
}
)cpp");

FileIndex Index;
FileIndex Index(true);
// Add test.cc
TestTU Test;
Test.HeaderCode = std::string(HeaderCode.code());
Expand All @@ -432,7 +432,7 @@ TEST(FileIndexTest, MacroRefs) {
}

TEST(FileIndexTest, CollectMacros) {
FileIndex M;
FileIndex M(true);
update(M, "f", "#define CLANGD 1");
EXPECT_THAT(runFuzzyFind(M, ""), Contains(qName("CLANGD")));
}
Expand All @@ -443,7 +443,7 @@ TEST(FileIndexTest, Relations) {
TU.HeaderFilename = "f.h";
TU.HeaderCode = "class A {}; class B : public A {};";
auto AST = TU.build();
FileIndex Index;
FileIndex Index(true);
Index.updatePreamble(testPath(TU.Filename), /*Version=*/"null",
AST.getASTContext(), AST.getPreprocessor(),
AST.getPragmaIncludes());
Expand Down Expand Up @@ -493,7 +493,7 @@ TEST(FileIndexTest, ReferencesInMainFileWithPreamble) {
)cpp");
TU.Code = std::string(Main.code());
auto AST = TU.build();
FileIndex Index;
FileIndex Index(true);
Index.updateMain(testPath(TU.Filename), AST);

// Expect to see references in main file, references in headers are excluded
Expand All @@ -510,7 +510,7 @@ TEST(FileIndexTest, MergeMainFileSymbols) {
Cpp.HeaderFilename = "foo.h";
Cpp.HeaderCode = CommonHeader;

FileIndex Index;
FileIndex Index(true);
auto HeaderAST = Header.build();
auto CppAST = Cpp.build();
Index.updateMain(testPath("foo.h"), HeaderAST);
Expand All @@ -524,7 +524,7 @@ TEST(FileIndexTest, MergeMainFileSymbols) {
}

TEST(FileSymbolsTest, CountReferencesNoRefSlabs) {
FileSymbols FS(IndexContents::All);
FileSymbols FS(IndexContents::All, true);
FS.update("f1", numSlab(1, 3), nullptr, nullptr, true);
FS.update("f2", numSlab(1, 3), nullptr, nullptr, false);
EXPECT_THAT(
Expand All @@ -536,7 +536,7 @@ TEST(FileSymbolsTest, CountReferencesNoRefSlabs) {
}

TEST(FileSymbolsTest, CountReferencesWithRefSlabs) {
FileSymbols FS(IndexContents::All);
FileSymbols FS(IndexContents::All, true);
FS.update("f1cpp", numSlab(1, 3), refSlab(SymbolID("1"), "f1.cpp"), nullptr,
true);
FS.update("f1h", numSlab(1, 3), refSlab(SymbolID("1"), "f1.h"), nullptr,
Expand All @@ -558,7 +558,7 @@ TEST(FileSymbolsTest, CountReferencesWithRefSlabs) {
}

TEST(FileIndexTest, StalePreambleSymbolsDeleted) {
FileIndex M;
FileIndex M(true);
TestTU File;
File.HeaderFilename = "a.h";

Expand All @@ -581,7 +581,7 @@ TEST(FileIndexTest, StalePreambleSymbolsDeleted) {

// Verifies that concurrent calls to updateMain don't "lose" any updates.
TEST(FileIndexTest, Threadsafety) {
FileIndex M;
FileIndex M(true);
Notification Go;

constexpr int Count = 10;
Expand Down Expand Up @@ -714,7 +714,7 @@ TEST(FileShardedIndexTest, Sharding) {
}

TEST(FileIndexTest, Profile) {
FileIndex FI;
FileIndex FI(true);

auto FileName = testPath("foo.cpp");
auto AST = TestTU::withHeaderCode("int a;").build();
Expand All @@ -738,7 +738,7 @@ TEST(FileIndexTest, Profile) {
}

TEST(FileSymbolsTest, Profile) {
FileSymbols FS(IndexContents::All);
FileSymbols FS(IndexContents::All, true);
FS.update("f1", numSlab(1, 2), nullptr, nullptr, false);
FS.update("f2", nullptr, refSlab(SymbolID("1"), "f1"), nullptr, false);
FS.update("f3", nullptr, nullptr,
Expand All @@ -758,7 +758,7 @@ TEST(FileSymbolsTest, Profile) {
}

TEST(FileIndexTest, MacrosFromMainFile) {
FileIndex Idx;
FileIndex Idx(true);
TestTU TU;
TU.Code = "#pragma once\n#define FOO";
TU.Filename = "foo.h";
Expand Down
8 changes: 4 additions & 4 deletions clang-tools-extra/clangd/unittests/IndexTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ TEST(MergeIndexTest, Lookup) {
}

TEST(MergeIndexTest, LookupRemovedDefinition) {
FileIndex DynamicIndex, StaticIndex;
FileIndex DynamicIndex(true), StaticIndex(true);
MergedIndex Merge(&DynamicIndex, &StaticIndex);

const char *HeaderCode = "class Foo;";
Expand Down Expand Up @@ -349,7 +349,7 @@ TEST(MergeIndexTest, FuzzyFind) {
}

TEST(MergeIndexTest, FuzzyFindRemovedSymbol) {
FileIndex DynamicIndex, StaticIndex;
FileIndex DynamicIndex(true), StaticIndex(true);
MergedIndex Merge(&DynamicIndex, &StaticIndex);

const char *HeaderCode = "class Foo;";
Expand Down Expand Up @@ -446,8 +446,8 @@ TEST(MergeTest, PreferSymbolLocationInCodegenFile) {
}

TEST(MergeIndexTest, Refs) {
FileIndex Dyn;
FileIndex StaticIndex;
FileIndex Dyn(true);
FileIndex StaticIndex(true);
MergedIndex Merge(&Dyn, &StaticIndex);

const char *HeaderCode = "class Foo;";
Expand Down
14 changes: 13 additions & 1 deletion clang-tools-extra/clangd/unittests/RenameTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1548,7 +1548,7 @@ TEST(CrossFileRenameTests, DirtyBuffer) {
std::string BarPath = testPath("bar.cc");
// Build the index, the index has "Foo" references from foo.cc and "Bar"
// references from bar.cc.
FileSymbols FSymbols(IndexContents::All);
FileSymbols FSymbols(IndexContents::All, true);
FSymbols.update(FooPath, nullptr, buildRefSlab(FooCode, "Foo", FooPath),
nullptr, false);
FSymbols.update(BarPath, nullptr, buildRefSlab(BarCode, "Bar", BarPath),
Expand Down Expand Up @@ -1601,6 +1601,12 @@ TEST(CrossFileRenameTests, DirtyBuffer) {
return true; // has more references
}

bool containedRefs(const ContainedRefsRequest &Req,
llvm::function_ref<void(const ContainedRefsResult &)>
Callback) const override {
return false;
}

bool fuzzyFind(
const FuzzyFindRequest &Req,
llvm::function_ref<void(const Symbol &)> Callback) const override {
Expand Down Expand Up @@ -1652,6 +1658,12 @@ TEST(CrossFileRenameTests, DeduplicateRefsFromIndex) {
return false;
}

bool containedRefs(const ContainedRefsRequest &Req,
llvm::function_ref<void(const ContainedRefsResult &)>
Callback) const override {
return false;
}

bool fuzzyFind(const FuzzyFindRequest &,
llvm::function_ref<void(const Symbol &)>) const override {
return false;
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/unittests/TestTU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ RefSlab TestTU::headerRefs() const {

std::unique_ptr<SymbolIndex> TestTU::index() const {
auto AST = build();
auto Idx = std::make_unique<FileIndex>();
auto Idx = std::make_unique<FileIndex>(/*SupportContainedRefs=*/true);
Idx->updatePreamble(testPath(Filename), /*Version=*/"null",
AST.getASTContext(), AST.getPreprocessor(),
AST.getPragmaIncludes());
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/unittests/TestWorkspace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace clang {
namespace clangd {

std::unique_ptr<SymbolIndex> TestWorkspace::index() {
auto Index = std::make_unique<FileIndex>();
auto Index = std::make_unique<FileIndex>(/*SupportContainedRefs=*/true);
for (const auto &Input : Inputs) {
if (!Input.second.IsMainFile)
continue;
Expand Down
7 changes: 7 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,7 @@ Bug Fixes to C++ Support
- Name independent data members were not correctly initialized from default member initializers. (#GH114069)
- Fixed expression transformation for ``[[assume(...)]]``, allowing using pack indexing expressions within the
assumption if they also occur inside of a dependent lambda. (#GH114787)
- Lambdas now capture function types without considering top-level const qualifiers. (#GH84961)
- Clang now uses valid deduced type locations when diagnosing functions with trailing return type
missing placeholder return type. (#GH78694)
- Fixed a bug where bounds of partially expanded pack indexing expressions were checked too early. (#GH116105)
Expand Down Expand Up @@ -935,9 +936,15 @@ and `-mbulk-memory` flags, which correspond to the [Bulk Memory Operations]
and [Non-trapping float-to-int Conversions] language features, which are
[widely implemented in engines].

A new Lime1 target CPU is added, -mcpu=lime1. This CPU follows the definition of
the Lime1 CPU [here], and enables -mmultivalue, -mmutable-globals,
-mcall-indirect-overlong, -msign-ext, -mbulk-memory-opt, -mnontrapping-fptoint,
and -mextended-const.

[Bulk Memory Operations]: https://github.com/WebAssembly/bulk-memory-operations/blob/master/proposals/bulk-memory-operations/Overview.md
[Non-trapping float-to-int Conversions]: https://github.com/WebAssembly/spec/blob/master/proposals/nontrapping-float-to-int-conversion/Overview.md
[widely implemented in engines]: https://webassembly.org/features/
[here]: https://github.com/WebAssembly/tool-conventions/blob/main/Lime.md#lime1

AVR Support
^^^^^^^^^^^
Expand Down
16 changes: 9 additions & 7 deletions clang/include/clang/AST/AttrIterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@
#define LLVM_CLANG_AST_ATTRITERATOR_H

#include "clang/Basic/LLVM.h"
#include "llvm/ADT/ADL.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
#include <cassert>
#include <cstddef>
#include <iterator>
#include <type_traits>

namespace clang {

Expand Down Expand Up @@ -113,13 +115,13 @@ inline bool hasSpecificAttr(const Container& container) {
specific_attr_end<SpecificAttr>(container);
}
template <typename SpecificAttr, typename Container>
inline SpecificAttr *getSpecificAttr(const Container& container) {
specific_attr_iterator<SpecificAttr, Container> i =
specific_attr_begin<SpecificAttr>(container);
if (i != specific_attr_end<SpecificAttr>(container))
return *i;
else
return nullptr;
inline auto *getSpecificAttr(const Container &container) {
using ValueTy = llvm::detail::ValueOfRange<Container>;
using ValuePointeeTy = std::remove_pointer_t<ValueTy>;
using IterTy = std::conditional_t<std::is_const_v<ValuePointeeTy>,
const SpecificAttr, SpecificAttr>;
auto It = specific_attr_begin<IterTy>(container);
return It != specific_attr_end<IterTy>(container) ? *It : nullptr;
}

} // namespace clang
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -3754,6 +3754,8 @@ class ArrayParameterType : public ConstantArrayType {
static bool classof(const Type *T) {
return T->getTypeClass() == ArrayParameter;
}

QualType getConstantArrayType(const ASTContext &Ctx) const;
};

/// Represents a C array with an unspecified size. For example 'int A[]' has
Expand Down
10 changes: 6 additions & 4 deletions clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -294,11 +294,13 @@ def : DiagGroup<"c++1z-compat-mangling", [CXX17CompatMangling]>;
// Name of this warning in GCC.
def NoexceptType : DiagGroup<"noexcept-type", [CXX17CompatMangling]>;

def VariadicMacroArgumentsOmitted : DiagGroup<"variadic-macro-arguments-omitted">;

// Warnings for C code which is not compatible with previous C standards.
def CPre11Compat : DiagGroup<"pre-c11-compat">;
def CPre11CompatPedantic : DiagGroup<"pre-c11-compat-pedantic",
[CPre11Compat]>;
def CPre23Compat : DiagGroup<"pre-c23-compat">;
def CPre23Compat : DiagGroup<"pre-c23-compat", [VariadicMacroArgumentsOmitted]>;
def CPre23CompatPedantic : DiagGroup<"pre-c23-compat-pedantic",
[CPre23Compat]>;
def : DiagGroup<"pre-c2x-compat", [CPre23Compat]>;
Expand Down Expand Up @@ -906,7 +908,7 @@ def VolatileRegisterVar : DiagGroup<"volatile-register-var">;
def Visibility : DiagGroup<"visibility">;
def ZeroLengthArray : DiagGroup<"zero-length-array">;
def GNUZeroLineDirective : DiagGroup<"gnu-zero-line-directive">;
def GNUZeroVariadicMacroArguments : DiagGroup<"gnu-zero-variadic-macro-arguments">;
def GNUZeroVariadicMacroArguments : DiagGroup<"gnu-zero-variadic-macro-arguments", [VariadicMacroArgumentsOmitted]>;
def MisleadingIndentation : DiagGroup<"misleading-indentation">;
def PtrAuthNullPointers : DiagGroup<"ptrauth-null-pointers">;

Expand Down Expand Up @@ -1199,7 +1201,7 @@ def CXX17 : DiagGroup<"c++17-extensions", [CXX17Attrs]>;

// A warning group for warnings about using C++20 features as extensions in
// earlier C++ versions.
def CXX20 : DiagGroup<"c++20-extensions", [CXX20Designator, CXX20Attrs]>;
def CXX20 : DiagGroup<"c++20-extensions", [CXX20Designator, CXX20Attrs, VariadicMacroArgumentsOmitted]>;

// A warning group for warnings about using C++23 features as extensions in
// earlier C++ versions.
Expand All @@ -1226,7 +1228,7 @@ def C11 : DiagGroup<"c11-extensions">;
def C99 : DiagGroup<"c99-extensions", [C99Designator]>;

// A warning group for warnings about using C23 features as extensions.
def C23 : DiagGroup<"c23-extensions">;
def C23 : DiagGroup<"c23-extensions", [VariadicMacroArgumentsOmitted]>;

def : DiagGroup<"c2x-extensions", [C23]>;

Expand Down
6 changes: 3 additions & 3 deletions clang/include/clang/Basic/DiagnosticLexKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -486,14 +486,14 @@ def ext_embedded_directive : Extension<
InGroup<DiagGroup<"embedded-directive">>;
def ext_c_missing_varargs_arg : Extension<
"passing no argument for the '...' parameter of a variadic macro is "
"a C23 extension">, InGroup<C23>;
"a C23 extension">, InGroup<VariadicMacroArgumentsOmitted>;
def ext_cxx_missing_varargs_arg : Extension<
"passing no argument for the '...' parameter of a variadic macro is "
"a C++20 extension">, InGroup<CXX20>;
"a C++20 extension">, InGroup<VariadicMacroArgumentsOmitted>;
def warn_c17_compat_missing_varargs_arg : Warning<
"passing no argument for the '...' parameter of a variadic macro is "
"incompatible with C standards before C23">,
InGroup<CPre23Compat>, DefaultIgnore;
InGroup<VariadicMacroArgumentsOmitted>, DefaultIgnore;
def warn_cxx17_compat_missing_varargs_arg : Warning<
"passing no argument for the '...' parameter of a variadic macro is "
"incompatible with C++ standards before C++20">,
Expand Down
95 changes: 95 additions & 0 deletions clang/lib/AST/ByteCode/BitcastBuffer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//===-------------------- Bitcastbuffer.cpp ---------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "BitcastBuffer.h"

using namespace clang;
using namespace clang::interp;

/// Returns the value of the bit in the given sequence of bytes.
static inline bool bitof(const std::byte *B, Bits BitIndex) {
return (B[BitIndex.roundToBytes()] &
(std::byte{1} << BitIndex.getOffsetInByte())) != std::byte{0};
}

void BitcastBuffer::pushData(const std::byte *In, Bits BitOffset, Bits BitWidth,
Endian TargetEndianness) {
for (unsigned It = 0; It != BitWidth.getQuantity(); ++It) {
bool BitValue = bitof(In, Bits(It));
if (!BitValue)
continue;

Bits DstBit;
if (TargetEndianness == Endian::Little)
DstBit = BitOffset + Bits(It);
else
DstBit = size() - BitOffset - BitWidth + Bits(It);

size_t DstByte = DstBit.roundToBytes();
Data[DstByte] |= std::byte{1} << DstBit.getOffsetInByte();
}
}

std::unique_ptr<std::byte[]>
BitcastBuffer::copyBits(Bits BitOffset, Bits BitWidth, Bits FullBitWidth,
Endian TargetEndianness) const {
assert(BitWidth.getQuantity() <= FullBitWidth.getQuantity());
assert(FullBitWidth.isFullByte());
auto Out = std::make_unique<std::byte[]>(FullBitWidth.roundToBytes());

for (unsigned It = 0; It != BitWidth.getQuantity(); ++It) {
Bits BitIndex;
if (TargetEndianness == Endian::Little)
BitIndex = BitOffset + Bits(It);
else
BitIndex = size() - BitWidth - BitOffset + Bits(It);

bool BitValue = bitof(Data.get(), BitIndex);
if (!BitValue)
continue;

Bits DstBit = Bits(It);
size_t DstByte = DstBit.roundToBytes();
Out[DstByte] |= std::byte{1} << DstBit.getOffsetInByte();
}

return Out;
}

#if 0
template<typename T>
static std::string hex(T t) {
std::stringstream stream;
stream << std::hex << (int)t;
return std::string(stream.str());
}


void BitcastBuffer::dump(bool AsHex = true) const {
llvm::errs() << "LSB\n ";
unsigned LineLength = 0;
for (unsigned I = 0; I != (FinalBitSize / 8); ++I) {
std::byte B = Data[I];
if (AsHex) {
std::stringstream stream;
stream << std::hex << (int)B;
llvm::errs() << stream.str();
LineLength += stream.str().size() + 1;
} else {
llvm::errs() << std::bitset<8>((int)B).to_string();
LineLength += 8 + 1;
// llvm::errs() << (int)B;
}
llvm::errs() << ' ';
}
llvm::errs() << '\n';

for (unsigned I = 0; I != LineLength; ++I)
llvm::errs() << ' ';
llvm::errs() << "MSB\n";
}
#endif
88 changes: 88 additions & 0 deletions clang/lib/AST/ByteCode/BitcastBuffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//===--------------------- BitcastBuffer.h ----------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_AST_INTERP_BITCAST_BUFFER_H
#define LLVM_CLANG_AST_INTERP_BITCAST_BUFFER_H

#include <cassert>
#include <cstddef>
#include <memory>

namespace clang {
namespace interp {

enum class Endian { Little, Big };

/// A quantity in bits.
struct Bits {
size_t N = 0;
Bits() = default;
static Bits zero() { return Bits(0); }
explicit Bits(size_t Quantity) : N(Quantity) {}
size_t getQuantity() const { return N; }
size_t roundToBytes() const { return N / 8; }
size_t getOffsetInByte() const { return N % 8; }
bool isFullByte() const { return N % 8 == 0; }
bool nonZero() const { return N != 0; }

Bits operator-(Bits Other) { return Bits(N - Other.N); }
Bits operator+(Bits Other) { return Bits(N + Other.N); }
Bits operator+=(size_t O) {
N += O;
return *this;
}

bool operator>=(Bits Other) { return N >= Other.N; }
};

/// A quantity in bytes.
struct Bytes {
size_t N;
explicit Bytes(size_t Quantity) : N(Quantity) {}
size_t getQuantity() const { return N; }
Bits toBits() const { return Bits(N * 8); }
};

/// Track what bits have been initialized to known values and which ones
/// have indeterminate value.
struct BitcastBuffer {
Bits FinalBitSize;
std::unique_ptr<std::byte[]> Data;

BitcastBuffer(Bits FinalBitSize) : FinalBitSize(FinalBitSize) {
assert(FinalBitSize.isFullByte());
unsigned ByteSize = FinalBitSize.roundToBytes();
Data = std::make_unique<std::byte[]>(ByteSize);
}

/// Returns the buffer size in bits.
Bits size() const { return FinalBitSize; }

/// Returns \c true if all bits in the buffer have been initialized.
bool allInitialized() const {
// FIXME: Implement.
return true;
}

/// Push \p BitWidth bits at \p BitOffset from \p In into the buffer.
/// \p TargetEndianness is the endianness of the target we're compiling for.
/// \p In must hold at least \p BitWidth many bits.
void pushData(const std::byte *In, Bits BitOffset, Bits BitWidth,
Endian TargetEndianness);

/// Copy \p BitWidth bits at offset \p BitOffset from the buffer.
/// \p TargetEndianness is the endianness of the target we're compiling for.
///
/// The returned output holds exactly (\p FullBitWidth / 8) bytes.
std::unique_ptr<std::byte[]> copyBits(Bits BitOffset, Bits BitWidth,
Bits FullBitWidth,
Endian TargetEndianness) const;
};

} // namespace interp
} // namespace clang
#endif
4 changes: 1 addition & 3 deletions clang/lib/AST/ByteCode/Boolean.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,7 @@ class Boolean final {
Boolean truncate(unsigned TruncBits) const { return *this; }

static Boolean bitcastFromMemory(const std::byte *Buff, unsigned BitWidth) {
// Boolean width is currently always 8 for all supported targets. If this
// changes we need to get the bool width from the target info.
assert(BitWidth == 8);
// Just load the first byte.
bool Val = static_cast<bool>(*Buff);
return Boolean(Val);
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/ByteCode/Integral.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ template <unsigned Bits, bool Signed> class Integral final {
}

Integral truncate(unsigned TruncBits) const {
assert(TruncBits >= 1);
if (TruncBits >= Bits)
return *this;
const ReprT BitMask = (ReprT(1) << ReprT(TruncBits)) - 1;
Expand Down
284 changes: 114 additions & 170 deletions clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions clang/lib/AST/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ add_clang_library(clangAST
ExternalASTSource.cpp
FormatString.cpp
InheritViz.cpp
ByteCode/BitcastBuffer.cpp
ByteCode/ByteCodeEmitter.cpp
ByteCode/Compiler.cpp
ByteCode/Context.cpp
Expand Down
10 changes: 9 additions & 1 deletion clang/lib/AST/MicrosoftMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3372,7 +3372,15 @@ void MicrosoftCXXNameMangler::mangleType(const MemberPointerType *T,

void MicrosoftCXXNameMangler::mangleType(const TemplateTypeParmType *T,
Qualifiers, SourceRange Range) {
Error(Range.getBegin(), "template type parameter type") << Range;
Out << '?';

llvm::SmallString<64> Name;
Name += "<TTPT_";
Name += llvm::utostr(T->getDepth());
Name += "_";
Name += llvm::utostr(T->getIndex());
Name += ">";
mangleSourceName(Name);
}

void MicrosoftCXXNameMangler::mangleType(const SubstTemplateTypeParmPackType *T,
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,12 @@ void ConstantArrayType::Profile(llvm::FoldingSetNodeID &ID,
SizeExpr->Profile(ID, Context, true);
}

QualType ArrayParameterType::getConstantArrayType(const ASTContext &Ctx) const {
return Ctx.getConstantArrayType(getElementType(), getSize(), getSizeExpr(),
getSizeModifier(),
getIndexTypeQualifiers().getAsOpaqueValue());
}

DependentSizedArrayType::DependentSizedArrayType(QualType et, QualType can,
Expr *e, ArraySizeModifier sm,
unsigned tq,
Expand Down
1 change: 1 addition & 0 deletions clang/lib/ASTMatchers/Dynamic/Registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ RegistryMaps::RegistryMaps() {
REGISTER_MATCHER(equalsBoundNode);
REGISTER_MATCHER(equalsIntegralValue);
REGISTER_MATCHER(explicitCastExpr);
REGISTER_MATCHER(exportDecl);
REGISTER_MATCHER(expr);
REGISTER_MATCHER(exprWithCleanups);
REGISTER_MATCHER(fieldDecl);
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/Basic/Targets/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,9 @@ void AArch64TargetInfo::getTargetDefines(const LangOptions &Opts,
Builder.defineMacro("__ARM_FP16_FORMAT_IEEE", "1");
Builder.defineMacro("__ARM_FP16_ARGS", "1");

// Clang supports arm_neon_sve_bridge.h
Builder.defineMacro("__ARM_NEON_SVE_BRIDGE", "1");

if (Opts.UnsafeFPMath)
Builder.defineMacro("__ARM_FP_FAST", "1");

Expand All @@ -464,9 +467,6 @@ void AArch64TargetInfo::getTargetDefines(const LangOptions &Opts,
if (FPU & SveMode)
Builder.defineMacro("__ARM_FEATURE_SVE", "1");

if ((FPU & NeonMode) && (FPU & SveMode))
Builder.defineMacro("__ARM_NEON_SVE_BRIDGE", "1");

if (HasSVE2)
Builder.defineMacro("__ARM_FEATURE_SVE2", "1");

Expand Down
15 changes: 14 additions & 1 deletion clang/lib/Basic/Targets/WebAssembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ static constexpr Builtin::Info BuiltinInfo[] = {
};

static constexpr llvm::StringLiteral ValidCPUNames[] = {
{"mvp"}, {"bleeding-edge"}, {"generic"}};
{"mvp"}, {"bleeding-edge"}, {"generic"}, {"lime"}};

StringRef WebAssemblyTargetInfo::getABI() const { return ABI; }

Expand Down Expand Up @@ -167,6 +167,17 @@ bool WebAssemblyTargetInfo::initFeatureMap(
Features["reference-types"] = true;
Features["sign-ext"] = true;
};
auto addLime1Features = [&]() {
// Lime1:
// <https://github.com/WebAssembly/tool-conventions/blob/main/Lime.md#lime1>
Features["bulk-memory-opt"] = true;
Features["call-indirect-overlong"] = true;
Features["extended-const"] = true;
Features["multivalue"] = true;
Features["mutable-globals"] = true;
Features["nontrapping-fptoint"] = true;
Features["sign-ext"] = true;
};
auto addBleedingEdgeFeatures = [&]() {
addGenericFeatures();
Features["atomics"] = true;
Expand All @@ -180,6 +191,8 @@ bool WebAssemblyTargetInfo::initFeatureMap(
};
if (CPU == "generic") {
addGenericFeatures();
} else if (CPU == "lime1") {
addLime1Features();
} else if (CPU == "bleeding-edge") {
addBleedingEdgeFeatures();
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Basic/Targets/X86.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1162,6 +1162,7 @@ bool X86TargetInfo::isValidFeatureName(StringRef Name) const {
.Case("pconfig", true)
.Case("pku", true)
.Case("popcnt", true)
.Case("prefer-256-bit", true)
.Case("prefetchi", true)
.Case("prfchw", true)
.Case("ptwrite", true)
Expand Down
16 changes: 13 additions & 3 deletions clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4725,15 +4725,17 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E,
return emitWritebackArg(*this, args, CRE);
}

assert(type->isReferenceType() == E->isGLValue() &&
"reference binding to unmaterialized r-value!");

// Add writeback for HLSLOutParamExpr.
// Needs to be before the assert below because HLSLOutArgExpr is an LValue
// and is not a reference.
if (const HLSLOutArgExpr *OE = dyn_cast<HLSLOutArgExpr>(E)) {
EmitHLSLOutArgExpr(OE, args, type);
return;
}

assert(type->isReferenceType() == E->isGLValue() &&
"reference binding to unmaterialized r-value!");

if (E->isGLValue()) {
assert(E->getObjectKind() == OK_Ordinary);
return args.add(EmitReferenceBindingToExpr(E), type);
Expand Down Expand Up @@ -5322,6 +5324,14 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
IRCallArgs[FirstIRArg] = Val;
break;
}
} else if (I->getType()->isArrayParameterType()) {
// Don't produce a temporary for ArrayParameterType arguments.
// ArrayParameterType arguments are only created from
// HLSL_ArrayRValue casts and HLSLOutArgExpr expressions, both
// of which create temporaries already. This allows us to just use the
// scalar for the decayed array pointer as the argument directly.
IRCallArgs[FirstIRArg] = I->getKnownRValue().getScalarVal();
break;
}

// For non-aggregate args and aggregate args meeting conditions above
Expand Down
7 changes: 5 additions & 2 deletions clang/lib/CodeGen/CGExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5827,9 +5827,12 @@ LValue CodeGenFunction::EmitBinaryOperatorLValue(const BinaryOperator *E) {
// This function implements trivial copy assignment for HLSL's
// assignable constant arrays.
LValue CodeGenFunction::EmitHLSLArrayAssignLValue(const BinaryOperator *E) {
LValue TrivialAssignmentRHS = EmitLValue(E->getRHS());
// Don't emit an LValue for the RHS because it might not be an LValue
LValue LHS = EmitLValue(E->getLHS());
EmitAggregateAssign(LHS, TrivialAssignmentRHS, E->getLHS()->getType());
// In C the RHS of an assignment operator is an RValue.
// EmitAggregateAssign takes anan LValue for the RHS. Instead we can call
// EmitInitializationToLValue to emit an RValue into an LValue.
EmitInitializationToLValue(E->getRHS(), LHS);
return LHS;
}

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CodeGen/CGLoopInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,7 @@ void LoopInfoStack::push(BasicBlock *Header, clang::ASTContext &Ctx,
// Identify loop attribute 'code_align' from Attrs.
// For attribute code_align:
// n - 'llvm.loop.align i32 n' metadata will be emitted.
if (const auto *CodeAlign = getSpecificAttr<const CodeAlignAttr>(Attrs)) {
if (const auto *CodeAlign = getSpecificAttr<CodeAlignAttr>(Attrs)) {
const auto *CE = cast<ConstantExpr>(CodeAlign->getAlignment());
llvm::APSInt ArgVal = CE->getResultAsAPSInt();
setCodeAlign(ArgVal.getSExtValue());
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,15 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty,
QualType ExprTy = Context.getCanonicalType(E->getType());
QualType TypeTy = Context.getCanonicalType(Ty);

// This cast is used in place of a regular LValue to RValue cast for
// HLSL Array Parameter Types. It needs to be emitted even if
// ExprTy == TypeTy, except if E is an HLSLOutArgExpr
// Emitting a cast in that case will prevent HLSLOutArgExpr from
// being handled properly in EmitCallArg
if (Kind == CK_HLSLArrayRValue && !isa<HLSLOutArgExpr>(E))
return ImplicitCastExpr::Create(Context, Ty, Kind, E, BasePath, VK,
CurFPFeatureOverrides());

if (ExprTy == TypeTy)
return E;

Expand Down
13 changes: 11 additions & 2 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18431,7 +18431,11 @@ static bool isVariableAlreadyCapturedInScopeInfo(CapturingScopeInfo *CSI,
// are mutable in the sense that user can change their value - they are
// private instances of the captured declarations.
const Capture &Cap = CSI->getCapture(Var);
if (Cap.isCopyCapture() &&
// C++ [expr.prim.lambda]p10:
// The type of such a data member is [...] an lvalue reference to the
// referenced function type if the entity is a reference to a function.
// [...]
if (Cap.isCopyCapture() && !DeclRefType->isFunctionType() &&
!(isa<LambdaScopeInfo>(CSI) &&
!cast<LambdaScopeInfo>(CSI)->lambdaCaptureShouldBeConst()) &&
!(isa<CapturedRegionScopeInfo>(CSI) &&
Expand Down Expand Up @@ -18741,7 +18745,12 @@ static bool captureInLambda(LambdaScopeInfo *LSI, ValueDecl *Var,
// parameter-declaration-clause is not followed by mutable.
DeclRefType = CaptureType.getNonReferenceType();
bool Const = LSI->lambdaCaptureShouldBeConst();
if (Const && !CaptureType->isReferenceType())
// C++ [expr.prim.lambda]p10:
// The type of such a data member is [...] an lvalue reference to the
// referenced function type if the entity is a reference to a function.
// [...]
if (Const && !CaptureType->isReferenceType() &&
!DeclRefType->isFunctionType())
DeclRefType.addConst();
}

Expand Down
19 changes: 15 additions & 4 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4431,10 +4431,21 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
break;

case ICK_HLSL_Array_RValue:
FromType = Context.getArrayParameterType(FromType);
From = ImpCastExprToType(From, FromType, CK_HLSLArrayRValue, VK_PRValue,
/*BasePath=*/nullptr, CCK)
.get();
if (ToType->isArrayParameterType()) {
FromType = Context.getArrayParameterType(FromType);
From = ImpCastExprToType(From, FromType, CK_HLSLArrayRValue, VK_PRValue,
/*BasePath=*/nullptr, CCK)
.get();
} else { // FromType must be ArrayParameterType
assert(FromType->isArrayParameterType() &&
"FromType must be ArrayParameterType in ICK_HLSL_Array_RValue \
if it is not ToType");
const ArrayParameterType *APT = cast<ArrayParameterType>(FromType);
FromType = APT->getConstantArrayType(Context);
From = ImpCastExprToType(From, FromType, CK_HLSLArrayRValue, VK_PRValue,
/*BasePath=*/nullptr, CCK)
.get();
}
break;

case ICK_Function_To_Pointer:
Expand Down
50 changes: 30 additions & 20 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2236,33 +2236,24 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
return false;
}
}
// Lvalue-to-rvalue conversion (C++11 4.1):
// A glvalue (3.10) of a non-function, non-array type T can
// be converted to a prvalue.
bool argIsLValue = From->isGLValue();
if (argIsLValue && !FromType->canDecayToPointerType() &&
S.Context.getCanonicalType(FromType) != S.Context.OverloadTy) {
SCS.First = ICK_Lvalue_To_Rvalue;

// C11 6.3.2.1p2:
// ... if the lvalue has atomic type, the value has the non-atomic version
// of the type of the lvalue ...
if (const AtomicType *Atomic = FromType->getAs<AtomicType>())
FromType = Atomic->getValueType();

// If T is a non-class type, the type of the rvalue is the
// cv-unqualified version of T. Otherwise, the type of the rvalue
// is T (C++ 4.1p1). C++ can't get here with class types; in C, we
// just strip the qualifiers because they don't matter.
FromType = FromType.getUnqualifiedType();
} else if (S.getLangOpts().HLSL && FromType->isConstantArrayType() &&
ToType->isConstantArrayType()) {
bool argIsLValue = From->isGLValue();
// To handle conversion from ArrayParameterType to ConstantArrayType
// this block must be above the one below because Array parameters
// do not decay and when handling HLSLOutArgExprs and
// the From expression is an LValue.
if (S.getLangOpts().HLSL && FromType->isConstantArrayType() &&
ToType->isConstantArrayType()) {
// HLSL constant array parameters do not decay, so if the argument is a
// constant array and the parameter is an ArrayParameterType we have special
// handling here.
if (ToType->isArrayParameterType()) {
FromType = S.Context.getArrayParameterType(FromType);
SCS.First = ICK_HLSL_Array_RValue;
} else if (FromType->isArrayParameterType()) {
const ArrayParameterType *APT = cast<ArrayParameterType>(FromType);
FromType = APT->getConstantArrayType(S.Context);
SCS.First = ICK_HLSL_Array_RValue;
} else {
SCS.First = ICK_Identity;
}
Expand All @@ -2273,6 +2264,25 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,

SCS.setAllToTypes(ToType);
return true;
} else if (argIsLValue && !FromType->canDecayToPointerType() &&
S.Context.getCanonicalType(FromType) != S.Context.OverloadTy) {
// Lvalue-to-rvalue conversion (C++11 4.1):
// A glvalue (3.10) of a non-function, non-array type T can
// be converted to a prvalue.

SCS.First = ICK_Lvalue_To_Rvalue;

// C11 6.3.2.1p2:
// ... if the lvalue has atomic type, the value has the non-atomic version
// of the type of the lvalue ...
if (const AtomicType *Atomic = FromType->getAs<AtomicType>())
FromType = Atomic->getValueType();

// If T is a non-class type, the type of the rvalue is the
// cv-unqualified version of T. Otherwise, the type of the rvalue
// is T (C++ 4.1p1). C++ can't get here with class types; in C, we
// just strip the qualifiers because they don't matter.
FromType = FromType.getUnqualifiedType();
} else if (FromType->isArrayType()) {
// Array-to-pointer conversion (C++ 4.2)
SCS.First = ICK_Array_To_Pointer;
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Sema/SemaType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5681,6 +5681,9 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
assert(!T.isNull() && "T must not be null at the end of this function");
if (!AreDeclaratorChunksValid)
return Context.getTrivialTypeSourceInfo(T);

if (state.didParseHLSLParamMod() && !T->isConstantArrayType())
T = S.HLSL().getInoutParameterType(T);
return GetTypeSourceInfoForDeclarator(state, T, TInfo);
}

Expand Down Expand Up @@ -8634,7 +8637,6 @@ static void HandleHLSLParamModifierAttr(TypeProcessingState &State,
return;
if (Attr.getSemanticSpelling() == HLSLParamModifierAttr::Keyword_inout ||
Attr.getSemanticSpelling() == HLSLParamModifierAttr::Keyword_out) {
CurType = S.HLSL().getInoutParameterType(CurType);
State.setParsedHLSLParamMod(true);
}
}
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,9 @@ class MallocSizeofChecker : public Checker<check::ASTCodeBody> {
continue;

const TypeSourceInfo *TSI = nullptr;
if (CallRec.CastedExprParent.is<const VarDecl *>()) {
TSI = CallRec.CastedExprParent.get<const VarDecl *>()
->getTypeSourceInfo();
if (const auto *VD =
dyn_cast<const VarDecl *>(CallRec.CastedExprParent)) {
TSI = VD->getTypeSourceInfo();
} else {
TSI = CallRec.ExplicitCastType;
}
Expand Down
8 changes: 4 additions & 4 deletions clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,13 @@ const PointerToMemberData *BasicValueFactory::accumCXXBase(
const NamedDecl *ND = nullptr;
llvm::ImmutableList<const CXXBaseSpecifier *> BaseSpecList;

if (PTMDT.isNull() || PTMDT.is<const NamedDecl *>()) {
if (PTMDT.is<const NamedDecl *>())
ND = PTMDT.get<const NamedDecl *>();
if (PTMDT.isNull() || isa<const NamedDecl *>(PTMDT)) {
if (const auto *NDP = dyn_cast_if_present<const NamedDecl *>(PTMDT))
ND = NDP;

BaseSpecList = CXXBaseListFactory.getEmptyList();
} else {
const PointerToMemberData *PTMD = PTMDT.get<const PointerToMemberData *>();
const auto *PTMD = cast<const PointerToMemberData *>(PTMDT);
ND = PTMD->getDeclaratorDecl();

BaseSpecList = PTMD->getCXXBaseList();
Expand Down
10 changes: 5 additions & 5 deletions clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,9 @@ void ExplodedNode::NodeGroup::replaceNode(ExplodedNode *node) {
assert(!getFlag());

GroupStorage &Storage = reinterpret_cast<GroupStorage&>(P);
assert(Storage.is<ExplodedNode *>());
assert(isa<ExplodedNode *>(Storage));
Storage = node;
assert(Storage.is<ExplodedNode *>());
assert(isa<ExplodedNode *>(Storage));
}

void ExplodedNode::NodeGroup::addNode(ExplodedNode *N, ExplodedGraph &G) {
Expand All @@ -222,23 +222,23 @@ void ExplodedNode::NodeGroup::addNode(ExplodedNode *N, ExplodedGraph &G) {
GroupStorage &Storage = reinterpret_cast<GroupStorage&>(P);
if (Storage.isNull()) {
Storage = N;
assert(Storage.is<ExplodedNode *>());
assert(isa<ExplodedNode *>(Storage));
return;
}

ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>();

if (!V) {
// Switch from single-node to multi-node representation.
ExplodedNode *Old = Storage.get<ExplodedNode *>();
auto *Old = cast<ExplodedNode *>(Storage);

BumpVectorContext &Ctx = G.getNodeAllocator();
V = new (G.getAllocator()) ExplodedNodeVector(Ctx, 4);
V->push_back(Old, Ctx);

Storage = V;
assert(!getFlag());
assert(Storage.is<ExplodedNodeVector *>());
assert(isa<ExplodedNodeVector *>(Storage));
}

V->push_back(N, G.getNodeAllocator());
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/StaticAnalyzer/Core/MemRegion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1068,10 +1068,10 @@ const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
llvm::PointerUnion<const StackFrameContext *, const VarRegion *> V =
getStackOrCaptureRegionForDeclContext(LC, DC, D);

if (V.is<const VarRegion*>())
return V.get<const VarRegion*>();
if (const auto *VR = dyn_cast_if_present<const VarRegion *>(V))
return VR;

const auto *STC = V.get<const StackFrameContext *>();
const auto *STC = cast<const StackFrameContext *>(V);

if (!STC) {
// FIXME: Assign a more sensible memory space to static locals
Expand Down
14 changes: 7 additions & 7 deletions clang/lib/StaticAnalyzer/Core/SVals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,10 @@ const NamedDecl *nonloc::PointerToMember::getDecl() const {
return nullptr;

const NamedDecl *ND = nullptr;
if (PTMD.is<const NamedDecl *>())
ND = PTMD.get<const NamedDecl *>();
if (const auto *NDP = dyn_cast<const NamedDecl *>(PTMD))
ND = NDP;
else
ND = PTMD.get<const PointerToMemberData *>()->getDeclaratorDecl();
ND = cast<const PointerToMemberData *>(PTMD)->getDeclaratorDecl();

return ND;
}
Expand All @@ -227,16 +227,16 @@ nonloc::CompoundVal::iterator nonloc::CompoundVal::end() const {

nonloc::PointerToMember::iterator nonloc::PointerToMember::begin() const {
const PTMDataType PTMD = getPTMData();
if (PTMD.is<const NamedDecl *>())
if (isa<const NamedDecl *>(PTMD))
return {};
return PTMD.get<const PointerToMemberData *>()->begin();
return cast<const PointerToMemberData *>(PTMD)->begin();
}

nonloc::PointerToMember::iterator nonloc::PointerToMember::end() const {
const PTMDataType PTMD = getPTMData();
if (PTMD.is<const NamedDecl *>())
if (isa<const NamedDecl *>(PTMD))
return {};
return PTMD.get<const PointerToMemberData *>()->end();
return cast<const PointerToMemberData *>(PTMD)->end();
}

//===----------------------------------------------------------------------===//
Expand Down
441 changes: 441 additions & 0 deletions clang/test/AST/ByteCode/builtin-bit-cast-bitfields.cpp

Large diffs are not rendered by default.

119 changes: 32 additions & 87 deletions clang/test/AST/ByteCode/builtin-bit-cast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,72 +186,6 @@ namespace bitint {
// ref-note {{initializer of 'IB' is not a constant expression}}
}

namespace BitFields {
struct BitFields {
unsigned a : 2;
unsigned b : 30;
};

constexpr unsigned A = __builtin_bit_cast(unsigned, BitFields{3, 16}); // ref-error {{must be initialized by a constant expression}} \
// ref-note {{not yet supported}} \
// ref-note {{declared here}}
static_assert(A == (LITTLE_END ? 67 : 3221225488)); // ref-error {{not an integral constant expression}} \
// ref-note {{initializer of 'A'}}


void bitfield_indeterminate() {
struct BF { unsigned char z : 2; };
enum byte : unsigned char {};

constexpr BF bf = {0x3};
/// Requires bitcasts to composite types.
// static_assert(bit_cast<bits<2>>(bf).bits == bf.z);
// static_assert(bit_cast<unsigned char>(bf));

#if 0
// static_assert(__builtin_bit_cast(byte, bf));

struct M {
// expected-note@+1 {{subobject declared here}}
unsigned char mem[sizeof(BF)];
};
// expected-error@+2 {{initialized by a constant expression}}
// expected-note@+1 {{not initialized}}
constexpr M m = bit_cast<M>(bf);

constexpr auto f = []() constexpr {
// bits<24, unsigned int, LITTLE_END ? 0 : 8> B = {0xc0ffee};
constexpr struct { unsigned short b1; unsigned char b0; } B = {0xc0ff, 0xee};
return bit_cast<bytes<4>>(B);
};

static_assert(f()[0] + f()[1] + f()[2] == 0xc0 + 0xff + 0xee);
{
// expected-error@+2 {{initialized by a constant expression}}
// expected-note@+1 {{read of uninitialized object is not allowed in a constant expression}}
constexpr auto _bad = f()[3];
}

struct B {
unsigned short s0 : 8;
unsigned short s1 : 8;
std::byte b0 : 4;
std::byte b1 : 4;
std::byte b2 : 4;
};
constexpr auto g = [f]() constexpr {
return bit_cast<B>(f());
};
static_assert(g().s0 + g().s1 + g().b0 + g().b1 == 0xc0 + 0xff + 0xe + 0xe);
{
// expected-error@+2 {{initialized by a constant expression}}
// expected-note@+1 {{read of uninitialized object is not allowed in a constant expression}}
constexpr auto _bad = g().b2;
}
#endif
}
}

namespace Classes {
class A {
public:
Expand Down Expand Up @@ -331,6 +265,21 @@ static_assert(check_round_trip<long long>(splice));
#endif


namespace Overread {
/// This used to crash becaus we were reading all elements of the
/// source array even though we should only be reading 1.
constexpr int a[] = {2,3, 4, 5};
constexpr int b = __builtin_bit_cast(int, *(a + 1));
static_assert(b == 3);

struct S {
int a;
};
constexpr S ss[] = {{1},{2}};
constexpr int c = __builtin_bit_cast(int, *(ss + 1));
static_assert(c == 2);
}


/// ---------------------------------------------------------------------------
/// From here on, it's things copied from test/SemaCXX/constexpr-builtin-bit.cast.cpp
Expand Down Expand Up @@ -510,27 +459,6 @@ static_assert(bit_cast<unsigned long long>(test_vector) == (LITTLE_END
static_assert(check_round_trip<uint2>(0xCAFEBABE0C05FEFEULL), "");
static_assert(check_round_trip<byte8>(0xCAFEBABE0C05FEFEULL), "");

typedef bool bool8 __attribute__((ext_vector_type(8)));
typedef bool bool9 __attribute__((ext_vector_type(9)));
typedef bool bool16 __attribute__((ext_vector_type(16)));
typedef bool bool17 __attribute__((ext_vector_type(17)));
typedef bool bool32 __attribute__((ext_vector_type(32)));
typedef bool bool128 __attribute__((ext_vector_type(128)));

static_assert(bit_cast<unsigned char>(bool8{1,0,1,0,1,0,1,0}) == (LITTLE_END ? 0x55 : 0xAA), "");
constexpr bool8 b8 = __builtin_bit_cast(bool8, 0x55); // both-error {{'__builtin_bit_cast' source type 'int' does not match destination type 'bool8' (vector of 8 'bool' values) (4 vs 1 bytes)}}
#if 0
static_assert(check_round_trip<bool8>(static_cast<unsigned char>(0)), "");
static_assert(check_round_trip<bool8>(static_cast<unsigned char>(1)), "");
static_assert(check_round_trip<bool8>(static_cast<unsigned char>(0x55)), "");

static_assert(bit_cast<unsigned short>(bool16{1,1,1,1,1,0,0,0, 1,1,1,1,0,1,0,0}) == (LITTLE_END ? 0x2F1F : 0xF8F4), "");

static_assert(check_round_trip<bool16>(static_cast<short>(0xCAFE)), "");
static_assert(check_round_trip<bool32>(static_cast<int>(0xCAFEBABE)), "");
static_assert(check_round_trip<bool128>(static_cast<__int128_t>(0xCAFEBABE0C05FEFEULL)), "");
#endif

#if 0
// expected-error@+2 {{constexpr variable 'bad_bool9_to_short' must be initialized by a constant expression}}
// expected-note@+1 {{bit_cast involving type 'bool __attribute__((ext_vector_type(9)))' (vector of 9 'bool' values) is not allowed in a constant expression; element size 1 * element count 9 is not a multiple of the byte size 8}}
Expand Down Expand Up @@ -559,3 +487,20 @@ namespace test_complex {
constexpr double D = __builtin_bit_cast(double, test_float_complex);
constexpr int M = __builtin_bit_cast(int, test_int_complex); // both-error {{size of '__builtin_bit_cast' source type 'const _Complex unsigned int' does not match destination type 'int' (8 vs 4 bytes)}}
}


namespace OversizedBitField {
#if defined(_WIN32)
/// This is an error (not just a warning) on Windows and the field ends up with a size of 1 instead of 4.
#else
typedef unsigned __INT16_TYPE__ uint16_t;
typedef unsigned __INT32_TYPE__ uint32_t;
struct S {
uint16_t a : 20; // both-warning {{exceeds the width of its type}}
};

static_assert(sizeof(S) == 4);
static_assert(__builtin_bit_cast(S, (uint32_t)32).a == (LITTLE_END ? 32 : 0)); // ref-error {{not an integral constant expression}} \
// ref-note {{constexpr bit_cast involving bit-field is not yet supported}}
#endif
}
63 changes: 63 additions & 0 deletions clang/test/AST/HLSL/ArrayOutArgExpr.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -ast-dump %s | FileCheck %s

// CHECK-LABEL: increment
void increment(inout int Arr[2]) {
for (int I = 0; I < 2; I++)
Arr[0] += 2;
}

// CHECK-LABEL: call
// CHECK: CallExpr 0x{{.*}} {{.*}} 'void'
// CHECK: ImplicitCastExpr 0x{{.*}} {{.*}} 'void (*)(inout int[2])' <FunctionToPointerDecay>
// CHECK: DeclRefExpr 0x{{.*}} {{.*}} 'void (inout int[2])' lvalue Function 0x{{.*}} 'increment' 'void (inout int[2])'
// CHECK: HLSLOutArgExpr 0x{{.*}} {{.*}} 'int[2]' lvalue inout
// CHECK: OpaqueValueExpr [[A:0x.*]] {{.*}} 'int[2]' lvalue
// CHECK: DeclRefExpr [[B:0x.*]] {{.*}} 'int[2]' lvalue Var [[E:0x.*]] 'A' 'int[2]'
// CHECK: OpaqueValueExpr [[C:0x.*]] {{.*}} 'int[2]' lvalue
// CHECK: ImplicitCastExpr [[D:0x.*]] {{.*}} 'int[2]' <HLSLArrayRValue>
// CHECK: OpaqueValueExpr [[A]] {{.*}} 'int[2]' lvalue
// CHECK: DeclRefExpr [[B]] {{.*}} 'int[2]' lvalue Var [[E]] 'A' 'int[2]'
// CHECK: BinaryOperator 0x{{.*}} {{.*}} 'int[2]' lvalue '='
// CHECK: OpaqueValueExpr [[A]] {{.*}} 'int[2]' lvalue
// CHECK: DeclRefExpr 0x{{.*}} {{.*}} 'int[2]' lvalue Var [[E]] 'A' 'int[2]'
// CHECK: ImplicitCastExpr 0x{{.*}} {{.*}} 'int[2]' <HLSLArrayRValue>
// CHECK: OpaqueValueExpr [[C]] {{.*}} 'int[2]' lvalue
// CHECK: ImplicitCastExpr [[D]] {{.*}} 'int[2]' <HLSLArrayRValue>
// CHECK: OpaqueValueExpr [[A]] {{.*}} 'int[2]' lvalue
// CHECK: DeclRefExpr [[B]] {{.*}} 'int[2]' lvalue Var [[E]] 'A' 'int[2]'
export int call() {
int A[2] = { 0, 1 };
increment(A);
return A[0];
}

// CHECK-LABEL: fn2
void fn2(out int Arr[2]) {
Arr[0] += 5;
Arr[1] += 6;
}

// CHECK-LABEL: call2
// CHECK: CallExpr 0x{{.*}} {{.*}} 'void'
// CHECK: ImplicitCastExpr 0x{{.*}} {{.*}} 'void (*)(out int[2])' <FunctionToPointerDecay>
// CHECK: DeclRefExpr 0x{{.*}} {{.*}} 'void (out int[2])' lvalue Function 0x{{.*}} 'fn2' 'void (out int[2])'
// CHECK: HLSLOutArgExpr 0x{{.*}} {{.*}} 'int[2]' lvalue out
// CHECK: OpaqueValueExpr [[A:0x.*]] {{.*}} 'int[2]' lvalue
// CHECK: DeclRefExpr [[B:0x.*]] {{.*}} 'int[2]' lvalue Var [[E:0x.*]] 'A' 'int[2]'
// CHECK: OpaqueValueExpr [[C:0x.*]] {{.*}} 'int[2]' lvalue
// CHECK: ImplicitCastExpr [[D:0x.*]] {{.*}} 'int[2]' <HLSLArrayRValue>
// CHECK: OpaqueValueExpr [[A]] {{.*}} 'int[2]' lvalue
// CHECK: DeclRefExpr [[B]] {{.*}} 'int[2]' lvalue Var [[E]] 'A' 'int[2]'
// CHECK: BinaryOperator 0x{{.*}} {{.*}} 'int[2]' lvalue '='
// CHECK: OpaqueValueExpr [[A]] {{.*}} 'int[2]' lvalue
// CHECK: DeclRefExpr [[B]] {{.*}} 'int[2]' lvalue Var [[E]] 'A' 'int[2]'
// CHECK: ImplicitCastExpr 0x{{.*}} {{.*}} 'int[2]' <HLSLArrayRValue>
// CHECK: OpaqueValueExpr [[C]] {{.*}} 'int[2]' lvalue
// CHECK: ImplicitCastExpr [[D]] {{.*}} 'int[2]' <HLSLArrayRValue>
// CHECK: OpaqueValueExpr [[A]] {{.*}} 'int[2]' lvalue
// CHECK: DeclRefExpr [[B]] {{.*}} 'int[2]' lvalue Var [[E]] 'A' 'int[2]'
export int call2() {
int A[2] = { 0, 1 };
fn2(A);
return 1;
}
Loading