https://github.com/StoeckOverflow created
https://github.com/llvm/llvm-project/pull/216148
This PR adds `Where.Object` selectors for C++ method API notes. They let API
notes distinguish overloads by the implicit object parameter qualifiers, such
as `const`, `volatile`, `&`, and `&&`. Static methods are unchanged because
they have no implicit object parameter.
```cpp
struct Builder {
Result build() &;
Result build() &&;
Result build() const &;
};
```
To distinguish these declarations, the selector model includes an `Object`
constraint:
```yaml
Tags:
- Name: Builder
Methods:
- Name: build
Where:
Parameters: []
Object:
Ref: lvalue
SwiftName: buildFromLValue()
- Name: build
Where:
Parameters: []
Object:
Ref: rvalue
SwiftName: buildFromRValue()
- Name: build
Where:
Parameters: []
Object:
Const: true
Ref: lvalue
SwiftName: buildFromConstLValue()
```
Changes:
- Adds `Where.Object` YAML support for C++ methods.
- Adds a shared object-selector representation with `Const`, `Volatile`, and
`Ref`.
- Extends C++ method lookup/writer keys to include object selectors.
- Applies less-constrained object selectors before more-constrained selectors
so specific notes can refine broader ones.
- Extends unmatched-selector diagnostics to track object-only and
parameter-plus-object selectors.
- Keeps global functions and static C++ methods unchanged.
Tests:
- Matching `const`, `volatile`, lvalue-ref, and rvalue-ref qualified C++
methods.
- Combined object qualifiers such as `const volatile &`, `const &&`, and
`volatile &&`.
- Coexistence of broad method notes with object-qualified notes.
- Object selector diagnostics for malformed, duplicate, and unmatched entries.
- Static methods remain unaffected because they have no implicit object
parameter.
Reviewers: @Xazax-hun @j-hui @egorzhdan
>From 42b6514b148da69dbd814eeeba9cb8e1742063fb Mon Sep 17 00:00:00 2001
From: stoeckoverflow <[email protected]>
Date: Tue, 11 Aug 2026 11:54:31 +0200
Subject: [PATCH] [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}
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits