[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