[clang] [APINotes] Add Where.Object selectors for C++ methods (PR #216148)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 19 04:01:43 PDT 2026
https://github.com/StoeckOverflow updated https://github.com/llvm/llvm-project/pull/216148
>From 42b6514b148da69dbd814eeeba9cb8e1742063fb Mon Sep 17 00:00:00 2001
From: stoeckoverflow <dominic-st at gmx.de>
Date: Tue, 11 Aug 2026 11:54:31 +0200
Subject: [PATCH 1/6] [APINotes] Add Where.Object selectors for C++ methods
---
clang/include/clang/APINotes/APINotesReader.h | 20 ++-
clang/include/clang/APINotes/APINotesWriter.h | 6 +
clang/include/clang/APINotes/Types.h | 150 ++++++++++++++++--
clang/lib/APINotes/APINotesFormat.h | 62 +++++++-
clang/lib/APINotes/APINotesReader.cpp | 122 +++++++++++---
clang/lib/APINotes/APINotesWriter.cpp | 77 ++++++++-
clang/lib/APINotes/APINotesYAMLCompiler.cpp | 132 ++++++++++-----
clang/lib/Sema/SemaAPINotes.cpp | 141 +++++++++++++---
clang/lib/Sema/SemaAPINotesInternal.h | 8 +-
.../Headers/WhereObjectQualifiers.apinotes | 96 +++++++++++
.../Inputs/Headers/WhereObjectQualifiers.h | 30 ++++
.../APINotes/Inputs/Headers/module.modulemap | 5 +
.../APINotes.apinotes | 46 ++++++
.../WhereObjectQualifiers.h | 14 ++
.../test/APINotes/where-object-qualifiers.cpp | 63 ++++++++
15 files changed, 865 insertions(+), 107 deletions(-)
create mode 100644 clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.apinotes
create mode 100644 clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.h
create mode 100644 clang/test/APINotes/Inputs/WhereObjectQualifiersDiag/APINotes.apinotes
create mode 100644 clang/test/APINotes/Inputs/WhereObjectQualifiersDiag/WhereObjectQualifiers.h
create mode 100644 clang/test/APINotes/where-object-qualifiers.cpp
diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h
index d74232bc334c6..99303c8a8ba02 100644
--- a/clang/include/clang/APINotes/APINotesReader.h
+++ b/clang/include/clang/APINotes/APINotesReader.h
@@ -170,6 +170,12 @@ class APINotesReader {
lookupCXXMethod(ContextID CtxID, llvm::StringRef Name,
llvm::ArrayRef<std::string> Parameters);
+ /// Look for information regarding the given C++ method with a composed
+ /// selector. Omitted selector components use the broad name-based key.
+ VersionedInfo<CXXMethodInfo>
+ lookupCXXMethod(ContextID CtxID, llvm::StringRef Name,
+ const FunctionSelector &Selector);
+
/// Build the selector key for the given C++ method.
std::optional<APINotesFunctionSelectorKey>
getCXXMethodSelectorKey(ContextID CtxID, llvm::StringRef Name);
@@ -180,6 +186,12 @@ class APINotesReader {
getCXXMethodSelectorKey(ContextID CtxID, llvm::StringRef Name,
llvm::ArrayRef<std::string> Parameters);
+ /// Build the selector key for the given C++ method with a composed
+ /// selector.
+ std::optional<APINotesFunctionSelectorKey>
+ getCXXMethodSelectorKey(ContextID CtxID, llvm::StringRef Name,
+ const FunctionSelector &Selector);
+
/// Look for information regarding the given global variable.
///
/// \param Name The name of the global variable.
@@ -218,8 +230,9 @@ class APINotesReader {
llvm::ArrayRef<std::string> Parameters,
std::optional<Context> Ctx = std::nullopt);
- /// Collect exact parameter selector keys stored by this reader.
- void collectExactFunctionParameterSelectors(
+ /// Collect selector keys stored by this reader that should be diagnosed if
+ /// unmatched.
+ void collectFunctionSelectorsForDiagnostics(
llvm::SmallVectorImpl<APINotesFunctionSelectorKey> &Selectors);
/// Reconstruct parameter selector strings for a stored exact selector key.
@@ -275,10 +288,9 @@ class APINotesReader {
private:
VersionedInfo<CXXMethodInfo> lookupCXXMethodImpl(ContextID CtxID,
llvm::StringRef Name);
- template <typename ParameterT>
VersionedInfo<CXXMethodInfo>
lookupCXXMethodImpl(ContextID CtxID, llvm::StringRef Name,
- llvm::ArrayRef<ParameterT> Parameters);
+ const FunctionSelector &Selector);
VersionedInfo<GlobalFunctionInfo>
lookupGlobalFunctionImpl(llvm::StringRef Name, std::optional<Context> Ctx);
diff --git a/clang/include/clang/APINotes/APINotesWriter.h b/clang/include/clang/APINotes/APINotesWriter.h
index 5ed6686e1bb85..c0d07ceeb30cc 100644
--- a/clang/include/clang/APINotes/APINotesWriter.h
+++ b/clang/include/clang/APINotes/APINotesWriter.h
@@ -95,6 +95,12 @@ class APINotesWriter {
llvm::ArrayRef<llvm::StringRef> Parameters,
const CXXMethodInfo &Info, llvm::VersionTuple SwiftVersion);
+ /// Add information about a C++ method with a composed selector. Omitted
+ /// selector components use the broad name-based key.
+ void addCXXMethod(ContextID CtxID, llvm::StringRef Name,
+ const FunctionSelector &Selector, const CXXMethodInfo &Info,
+ llvm::VersionTuple SwiftVersion);
+
/// Add information about a specific C record field.
///
/// \param CtxID The context in which this field resides, i.e. a C/C++ tag.
diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h
index af989d3a1b7f0..4151e26a98b45 100644
--- a/clang/include/clang/APINotes/Types.h
+++ b/clang/include/clang/APINotes/Types.h
@@ -1008,19 +1008,132 @@ struct Context {
using IdentifierID = llvm::PointerEmbeddedInt<unsigned, 31>;
+/// Describes the C++ implicit-object ref-qualifier portion of a method
+/// selector.
+enum class FunctionObjectRefQualifier : uint8_t {
+ None,
+ LValue,
+ RValue,
+};
+
+/// Describes optional constraints on a C++ method's implicit object parameter.
+struct FunctionObjectSelector {
+ std::optional<bool> Const;
+ std::optional<bool> Volatile;
+ std::optional<FunctionObjectRefQualifier> Ref;
+};
+
+inline bool operator==(const FunctionObjectSelector &LHS,
+ const FunctionObjectSelector &RHS) {
+ return LHS.Const == RHS.Const && LHS.Volatile == RHS.Volatile &&
+ LHS.Ref == RHS.Ref;
+}
+
+inline bool operator!=(const FunctionObjectSelector &LHS,
+ const FunctionObjectSelector &RHS) {
+ return !(LHS == RHS);
+}
+
+inline std::string formatAPINotesObjectSelector(FunctionObjectSelector Object) {
+ std::string Result;
+ llvm::raw_string_ostream OS(Result);
+ llvm::SmallVector<std::string, 3> Parts;
+
+ if (Object.Const)
+ Parts.push_back(std::string("Const: ") +
+ (*Object.Const ? "true" : "false"));
+ if (Object.Volatile)
+ Parts.push_back(std::string("Volatile: ") +
+ (*Object.Volatile ? "true" : "false"));
+ if (Object.Ref) {
+ std::string Ref = "Ref: ";
+ switch (*Object.Ref) {
+ case FunctionObjectRefQualifier::None:
+ Ref += "none";
+ break;
+ case FunctionObjectRefQualifier::LValue:
+ Ref += "lvalue";
+ break;
+ case FunctionObjectRefQualifier::RValue:
+ Ref += "rvalue";
+ break;
+ }
+ Parts.push_back(Ref);
+ }
+
+ OS << "Object{";
+ llvm::interleaveComma(Parts, OS);
+ OS << "}";
+ return Result;
+}
+
+/// Describes a C++ function selector composed from optional exact explicit
+/// parameters plus optional implicit object constraints.
+struct FunctionSelector {
+ std::optional<llvm::SmallVector<std::string, 4>> Parameters;
+ std::optional<FunctionObjectSelector> Object;
+};
+
+inline bool operator==(const FunctionSelector &LHS,
+ const FunctionSelector &RHS) {
+ return LHS.Parameters == RHS.Parameters && LHS.Object == RHS.Object;
+}
+
+inline bool operator!=(const FunctionSelector &LHS,
+ const FunctionSelector &RHS) {
+ return !(LHS == RHS);
+}
+
+inline std::string formatAPINotesFunctionSelector(
+ std::optional<llvm::ArrayRef<std::string>> Parameters,
+ std::optional<FunctionObjectSelector> Object) {
+ std::string Result;
+ if (Parameters)
+ Result = (llvm::Twine("Where.Parameters ") +
+ formatAPINotesParameterSelector(*Parameters))
+ .str();
+
+ if (Object) {
+ if (!Result.empty())
+ Result += " ";
+ else
+ Result = "Where.Object ";
+ Result += formatAPINotesObjectSelector(*Object);
+ }
+
+ return Result;
+}
+
+inline std::string
+formatAPINotesFunctionSelector(const FunctionSelector &Selector) {
+ std::optional<llvm::ArrayRef<std::string>> Parameters;
+ if (Selector.Parameters)
+ Parameters = llvm::ArrayRef<std::string>(*Selector.Parameters);
+ return formatAPINotesFunctionSelector(Parameters, Selector.Object);
+}
+
+struct FunctionTableSelectorKey {
+ std::optional<llvm::SmallVector<IdentifierID, 2>> Parameters;
+ std::optional<FunctionObjectSelector> Object;
+};
+
/// A key for a stored global-function or C++-method API notes entry.
///
/// The key is represented by the ID of its parent context, the declaration
-/// name, and optional exact parameter types.
+/// name, and optional exact parameter/object selector data.
struct FunctionTableKey {
uint32_t parentContextID;
uint32_t nameID;
std::optional<llvm::SmallVector<IdentifierID, 2>> parameterTypeIDs;
+ std::optional<FunctionObjectSelector> objectSelector;
FunctionTableKey() : parentContextID(-1), nameID(-1) {}
- FunctionTableKey(uint32_t ParentContextID, uint32_t NameID)
- : parentContextID(ParentContextID), nameID(NameID) {}
+ FunctionTableKey(uint32_t ParentContextID, uint32_t NameID,
+ FunctionTableSelectorKey Selector = {})
+ : parentContextID(ParentContextID), nameID(NameID),
+ parameterTypeIDs(std::move(Selector.Parameters)),
+ objectSelector(Selector.Object) {}
FunctionTableKey(uint32_t ParentContextID, uint32_t NameID,
const llvm::SmallVectorImpl<IdentifierID> &ParameterTypeIDs)
@@ -1028,27 +1141,37 @@ struct FunctionTableKey {
parameterTypeIDs.emplace(ParameterTypeIDs.begin(), ParameterTypeIDs.end());
}
- FunctionTableKey(std::optional<Context> ParentCtx, IdentifierID NameID)
- : parentContextID(ParentCtx ? ParentCtx->id.Value
- : static_cast<uint32_t>(-1)),
- nameID(NameID) {}
+ FunctionTableKey(std::optional<Context> ParentCtx, IdentifierID NameID,
+ FunctionTableSelectorKey Selector = {})
+ : FunctionTableKey(ParentCtx ? ParentCtx->id.Value
+ : static_cast<uint32_t>(-1),
+ NameID, std::move(Selector)) {}
FunctionTableKey(std::optional<Context> ParentCtx, IdentifierID NameID,
const llvm::SmallVectorImpl<IdentifierID> &ParameterTypeIDs)
- : parentContextID(ParentCtx ? ParentCtx->id.Value
- : static_cast<uint32_t>(-1)),
- nameID(NameID) {
+ : FunctionTableKey(ParentCtx ? ParentCtx->id.Value
+ : static_cast<uint32_t>(-1),
+ NameID) {
parameterTypeIDs.emplace(ParameterTypeIDs.begin(), ParameterTypeIDs.end());
}
llvm::hash_code hashValue() const {
auto Hash = llvm::hash_combine(parentContextID, nameID,
- static_cast<bool>(parameterTypeIDs));
+ static_cast<bool>(parameterTypeIDs),
+ static_cast<bool>(objectSelector));
if (parameterTypeIDs) {
Hash = llvm::hash_combine(Hash, parameterTypeIDs->size());
for (IdentifierID TypeID : *parameterTypeIDs)
Hash = llvm::hash_combine(Hash, static_cast<unsigned>(TypeID));
}
+ if (objectSelector)
+ Hash = llvm::hash_combine(
+ Hash, objectSelector->Const.value_or(false),
+ static_cast<bool>(objectSelector->Const),
+ objectSelector->Volatile.value_or(false),
+ static_cast<bool>(objectSelector->Volatile),
+ objectSelector->Ref ? static_cast<unsigned>(*objectSelector->Ref) + 1
+ : 0);
return Hash;
}
};
@@ -1057,7 +1180,8 @@ inline bool operator==(const FunctionTableKey &LHS,
const FunctionTableKey &RHS) {
return LHS.parentContextID == RHS.parentContextID &&
LHS.nameID == RHS.nameID &&
- LHS.parameterTypeIDs == RHS.parameterTypeIDs;
+ LHS.parameterTypeIDs == RHS.parameterTypeIDs &&
+ LHS.objectSelector == RHS.objectSelector;
}
/// Stable reader-facing identity for an API notes function selector entry.
@@ -1069,7 +1193,7 @@ struct APINotesFunctionSelectorKey {
FunctionTableKey Key;
bool IsCXXMethod = false;
- APINotesFunctionSelectorKey getWithoutParameterSelector() const {
+ APINotesFunctionSelectorKey getNameOnlyKey() const {
return {FunctionTableKey(Key.parentContextID, Key.nameID), IsCXXMethod};
}
diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h
index 30fc8599349bf..f0ea371e3ccef 100644
--- a/clang/lib/APINotes/APINotesFormat.h
+++ b/clang/lib/APINotes/APINotesFormat.h
@@ -16,6 +16,7 @@
#include "llvm/Bitcode/BitcodeConvenience.h"
#include <optional>
+#include <utility>
namespace clang {
namespace api_notes {
@@ -28,9 +29,10 @@ const uint16_t VERSION_MAJOR = 0;
/// API notes file minor version number.
///
/// When the format changes IN ANY WAY, this number should be incremented.
-const uint16_t VERSION_MINOR = 41; // 39 for BoundsSafety;
+const uint16_t VERSION_MINOR = 42; // 39 for BoundsSafety;
// 40 for UnsafeBufferUsageAttr
// 41 for FunctionTableKey parameters
+ // 42 for FunctionTableKey object selectors
const uint8_t kSwiftConforms = 1;
const uint8_t kSwiftDoesNotConform = 2;
@@ -359,8 +361,21 @@ inline bool operator==(const SingleDeclTableKey &lhs,
}
/// A stored C or C++ function declaration, represented by the ID of its parent
-/// context, the name of the declaration, and optional exact parameter types.
+/// context, the name of the declaration, and optional exact parameter/object
+/// selector data.
constexpr uint8_t FunctionKeyHasParameterSelector = 0x01;
+constexpr uint8_t FunctionKeyObjectConstPresent = 0x02;
+constexpr uint8_t FunctionKeyObjectConstValue = 0x04;
+constexpr uint8_t FunctionKeyObjectVolatilePresent = 0x08;
+constexpr uint8_t FunctionKeyObjectVolatileValue = 0x10;
+constexpr uint8_t FunctionKeyObjectRefPresent = 0x20;
+constexpr uint8_t FunctionKeyObjectRefLValue = 0x40;
+constexpr uint8_t FunctionKeyObjectRefRValue = 0x80;
+constexpr uint8_t FunctionKeyObjectSelectorMask =
+ FunctionKeyObjectConstPresent | FunctionKeyObjectConstValue |
+ FunctionKeyObjectVolatilePresent | FunctionKeyObjectVolatileValue |
+ FunctionKeyObjectRefPresent | FunctionKeyObjectRefLValue |
+ FunctionKeyObjectRefRValue;
constexpr unsigned FunctionTableKeyBaseLength =
sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint16_t);
@@ -375,10 +390,48 @@ getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name,
return FunctionTableKey(ParentContextID, *NameID);
}
+template <typename GetIdentifierFn>
+std::optional<FunctionTableKey>
+getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name,
+ FunctionObjectSelector ObjectSelector,
+ GetIdentifierFn GetIdentifier) {
+ std::optional<IdentifierID> NameID = GetIdentifier(Name);
+ if (!NameID)
+ return std::nullopt;
+
+ FunctionTableSelectorKey Selector;
+ Selector.Object = ObjectSelector;
+ return FunctionTableKey(ParentContextID, *NameID, std::move(Selector));
+}
+
+template <typename ParameterT, typename GetIdentifierFn>
+std::optional<FunctionTableKey>
+getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name,
+ llvm::ArrayRef<ParameterT> Parameters,
+ GetIdentifierFn GetIdentifier) {
+ std::optional<IdentifierID> NameID = GetIdentifier(Name);
+ if (!NameID)
+ return std::nullopt;
+
+ llvm::SmallVector<IdentifierID, 2> ParameterTypeIDs;
+ ParameterTypeIDs.reserve(Parameters.size());
+ for (const ParameterT &Parameter : Parameters) {
+ std::optional<IdentifierID> ParameterID =
+ GetIdentifier(llvm::StringRef(Parameter));
+ if (!ParameterID)
+ return std::nullopt;
+ ParameterTypeIDs.push_back(*ParameterID);
+ }
+ FunctionTableSelectorKey Selector;
+ Selector.Parameters.emplace(ParameterTypeIDs.begin(), ParameterTypeIDs.end());
+ return FunctionTableKey(ParentContextID, *NameID, std::move(Selector));
+}
+
template <typename ParameterT, typename GetIdentifierFn>
std::optional<FunctionTableKey>
getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name,
llvm::ArrayRef<ParameterT> Parameters,
+ FunctionObjectSelector ObjectSelector,
GetIdentifierFn GetIdentifier) {
std::optional<IdentifierID> NameID = GetIdentifier(Name);
if (!NameID)
@@ -393,7 +446,10 @@ getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name,
return std::nullopt;
ParameterTypeIDs.push_back(*ParameterID);
}
- return FunctionTableKey(ParentContextID, *NameID, ParameterTypeIDs);
+ FunctionTableSelectorKey Selector;
+ Selector.Parameters.emplace(ParameterTypeIDs.begin(), ParameterTypeIDs.end());
+ Selector.Object = ObjectSelector;
+ return FunctionTableKey(ParentContextID, *NameID, std::move(Selector));
}
} // namespace api_notes
diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp
index aad597d93d9ee..ab06d75826d1a 100644
--- a/clang/lib/APINotes/APINotesReader.cpp
+++ b/clang/lib/APINotes/APINotesReader.cpp
@@ -72,14 +72,47 @@ static FunctionTableKey readFunctionTableKey(const uint8_t *Data,
ParameterTypeIDs.push_back(
endian::readNext<uint32_t, llvm::endianness::little>(Data));
- assert((FunctionKeyFlags & ~FunctionKeyHasParameterSelector) == 0 &&
- "Unexpected function table key flags");
+ if (FunctionKeyFlags & FunctionKeyObjectRefLValue)
+ assert(!(FunctionKeyFlags & FunctionKeyObjectRefRValue) &&
+ "Unexpected function table key ref qualifier flags");
+ assert(((FunctionKeyFlags & FunctionKeyObjectConstValue) == 0 ||
+ (FunctionKeyFlags & FunctionKeyObjectConstPresent)) &&
+ "Function table key const value requires presence flag");
+ assert(((FunctionKeyFlags & FunctionKeyObjectVolatileValue) == 0 ||
+ (FunctionKeyFlags & FunctionKeyObjectVolatilePresent)) &&
+ "Function table key volatile value requires presence flag");
+ assert(((FunctionKeyFlags &
+ (FunctionKeyObjectRefLValue | FunctionKeyObjectRefRValue)) == 0 ||
+ (FunctionKeyFlags & FunctionKeyObjectRefPresent)) &&
+ "Function table key ref value requires presence flag");
+ std::optional<FunctionObjectSelector> ObjectSelector;
+ if (FunctionKeyFlags & FunctionKeyObjectSelectorMask) {
+ FunctionObjectSelector Selector;
+ if (FunctionKeyFlags & FunctionKeyObjectConstPresent)
+ Selector.Const = (FunctionKeyFlags & FunctionKeyObjectConstValue) != 0;
+ if (FunctionKeyFlags & FunctionKeyObjectVolatilePresent)
+ Selector.Volatile =
+ (FunctionKeyFlags & FunctionKeyObjectVolatileValue) != 0;
+ if (FunctionKeyFlags & FunctionKeyObjectRefPresent) {
+ if (FunctionKeyFlags & FunctionKeyObjectRefLValue)
+ Selector.Ref = FunctionObjectRefQualifier::LValue;
+ else if (FunctionKeyFlags & FunctionKeyObjectRefRValue)
+ Selector.Ref = FunctionObjectRefQualifier::RValue;
+ else
+ Selector.Ref = FunctionObjectRefQualifier::None;
+ }
+ ObjectSelector = Selector;
+ }
+
+ FunctionTableSelectorKey Selector;
if (FunctionKeyFlags & FunctionKeyHasParameterSelector)
- return {CtxID, NameID, ParameterTypeIDs};
-
- assert(ParameterTypeIDs.empty() &&
- "Broad function table key should not store parameters");
- return {CtxID, NameID};
+ Selector.Parameters.emplace(ParameterTypeIDs.begin(),
+ ParameterTypeIDs.end());
+ else
+ assert(ParameterTypeIDs.empty() &&
+ "Broad function table key should not store parameters");
+ Selector.Object = ObjectSelector;
+ return FunctionTableKey(CtxID, NameID, std::move(Selector));
}
/// An on-disk hash table whose data is versioned based on the Swift version.
@@ -844,10 +877,10 @@ class APINotesReader::Implementation {
/// the ID is unknown.
std::optional<llvm::StringRef> getIdentifierString(IdentifierID ID);
- /// Collect exact parameter selector keys stored in the given function-like
- /// table.
+ /// Collect selector keys stored in the given function-like table that
+ /// should be diagnosed if unmatched.
template <typename TableT>
- void collectExactFunctionParameterSelectors(
+ void collectFunctionSelectorsForDiagnostics(
TableT &Table,
llvm::SmallVectorImpl<APINotesFunctionSelectorKey> &Selectors);
@@ -880,11 +913,17 @@ class APINotesReader::Implementation {
getFunctionKey(uint32_t ParentContextID, llvm::StringRef Name,
llvm::ArrayRef<ParameterT> Parameters);
std::optional<FunctionTableKey>
+ getFunctionKey(uint32_t ParentContextID, llvm::StringRef Name,
+ const FunctionSelector &Selector);
+ std::optional<FunctionTableKey>
getFunctionKey(std::optional<Context> ParentContext, llvm::StringRef Name);
template <typename ParameterT>
std::optional<FunctionTableKey>
getFunctionKey(std::optional<Context> ParentContext, llvm::StringRef Name,
llvm::ArrayRef<ParameterT> Parameters);
+ std::optional<FunctionTableKey>
+ getFunctionKey(std::optional<Context> ParentContext, llvm::StringRef Name,
+ const FunctionSelector &Selector);
llvm::Error readGlobalFunctionBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
@@ -944,7 +983,7 @@ APINotesReader::Implementation::getIdentifierString(IdentifierID ID) {
}
template <typename TableT>
-void APINotesReader::Implementation::collectExactFunctionParameterSelectors(
+void APINotesReader::Implementation::collectFunctionSelectorsForDiagnostics(
TableT &Table,
llvm::SmallVectorImpl<APINotesFunctionSelectorKey> &Selectors) {
static_assert(std::is_same_v<TableT, SerializedGlobalFunctionTable> ||
@@ -952,7 +991,7 @@ void APINotesReader::Implementation::collectExactFunctionParameterSelectors(
constexpr bool IsCXXMethod = std::is_same_v<TableT, SerializedCXXMethodTable>;
for (const FunctionTableKey &Key : Table.keys()) {
- if (!Key.parameterTypeIDs)
+ if (!Key.parameterTypeIDs && !Key.objectSelector)
continue;
Selectors.push_back(APINotesFunctionSelectorKey{Key, IsCXXMethod});
@@ -976,6 +1015,26 @@ std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey(
[this](llvm::StringRef S) { return getIdentifier(S); });
}
+std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey(
+ uint32_t ParentContextID, llvm::StringRef Name,
+ const FunctionSelector &Selector) {
+ auto GetIdentifier = [this](llvm::StringRef S) { return getIdentifier(S); };
+ if (Selector.Parameters) {
+ if (Selector.Object)
+ return getFunctionKeyImpl(
+ ParentContextID, Name,
+ llvm::ArrayRef<std::string>(*Selector.Parameters), *Selector.Object,
+ GetIdentifier);
+ return getFunctionKeyImpl(ParentContextID, Name,
+ llvm::ArrayRef<std::string>(*Selector.Parameters),
+ GetIdentifier);
+ }
+ if (Selector.Object)
+ return getFunctionKeyImpl(ParentContextID, Name, *Selector.Object,
+ GetIdentifier);
+ return getFunctionKeyImpl(ParentContextID, Name, GetIdentifier);
+}
+
std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey(
std::optional<Context> ParentContext, llvm::StringRef Name) {
uint32_t ParentContextID =
@@ -992,6 +1051,14 @@ std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey(
return getFunctionKey(ParentContextID, Name, Parameters);
}
+std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey(
+ std::optional<Context> ParentContext, llvm::StringRef Name,
+ const FunctionSelector &Selector) {
+ uint32_t ParentContextID =
+ ParentContext ? ParentContext->id.Value : static_cast<uint32_t>(-1);
+ return getFunctionKey(ParentContextID, Name, Selector);
+}
+
std::optional<SelectorID>
APINotesReader::Implementation::getSelector(ObjCSelectorRef Selector) {
if (!ObjCSelectorTable || !IdentifierTable)
@@ -2414,7 +2481,15 @@ auto APINotesReader::lookupCXXMethod(ContextID CtxID, llvm::StringRef Name)
auto APINotesReader::lookupCXXMethod(ContextID CtxID, llvm::StringRef Name,
llvm::ArrayRef<std::string> Parameters)
-> VersionedInfo<CXXMethodInfo> {
- return lookupCXXMethodImpl(CtxID, Name, Parameters);
+ FunctionSelector Selector;
+ Selector.Parameters.emplace(Parameters.begin(), Parameters.end());
+ return lookupCXXMethodImpl(CtxID, Name, Selector);
+}
+
+auto APINotesReader::lookupCXXMethod(ContextID CtxID, llvm::StringRef Name,
+ const FunctionSelector &Selector)
+ -> VersionedInfo<CXXMethodInfo> {
+ return lookupCXXMethodImpl(CtxID, Name, Selector);
}
std::optional<APINotesFunctionSelectorKey>
@@ -2437,6 +2512,16 @@ APINotesReader::getCXXMethodSelectorKey(
return APINotesFunctionSelectorKey{*Key, /*IsCXXMethod=*/true};
}
+std::optional<APINotesFunctionSelectorKey>
+APINotesReader::getCXXMethodSelectorKey(ContextID CtxID, llvm::StringRef Name,
+ const FunctionSelector &Selector) {
+ std::optional<FunctionTableKey> Key =
+ Implementation->getFunctionKey(CtxID.Value, Name, Selector);
+ if (!Key)
+ return std::nullopt;
+ return APINotesFunctionSelectorKey{*Key, /*IsCXXMethod=*/true};
+}
+
auto APINotesReader::lookupCXXMethodImpl(ContextID CtxID, llvm::StringRef Name)
-> VersionedInfo<CXXMethodInfo> {
if (!Implementation->CXXMethodTable)
@@ -2454,15 +2539,14 @@ auto APINotesReader::lookupCXXMethodImpl(ContextID CtxID, llvm::StringRef Name)
return {Implementation->SwiftVersion, *Known};
}
-template <typename ParameterT>
auto APINotesReader::lookupCXXMethodImpl(ContextID CtxID, llvm::StringRef Name,
- llvm::ArrayRef<ParameterT> Parameters)
+ const FunctionSelector &Selector)
-> VersionedInfo<CXXMethodInfo> {
if (!Implementation->CXXMethodTable)
return std::nullopt;
std::optional<FunctionTableKey> Key =
- Implementation->getFunctionKey(CtxID.Value, Name, Parameters);
+ Implementation->getFunctionKey(CtxID.Value, Name, Selector);
if (!Key)
return std::nullopt;
@@ -2525,13 +2609,13 @@ APINotesReader::getGlobalFunctionSelectorKey(
return APINotesFunctionSelectorKey{*Key, /*IsCXXMethod=*/false};
}
-void APINotesReader::collectExactFunctionParameterSelectors(
+void APINotesReader::collectFunctionSelectorsForDiagnostics(
llvm::SmallVectorImpl<APINotesFunctionSelectorKey> &Selectors) {
if (Implementation->GlobalFunctionTable)
- Implementation->collectExactFunctionParameterSelectors(
+ Implementation->collectFunctionSelectorsForDiagnostics(
*Implementation->GlobalFunctionTable, Selectors);
if (Implementation->CXXMethodTable)
- Implementation->collectExactFunctionParameterSelectors(
+ Implementation->collectFunctionSelectorsForDiagnostics(
*Implementation->CXXMethodTable, Selectors);
}
diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp
index 86fee93ec4765..4027d096bf02a 100644
--- a/clang/lib/APINotes/APINotesWriter.cpp
+++ b/clang/lib/APINotes/APINotesWriter.cpp
@@ -158,6 +158,32 @@ class APINotesWriter::Implementation {
return *Key;
}
+ FunctionTableKey getFunctionKey(uint32_t ParentContextID, StringRef Name,
+ const FunctionSelector &Selector) {
+ std::optional<FunctionTableKey> Key;
+ auto GetIdentifier = [this](StringRef S) -> std::optional<IdentifierID> {
+ return getIdentifier(S);
+ };
+ if (Selector.Parameters) {
+ if (Selector.Object)
+ Key = getFunctionKeyImpl(
+ ParentContextID, Name,
+ llvm::ArrayRef<std::string>(*Selector.Parameters), *Selector.Object,
+ GetIdentifier);
+ else
+ Key = getFunctionKeyImpl(
+ ParentContextID, Name,
+ llvm::ArrayRef<std::string>(*Selector.Parameters), GetIdentifier);
+ } else if (Selector.Object) {
+ Key = getFunctionKeyImpl(ParentContextID, Name, *Selector.Object,
+ GetIdentifier);
+ } else {
+ Key = getFunctionKeyImpl(ParentContextID, Name, GetIdentifier);
+ }
+ assert(Key && "Writer identifier lookup should not fail");
+ return *Key;
+ }
+
FunctionTableKey getFunctionKey(std::optional<Context> ParentContext,
StringRef Name) {
uint32_t ParentContextID =
@@ -173,6 +199,14 @@ class APINotesWriter::Implementation {
return getFunctionKey(ParentContextID, Name, Parameters);
}
+ FunctionTableKey getFunctionKey(std::optional<Context> ParentContext,
+ StringRef Name,
+ const FunctionSelector &Selector) {
+ uint32_t ParentContextID =
+ ParentContext ? ParentContext->id.Value : static_cast<uint32_t>(-1);
+ return getFunctionKey(ParentContextID, Name, Selector);
+ }
+
/// Retrieve the ID for the given selector.
SelectorID getSelector(ObjCSelectorRef SelectorRef) {
// Translate the selector reference into a stored selector.
@@ -507,12 +541,42 @@ static unsigned getFunctionTableKeyLength(const FunctionTableKey &Key) {
: 0);
}
+static uint8_t getFunctionTableKeyFlags(const FunctionTableKey &Key) {
+ uint8_t Flags = Key.parameterTypeIDs ? FunctionKeyHasParameterSelector : 0;
+ if (!Key.objectSelector)
+ return Flags;
+
+ if (Key.objectSelector->Const) {
+ Flags |= FunctionKeyObjectConstPresent;
+ if (*Key.objectSelector->Const)
+ Flags |= FunctionKeyObjectConstValue;
+ }
+ if (Key.objectSelector->Volatile) {
+ Flags |= FunctionKeyObjectVolatilePresent;
+ if (*Key.objectSelector->Volatile)
+ Flags |= FunctionKeyObjectVolatileValue;
+ }
+ if (Key.objectSelector->Ref) {
+ Flags |= FunctionKeyObjectRefPresent;
+ switch (*Key.objectSelector->Ref) {
+ case FunctionObjectRefQualifier::None:
+ break;
+ case FunctionObjectRefQualifier::LValue:
+ Flags |= FunctionKeyObjectRefLValue;
+ break;
+ case FunctionObjectRefQualifier::RValue:
+ Flags |= FunctionKeyObjectRefRValue;
+ break;
+ }
+ }
+ return Flags;
+}
+
static void emitFunctionTableKey(raw_ostream &OS, const FunctionTableKey &Key) {
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
writer.write<uint32_t>(Key.parentContextID);
writer.write<uint32_t>(Key.nameID);
- writer.write<uint8_t>(Key.parameterTypeIDs ? FunctionKeyHasParameterSelector
- : 0);
+ writer.write<uint8_t>(getFunctionTableKeyFlags(Key));
writer.write<uint16_t>(Key.parameterTypeIDs ? Key.parameterTypeIDs->size()
: 0);
if (Key.parameterTypeIDs)
@@ -1631,6 +1695,15 @@ void APINotesWriter::addCXXMethod(ContextID CtxID, llvm::StringRef Name,
Implementation->CXXMethods[Key].push_back({SwiftVersion, Info});
}
+void APINotesWriter::addCXXMethod(ContextID CtxID, llvm::StringRef Name,
+ const FunctionSelector &Selector,
+ const CXXMethodInfo &Info,
+ VersionTuple SwiftVersion) {
+ FunctionTableKey Key =
+ Implementation->getFunctionKey(CtxID.Value, Name, Selector);
+ Implementation->CXXMethods[Key].push_back({SwiftVersion, Info});
+}
+
void APINotesWriter::addField(ContextID CtxID, llvm::StringRef Name,
const FieldInfo &Info,
VersionTuple SwiftVersion) {
diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
index 4079675228a21..608152b2f4442 100644
--- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp
+++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -18,7 +18,6 @@
#include "clang/APINotes/Types.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/Specifiers.h"
-#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringSet.h"
@@ -347,6 +346,7 @@ typedef std::vector<StringRef> WhereParamsSeq;
struct FunctionWhere {
std::optional<WhereParamsSeq> Parameters;
+ std::optional<api_notes::FunctionObjectSelector> Object;
};
struct Function {
@@ -373,9 +373,27 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(Function)
namespace llvm {
namespace yaml {
+template <>
+struct ScalarEnumerationTraits<api_notes::FunctionObjectRefQualifier> {
+ static void enumeration(IO &IO, api_notes::FunctionObjectRefQualifier &Ref) {
+ IO.enumCase(Ref, "none", api_notes::FunctionObjectRefQualifier::None);
+ IO.enumCase(Ref, "lvalue", api_notes::FunctionObjectRefQualifier::LValue);
+ IO.enumCase(Ref, "rvalue", api_notes::FunctionObjectRefQualifier::RValue);
+ }
+};
+
+template <> struct MappingTraits<api_notes::FunctionObjectSelector> {
+ static void mapping(IO &IO, api_notes::FunctionObjectSelector &O) {
+ IO.mapOptional("Const", O.Const);
+ IO.mapOptional("Volatile", O.Volatile);
+ IO.mapOptional("Ref", O.Ref);
+ }
+};
+
template <> struct MappingTraits<FunctionWhere> {
static void mapping(IO &IO, FunctionWhere &W) {
IO.mapOptional("Parameters", W.Parameters);
+ IO.mapOptional("Object", W.Object);
}
};
@@ -793,19 +811,20 @@ bool clang::api_notes::parseAndDumpAPINotes(StringRef YI,
namespace {
using namespace api_notes;
+static void appendDuplicateKeyPart(llvm::raw_ostream &OS,
+ llvm::StringRef Part) {
+ OS << Part.size() << ':' << Part;
+}
+
static std::string
-getFunctionSelectorKey(llvm::StringRef Name,
- llvm::ArrayRef<llvm::StringRef> Parameters) {
+getFunctionSelectorDuplicateKey(llvm::StringRef Name,
+ const FunctionSelector &Selector) {
llvm::SmallString<64> Key;
llvm::raw_svector_ostream OS(Key);
- auto AppendKeyPart = [&OS](llvm::StringRef Part) {
- OS << Part.size() << ':' << Part;
- };
-
- AppendKeyPart(Name);
- OS << ';';
- for (llvm::StringRef Parameter : Parameters)
- AppendKeyPart(Parameter);
+ appendDuplicateKeyPart(OS, Name);
+ std::string SelectorText =
+ api_notes::formatAPINotesFunctionSelector(Selector);
+ appendDuplicateKeyPart(OS, SelectorText);
return Key.str().str();
}
@@ -1064,16 +1083,38 @@ class YAMLConverter {
TheNamespace.Items, SwiftVersion);
}
- std::pair<bool, std::optional<llvm::ArrayRef<llvm::StringRef>>>
- getWhereParameters(const Function &Function) {
+ std::optional<FunctionSelector> getWhereSelector(const Function &Function,
+ bool AllowObject) {
if (!Function.Where)
- return {true, std::nullopt};
+ return FunctionSelector{};
+
+ if (!Function.Where->Parameters && !Function.Where->Object) {
+ emitError("'Where' requires 'Parameters' or 'Object'");
+ return std::nullopt;
+ }
+
+ FunctionSelector Selector;
+ if (Function.Where->Parameters) {
+ Selector.Parameters.emplace();
+ Selector.Parameters->reserve(Function.Where->Parameters->size());
+ for (llvm::StringRef Parameter : *Function.Where->Parameters)
+ Selector.Parameters->push_back(Parameter.str());
+ }
- if (!Function.Where->Parameters) {
- emitError("'Where' requires 'Parameters'");
- return {false, std::nullopt};
+ if (Function.Where->Object) {
+ if (!AllowObject) {
+ emitError("'Object' is only supported on C++ methods");
+ return std::nullopt;
+ }
+ if (!Function.Where->Object->Const && !Function.Where->Object->Volatile &&
+ !Function.Where->Object->Ref) {
+ emitError("'Object' requires at least one field");
+ return std::nullopt;
+ }
+ Selector.Object = Function.Where->Object;
}
- return {true, llvm::ArrayRef<llvm::StringRef>(*Function.Where->Parameters)};
+
+ return Selector;
}
template <typename FuncOrMethodInfo>
@@ -1185,27 +1226,26 @@ class YAMLConverter {
llvm::StringSet<> KnownMethodSelectors;
for (const auto &CXXMethod : T.Methods) {
- auto WhereParameters = getWhereParameters(CXXMethod);
- if (!WhereParameters.first)
+ auto WhereSelector = getWhereSelector(CXXMethod, /*AllowObject=*/true);
+ if (!WhereSelector)
continue;
- if (WhereParameters.second) {
- if (!KnownMethodSelectors
- .insert(getFunctionSelectorKey(CXXMethod.Name,
- *WhereParameters.second))
- .second) {
+ if (WhereSelector->Parameters || WhereSelector->Object) {
+ std::string DuplicateKey =
+ getFunctionSelectorDuplicateKey(CXXMethod.Name, *WhereSelector);
+ if (!KnownMethodSelectors.insert(DuplicateKey).second) {
emitError(llvm::Twine("multiple API notes entries for C++ method '") +
- CXXMethod.Name + "' with Where.Parameters " +
- formatAPINotesParameterSelector(*WhereParameters.second));
+ CXXMethod.Name + "' with " +
+ api_notes::formatAPINotesFunctionSelector(*WhereSelector));
continue;
}
}
CXXMethodInfo MI;
convertFunction(CXXMethod, MI);
- if (WhereParameters.second)
- Writer.addCXXMethod(TagCtxID, CXXMethod.Name, *WhereParameters.second,
- MI, SwiftVersion);
+ if (WhereSelector->Parameters || WhereSelector->Object)
+ Writer.addCXXMethod(TagCtxID, CXXMethod.Name, *WhereSelector, MI,
+ SwiftVersion);
else
Writer.addCXXMethod(TagCtxID, CXXMethod.Name, MI, SwiftVersion);
}
@@ -1279,25 +1319,24 @@ class YAMLConverter {
llvm::StringSet<> KnownNameOnlyFunctions;
llvm::StringSet<> KnownFunctionSelectors;
for (const auto &Function : TLItems.Functions) {
- auto WhereParameters = getWhereParameters(Function);
- if (!WhereParameters.first)
+ auto WhereSelector = getWhereSelector(Function, /*AllowObject=*/false);
+ if (!WhereSelector)
continue;
- if (WhereParameters.second) {
- if (!KnownFunctionSelectors
- .insert(getFunctionSelectorKey(Function.Name,
- *WhereParameters.second))
- .second) {
+ if (WhereSelector->Parameters) {
+ std::string DuplicateKey =
+ getFunctionSelectorDuplicateKey(Function.Name, *WhereSelector);
+ if (!KnownFunctionSelectors.insert(DuplicateKey).second) {
emitError(
llvm::Twine("multiple API notes entries for global function '") +
- Function.Name + "' with Where.Parameters " +
- formatAPINotesParameterSelector(*WhereParameters.second));
+ Function.Name + "' with " +
+ api_notes::formatAPINotesFunctionSelector(*WhereSelector));
continue;
}
}
// Check for duplicate name-only global functions.
- if (!WhereParameters.second &&
+ if (!WhereSelector->Parameters &&
!KnownNameOnlyFunctions.insert(Function.Name).second) {
emitError(llvm::Twine("multiple definitions of global function '") +
Function.Name + "'");
@@ -1306,11 +1345,16 @@ class YAMLConverter {
GlobalFunctionInfo GFI;
convertFunction(Function, GFI);
- if (WhereParameters.second)
- Writer.addGlobalFunction(Ctx, Function.Name, *WhereParameters.second,
- GFI, SwiftVersion);
- else
+ if (WhereSelector->Parameters) {
+ llvm::SmallVector<llvm::StringRef, 4> ParameterRefs;
+ ParameterRefs.reserve(WhereSelector->Parameters->size());
+ for (const std::string &Parameter : *WhereSelector->Parameters)
+ ParameterRefs.push_back(Parameter);
+ Writer.addGlobalFunction(Ctx, Function.Name, ParameterRefs, GFI,
+ SwiftVersion);
+ } else {
Writer.addGlobalFunction(Ctx, Function.Name, GFI, SwiftVersion);
+ }
}
// Write all enumerators.
diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp
index 78153d9ddf39d..e7ddacf9d2515 100644
--- a/clang/lib/Sema/SemaAPINotes.cpp
+++ b/clang/lib/Sema/SemaAPINotes.cpp
@@ -23,6 +23,7 @@
#include "clang/Lex/Lexer.h"
#include "clang/Sema/SemaObjC.h"
#include "clang/Sema/SemaSwift.h"
+#include "llvm/ADT/bit.h"
#include <stack>
using namespace clang;
@@ -1085,7 +1086,7 @@ APINotesSelectorDiagnosticState::getOrCreateReaderState(
return State;
SmallVector<api_notes::APINotesFunctionSelectorKey, 4> Selectors;
- Reader.collectExactFunctionParameterSelectors(Selectors);
+ Reader.collectFunctionSelectorsForDiagnostics(Selectors);
State.addSelectors(Selectors);
return State;
}
@@ -1112,6 +1113,58 @@ void APINotesSelectorDiagnosticReaderState::markCandidatesUsed(
}
}
+static api_notes::FunctionObjectSelector
+getAPINotesObjectSelector(const CXXMethodDecl *Method) {
+ api_notes::FunctionObjectSelector Selector;
+ Selector.Const = Method->isConst();
+ Selector.Volatile = Method->isVolatile();
+ switch (Method->getRefQualifier()) {
+ case RQ_None:
+ Selector.Ref = api_notes::FunctionObjectRefQualifier::None;
+ break;
+ case RQ_LValue:
+ Selector.Ref = api_notes::FunctionObjectRefQualifier::LValue;
+ break;
+ case RQ_RValue:
+ Selector.Ref = api_notes::FunctionObjectRefQualifier::RValue;
+ break;
+ }
+ return Selector;
+}
+
+static void getAPINotesObjectSelectorSubsets(
+ api_notes::FunctionObjectSelector ObjectSelector,
+ SmallVectorImpl<api_notes::FunctionObjectSelector> &Subsets) {
+ enum ObjectSelectorField : unsigned {
+ ConstField = 1u << 0,
+ VolatileField = 1u << 1,
+ RefField = 1u << 2,
+ };
+
+ constexpr unsigned ObjectSelectorSubsetMasks[] = {
+ ConstField,
+ VolatileField,
+ RefField,
+ ConstField | VolatileField,
+ ConstField | RefField,
+ VolatileField | RefField,
+ ConstField | VolatileField | RefField,
+ };
+
+ // Apply less-constrained object selectors before more-constrained ones so
+ // entries that specify more Object fields can refine earlier effects.
+ for (unsigned Mask : ObjectSelectorSubsetMasks) {
+ api_notes::FunctionObjectSelector Subset;
+ if (Mask & ConstField)
+ Subset.Const = ObjectSelector.Const;
+ if (Mask & VolatileField)
+ Subset.Volatile = ObjectSelector.Volatile;
+ if (Mask & RefField)
+ Subset.Ref = ObjectSelector.Ref;
+ Subsets.push_back(Subset);
+ }
+}
+
// Apply the first exact selector entry found. This preserves source-spelling
// precedence over the desugared fallback and avoids applying multiple exact
// entries for the same declaration.
@@ -1397,21 +1450,20 @@ void Sema::ProcessAPINotes(Decl *D) {
auto Info = Reader->lookupCXXMethod(Context->id, MethodName);
ProcessVersionedAPINotes(*this, CXXMethod, Info);
- if (ParameterSelectorCandidates)
+ auto &DiagnosticState =
+ getAPINotesSelectorDiagnosticState(*this, Reader);
+ if (auto NameOnlyKey =
+ Reader->getCXXMethodSelectorKey(Context->id, MethodName))
+ DiagnosticState.noteSeenDeclaration(*NameOnlyKey, MethodName,
+ CXXMethod->getLocation());
+
+ if (ParameterSelectorCandidates) {
processExactAPINotes<api_notes::CXXMethodInfo>(
*this, CXXMethod, *ParameterSelectorCandidates,
[&](ArrayRef<std::string> Parameters) {
return Reader->lookupCXXMethod(Context->id, MethodName,
Parameters);
});
-
- if (ParameterSelectorCandidates) {
- auto &DiagnosticState =
- getAPINotesSelectorDiagnosticState(*this, Reader);
- if (auto BroadKey =
- Reader->getCXXMethodSelectorKey(Context->id, MethodName))
- DiagnosticState.noteSeenDeclaration(*BroadKey, MethodName,
- CXXMethod->getLocation());
DiagnosticState.markCandidatesUsed(
[&](ArrayRef<std::string> Parameters) {
return Reader->getCXXMethodSelectorKey(
@@ -1419,6 +1471,52 @@ void Sema::ProcessAPINotes(Decl *D) {
},
*ParameterSelectorCandidates);
}
+
+ if (!CXXMethod->isStatic()) {
+ SmallVector<api_notes::FunctionObjectSelector, 7> ObjectSelectors;
+ getAPINotesObjectSelectorSubsets(
+ getAPINotesObjectSelector(CXXMethod), ObjectSelectors);
+ // Apply every matching object selector in increasing specificity.
+ // Wildcard object constraints are broad refinements. Selectors
+ // that also constrain explicit parameters are applied last.
+ for (api_notes::FunctionObjectSelector ObjectSelector :
+ ObjectSelectors) {
+ api_notes::FunctionSelector Selector;
+ Selector.Object = ObjectSelector;
+ auto ObjectInfo =
+ Reader->lookupCXXMethod(Context->id, MethodName, Selector);
+ ProcessVersionedAPINotes(*this, CXXMethod, ObjectInfo);
+ if (auto ObjectKey = Reader->getCXXMethodSelectorKey(
+ Context->id, MethodName, Selector))
+ DiagnosticState.markUsed(*ObjectKey);
+ }
+
+ if (ParameterSelectorCandidates) {
+ for (api_notes::FunctionObjectSelector ObjectSelector :
+ ObjectSelectors) {
+ processExactAPINotes<api_notes::CXXMethodInfo>(
+ *this, CXXMethod, *ParameterSelectorCandidates,
+ [&](ArrayRef<std::string> Parameters) {
+ api_notes::FunctionSelector Selector;
+ Selector.Parameters.emplace(Parameters.begin(),
+ Parameters.end());
+ Selector.Object = ObjectSelector;
+ return Reader->lookupCXXMethod(Context->id, MethodName,
+ Selector);
+ });
+ DiagnosticState.markCandidatesUsed(
+ [&](ArrayRef<std::string> Parameters) {
+ api_notes::FunctionSelector Selector;
+ Selector.Parameters.emplace(Parameters.begin(),
+ Parameters.end());
+ Selector.Object = ObjectSelector;
+ return Reader->getCXXMethodSelectorKey(
+ Context->id, MethodName, Selector);
+ },
+ *ParameterSelectorCandidates);
+ }
+ }
+ }
}
}
}
@@ -1452,20 +1550,27 @@ void APINotesSelectorDiagnosticReaderState::diagnoseUnused(
if (Selector.second)
continue;
- auto SeenName =
- SeenNames.find(Selector.first.getWithoutParameterSelector());
+ auto SeenName = SeenNames.find(Selector.first.getNameOnlyKey());
if (SeenName == SeenNames.end())
continue;
- std::optional<SmallVector<std::string, 4>> ParameterSpellings =
- Reader.getParameterSelectorSpellingsForDiagnostics(Selector.first);
- if (!ParameterSpellings)
- continue;
+ std::optional<SmallVector<std::string, 4>> ParameterSpellings;
+ if (Selector.first.Key.parameterTypeIDs) {
+ ParameterSpellings =
+ Reader.getParameterSelectorSpellingsForDiagnostics(Selector.first);
+ if (!ParameterSpellings)
+ continue;
+ }
+
+ std::optional<ArrayRef<std::string>> ParameterRefs;
+ if (ParameterSpellings)
+ ParameterRefs = ArrayRef<std::string>(*ParameterSpellings);
S.Diag(SeenName->second.Loc, diag::warn_apinotes_message)
<< (llvm::Twine("API notes entry for '") + SeenName->second.Name +
- "' has unmatched Where.Parameters " +
- api_notes::formatAPINotesParameterSelector(*ParameterSpellings))
+ "' has unmatched " +
+ api_notes::formatAPINotesFunctionSelector(
+ ParameterRefs, Selector.first.Key.objectSelector))
.str();
}
}
diff --git a/clang/lib/Sema/SemaAPINotesInternal.h b/clang/lib/Sema/SemaAPINotesInternal.h
index 5766343956ce5..b09968e166243 100644
--- a/clang/lib/Sema/SemaAPINotesInternal.h
+++ b/clang/lib/Sema/SemaAPINotesInternal.h
@@ -39,11 +39,11 @@ struct APINotesSelectorDiagnosticName {
/// end-of-TU diagnostics can warn about exact selectors for known names that
/// were never matched.
struct APINotesSelectorDiagnosticReaderState {
- /// Exact Where.Parameters selector keys stored by API notes. The bool is
- /// true once Sema sees a declaration matching the exact selector.
+ /// Function selector keys stored by API notes. The bool is true once
+ /// Sema sees a declaration matching the selector.
llvm::DenseMap<api_notes::APINotesFunctionSelectorKey, bool> SelectorUsed;
- /// Maps broad/name-only keys to a declaration location/name used for
+ /// Maps broad name-only keys to a declaration location/name used for
/// diagnostics.
llvm::DenseMap<api_notes::APINotesFunctionSelectorKey,
APINotesSelectorDiagnosticName>
@@ -63,7 +63,7 @@ struct APINotesSelectorDiagnosticReaderState {
void noteSeenDeclaration(const api_notes::APINotesFunctionSelectorKey &Key,
llvm::StringRef Name, SourceLocation Loc) {
- SeenNames.insert({Key.getWithoutParameterSelector(), {Loc, Name.str()}});
+ SeenNames.insert({Key.getNameOnlyKey(), {Loc, Name.str()}});
}
void markUsed(const api_notes::APINotesFunctionSelectorKey &Key) {
diff --git a/clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.apinotes b/clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.apinotes
new file mode 100644
index 0000000000000..5397d46fbd883
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.apinotes
@@ -0,0 +1,96 @@
+---
+Name: WhereObjectQualifiers
+Tags:
+- Name: ObjectBuilder
+ Methods:
+ - Name: buildRef
+ Where:
+ Object:
+ Ref: lvalue
+ SwiftName: buildFromLValue()
+ - Name: buildRef
+ Where:
+ Parameters: []
+ Object:
+ Ref: rvalue
+ SwiftName: buildFromRValue()
+ - Name: buildConst
+ Where:
+ Parameters: []
+ Object:
+ Const: false
+ Ref: lvalue
+ SwiftName: buildMutableLValue()
+ - Name: buildConst
+ Where:
+ Parameters: []
+ Object:
+ Const: true
+ Ref: lvalue
+ SwiftName: buildConstLValue()
+ - Name: buildVolatile
+ Where:
+ Parameters: []
+ Object:
+ Volatile: false
+ SwiftName: buildNonVolatile()
+ - Name: buildVolatile
+ Where:
+ Parameters: []
+ Object:
+ Volatile: true
+ SwiftName: buildVolatile()
+ - Name: buildNone
+ Where:
+ Parameters: []
+ Object:
+ Ref: none
+ SwiftName: buildUnqualified()
+ - Name: buildCombined
+ Where:
+ Parameters: []
+ Object:
+ Const: true
+ Volatile: true
+ Ref: lvalue
+ SwiftName: buildConstVolatileLValue()
+ - Name: buildConstRValue
+ Where:
+ Parameters: []
+ Object:
+ Const: true
+ Ref: rvalue
+ SwiftName: buildConstRValue()
+ - Name: buildVolatileRValue
+ Where:
+ Parameters: []
+ Object:
+ Volatile: true
+ Ref: rvalue
+ SwiftName: buildVolatileRValue()
+ - Name: buildObjectOnly
+ Where:
+ Object:
+ Ref: lvalue
+ SwiftName: buildObjectOnlyLValue(_:)
+ - Name: buildStatic
+ Where:
+ Parameters: []
+ Object:
+ Ref: none
+ SwiftName: shouldNotApplyToStatic()
+ - Name: buildStaticObjectOnly
+ Where:
+ Object:
+ Ref: none
+ SwiftName: shouldNotApplyToStaticObjectOnly()
+ - Name: consume
+ Where:
+ Parameters:
+ - 'ObjectBuffer &'
+ SwiftName: consumeBorrowed(_:)
+ - Name: consume
+ Where:
+ Parameters:
+ - 'ObjectBuffer &&'
+ SwiftName: consumeOwned(_:)
diff --git a/clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.h b/clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.h
new file mode 100644
index 0000000000000..ed3c5ed92e8e1
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.h
@@ -0,0 +1,30 @@
+#ifndef WHERE_OBJECT_QUALIFIERS_H
+#define WHERE_OBJECT_QUALIFIERS_H
+
+struct ObjectBuffer {};
+
+struct ObjectBuilder {
+ void buildRef() &;
+ void buildRef() &&;
+
+ void buildConst() &;
+ void buildConst() const &;
+
+ void buildVolatile();
+ void buildVolatile() volatile;
+
+ void buildNone();
+ void buildCombined() const volatile &;
+ void buildConstRValue() const &&;
+ void buildVolatileRValue() volatile &&;
+ void buildObjectOnly(int) &;
+ void buildObjectOnly(double) &;
+ void buildObjectOnly(int) &&;
+ static void buildStatic();
+ static void buildStaticObjectOnly();
+
+ void consume(ObjectBuffer &);
+ void consume(ObjectBuffer &&);
+};
+
+#endif // WHERE_OBJECT_QUALIFIERS_H
diff --git a/clang/test/APINotes/Inputs/Headers/module.modulemap b/clang/test/APINotes/Inputs/Headers/module.modulemap
index 592d482ea7a57..602e44273ff60 100644
--- a/clang/test/APINotes/Inputs/Headers/module.modulemap
+++ b/clang/test/APINotes/Inputs/Headers/module.modulemap
@@ -75,3 +75,8 @@ module WhereParametersSema {
header "WhereParametersSema.h"
export *
}
+
+module WhereObjectQualifiers {
+ header "WhereObjectQualifiers.h"
+ export *
+}
diff --git a/clang/test/APINotes/Inputs/WhereObjectQualifiersDiag/APINotes.apinotes b/clang/test/APINotes/Inputs/WhereObjectQualifiersDiag/APINotes.apinotes
new file mode 100644
index 0000000000000..3a6a39c17d9c8
--- /dev/null
+++ b/clang/test/APINotes/Inputs/WhereObjectQualifiersDiag/APINotes.apinotes
@@ -0,0 +1,46 @@
+---
+Name: WhereObjectQualifiers
+Functions:
+- Name: invalidGlobal
+ Where:
+ Parameters: []
+ Object:
+ Ref: none
+ SwiftName: invalidGlobal()
+Tags:
+- Name: ObjectDiagBuilder
+ Methods:
+ - Name: objectOnly
+ Where:
+ Object:
+ Ref: none
+ SwiftName: objectOnly()
+ - Name: emptyObject
+ Where:
+ Parameters: []
+ Object: {}
+ SwiftName: emptyObject()
+ - Name: duplicate
+ Where:
+ Parameters: []
+ Object:
+ Ref: lvalue
+ SwiftName: duplicateOne()
+ - Name: duplicate
+ Where:
+ Parameters: []
+ Object:
+ Ref: lvalue
+ SwiftName: duplicateTwo()
+ - Name: acceptedDifferentRef
+ Where:
+ Parameters: []
+ Object:
+ Ref: lvalue
+ SwiftName: acceptedLValue()
+ - Name: acceptedDifferentRef
+ Where:
+ Parameters: []
+ Object:
+ Ref: rvalue
+ SwiftName: acceptedRValue()
diff --git a/clang/test/APINotes/Inputs/WhereObjectQualifiersDiag/WhereObjectQualifiers.h b/clang/test/APINotes/Inputs/WhereObjectQualifiersDiag/WhereObjectQualifiers.h
new file mode 100644
index 0000000000000..6748c36ceb23c
--- /dev/null
+++ b/clang/test/APINotes/Inputs/WhereObjectQualifiersDiag/WhereObjectQualifiers.h
@@ -0,0 +1,14 @@
+#ifndef WHERE_OBJECT_QUALIFIERS_DIAG_H
+#define WHERE_OBJECT_QUALIFIERS_DIAG_H
+
+void invalidGlobal();
+
+struct ObjectDiagBuilder {
+ void objectOnly();
+ void emptyObject();
+ void duplicate();
+ void acceptedDifferentRef() &;
+ void acceptedDifferentRef() &&;
+};
+
+#endif // WHERE_OBJECT_QUALIFIERS_DIAG_H
diff --git a/clang/test/APINotes/where-object-qualifiers.cpp b/clang/test/APINotes/where-object-qualifiers.cpp
new file mode 100644
index 0000000000000..78eff1d56e3cd
--- /dev/null
+++ b/clang/test/APINotes/where-object-qualifiers.cpp
@@ -0,0 +1,63 @@
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -fsyntax-only -I %S/Inputs/Headers %s -x c++
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildRef -x c++ | FileCheck --check-prefix=REF %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildConst -x c++ | FileCheck --check-prefix=CONST %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildVolatile -x c++ | FileCheck --check-prefix=VOLATILE %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildNone -x c++ | FileCheck --check-prefix=NONE %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildCombined -x c++ | FileCheck --check-prefix=COMBINED-CVREF %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildConstRValue -x c++ | FileCheck --check-prefix=COMBINED-CONST-RVALUE %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildVolatileRValue -x c++ | FileCheck --check-prefix=COMBINED-VOLATILE-RVALUE %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildObjectOnly -x c++ | FileCheck --check-prefix=OBJECT-ONLY %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildStatic -x c++ | FileCheck --check-prefix=STATIC %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::consume -x c++ | FileCheck --check-prefix=PARAM-REF %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiersWarnings -fdisable-module-hash -fapinotes-modules -Wapinotes -fsyntax-only -I %S/Inputs/Headers %s -x c++ 2>&1 | FileCheck --check-prefix=UNMATCHED %s
+// RUN: not %clang_cc1 -fsyntax-only -fapinotes %s -I %S/Inputs/WhereObjectQualifiersDiag 2>&1 | FileCheck --check-prefix=DIAG %s
+
+#include "WhereObjectQualifiers.h"
+
+// REF: CXXMethodDecl {{.+}} buildRef 'void () &{{.*}}'
+// REF: SwiftNameAttr {{.+}} "buildFromLValue()"
+// REF: CXXMethodDecl {{.+}} buildRef 'void () &&{{.*}}'
+// REF: SwiftNameAttr {{.+}} "buildFromRValue()"
+
+// CONST: CXXMethodDecl {{.+}} buildConst 'void () &{{.*}}'
+// CONST: SwiftNameAttr {{.+}} "buildMutableLValue()"
+// CONST: CXXMethodDecl {{.+}} buildConst 'void () const &{{.*}}'
+// CONST: SwiftNameAttr {{.+}} "buildConstLValue()"
+
+// VOLATILE: CXXMethodDecl {{.+}} buildVolatile 'void (){{.*}}'
+// VOLATILE: SwiftNameAttr {{.+}} "buildNonVolatile()"
+// VOLATILE: CXXMethodDecl {{.+}} buildVolatile 'void () volatile{{.*}}'
+// VOLATILE: SwiftNameAttr {{.+}} "buildVolatile()"
+
+// NONE: CXXMethodDecl {{.+}} buildNone 'void (){{.*}}'
+// NONE: SwiftNameAttr {{.+}} "buildUnqualified()"
+
+// COMBINED-CVREF: CXXMethodDecl {{.+}} buildCombined 'void () const volatile &{{.*}}'
+// COMBINED-CVREF: SwiftNameAttr {{.+}} "buildConstVolatileLValue()"
+// COMBINED-CONST-RVALUE: CXXMethodDecl {{.+}} buildConstRValue 'void () const &&{{.*}}'
+// COMBINED-CONST-RVALUE: SwiftNameAttr {{.+}} "buildConstRValue()"
+// COMBINED-VOLATILE-RVALUE: CXXMethodDecl {{.+}} buildVolatileRValue 'void () volatile &&{{.*}}'
+// COMBINED-VOLATILE-RVALUE: SwiftNameAttr {{.+}} "buildVolatileRValue()"
+
+// OBJECT-ONLY: CXXMethodDecl {{.+}} buildObjectOnly 'void (int) &{{.*}}'
+// OBJECT-ONLY: SwiftNameAttr {{.+}} "buildObjectOnlyLValue(_:)"
+// OBJECT-ONLY: CXXMethodDecl {{.+}} buildObjectOnly 'void (double) &{{.*}}'
+// OBJECT-ONLY: SwiftNameAttr {{.+}} "buildObjectOnlyLValue(_:)"
+// OBJECT-ONLY: CXXMethodDecl {{.+}} buildObjectOnly 'void (int) &&{{.*}}'
+// OBJECT-ONLY-NOT: SwiftNameAttr
+
+// STATIC: CXXMethodDecl {{.+}} buildStatic 'void ()' static
+// STATIC-NOT: SwiftNameAttr
+
+// UNMATCHED-DAG: warning: API notes entry for 'buildStatic' has unmatched Where.Parameters [] Object{Ref: none}
+// UNMATCHED-DAG: warning: API notes entry for 'buildStaticObjectOnly' has unmatched Where.Object Object{Ref: none}
+
+// PARAM-REF: CXXMethodDecl {{.+}} consume 'void (ObjectBuffer &)'
+// PARAM-REF: SwiftNameAttr {{.+}} "consumeBorrowed(_:)"
+// PARAM-REF: CXXMethodDecl {{.+}} consume 'void (ObjectBuffer &&)'
+// PARAM-REF: SwiftNameAttr {{.+}} "consumeOwned(_:)"
+
+// DIAG-DAG: error: 'Object' is only supported on C++ methods
+// DIAG-DAG: error: 'Object' requires at least one field
+// DIAG-DAG: error: multiple API notes entries for C++ method 'duplicate' with Where.Parameters [] Object{Ref: lvalue}
>From 5bd5ba6cdb86f29d02a2b85badb8eb333b9708a1 Mon Sep 17 00:00:00 2001
From: stoeckoverflow <dominic-st at gmx.de>
Date: Mon, 17 Aug 2026 15:07:53 +0200
Subject: [PATCH 2/6] [APINotes] Address Where.Object selector review feedback
---
clang/include/clang/APINotes/Types.h | 48 +++++++++------------
clang/lib/APINotes/APINotesFormat.h | 48 ++++++++++-----------
clang/lib/APINotes/APINotesReader.cpp | 6 +--
clang/lib/APINotes/APINotesWriter.cpp | 6 +--
clang/lib/APINotes/APINotesYAMLCompiler.cpp | 20 ++++-----
clang/lib/Sema/SemaAPINotes.cpp | 33 +++++---------
6 files changed, 70 insertions(+), 91 deletions(-)
diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h
index 4151e26a98b45..7ca3fff6f3718 100644
--- a/clang/include/clang/APINotes/Types.h
+++ b/clang/include/clang/APINotes/Types.h
@@ -9,6 +9,7 @@
#ifndef LLVM_CLANG_APINOTES_TYPES_H
#define LLVM_CLANG_APINOTES_TYPES_H
+#include "clang/AST/TypeBase.h"
#include "clang/Basic/Specifiers.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMapInfo.h"
@@ -1008,19 +1009,13 @@ struct Context {
using IdentifierID = llvm::PointerEmbeddedInt<unsigned, 31>;
-/// Describes the C++ implicit-object ref-qualifier portion of a method
-/// selector.
-enum class FunctionObjectRefQualifier : uint8_t {
- None,
- LValue,
- RValue,
-};
-
/// Describes optional constraints on a C++ method's implicit object parameter.
struct FunctionObjectSelector {
std::optional<bool> Const;
std::optional<bool> Volatile;
- std::optional<FunctionObjectRefQualifier> Ref;
+ std::optional<RefQualifierKind> Ref;
+
+ std::string format() const;
};
inline bool operator==(const FunctionObjectSelector &LHS,
@@ -1034,27 +1029,25 @@ inline bool operator!=(const FunctionObjectSelector &LHS,
return !(LHS == RHS);
}
-inline std::string formatAPINotesObjectSelector(FunctionObjectSelector Object) {
+inline std::string FunctionObjectSelector::format() const {
std::string Result;
llvm::raw_string_ostream OS(Result);
llvm::SmallVector<std::string, 3> Parts;
- if (Object.Const)
- Parts.push_back(std::string("Const: ") +
- (*Object.Const ? "true" : "false"));
- if (Object.Volatile)
- Parts.push_back(std::string("Volatile: ") +
- (*Object.Volatile ? "true" : "false"));
- if (Object.Ref) {
+ if (Const)
+ Parts.push_back(std::string("Const: ") + (*Const ? "true" : "false"));
+ if (Volatile)
+ Parts.push_back(std::string("Volatile: ") + (*Volatile ? "true" : "false"));
+ if (Ref) {
std::string Ref = "Ref: ";
- switch (*Object.Ref) {
- case FunctionObjectRefQualifier::None:
+ switch (*this->Ref) {
+ case RQ_None:
Ref += "none";
break;
- case FunctionObjectRefQualifier::LValue:
+ case RQ_LValue:
Ref += "lvalue";
break;
- case FunctionObjectRefQualifier::RValue:
+ case RQ_RValue:
Ref += "rvalue";
break;
}
@@ -1072,6 +1065,8 @@ inline std::string formatAPINotesObjectSelector(FunctionObjectSelector Object) {
struct FunctionSelector {
std::optional<llvm::SmallVector<std::string, 4>> Parameters;
std::optional<FunctionObjectSelector> Object;
+
+ std::string format() const;
};
inline bool operator==(const FunctionSelector &LHS,
@@ -1098,18 +1093,17 @@ inline std::string formatAPINotesFunctionSelector(
Result += " ";
else
Result = "Where.Object ";
- Result += formatAPINotesObjectSelector(*Object);
+ Result += Object->format();
}
return Result;
}
-inline std::string
-formatAPINotesFunctionSelector(const FunctionSelector &Selector) {
+inline std::string FunctionSelector::format() const {
std::optional<llvm::ArrayRef<std::string>> Parameters;
- if (Selector.Parameters)
- Parameters = llvm::ArrayRef<std::string>(*Selector.Parameters);
- return formatAPINotesFunctionSelector(Parameters, Selector.Object);
+ if (this->Parameters)
+ Parameters = llvm::ArrayRef<std::string>(*this->Parameters);
+ return formatAPINotesFunctionSelector(Parameters, Object);
}
struct FunctionTableSelectorKey {
diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h
index f0ea371e3ccef..f77706d04befb 100644
--- a/clang/lib/APINotes/APINotesFormat.h
+++ b/clang/lib/APINotes/APINotesFormat.h
@@ -12,6 +12,7 @@
#include "clang/APINotes/Types.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/PointerEmbeddedInt.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Bitcode/BitcodeConvenience.h"
@@ -29,10 +30,7 @@ const uint16_t VERSION_MAJOR = 0;
/// API notes file minor version number.
///
/// When the format changes IN ANY WAY, this number should be incremented.
-const uint16_t VERSION_MINOR = 42; // 39 for BoundsSafety;
- // 40 for UnsafeBufferUsageAttr
- // 41 for FunctionTableKey parameters
- // 42 for FunctionTableKey object selectors
+const uint16_t VERSION_MINOR = 42; // 42 for FunctionTableKey object selectors
const uint8_t kSwiftConforms = 1;
const uint8_t kSwiftDoesNotConform = 2;
@@ -379,10 +377,10 @@ constexpr uint8_t FunctionKeyObjectSelectorMask =
constexpr unsigned FunctionTableKeyBaseLength =
sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint16_t);
-template <typename GetIdentifierFn>
-std::optional<FunctionTableKey>
-getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name,
- GetIdentifierFn GetIdentifier) {
+inline std::optional<FunctionTableKey> getFunctionKeyImpl(
+ uint32_t ParentContextID, llvm::StringRef Name,
+ llvm::function_ref<std::optional<IdentifierID>(llvm::StringRef)>
+ GetIdentifier) {
std::optional<IdentifierID> NameID = GetIdentifier(Name);
if (!NameID)
return std::nullopt;
@@ -390,11 +388,11 @@ getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name,
return FunctionTableKey(ParentContextID, *NameID);
}
-template <typename GetIdentifierFn>
-std::optional<FunctionTableKey>
-getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name,
- FunctionObjectSelector ObjectSelector,
- GetIdentifierFn GetIdentifier) {
+inline std::optional<FunctionTableKey> getFunctionKeyImpl(
+ uint32_t ParentContextID, llvm::StringRef Name,
+ FunctionObjectSelector ObjectSelector,
+ llvm::function_ref<std::optional<IdentifierID>(llvm::StringRef)>
+ GetIdentifier) {
std::optional<IdentifierID> NameID = GetIdentifier(Name);
if (!NameID)
return std::nullopt;
@@ -404,11 +402,12 @@ getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name,
return FunctionTableKey(ParentContextID, *NameID, std::move(Selector));
}
-template <typename ParameterT, typename GetIdentifierFn>
-std::optional<FunctionTableKey>
-getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name,
- llvm::ArrayRef<ParameterT> Parameters,
- GetIdentifierFn GetIdentifier) {
+template <typename ParameterT>
+std::optional<FunctionTableKey> getFunctionKeyImpl(
+ uint32_t ParentContextID, llvm::StringRef Name,
+ llvm::ArrayRef<ParameterT> Parameters,
+ llvm::function_ref<std::optional<IdentifierID>(llvm::StringRef)>
+ GetIdentifier) {
std::optional<IdentifierID> NameID = GetIdentifier(Name);
if (!NameID)
return std::nullopt;
@@ -427,12 +426,13 @@ getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name,
return FunctionTableKey(ParentContextID, *NameID, std::move(Selector));
}
-template <typename ParameterT, typename GetIdentifierFn>
-std::optional<FunctionTableKey>
-getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name,
- llvm::ArrayRef<ParameterT> Parameters,
- FunctionObjectSelector ObjectSelector,
- GetIdentifierFn GetIdentifier) {
+template <typename ParameterT>
+std::optional<FunctionTableKey> getFunctionKeyImpl(
+ uint32_t ParentContextID, llvm::StringRef Name,
+ llvm::ArrayRef<ParameterT> Parameters,
+ FunctionObjectSelector ObjectSelector,
+ llvm::function_ref<std::optional<IdentifierID>(llvm::StringRef)>
+ GetIdentifier) {
std::optional<IdentifierID> NameID = GetIdentifier(Name);
if (!NameID)
return std::nullopt;
diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp
index ab06d75826d1a..b291cdded9a1b 100644
--- a/clang/lib/APINotes/APINotesReader.cpp
+++ b/clang/lib/APINotes/APINotesReader.cpp
@@ -95,11 +95,11 @@ static FunctionTableKey readFunctionTableKey(const uint8_t *Data,
(FunctionKeyFlags & FunctionKeyObjectVolatileValue) != 0;
if (FunctionKeyFlags & FunctionKeyObjectRefPresent) {
if (FunctionKeyFlags & FunctionKeyObjectRefLValue)
- Selector.Ref = FunctionObjectRefQualifier::LValue;
+ Selector.Ref = RQ_LValue;
else if (FunctionKeyFlags & FunctionKeyObjectRefRValue)
- Selector.Ref = FunctionObjectRefQualifier::RValue;
+ Selector.Ref = RQ_RValue;
else
- Selector.Ref = FunctionObjectRefQualifier::None;
+ Selector.Ref = RQ_None;
}
ObjectSelector = Selector;
}
diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp
index 4027d096bf02a..b8231e28f4bef 100644
--- a/clang/lib/APINotes/APINotesWriter.cpp
+++ b/clang/lib/APINotes/APINotesWriter.cpp
@@ -559,12 +559,12 @@ static uint8_t getFunctionTableKeyFlags(const FunctionTableKey &Key) {
if (Key.objectSelector->Ref) {
Flags |= FunctionKeyObjectRefPresent;
switch (*Key.objectSelector->Ref) {
- case FunctionObjectRefQualifier::None:
+ case RQ_None:
break;
- case FunctionObjectRefQualifier::LValue:
+ case RQ_LValue:
Flags |= FunctionKeyObjectRefLValue;
break;
- case FunctionObjectRefQualifier::RValue:
+ case RQ_RValue:
Flags |= FunctionKeyObjectRefRValue;
break;
}
diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
index 608152b2f4442..77c93b703367f 100644
--- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp
+++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -373,12 +373,11 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(Function)
namespace llvm {
namespace yaml {
-template <>
-struct ScalarEnumerationTraits<api_notes::FunctionObjectRefQualifier> {
- static void enumeration(IO &IO, api_notes::FunctionObjectRefQualifier &Ref) {
- IO.enumCase(Ref, "none", api_notes::FunctionObjectRefQualifier::None);
- IO.enumCase(Ref, "lvalue", api_notes::FunctionObjectRefQualifier::LValue);
- IO.enumCase(Ref, "rvalue", api_notes::FunctionObjectRefQualifier::RValue);
+template <> struct ScalarEnumerationTraits<clang::RefQualifierKind> {
+ static void enumeration(IO &IO, clang::RefQualifierKind &Ref) {
+ IO.enumCase(Ref, "none", clang::RQ_None);
+ IO.enumCase(Ref, "lvalue", clang::RQ_LValue);
+ IO.enumCase(Ref, "rvalue", clang::RQ_RValue);
}
};
@@ -822,8 +821,7 @@ getFunctionSelectorDuplicateKey(llvm::StringRef Name,
llvm::SmallString<64> Key;
llvm::raw_svector_ostream OS(Key);
appendDuplicateKeyPart(OS, Name);
- std::string SelectorText =
- api_notes::formatAPINotesFunctionSelector(Selector);
+ std::string SelectorText = Selector.format();
appendDuplicateKeyPart(OS, SelectorText);
return Key.str().str();
}
@@ -1235,8 +1233,7 @@ class YAMLConverter {
getFunctionSelectorDuplicateKey(CXXMethod.Name, *WhereSelector);
if (!KnownMethodSelectors.insert(DuplicateKey).second) {
emitError(llvm::Twine("multiple API notes entries for C++ method '") +
- CXXMethod.Name + "' with " +
- api_notes::formatAPINotesFunctionSelector(*WhereSelector));
+ CXXMethod.Name + "' with " + WhereSelector->format());
continue;
}
}
@@ -1329,8 +1326,7 @@ class YAMLConverter {
if (!KnownFunctionSelectors.insert(DuplicateKey).second) {
emitError(
llvm::Twine("multiple API notes entries for global function '") +
- Function.Name + "' with " +
- api_notes::formatAPINotesFunctionSelector(*WhereSelector));
+ Function.Name + "' with " + WhereSelector->format());
continue;
}
}
diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp
index e7ddacf9d2515..b701baca1bc83 100644
--- a/clang/lib/Sema/SemaAPINotes.cpp
+++ b/clang/lib/Sema/SemaAPINotes.cpp
@@ -1118,17 +1118,7 @@ getAPINotesObjectSelector(const CXXMethodDecl *Method) {
api_notes::FunctionObjectSelector Selector;
Selector.Const = Method->isConst();
Selector.Volatile = Method->isVolatile();
- switch (Method->getRefQualifier()) {
- case RQ_None:
- Selector.Ref = api_notes::FunctionObjectRefQualifier::None;
- break;
- case RQ_LValue:
- Selector.Ref = api_notes::FunctionObjectRefQualifier::LValue;
- break;
- case RQ_RValue:
- Selector.Ref = api_notes::FunctionObjectRefQualifier::RValue;
- break;
- }
+ Selector.Ref = Method->getRefQualifier();
return Selector;
}
@@ -1138,17 +1128,17 @@ static void getAPINotesObjectSelectorSubsets(
enum ObjectSelectorField : unsigned {
ConstField = 1u << 0,
VolatileField = 1u << 1,
- RefField = 1u << 2,
+ RefQualifierField = 1u << 2,
};
constexpr unsigned ObjectSelectorSubsetMasks[] = {
ConstField,
VolatileField,
- RefField,
+ RefQualifierField,
ConstField | VolatileField,
- ConstField | RefField,
- VolatileField | RefField,
- ConstField | VolatileField | RefField,
+ ConstField | RefQualifierField,
+ VolatileField | RefQualifierField,
+ ConstField | VolatileField | RefQualifierField,
};
// Apply less-constrained object selectors before more-constrained ones so
@@ -1159,7 +1149,7 @@ static void getAPINotesObjectSelectorSubsets(
Subset.Const = ObjectSelector.Const;
if (Mask & VolatileField)
Subset.Volatile = ObjectSelector.Volatile;
- if (Mask & RefField)
+ if (Mask & RefQualifierField)
Subset.Ref = ObjectSelector.Ref;
Subsets.push_back(Subset);
}
@@ -1562,15 +1552,14 @@ void APINotesSelectorDiagnosticReaderState::diagnoseUnused(
continue;
}
- std::optional<ArrayRef<std::string>> ParameterRefs;
+ api_notes::FunctionSelector FunctionSelector;
if (ParameterSpellings)
- ParameterRefs = ArrayRef<std::string>(*ParameterSpellings);
+ FunctionSelector.Parameters = *ParameterSpellings;
+ FunctionSelector.Object = Selector.first.Key.objectSelector;
S.Diag(SeenName->second.Loc, diag::warn_apinotes_message)
<< (llvm::Twine("API notes entry for '") + SeenName->second.Name +
- "' has unmatched " +
- api_notes::formatAPINotesFunctionSelector(
- ParameterRefs, Selector.first.Key.objectSelector))
+ "' has unmatched " + FunctionSelector.format())
.str();
}
}
>From 5f07c0647ab48d399b88e02f3ff37bd26d39e9c0 Mon Sep 17 00:00:00 2001
From: stoeckoverflow <dominic-st at gmx.de>
Date: Tue, 18 Aug 2026 16:11:28 +0200
Subject: [PATCH 3/6] [APINotes] Refine Where.Object selector handling
---
clang/include/clang/APINotes/APINotesWriter.h | 42 +++------
clang/include/clang/APINotes/Types.h | 3 +-
clang/lib/APINotes/APINotesFormat.h | 21 +++++
clang/lib/APINotes/APINotesReader.cpp | 18 +---
clang/lib/APINotes/APINotesWriter.cpp | 89 ++-----------------
clang/lib/APINotes/APINotesYAMLCompiler.cpp | 19 +---
clang/lib/Sema/SemaAPINotes.cpp | 2 +-
.../Headers/WhereObjectQualifiers.apinotes | 34 +++++++
.../Inputs/Headers/WhereObjectQualifiers.h | 9 ++
.../test/APINotes/where-object-qualifiers.cpp | 20 +++++
10 files changed, 112 insertions(+), 145 deletions(-)
diff --git a/clang/include/clang/APINotes/APINotesWriter.h b/clang/include/clang/APINotes/APINotesWriter.h
index c0d07ceeb30cc..2fea3998c902d 100644
--- a/clang/include/clang/APINotes/APINotesWriter.h
+++ b/clang/include/clang/APINotes/APINotesWriter.h
@@ -80,26 +80,12 @@ class APINotesWriter {
bool IsInstanceMethod, const ObjCMethodInfo &Info,
llvm::VersionTuple SwiftVersion);
- /// Add information about a specific C++ method.
- ///
- /// \param CtxID The context in which this method resides, i.e. a C++ tag.
- /// \param Name The name of the method.
- /// \param Info Information about this method.
- void addCXXMethod(ContextID CtxID, llvm::StringRef Name,
- const CXXMethodInfo &Info, llvm::VersionTuple SwiftVersion);
-
- /// Add information about a C++ method with an exact parameter selector. An
- /// empty parameter list uses an exact zero-parameter key, and a non-empty
- /// list uses an exact ordered parameter key.
- void addCXXMethod(ContextID CtxID, llvm::StringRef Name,
- llvm::ArrayRef<llvm::StringRef> Parameters,
- const CXXMethodInfo &Info, llvm::VersionTuple SwiftVersion);
-
- /// Add information about a C++ method with a composed selector. Omitted
- /// selector components use the broad name-based key.
+ /// Add information about a C++ method. Omitted selector components use the
+ /// broad name-based key. An empty parameter selector uses an exact
+ /// zero-parameter key.
void addCXXMethod(ContextID CtxID, llvm::StringRef Name,
- const FunctionSelector &Selector, const CXXMethodInfo &Info,
- llvm::VersionTuple SwiftVersion);
+ const CXXMethodInfo &Info, llvm::VersionTuple SwiftVersion,
+ const FunctionSelector &Selector = FunctionSelector());
/// Add information about a specific C record field.
///
@@ -117,21 +103,13 @@ class APINotesWriter {
const GlobalVariableInfo &Info,
llvm::VersionTuple SwiftVersion);
- /// Add information about a global function.
- ///
- /// \param Name The name of this global function.
- /// \param Info Information about this global function.
- void addGlobalFunction(std::optional<Context> Ctx, llvm::StringRef Name,
- const GlobalFunctionInfo &Info,
- llvm::VersionTuple SwiftVersion);
-
- /// Add information about a global function with an exact parameter selector.
- /// An empty parameter list uses an exact zero-parameter key, and a non-empty
- /// list uses an exact ordered parameter key.
+ /// Add information about a global function. Omitted selector components use
+ /// the broad name-based key. An empty parameter selector uses an exact
+ /// zero-parameter key.
void addGlobalFunction(std::optional<Context> Ctx, llvm::StringRef Name,
- llvm::ArrayRef<llvm::StringRef> Parameters,
const GlobalFunctionInfo &Info,
- llvm::VersionTuple SwiftVersion);
+ llvm::VersionTuple SwiftVersion,
+ const FunctionSelector &Selector = FunctionSelector());
/// Add information about an enumerator.
///
diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h
index 7ca3fff6f3718..2642e94bd12ec 100644
--- a/clang/include/clang/APINotes/Types.h
+++ b/clang/include/clang/APINotes/Types.h
@@ -15,6 +15,7 @@
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/PointerEmbeddedInt.h"
+#include "llvm/ADT/STLForwardCompat.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
@@ -1164,7 +1165,7 @@ struct FunctionTableKey {
static_cast<bool>(objectSelector->Const),
objectSelector->Volatile.value_or(false),
static_cast<bool>(objectSelector->Volatile),
- objectSelector->Ref ? static_cast<unsigned>(*objectSelector->Ref) + 1
+ objectSelector->Ref ? llvm::to_underlying(*objectSelector->Ref) + 1
: 0);
return Hash;
}
diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h
index f77706d04befb..2197e8e2f08c5 100644
--- a/clang/lib/APINotes/APINotesFormat.h
+++ b/clang/lib/APINotes/APINotesFormat.h
@@ -452,6 +452,27 @@ std::optional<FunctionTableKey> getFunctionKeyImpl(
return FunctionTableKey(ParentContextID, *NameID, std::move(Selector));
}
+inline std::optional<FunctionTableKey> getFunctionKeyImpl(
+ uint32_t ParentContextID, llvm::StringRef Name,
+ const FunctionSelector &Selector,
+ llvm::function_ref<std::optional<IdentifierID>(llvm::StringRef)>
+ GetIdentifier) {
+ if (Selector.Parameters) {
+ if (Selector.Object)
+ return getFunctionKeyImpl(
+ ParentContextID, Name,
+ llvm::ArrayRef<std::string>(*Selector.Parameters), *Selector.Object,
+ GetIdentifier);
+ return getFunctionKeyImpl(ParentContextID, Name,
+ llvm::ArrayRef<std::string>(*Selector.Parameters),
+ GetIdentifier);
+ }
+ if (Selector.Object)
+ return getFunctionKeyImpl(ParentContextID, Name, *Selector.Object,
+ GetIdentifier);
+ return getFunctionKeyImpl(ParentContextID, Name, GetIdentifier);
+}
+
} // namespace api_notes
} // namespace clang
diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp
index b291cdded9a1b..b0e786640490b 100644
--- a/clang/lib/APINotes/APINotesReader.cpp
+++ b/clang/lib/APINotes/APINotesReader.cpp
@@ -1018,21 +1018,9 @@ std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey(
std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey(
uint32_t ParentContextID, llvm::StringRef Name,
const FunctionSelector &Selector) {
- auto GetIdentifier = [this](llvm::StringRef S) { return getIdentifier(S); };
- if (Selector.Parameters) {
- if (Selector.Object)
- return getFunctionKeyImpl(
- ParentContextID, Name,
- llvm::ArrayRef<std::string>(*Selector.Parameters), *Selector.Object,
- GetIdentifier);
- return getFunctionKeyImpl(ParentContextID, Name,
- llvm::ArrayRef<std::string>(*Selector.Parameters),
- GetIdentifier);
- }
- if (Selector.Object)
- return getFunctionKeyImpl(ParentContextID, Name, *Selector.Object,
- GetIdentifier);
- return getFunctionKeyImpl(ParentContextID, Name, GetIdentifier);
+ return getFunctionKeyImpl(
+ ParentContextID, Name, Selector,
+ [this](llvm::StringRef S) { return getIdentifier(S); });
}
std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey(
diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp
index b8231e28f4bef..570575bbce172 100644
--- a/clang/lib/APINotes/APINotesWriter.cpp
+++ b/clang/lib/APINotes/APINotesWriter.cpp
@@ -137,20 +137,10 @@ class APINotesWriter::Implementation {
.first->second;
}
- FunctionTableKey getFunctionKey(uint32_t ParentContextID, StringRef Name) {
- std::optional<FunctionTableKey> Key =
- getFunctionKeyImpl(ParentContextID, Name,
- [this](StringRef S) -> std::optional<IdentifierID> {
- return getIdentifier(S);
- });
- assert(Key && "Writer identifier lookup should not fail");
- return *Key;
- }
-
FunctionTableKey getFunctionKey(uint32_t ParentContextID, StringRef Name,
- ArrayRef<StringRef> Parameters) {
+ const FunctionSelector &Selector) {
std::optional<FunctionTableKey> Key =
- getFunctionKeyImpl(ParentContextID, Name, Parameters,
+ getFunctionKeyImpl(ParentContextID, Name, Selector,
[this](StringRef S) -> std::optional<IdentifierID> {
return getIdentifier(S);
});
@@ -158,47 +148,6 @@ class APINotesWriter::Implementation {
return *Key;
}
- FunctionTableKey getFunctionKey(uint32_t ParentContextID, StringRef Name,
- const FunctionSelector &Selector) {
- std::optional<FunctionTableKey> Key;
- auto GetIdentifier = [this](StringRef S) -> std::optional<IdentifierID> {
- return getIdentifier(S);
- };
- if (Selector.Parameters) {
- if (Selector.Object)
- Key = getFunctionKeyImpl(
- ParentContextID, Name,
- llvm::ArrayRef<std::string>(*Selector.Parameters), *Selector.Object,
- GetIdentifier);
- else
- Key = getFunctionKeyImpl(
- ParentContextID, Name,
- llvm::ArrayRef<std::string>(*Selector.Parameters), GetIdentifier);
- } else if (Selector.Object) {
- Key = getFunctionKeyImpl(ParentContextID, Name, *Selector.Object,
- GetIdentifier);
- } else {
- Key = getFunctionKeyImpl(ParentContextID, Name, GetIdentifier);
- }
- assert(Key && "Writer identifier lookup should not fail");
- return *Key;
- }
-
- FunctionTableKey getFunctionKey(std::optional<Context> ParentContext,
- StringRef Name) {
- uint32_t ParentContextID =
- ParentContext ? ParentContext->id.Value : static_cast<uint32_t>(-1);
- return getFunctionKey(ParentContextID, Name);
- }
-
- FunctionTableKey getFunctionKey(std::optional<Context> ParentContext,
- StringRef Name,
- ArrayRef<StringRef> Parameters) {
- uint32_t ParentContextID =
- ParentContext ? ParentContext->id.Value : static_cast<uint32_t>(-1);
- return getFunctionKey(ParentContextID, Name, Parameters);
- }
-
FunctionTableKey getFunctionKey(std::optional<Context> ParentContext,
StringRef Name,
const FunctionSelector &Selector) {
@@ -1681,24 +1630,8 @@ void APINotesWriter::addObjCMethod(ContextID CtxID, ObjCSelectorRef Selector,
void APINotesWriter::addCXXMethod(ContextID CtxID, llvm::StringRef Name,
const CXXMethodInfo &Info,
- VersionTuple SwiftVersion) {
- FunctionTableKey Key = Implementation->getFunctionKey(CtxID.Value, Name);
- Implementation->CXXMethods[Key].push_back({SwiftVersion, Info});
-}
-
-void APINotesWriter::addCXXMethod(ContextID CtxID, llvm::StringRef Name,
- llvm::ArrayRef<llvm::StringRef> Parameters,
- const CXXMethodInfo &Info,
- VersionTuple SwiftVersion) {
- FunctionTableKey Key =
- Implementation->getFunctionKey(CtxID.Value, Name, Parameters);
- Implementation->CXXMethods[Key].push_back({SwiftVersion, Info});
-}
-
-void APINotesWriter::addCXXMethod(ContextID CtxID, llvm::StringRef Name,
- const FunctionSelector &Selector,
- const CXXMethodInfo &Info,
- VersionTuple SwiftVersion) {
+ VersionTuple SwiftVersion,
+ const FunctionSelector &Selector) {
FunctionTableKey Key =
Implementation->getFunctionKey(CtxID.Value, Name, Selector);
Implementation->CXXMethods[Key].push_back({SwiftVersion, Info});
@@ -1724,16 +1657,10 @@ void APINotesWriter::addGlobalVariable(std::optional<Context> Ctx,
void APINotesWriter::addGlobalFunction(std::optional<Context> Ctx,
llvm::StringRef Name,
const GlobalFunctionInfo &Info,
- VersionTuple SwiftVersion) {
- FunctionTableKey Key = Implementation->getFunctionKey(Ctx, Name);
- Implementation->GlobalFunctions[Key].push_back({SwiftVersion, Info});
-}
-
-void APINotesWriter::addGlobalFunction(
- std::optional<Context> Ctx, llvm::StringRef Name,
- llvm::ArrayRef<llvm::StringRef> Parameters, const GlobalFunctionInfo &Info,
- VersionTuple SwiftVersion) {
- FunctionTableKey Key = Implementation->getFunctionKey(Ctx, Name, Parameters);
+ VersionTuple SwiftVersion,
+ const FunctionSelector &Selector) {
+ assert(!Selector.Object && "Object selectors only apply to C++ methods");
+ FunctionTableKey Key = Implementation->getFunctionKey(Ctx, Name, Selector);
Implementation->GlobalFunctions[Key].push_back({SwiftVersion, Info});
}
diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
index 77c93b703367f..9f21c8baa0f73 100644
--- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp
+++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -1240,11 +1240,8 @@ class YAMLConverter {
CXXMethodInfo MI;
convertFunction(CXXMethod, MI);
- if (WhereSelector->Parameters || WhereSelector->Object)
- Writer.addCXXMethod(TagCtxID, CXXMethod.Name, *WhereSelector, MI,
- SwiftVersion);
- else
- Writer.addCXXMethod(TagCtxID, CXXMethod.Name, MI, SwiftVersion);
+ Writer.addCXXMethod(TagCtxID, CXXMethod.Name, MI, SwiftVersion,
+ *WhereSelector);
}
// Convert nested tags.
@@ -1341,16 +1338,8 @@ class YAMLConverter {
GlobalFunctionInfo GFI;
convertFunction(Function, GFI);
- if (WhereSelector->Parameters) {
- llvm::SmallVector<llvm::StringRef, 4> ParameterRefs;
- ParameterRefs.reserve(WhereSelector->Parameters->size());
- for (const std::string &Parameter : *WhereSelector->Parameters)
- ParameterRefs.push_back(Parameter);
- Writer.addGlobalFunction(Ctx, Function.Name, ParameterRefs, GFI,
- SwiftVersion);
- } else {
- Writer.addGlobalFunction(Ctx, Function.Name, GFI, SwiftVersion);
- }
+ Writer.addGlobalFunction(Ctx, Function.Name, GFI, SwiftVersion,
+ *WhereSelector);
}
// Write all enumerators.
diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp
index b701baca1bc83..5c6ec83b12701 100644
--- a/clang/lib/Sema/SemaAPINotes.cpp
+++ b/clang/lib/Sema/SemaAPINotes.cpp
@@ -1462,7 +1462,7 @@ void Sema::ProcessAPINotes(Decl *D) {
*ParameterSelectorCandidates);
}
- if (!CXXMethod->isStatic()) {
+ if (CXXMethod->isImplicitObjectMemberFunction()) {
SmallVector<api_notes::FunctionObjectSelector, 7> ObjectSelectors;
getAPINotesObjectSelectorSubsets(
getAPINotesObjectSelector(CXXMethod), ObjectSelectors);
diff --git a/clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.apinotes b/clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.apinotes
index 5397d46fbd883..f35455863e6de 100644
--- a/clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.apinotes
+++ b/clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.apinotes
@@ -73,6 +73,26 @@ Tags:
Object:
Ref: lvalue
SwiftName: buildObjectOnlyLValue(_:)
+ - Name: buildTwoObjectNotes
+ Where:
+ Object:
+ Const: false
+ SwiftPrivate: true
+ - Name: buildTwoObjectNotes
+ Where:
+ Object:
+ Ref: lvalue
+ SwiftName: buildTwoObjectNotesLValue()
+ - Name: buildObjectAndParameter
+ Where:
+ Object:
+ Const: true
+ SwiftPrivate: true
+ - Name: buildObjectAndParameter
+ Where:
+ Parameters:
+ - int
+ SwiftName: buildObjectAndParameterInt(_:)
- Name: buildStatic
Where:
Parameters: []
@@ -94,3 +114,17 @@ Tags:
Parameters:
- 'ObjectBuffer &&'
SwiftName: consumeOwned(_:)
+
+- Name: ExplicitObjectBuilder
+ Methods:
+ - Name: buildExplicitConst
+ Where:
+ Object:
+ Const: false
+ Ref: none
+ SwiftName: shouldNotApplyToExplicitConst()
+ - Name: buildExplicitLValue
+ Where:
+ Object:
+ Ref: lvalue
+ SwiftName: shouldNotApplyToExplicitLValue()
diff --git a/clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.h b/clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.h
index ed3c5ed92e8e1..57db26c010351 100644
--- a/clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.h
+++ b/clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.h
@@ -20,6 +20,8 @@ struct ObjectBuilder {
void buildObjectOnly(int) &;
void buildObjectOnly(double) &;
void buildObjectOnly(int) &&;
+ void buildTwoObjectNotes() &;
+ void buildObjectAndParameter(int) const;
static void buildStatic();
static void buildStaticObjectOnly();
@@ -27,4 +29,11 @@ struct ObjectBuilder {
void consume(ObjectBuffer &&);
};
+#if __cplusplus >= 202302L
+struct ExplicitObjectBuilder {
+ void buildExplicitConst(this const ExplicitObjectBuilder &);
+ void buildExplicitLValue(this ExplicitObjectBuilder &);
+};
+#endif
+
#endif // WHERE_OBJECT_QUALIFIERS_H
diff --git a/clang/test/APINotes/where-object-qualifiers.cpp b/clang/test/APINotes/where-object-qualifiers.cpp
index 78eff1d56e3cd..a508e2a42248b 100644
--- a/clang/test/APINotes/where-object-qualifiers.cpp
+++ b/clang/test/APINotes/where-object-qualifiers.cpp
@@ -8,9 +8,13 @@
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildConstRValue -x c++ | FileCheck --check-prefix=COMBINED-CONST-RVALUE %s
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildVolatileRValue -x c++ | FileCheck --check-prefix=COMBINED-VOLATILE-RVALUE %s
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildObjectOnly -x c++ | FileCheck --check-prefix=OBJECT-ONLY %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildTwoObjectNotes -x c++ | FileCheck --check-prefix=MULTI-OBJECT %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildObjectAndParameter -x c++ | FileCheck --check-prefix=OBJECT-PARAMETER %s
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildStatic -x c++ | FileCheck --check-prefix=STATIC %s
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::consume -x c++ | FileCheck --check-prefix=PARAM-REF %s
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiersWarnings -fdisable-module-hash -fapinotes-modules -Wapinotes -fsyntax-only -I %S/Inputs/Headers %s -x c++ 2>&1 | FileCheck --check-prefix=UNMATCHED %s
+// RUN: %clang_cc1 -std=c++23 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiersCXX23 -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter buildExplicit -x c++ | FileCheck --check-prefix=EXPLICIT-OBJECT %s
+// RUN: %clang_cc1 -std=c++23 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiersCXX23Warnings -fdisable-module-hash -fapinotes-modules -Wapinotes -fsyntax-only -I %S/Inputs/Headers %s -x c++ 2>&1 | FileCheck --check-prefix=EXPLICIT-OBJECT-UNMATCHED %s
// RUN: not %clang_cc1 -fsyntax-only -fapinotes %s -I %S/Inputs/WhereObjectQualifiersDiag 2>&1 | FileCheck --check-prefix=DIAG %s
#include "WhereObjectQualifiers.h"
@@ -47,12 +51,28 @@
// OBJECT-ONLY: CXXMethodDecl {{.+}} buildObjectOnly 'void (int) &&{{.*}}'
// OBJECT-ONLY-NOT: SwiftNameAttr
+// MULTI-OBJECT: CXXMethodDecl {{.+}} buildTwoObjectNotes 'void () &{{.*}}'
+// MULTI-OBJECT-DAG: SwiftPrivateAttr
+// MULTI-OBJECT-DAG: SwiftNameAttr {{.+}} "buildTwoObjectNotesLValue()"
+
+// OBJECT-PARAMETER: CXXMethodDecl {{.+}} buildObjectAndParameter 'void (int) const{{.*}}'
+// OBJECT-PARAMETER-DAG: SwiftPrivateAttr
+// OBJECT-PARAMETER-DAG: SwiftNameAttr {{.+}} "buildObjectAndParameterInt(_:)"
+
// STATIC: CXXMethodDecl {{.+}} buildStatic 'void ()' static
// STATIC-NOT: SwiftNameAttr
// UNMATCHED-DAG: warning: API notes entry for 'buildStatic' has unmatched Where.Parameters [] Object{Ref: none}
// UNMATCHED-DAG: warning: API notes entry for 'buildStaticObjectOnly' has unmatched Where.Object Object{Ref: none}
+// EXPLICIT-OBJECT: CXXMethodDecl {{.+}} buildExplicitConst 'void (const ExplicitObjectBuilder &)'
+// EXPLICIT-OBJECT-NOT: SwiftNameAttr
+// EXPLICIT-OBJECT: CXXMethodDecl {{.+}} buildExplicitLValue 'void (ExplicitObjectBuilder &)'
+// EXPLICIT-OBJECT-NOT: SwiftNameAttr
+
+// EXPLICIT-OBJECT-UNMATCHED-DAG: warning: API notes entry for 'buildExplicitConst' has unmatched Where.Object Object{Const: false, Ref: none}
+// EXPLICIT-OBJECT-UNMATCHED-DAG: warning: API notes entry for 'buildExplicitLValue' has unmatched Where.Object Object{Ref: lvalue}
+
// PARAM-REF: CXXMethodDecl {{.+}} consume 'void (ObjectBuffer &)'
// PARAM-REF: SwiftNameAttr {{.+}} "consumeBorrowed(_:)"
// PARAM-REF: CXXMethodDecl {{.+}} consume 'void (ObjectBuffer &&)'
>From 16decf3ed54882a4c2531b5056d92e219ae4c4c7 Mon Sep 17 00:00:00 2001
From: stoeckoverflow <dominic-st at gmx.de>
Date: Wed, 19 Aug 2026 10:16:10 +0200
Subject: [PATCH 4/6] [APINotes] Address object selector review nits
---
clang/include/clang/APINotes/Types.h | 10 ++++------
clang/lib/APINotes/APINotesFormat.h | 4 ++--
clang/lib/APINotes/APINotesReader.cpp | 2 +-
3 files changed, 7 insertions(+), 9 deletions(-)
diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h
index 2642e94bd12ec..ba5d2286757ad 100644
--- a/clang/include/clang/APINotes/Types.h
+++ b/clang/include/clang/APINotes/Types.h
@@ -1019,14 +1019,12 @@ struct FunctionObjectSelector {
std::string format() const;
};
-inline bool operator==(const FunctionObjectSelector &LHS,
- const FunctionObjectSelector &RHS) {
+inline bool operator==(FunctionObjectSelector LHS, FunctionObjectSelector RHS) {
return LHS.Const == RHS.Const && LHS.Volatile == RHS.Volatile &&
LHS.Ref == RHS.Ref;
}
-inline bool operator!=(const FunctionObjectSelector &LHS,
- const FunctionObjectSelector &RHS) {
+inline bool operator!=(FunctionObjectSelector LHS, FunctionObjectSelector RHS) {
return !(LHS == RHS);
}
@@ -1108,7 +1106,7 @@ inline std::string FunctionSelector::format() const {
}
struct FunctionTableSelectorKey {
- std::optional<llvm::SmallVector<IdentifierID, 2>> Parameters;
+ std::optional<llvm::SmallVector<IdentifierID, 4>> Parameters;
std::optional<FunctionObjectSelector> Object;
};
@@ -1119,7 +1117,7 @@ struct FunctionTableSelectorKey {
struct FunctionTableKey {
uint32_t parentContextID;
uint32_t nameID;
- std::optional<llvm::SmallVector<IdentifierID, 2>> parameterTypeIDs;
+ std::optional<llvm::SmallVector<IdentifierID, 4>> parameterTypeIDs;
std::optional<FunctionObjectSelector> objectSelector;
FunctionTableKey() : parentContextID(-1), nameID(-1) {}
diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h
index 2197e8e2f08c5..28f031320e4c6 100644
--- a/clang/lib/APINotes/APINotesFormat.h
+++ b/clang/lib/APINotes/APINotesFormat.h
@@ -412,7 +412,7 @@ std::optional<FunctionTableKey> getFunctionKeyImpl(
if (!NameID)
return std::nullopt;
- llvm::SmallVector<IdentifierID, 2> ParameterTypeIDs;
+ llvm::SmallVector<IdentifierID, 4> ParameterTypeIDs;
ParameterTypeIDs.reserve(Parameters.size());
for (const ParameterT &Parameter : Parameters) {
std::optional<IdentifierID> ParameterID =
@@ -437,7 +437,7 @@ std::optional<FunctionTableKey> getFunctionKeyImpl(
if (!NameID)
return std::nullopt;
- llvm::SmallVector<IdentifierID, 2> ParameterTypeIDs;
+ llvm::SmallVector<IdentifierID, 4> ParameterTypeIDs;
ParameterTypeIDs.reserve(Parameters.size());
for (const ParameterT &Parameter : Parameters) {
std::optional<IdentifierID> ParameterID =
diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp
index b0e786640490b..751d46123f532 100644
--- a/clang/lib/APINotes/APINotesReader.cpp
+++ b/clang/lib/APINotes/APINotesReader.cpp
@@ -66,7 +66,7 @@ static FunctionTableKey readFunctionTableKey(const uint8_t *Data,
FunctionTableKeyBaseLength + ParameterCount * sizeof(uint32_t) &&
"Unexpected function table key length");
- llvm::SmallVector<IdentifierID, 2> ParameterTypeIDs;
+ llvm::SmallVector<IdentifierID, 4> ParameterTypeIDs;
ParameterTypeIDs.reserve(ParameterCount);
for (unsigned I = 0; I != ParameterCount; ++I)
ParameterTypeIDs.push_back(
>From 94cb0a492a898fbc556c9d9498a46c4003c6c3ec Mon Sep 17 00:00:00 2001
From: stoeckoverflow <dominic-st at gmx.de>
Date: Wed, 19 Aug 2026 11:18:36 +0200
Subject: [PATCH 5/6] [APINotes] Simplify selector lookup APIs and ordering
comments
---
clang/include/clang/APINotes/APINotesReader.h | 41 +-----
clang/include/clang/APINotes/Types.h | 4 +
clang/lib/APINotes/APINotesFormat.h | 98 ++-----------
clang/lib/APINotes/APINotesReader.cpp | 133 ++----------------
clang/lib/Sema/SemaAPINotes.cpp | 22 +--
5 files changed, 50 insertions(+), 248 deletions(-)
diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h
index 99303c8a8ba02..ebfc1f8ecb9ca 100644
--- a/clang/include/clang/APINotes/APINotesReader.h
+++ b/clang/include/clang/APINotes/APINotesReader.h
@@ -160,37 +160,16 @@ class APINotesReader {
/// \param Name The name of the C++ method we're looking for.
///
/// \returns Information about the method, if known.
- VersionedInfo<CXXMethodInfo> lookupCXXMethod(ContextID CtxID,
- llvm::StringRef Name);
-
- /// Look for information regarding the given C++ method with an exact
- /// parameter selector. An empty parameter list uses an exact zero-parameter
- /// key, and a non-empty list uses an exact ordered parameter key.
- VersionedInfo<CXXMethodInfo>
- lookupCXXMethod(ContextID CtxID, llvm::StringRef Name,
- llvm::ArrayRef<std::string> Parameters);
-
/// Look for information regarding the given C++ method with a composed
/// selector. Omitted selector components use the broad name-based key.
VersionedInfo<CXXMethodInfo>
lookupCXXMethod(ContextID CtxID, llvm::StringRef Name,
- const FunctionSelector &Selector);
+ const FunctionSelector &Selector = FunctionSelector());
- /// Build the selector key for the given C++ method.
- std::optional<APINotesFunctionSelectorKey>
- getCXXMethodSelectorKey(ContextID CtxID, llvm::StringRef Name);
-
- /// Build the selector key for the given C++ method with an exact parameter
- /// selector.
- std::optional<APINotesFunctionSelectorKey>
- getCXXMethodSelectorKey(ContextID CtxID, llvm::StringRef Name,
- llvm::ArrayRef<std::string> Parameters);
-
- /// Build the selector key for the given C++ method with a composed
- /// selector.
- std::optional<APINotesFunctionSelectorKey>
- getCXXMethodSelectorKey(ContextID CtxID, llvm::StringRef Name,
- const FunctionSelector &Selector);
+ /// Build the selector key for the given C++ method with a composed selector.
+ std::optional<APINotesFunctionSelectorKey> getCXXMethodSelectorKey(
+ ContextID CtxID, llvm::StringRef Name,
+ const FunctionSelector &Selector = FunctionSelector());
/// Look for information regarding the given global variable.
///
@@ -286,19 +265,13 @@ class APINotesReader {
std::optional<ContextID> ParentNamespaceID = std::nullopt);
private:
- VersionedInfo<CXXMethodInfo> lookupCXXMethodImpl(ContextID CtxID,
- llvm::StringRef Name);
VersionedInfo<CXXMethodInfo>
lookupCXXMethodImpl(ContextID CtxID, llvm::StringRef Name,
const FunctionSelector &Selector);
VersionedInfo<GlobalFunctionInfo>
- lookupGlobalFunctionImpl(llvm::StringRef Name, std::optional<Context> Ctx);
- template <typename ParameterT>
- VersionedInfo<GlobalFunctionInfo>
- lookupGlobalFunctionImpl(llvm::StringRef Name,
- llvm::ArrayRef<ParameterT> Parameters,
- std::optional<Context> Ctx);
+ lookupGlobalFunctionImpl(llvm::StringRef Name, std::optional<Context> Ctx,
+ const FunctionSelector &Selector);
};
} // end namespace api_notes
diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h
index ba5d2286757ad..d30e35a383527 100644
--- a/clang/include/clang/APINotes/Types.h
+++ b/clang/include/clang/APINotes/Types.h
@@ -1065,6 +1065,10 @@ struct FunctionSelector {
std::optional<llvm::SmallVector<std::string, 4>> Parameters;
std::optional<FunctionObjectSelector> Object;
+ void setParameters(llvm::ArrayRef<std::string> NewParameters) {
+ Parameters.emplace(NewParameters.begin(), NewParameters.end());
+ }
+
std::string format() const;
};
diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h
index 28f031320e4c6..920f2bb4988f9 100644
--- a/clang/lib/APINotes/APINotesFormat.h
+++ b/clang/lib/APINotes/APINotesFormat.h
@@ -379,98 +379,28 @@ constexpr unsigned FunctionTableKeyBaseLength =
inline std::optional<FunctionTableKey> getFunctionKeyImpl(
uint32_t ParentContextID, llvm::StringRef Name,
+ const FunctionSelector &Selector,
llvm::function_ref<std::optional<IdentifierID>(llvm::StringRef)>
GetIdentifier) {
std::optional<IdentifierID> NameID = GetIdentifier(Name);
if (!NameID)
return std::nullopt;
- return FunctionTableKey(ParentContextID, *NameID);
-}
-
-inline std::optional<FunctionTableKey> getFunctionKeyImpl(
- uint32_t ParentContextID, llvm::StringRef Name,
- FunctionObjectSelector ObjectSelector,
- llvm::function_ref<std::optional<IdentifierID>(llvm::StringRef)>
- GetIdentifier) {
- std::optional<IdentifierID> NameID = GetIdentifier(Name);
- if (!NameID)
- return std::nullopt;
-
- FunctionTableSelectorKey Selector;
- Selector.Object = ObjectSelector;
- return FunctionTableKey(ParentContextID, *NameID, std::move(Selector));
-}
-
-template <typename ParameterT>
-std::optional<FunctionTableKey> getFunctionKeyImpl(
- uint32_t ParentContextID, llvm::StringRef Name,
- llvm::ArrayRef<ParameterT> Parameters,
- llvm::function_ref<std::optional<IdentifierID>(llvm::StringRef)>
- GetIdentifier) {
- std::optional<IdentifierID> NameID = GetIdentifier(Name);
- if (!NameID)
- return std::nullopt;
-
- llvm::SmallVector<IdentifierID, 4> ParameterTypeIDs;
- ParameterTypeIDs.reserve(Parameters.size());
- for (const ParameterT &Parameter : Parameters) {
- std::optional<IdentifierID> ParameterID =
- GetIdentifier(llvm::StringRef(Parameter));
- if (!ParameterID)
- return std::nullopt;
- ParameterTypeIDs.push_back(*ParameterID);
- }
- FunctionTableSelectorKey Selector;
- Selector.Parameters.emplace(ParameterTypeIDs.begin(), ParameterTypeIDs.end());
- return FunctionTableKey(ParentContextID, *NameID, std::move(Selector));
-}
-
-template <typename ParameterT>
-std::optional<FunctionTableKey> getFunctionKeyImpl(
- uint32_t ParentContextID, llvm::StringRef Name,
- llvm::ArrayRef<ParameterT> Parameters,
- FunctionObjectSelector ObjectSelector,
- llvm::function_ref<std::optional<IdentifierID>(llvm::StringRef)>
- GetIdentifier) {
- std::optional<IdentifierID> NameID = GetIdentifier(Name);
- if (!NameID)
- return std::nullopt;
-
- llvm::SmallVector<IdentifierID, 4> ParameterTypeIDs;
- ParameterTypeIDs.reserve(Parameters.size());
- for (const ParameterT &Parameter : Parameters) {
- std::optional<IdentifierID> ParameterID =
- GetIdentifier(llvm::StringRef(Parameter));
- if (!ParameterID)
- return std::nullopt;
- ParameterTypeIDs.push_back(*ParameterID);
- }
- FunctionTableSelectorKey Selector;
- Selector.Parameters.emplace(ParameterTypeIDs.begin(), ParameterTypeIDs.end());
- Selector.Object = ObjectSelector;
- return FunctionTableKey(ParentContextID, *NameID, std::move(Selector));
-}
-
-inline std::optional<FunctionTableKey> getFunctionKeyImpl(
- uint32_t ParentContextID, llvm::StringRef Name,
- const FunctionSelector &Selector,
- llvm::function_ref<std::optional<IdentifierID>(llvm::StringRef)>
- GetIdentifier) {
+ FunctionTableSelectorKey KeySelector;
if (Selector.Parameters) {
- if (Selector.Object)
- return getFunctionKeyImpl(
- ParentContextID, Name,
- llvm::ArrayRef<std::string>(*Selector.Parameters), *Selector.Object,
- GetIdentifier);
- return getFunctionKeyImpl(ParentContextID, Name,
- llvm::ArrayRef<std::string>(*Selector.Parameters),
- GetIdentifier);
+ llvm::SmallVector<IdentifierID, 4> ParameterTypeIDs;
+ ParameterTypeIDs.reserve(Selector.Parameters->size());
+ for (const std::string &Parameter : *Selector.Parameters) {
+ std::optional<IdentifierID> ParameterID = GetIdentifier(Parameter);
+ if (!ParameterID)
+ return std::nullopt;
+ ParameterTypeIDs.push_back(*ParameterID);
+ }
+ KeySelector.Parameters.emplace(ParameterTypeIDs.begin(),
+ ParameterTypeIDs.end());
}
- if (Selector.Object)
- return getFunctionKeyImpl(ParentContextID, Name, *Selector.Object,
- GetIdentifier);
- return getFunctionKeyImpl(ParentContextID, Name, GetIdentifier);
+ KeySelector.Object = Selector.Object;
+ return FunctionTableKey(ParentContextID, *NameID, std::move(KeySelector));
}
} // namespace api_notes
diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp
index 751d46123f532..36f4e89bfe640 100644
--- a/clang/lib/APINotes/APINotesReader.cpp
+++ b/clang/lib/APINotes/APINotesReader.cpp
@@ -906,24 +906,12 @@ class APINotesReader::Implementation {
llvm::SmallVectorImpl<uint64_t> &Scratch);
llvm::Error readGlobalVariableBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
- std::optional<FunctionTableKey> getFunctionKey(uint32_t ParentContextID,
- llvm::StringRef Name);
- template <typename ParameterT>
std::optional<FunctionTableKey>
getFunctionKey(uint32_t ParentContextID, llvm::StringRef Name,
- llvm::ArrayRef<ParameterT> Parameters);
- std::optional<FunctionTableKey>
- getFunctionKey(uint32_t ParentContextID, llvm::StringRef Name,
- const FunctionSelector &Selector);
- std::optional<FunctionTableKey>
- getFunctionKey(std::optional<Context> ParentContext, llvm::StringRef Name);
- template <typename ParameterT>
- std::optional<FunctionTableKey>
- getFunctionKey(std::optional<Context> ParentContext, llvm::StringRef Name,
- llvm::ArrayRef<ParameterT> Parameters);
+ const FunctionSelector &Selector = FunctionSelector());
std::optional<FunctionTableKey>
getFunctionKey(std::optional<Context> ParentContext, llvm::StringRef Name,
- const FunctionSelector &Selector);
+ const FunctionSelector &Selector = FunctionSelector());
llvm::Error readGlobalFunctionBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
@@ -998,23 +986,6 @@ void APINotesReader::Implementation::collectFunctionSelectorsForDiagnostics(
}
}
-std::optional<FunctionTableKey>
-APINotesReader::Implementation::getFunctionKey(uint32_t ParentContextID,
- llvm::StringRef Name) {
- return getFunctionKeyImpl(ParentContextID, Name, [this](llvm::StringRef S) {
- return getIdentifier(S);
- });
-}
-
-template <typename ParameterT>
-std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey(
- uint32_t ParentContextID, llvm::StringRef Name,
- llvm::ArrayRef<ParameterT> Parameters) {
- return getFunctionKeyImpl(
- ParentContextID, Name, Parameters,
- [this](llvm::StringRef S) { return getIdentifier(S); });
-}
-
std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey(
uint32_t ParentContextID, llvm::StringRef Name,
const FunctionSelector &Selector) {
@@ -1023,22 +994,6 @@ std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey(
[this](llvm::StringRef S) { return getIdentifier(S); });
}
-std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey(
- std::optional<Context> ParentContext, llvm::StringRef Name) {
- uint32_t ParentContextID =
- ParentContext ? ParentContext->id.Value : static_cast<uint32_t>(-1);
- return getFunctionKey(ParentContextID, Name);
-}
-
-template <typename ParameterT>
-std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey(
- std::optional<Context> ParentContext, llvm::StringRef Name,
- llvm::ArrayRef<ParameterT> Parameters) {
- uint32_t ParentContextID =
- ParentContext ? ParentContext->id.Value : static_cast<uint32_t>(-1);
- return getFunctionKey(ParentContextID, Name, Parameters);
-}
-
std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey(
std::optional<Context> ParentContext, llvm::StringRef Name,
const FunctionSelector &Selector) {
@@ -2461,45 +2416,12 @@ auto APINotesReader::lookupField(ContextID CtxID, llvm::StringRef Name)
return {Implementation->SwiftVersion, *Known};
}
-auto APINotesReader::lookupCXXMethod(ContextID CtxID, llvm::StringRef Name)
- -> VersionedInfo<CXXMethodInfo> {
- return lookupCXXMethodImpl(CtxID, Name);
-}
-
-auto APINotesReader::lookupCXXMethod(ContextID CtxID, llvm::StringRef Name,
- llvm::ArrayRef<std::string> Parameters)
- -> VersionedInfo<CXXMethodInfo> {
- FunctionSelector Selector;
- Selector.Parameters.emplace(Parameters.begin(), Parameters.end());
- return lookupCXXMethodImpl(CtxID, Name, Selector);
-}
-
auto APINotesReader::lookupCXXMethod(ContextID CtxID, llvm::StringRef Name,
const FunctionSelector &Selector)
-> VersionedInfo<CXXMethodInfo> {
return lookupCXXMethodImpl(CtxID, Name, Selector);
}
-std::optional<APINotesFunctionSelectorKey>
-APINotesReader::getCXXMethodSelectorKey(ContextID CtxID, llvm::StringRef Name) {
- std::optional<FunctionTableKey> Key =
- Implementation->getFunctionKey(CtxID.Value, Name);
- if (!Key)
- return std::nullopt;
- return APINotesFunctionSelectorKey{*Key, /*IsCXXMethod=*/true};
-}
-
-std::optional<APINotesFunctionSelectorKey>
-APINotesReader::getCXXMethodSelectorKey(
- ContextID CtxID, llvm::StringRef Name,
- llvm::ArrayRef<std::string> Parameters) {
- std::optional<FunctionTableKey> Key =
- Implementation->getFunctionKey(CtxID.Value, Name, Parameters);
- if (!Key)
- return std::nullopt;
- return APINotesFunctionSelectorKey{*Key, /*IsCXXMethod=*/true};
-}
-
std::optional<APINotesFunctionSelectorKey>
APINotesReader::getCXXMethodSelectorKey(ContextID CtxID, llvm::StringRef Name,
const FunctionSelector &Selector) {
@@ -2510,23 +2432,6 @@ APINotesReader::getCXXMethodSelectorKey(ContextID CtxID, llvm::StringRef Name,
return APINotesFunctionSelectorKey{*Key, /*IsCXXMethod=*/true};
}
-auto APINotesReader::lookupCXXMethodImpl(ContextID CtxID, llvm::StringRef Name)
- -> VersionedInfo<CXXMethodInfo> {
- if (!Implementation->CXXMethodTable)
- return std::nullopt;
-
- std::optional<FunctionTableKey> Key =
- Implementation->getFunctionKey(CtxID.Value, Name);
- if (!Key)
- return std::nullopt;
-
- auto Known = Implementation->CXXMethodTable->find(*Key);
- if (Known == Implementation->CXXMethodTable->end())
- return std::nullopt;
-
- return {Implementation->SwiftVersion, *Known};
-}
-
auto APINotesReader::lookupCXXMethodImpl(ContextID CtxID, llvm::StringRef Name,
const FunctionSelector &Selector)
-> VersionedInfo<CXXMethodInfo> {
@@ -2567,13 +2472,15 @@ auto APINotesReader::lookupGlobalVariable(llvm::StringRef Name,
auto APINotesReader::lookupGlobalFunction(llvm::StringRef Name,
std::optional<Context> Ctx)
-> VersionedInfo<GlobalFunctionInfo> {
- return lookupGlobalFunctionImpl(Name, Ctx);
+ return lookupGlobalFunctionImpl(Name, Ctx, FunctionSelector());
}
auto APINotesReader::lookupGlobalFunction(
llvm::StringRef Name, llvm::ArrayRef<std::string> Parameters,
std::optional<Context> Ctx) -> VersionedInfo<GlobalFunctionInfo> {
- return lookupGlobalFunctionImpl(Name, Parameters, Ctx);
+ FunctionSelector Selector;
+ Selector.setParameters(Parameters);
+ return lookupGlobalFunctionImpl(Name, Ctx, Selector);
}
std::optional<APINotesFunctionSelectorKey>
@@ -2590,8 +2497,10 @@ std::optional<APINotesFunctionSelectorKey>
APINotesReader::getGlobalFunctionSelectorKey(
llvm::StringRef Name, llvm::ArrayRef<std::string> Parameters,
std::optional<Context> Ctx) {
+ FunctionSelector Selector;
+ Selector.setParameters(Parameters);
std::optional<FunctionTableKey> Key =
- Implementation->getFunctionKey(Ctx, Name, Parameters);
+ Implementation->getFunctionKey(Ctx, Name, Selector);
if (!Key)
return std::nullopt;
return APINotesFunctionSelectorKey{*Key, /*IsCXXMethod=*/false};
@@ -2626,32 +2535,14 @@ APINotesReader::getParameterSelectorSpellingsForDiagnostics(
}
auto APINotesReader::lookupGlobalFunctionImpl(llvm::StringRef Name,
- std::optional<Context> Ctx)
+ std::optional<Context> Ctx,
+ const FunctionSelector &Selector)
-> VersionedInfo<GlobalFunctionInfo> {
if (!Implementation->GlobalFunctionTable)
return std::nullopt;
std::optional<FunctionTableKey> Key =
- Implementation->getFunctionKey(Ctx, Name);
- if (!Key)
- return std::nullopt;
-
- auto Known = Implementation->GlobalFunctionTable->find(*Key);
- if (Known == Implementation->GlobalFunctionTable->end())
- return std::nullopt;
-
- return {Implementation->SwiftVersion, *Known};
-}
-
-template <typename ParameterT>
-auto APINotesReader::lookupGlobalFunctionImpl(
- llvm::StringRef Name, llvm::ArrayRef<ParameterT> Parameters,
- std::optional<Context> Ctx) -> VersionedInfo<GlobalFunctionInfo> {
- if (!Implementation->GlobalFunctionTable)
- return std::nullopt;
-
- std::optional<FunctionTableKey> Key =
- Implementation->getFunctionKey(Ctx, Name, Parameters);
+ Implementation->getFunctionKey(Ctx, Name, Selector);
if (!Key)
return std::nullopt;
diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp
index 5c6ec83b12701..c104992227bae 100644
--- a/clang/lib/Sema/SemaAPINotes.cpp
+++ b/clang/lib/Sema/SemaAPINotes.cpp
@@ -1451,13 +1451,17 @@ void Sema::ProcessAPINotes(Decl *D) {
processExactAPINotes<api_notes::CXXMethodInfo>(
*this, CXXMethod, *ParameterSelectorCandidates,
[&](ArrayRef<std::string> Parameters) {
+ api_notes::FunctionSelector Selector;
+ Selector.setParameters(Parameters);
return Reader->lookupCXXMethod(Context->id, MethodName,
- Parameters);
+ Selector);
});
DiagnosticState.markCandidatesUsed(
[&](ArrayRef<std::string> Parameters) {
+ api_notes::FunctionSelector Selector;
+ Selector.setParameters(Parameters);
return Reader->getCXXMethodSelectorKey(
- Context->id, MethodName, Parameters);
+ Context->id, MethodName, Selector);
},
*ParameterSelectorCandidates);
}
@@ -1466,9 +1470,11 @@ void Sema::ProcessAPINotes(Decl *D) {
SmallVector<api_notes::FunctionObjectSelector, 7> ObjectSelectors;
getAPINotesObjectSelectorSubsets(
getAPINotesObjectSelector(CXXMethod), ObjectSelectors);
- // Apply every matching object selector in increasing specificity.
- // Wildcard object constraints are broad refinements. Selectors
- // that also constrain explicit parameters are applied last.
+ // Apply broad object-selector matches before more specific ones.
+ // For example, Object:{Const:true} can provide defaults for all
+ // const methods. Object:{Const:true, Ref:lvalue} is more
+ // specific for a const lvalue-ref-qualified method, so if both
+ // notes set the same field, the more specific selector wins.
for (api_notes::FunctionObjectSelector ObjectSelector :
ObjectSelectors) {
api_notes::FunctionSelector Selector;
@@ -1488,8 +1494,7 @@ void Sema::ProcessAPINotes(Decl *D) {
*this, CXXMethod, *ParameterSelectorCandidates,
[&](ArrayRef<std::string> Parameters) {
api_notes::FunctionSelector Selector;
- Selector.Parameters.emplace(Parameters.begin(),
- Parameters.end());
+ Selector.setParameters(Parameters);
Selector.Object = ObjectSelector;
return Reader->lookupCXXMethod(Context->id, MethodName,
Selector);
@@ -1497,8 +1502,7 @@ void Sema::ProcessAPINotes(Decl *D) {
DiagnosticState.markCandidatesUsed(
[&](ArrayRef<std::string> Parameters) {
api_notes::FunctionSelector Selector;
- Selector.Parameters.emplace(Parameters.begin(),
- Parameters.end());
+ Selector.setParameters(Parameters);
Selector.Object = ObjectSelector;
return Reader->getCXXMethodSelectorKey(
Context->id, MethodName, Selector);
>From 3fdcb172dbb9a80328b124f642576dec3468e410 Mon Sep 17 00:00:00 2001
From: stoeckoverflow <dominic-st at gmx.de>
Date: Wed, 19 Aug 2026 12:00:42 +0200
Subject: [PATCH 6/6] [APINotes] Simplify object selector lookup paths
---
clang/include/clang/APINotes/APINotesReader.h | 8 +-
clang/lib/Sema/SemaAPINotes.cpp | 82 ++++++++-----------
2 files changed, 38 insertions(+), 52 deletions(-)
diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h
index ebfc1f8ecb9ca..518f7d4690614 100644
--- a/clang/include/clang/APINotes/APINotesReader.h
+++ b/clang/include/clang/APINotes/APINotesReader.h
@@ -153,15 +153,15 @@ class APINotesReader {
/// \returns information about the field, if known.
VersionedInfo<FieldInfo> lookupField(ContextID CtxID, llvm::StringRef Name);
- /// Look for information regarding the given C++ method in the given C++ tag
- /// context.
+ /// Look for information regarding the given C++ method with a composed
+ /// selector in the given C++ tag context.
///
/// \param CtxID The ID that references the parent context, i.e. a C++ tag.
/// \param Name The name of the C++ method we're looking for.
///
+ /// Omitted selector components use the broad name-based key.
+ ///
/// \returns Information about the method, if known.
- /// Look for information regarding the given C++ method with a composed
- /// selector. Omitted selector components use the broad name-based key.
VersionedInfo<CXXMethodInfo>
lookupCXXMethod(ContextID CtxID, llvm::StringRef Name,
const FunctionSelector &Selector = FunctionSelector());
diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp
index c104992227bae..0947d1de0ff37 100644
--- a/clang/lib/Sema/SemaAPINotes.cpp
+++ b/clang/lib/Sema/SemaAPINotes.cpp
@@ -1447,68 +1447,54 @@ void Sema::ProcessAPINotes(Decl *D) {
DiagnosticState.noteSeenDeclaration(*NameOnlyKey, MethodName,
CXXMethod->getLocation());
- if (ParameterSelectorCandidates) {
- processExactAPINotes<api_notes::CXXMethodInfo>(
- *this, CXXMethod, *ParameterSelectorCandidates,
- [&](ArrayRef<std::string> Parameters) {
- api_notes::FunctionSelector Selector;
- Selector.setParameters(Parameters);
- return Reader->lookupCXXMethod(Context->id, MethodName,
- Selector);
- });
- DiagnosticState.markCandidatesUsed(
- [&](ArrayRef<std::string> Parameters) {
- api_notes::FunctionSelector Selector;
- Selector.setParameters(Parameters);
- return Reader->getCXXMethodSelectorKey(
- Context->id, MethodName, Selector);
- },
- *ParameterSelectorCandidates);
- }
-
+ SmallVector<api_notes::FunctionSelector, 8> BaseSelectors;
+ BaseSelectors.emplace_back();
if (CXXMethod->isImplicitObjectMemberFunction()) {
SmallVector<api_notes::FunctionObjectSelector, 7> ObjectSelectors;
getAPINotesObjectSelectorSubsets(
getAPINotesObjectSelector(CXXMethod), ObjectSelectors);
- // Apply broad object-selector matches before more specific ones.
- // For example, Object:{Const:true} can provide defaults for all
- // const methods. Object:{Const:true, Ref:lvalue} is more
- // specific for a const lvalue-ref-qualified method, so if both
- // notes set the same field, the more specific selector wins.
for (api_notes::FunctionObjectSelector ObjectSelector :
ObjectSelectors) {
api_notes::FunctionSelector Selector;
Selector.Object = ObjectSelector;
- auto ObjectInfo =
- Reader->lookupCXXMethod(Context->id, MethodName, Selector);
+ BaseSelectors.push_back(std::move(Selector));
+ }
+ }
+
+ // Apply less constrained object selectors first, then more
+ // constrained ones. This gives specific selectors refinement
+ // precedence over broad defaults. For example,
+ // Object:{Const:true} can provide defaults for all const methods,
+ // while Object:{Const:true, Ref:lvalue} can override them for
+ // const lvalue methods.
+ for (const api_notes::FunctionSelector &BaseSelector :
+ BaseSelectors) {
+ if (BaseSelector.Object) {
+ auto ObjectInfo = Reader->lookupCXXMethod(
+ Context->id, MethodName, BaseSelector);
ProcessVersionedAPINotes(*this, CXXMethod, ObjectInfo);
if (auto ObjectKey = Reader->getCXXMethodSelectorKey(
- Context->id, MethodName, Selector))
+ Context->id, MethodName, BaseSelector))
DiagnosticState.markUsed(*ObjectKey);
}
if (ParameterSelectorCandidates) {
- for (api_notes::FunctionObjectSelector ObjectSelector :
- ObjectSelectors) {
- processExactAPINotes<api_notes::CXXMethodInfo>(
- *this, CXXMethod, *ParameterSelectorCandidates,
- [&](ArrayRef<std::string> Parameters) {
- api_notes::FunctionSelector Selector;
- Selector.setParameters(Parameters);
- Selector.Object = ObjectSelector;
- return Reader->lookupCXXMethod(Context->id, MethodName,
- Selector);
- });
- DiagnosticState.markCandidatesUsed(
- [&](ArrayRef<std::string> Parameters) {
- api_notes::FunctionSelector Selector;
- Selector.setParameters(Parameters);
- Selector.Object = ObjectSelector;
- return Reader->getCXXMethodSelectorKey(
- Context->id, MethodName, Selector);
- },
- *ParameterSelectorCandidates);
- }
+ processExactAPINotes<api_notes::CXXMethodInfo>(
+ *this, CXXMethod, *ParameterSelectorCandidates,
+ [&](ArrayRef<std::string> Parameters) {
+ api_notes::FunctionSelector Selector = BaseSelector;
+ Selector.setParameters(Parameters);
+ return Reader->lookupCXXMethod(Context->id, MethodName,
+ Selector);
+ });
+ DiagnosticState.markCandidatesUsed(
+ [&](ArrayRef<std::string> Parameters) {
+ api_notes::FunctionSelector Selector = BaseSelector;
+ Selector.setParameters(Parameters);
+ return Reader->getCXXMethodSelectorKey(
+ Context->id, MethodName, Selector);
+ },
+ *ParameterSelectorCandidates);
}
}
}
More information about the cfe-commits
mailing list