https://github.com/artemcm updated 
https://github.com/llvm/llvm-project/pull/224860

>From 3e708bf47cd0e2f750c57753f284a97c7c27d4fd Mon Sep 17 00:00:00 2001
From: Artem Chikin <[email protected]>
Date: Mon, 21 Sep 2026 10:30:00 +0100
Subject: [PATCH 1/2] [APINotes][Unversioned] Group versioned slices by lookup
 rather than by reader

Under `-fswift-version-independent-apinotes`, Clang attaches every APINotes 
slice unapplied, wrapped in `SwiftVersionedAdditionAttr` or 
`SwiftVersionedRemovalAttr`, and leaves the version selection to the client. To 
redo that selection the client has to know which slices compete with each other 
across different APINote lookups, and nothing in the attributes said so.

The slices a client must choose between are the ones a single *lookup* 
produced, not the ones a single APINotes reader supplied. Sema runs selection 
once per lookup and applies every winner, and there can be more than one lookup 
for the same declaration against the same reader. A global function with a 
`Where:` parameter selector gets two: the broad lookup, and the exact one.

For example, consider module `Bar`:
```
Name: Bar
Functions:
  - Name: overloaded
    SwiftName: 'broadBase()'      # the broad lookup, unversioned
  - Name: overloaded
    Where:
      Parameters:
        - int
    SwiftName: 'exactBase(_:)'    # the exact lookup, unversioned
SwiftVersions:
  - Version: 4
    Functions:
      - Name: overloaded
        SwiftName: 'broadFour()'  # the broad lookup, 4.0 slice
```

Each lookup selects its own winner and both are applied, so a client at version 
3.0 must end up with `broadFour()` from the broad lookup and `exactBase(_:)` 
from the exact one.

If the two lookups were labelled with the reader they share, the client would 
instead reconstruct one competition:
- Unversioned: SwiftNameAttr "broadBase()"
- Unversioned: SwiftNameAttr "exactBase(_:)"
- 4.0: SwiftNameAttr "broadFour()"
and select a single winner, dropping one of the two lookups entirely.

Instead, this change makes the attributes carry a slice group. One group is one 
lookup: a second reader is a different lookup, and so is a parameter selector 
query beside a broad one.

Sema applies each group's winner in turn, and when two winners set the same key 
the last one applied owns it, so the order the groups were applied in is part 
of what a consumer has to reproduce. Both facts were previously implicit in 
Sema's control flow and discarded, because legacy mode selected in-process and 
only the winner survived onto the declaration.

The slice group numbering interleaves the two lookups per reader, giving reader 
`i` the groups `2 * i` and `2 * i + 1`, so ascending group order is application 
order. Numbering all the broad lookups before all the selector lookups would 
instead apply them in the order 0, 2, 1, 3, and a consumer sorting on the 
ordinal would pick the wrong winner.
---
 clang/include/clang/Basic/Attr.td             |  33 +++-
 clang/lib/Sema/SemaAPINotes.cpp               | 154 ++++++++++++------
 .../APINotes/Inputs/Headers/ExportAs.apinotes |  15 ++
 .../Inputs/Headers/ExportAsCore.apinotes      |  20 +++
 .../APINotes/Inputs/Headers/ExportAsCore.h    |  11 ++
 .../Inputs/Headers/SliceGroupsExact.apinotes  |  19 +++
 .../Inputs/Headers/SliceGroupsExact.h         |   3 +
 .../APINotes/Inputs/Headers/module.modulemap  |   4 +
 clang/test/APINotes/properties.m              |  12 +-
 clang/test/APINotes/slice-groups-exact.c      |  35 ++++
 clang/test/APINotes/slice-groups-order.c      |  47 ++++++
 clang/test/APINotes/slice-groups.c            |  33 ++++
 .../APINotes/versioned-version-independent.m  |  16 +-
 clang/test/APINotes/versioned.m               |  20 +--
 14 files changed, 352 insertions(+), 70 deletions(-)
 create mode 100644 clang/test/APINotes/Inputs/Headers/ExportAsCore.apinotes
 create mode 100644 clang/test/APINotes/Inputs/Headers/SliceGroupsExact.apinotes
 create mode 100644 clang/test/APINotes/Inputs/Headers/SliceGroupsExact.h
 create mode 100644 clang/test/APINotes/slice-groups-exact.c
 create mode 100644 clang/test/APINotes/slice-groups-order.c
 create mode 100644 clang/test/APINotes/slice-groups.c

diff --git a/clang/include/clang/Basic/Attr.td 
b/clang/include/clang/Basic/Attr.td
index 569475f4f74fea..1ece6d5f34b29e 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3336,9 +3336,34 @@ def SwiftPrivate : InheritableAttr {
 def SwiftVersionedAddition : Attr {
   // This attribute has no spellings as it is only ever created implicitly
   // from API notes.
+  //
+  // A "slice" is one element of an APINotesReader::VersionedInfo: the
+  // annotations that one version of a sidecar '.apinotes' file supplies for 
one
+  // declaration, paired with that version. An unversioned section supplies a
+  // slice whose version is empty.
+  //
+  // A "group" is all the slices one lookup returned, which is one whole
+  // VersionedInfo. Clang selects at most one slice per group and applies it, 
so
+  // a slice can only suppress other slices in its own group.
+  //
+  // SliceGroup says which group this slice came from.
+  //
+  // This is needed because capture mode (version-independent apinotes) 
attaches
+  // every slice from every lookup to the same declaration, which erases the
+  // VersionedInfo boundaries. SliceGroup restores them, so a consumer redoes
+  // the selection once per group instead of pooling unrelated slices.
+  // One group is one lookup: a second API notes reader is a different lookup,
+  // and so is an exact parameter-selector query beside a broad one.
+  //
+  // Group numbers also carry precedence. Clang applies each group's winner in
+  // ascending group order, and when two winners set the same key the last one
+  // applied owns it. So a consumer partitions by group, selects within each
+  // group, then resolves any remaining collision in ascending group order.
+  //
   let Spellings = [];
   let Args = [VersionArgument<"Version">, WrappedAttr<"AdditionalAttr">,
-              BoolArgument<"IsReplacedByActive">];
+              BoolArgument<"IsReplacedByActive">,
+              UnsignedArgument<"SliceGroup">];
   let SemaHandler = 0;
   let Documentation = [InternalOnly];
 }
@@ -3346,9 +3371,13 @@ def SwiftVersionedAddition : Attr {
 def SwiftVersionedRemoval : Attr {
   // This attribute has no spellings as it is only ever created implicitly
   // from API notes.
+  //
+  // SliceGroup says which lookup this slice belongs to. See the
+  // SwiftVersionedAddition comment above.
   let Spellings = [];
   let Args = [VersionArgument<"Version">, UnsignedArgument<"RawKind">,
-              BoolArgument<"IsReplacedByActive">];
+              BoolArgument<"IsReplacedByActive">,
+              UnsignedArgument<"SliceGroup">];
   let SemaHandler = 0;
   let Documentation = [InternalOnly];
   let AdditionalMembers = [{
diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp
index 4c7e5ea16cfd7d..3e5e6d56f0b4ea 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/STLExtras.h"
 #include <stack>
 
 using namespace clang;
@@ -34,16 +35,38 @@ enum class IsSubstitution_t : bool { Original, Replacement 
};
 struct VersionedInfoMetadata {
   /// An empty version refers to unversioned metadata.
   VersionTuple Version;
+  /// Which lookup group this slice came from.
+  /// See the SwiftVersionedAddition comment in Attr.td.
+  unsigned SliceGroup;
   unsigned IsActive : 1;
   unsigned IsReplacement : 1;
 
-  VersionedInfoMetadata(VersionTuple Version, IsActive_t Active,
-                        IsSubstitution_t Replacement)
-      : Version(Version), IsActive(Active == IsActive_t::Active),
+  VersionedInfoMetadata(VersionTuple Version, unsigned SliceGroup,
+                        IsActive_t Active, IsSubstitution_t Replacement)
+      : Version(Version), SliceGroup(SliceGroup),
+        IsActive(Active == IsActive_t::Active),
         IsReplacement(Replacement == IsSubstitution_t::Replacement) {}
 };
 } // end anonymous namespace
 
+/// The slice lookup groups a declaration can receive, numbered so that
+/// ascending group order is the order Sema applies them in.
+///
+/// Sema makes up to two lookups per API notes reader. The broad lookup matches
+/// on the declaration's name alone. The parameter-selector lookup matches on
+/// the name plus a `Where: Parameters:` entry, and runs only for a declaration
+/// that has a parameter selector. Sema applies them reader by reader, broad
+/// first, so giving each reader an adjacent pair keeps the ordinals in
+/// application order. A consumer needs that order to resolve two groups whose
+/// winners set the same key.
+static unsigned broadSliceGroup(unsigned ReaderIndex) {
+  return 2 * ReaderIndex;
+}
+
+static unsigned parameterSelectorSliceGroup(unsigned ReaderIndex) {
+  return 2 * ReaderIndex + 1;
+}
+
 /// Determine whether this is a multi-level pointer type.
 static bool isIndirectPointerType(QualType Type) {
   QualType Pointee = Type->getPointeeType();
@@ -65,7 +88,8 @@ static void applyAPINotesType(Sema &S, Decl *decl, StringRef 
typeString,
   if (S.captureSwiftVersionIndependentAPINotes()) {
     auto *typeAttr = SwiftTypeAttr::CreateImplicit(S.Context, typeString);
     auto *versioned = SwiftVersionedAdditionAttr::CreateImplicit(
-        S.Context, metadata.Version, typeAttr, metadata.IsReplacement);
+        S.Context, metadata.Version, typeAttr, metadata.IsReplacement,
+        metadata.SliceGroup);
     decl->addAttr(versioned);
   } else {
     if (!metadata.IsActive)
@@ -98,7 +122,8 @@ static void applyNullability(Sema &S, Decl *decl, 
NullabilityKind nullability,
     auto *nullabilityAttr =
         SwiftNullabilityAttr::CreateImplicit(S.Context, attrNullabilityKind);
     auto *versioned = SwiftVersionedAdditionAttr::CreateImplicit(
-        S.Context, metadata.Version, nullabilityAttr, metadata.IsReplacement);
+        S.Context, metadata.Version, nullabilityAttr, metadata.IsReplacement,
+        metadata.SliceGroup);
     decl->addAttr(versioned);
     return;
   } else {
@@ -149,7 +174,8 @@ void handleAPINotedAttribute(
       // Remove the existing attribute, and treat it as a superseded
       // non-versioned attribute.
       auto *Versioned = SwiftVersionedAdditionAttr::CreateImplicit(
-          S.Context, Metadata.Version, *Existing, /*IsReplacedByActive*/ true);
+          S.Context, Metadata.Version, *Existing, /*IsReplacedByActive*/ true,
+          Metadata.SliceGroup);
 
       D->getAttrs().erase(Existing);
       D->addAttr(Versioned);
@@ -167,7 +193,7 @@ void handleAPINotedAttribute(
     if (auto Attr = CreateAttr()) {
       auto *Versioned = SwiftVersionedAdditionAttr::CreateImplicit(
           S.Context, Metadata.Version, Attr,
-          /*IsReplacedByActive*/ Metadata.IsReplacement);
+          /*IsReplacedByActive*/ Metadata.IsReplacement, Metadata.SliceGroup);
       D->addAttr(Versioned);
     }
   } else {
@@ -176,7 +202,7 @@ void handleAPINotedAttribute(
     // attribute.
     auto *Versioned = SwiftVersionedRemovalAttr::CreateImplicit(
         S.Context, Metadata.Version, AttrKindFor<A>::value,
-        /*IsReplacedByActive*/ Metadata.IsReplacement);
+        /*IsReplacedByActive*/ Metadata.IsReplacement, Metadata.SliceGroup);
     D->addAttr(Versioned);
   }
 }
@@ -872,7 +898,8 @@ static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D,
 template <typename SpecificInfo>
 static void maybeAttachUnversionedSwiftName(
     Sema &S, Decl *D,
-    const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info) {
+    const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info,
+    unsigned SliceGroup) {
   if (D->hasAttr<SwiftNameAttr>())
     return;
   if (!Info.getSelected())
@@ -896,8 +923,9 @@ static void maybeAttachUnversionedSwiftName(
   }
 
   // Then explicitly call that out with a removal attribute.
-  VersionedInfoMetadata DummyFutureMetadata(
-      SelectedVersion, IsActive_t::Inactive, IsSubstitution_t::Replacement);
+  VersionedInfoMetadata DummyFutureMetadata(SelectedVersion, SliceGroup,
+                                            IsActive_t::Inactive,
+                                            IsSubstitution_t::Replacement);
   handleAPINotedAttribute<SwiftNameAttr>(
       S, D, /*add*/ false, DummyFutureMetadata, []() -> SwiftNameAttr * {
         llvm_unreachable("should not try to add an attribute here");
@@ -907,13 +935,17 @@ static void maybeAttachUnversionedSwiftName(
 /// Processes all versions of versioned API notes.
 ///
 /// Just dispatches to the various ProcessAPINotes functions in this file.
+///
+/// \param SliceGroup Which group the slices in \p Info form. Selection runs
+/// independently per group, so this has to travel with every slice.
 template <typename SpecificDecl, typename SpecificInfo>
 static void ProcessVersionedAPINotes(
     Sema &S, SpecificDecl *D,
-    const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info) {
+    const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info,
+    unsigned SliceGroup) {
 
   if (!S.captureSwiftVersionIndependentAPINotes())
-    maybeAttachUnversionedSwiftName(S, D, Info);
+    maybeAttachUnversionedSwiftName(S, D, Info, SliceGroup);
 
   unsigned Selected = Info.getSelected().value_or(Info.size());
 
@@ -935,8 +967,9 @@ static void ProcessVersionedAPINotes(
       Version = Info[Selected].first;
     }
 
-    ProcessAPINotes(S, D, InfoSlice,
-                    VersionedInfoMetadata(Version, Active, Replacement));
+    ProcessAPINotes(
+        S, D, InfoSlice,
+        VersionedInfoMetadata(Version, SliceGroup, Active, Replacement));
   }
 }
 
@@ -1109,22 +1142,28 @@ void 
APINotesSelectorDiagnosticReaderState::markCandidatesUsed(
   }
 }
 
-// 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.
+/// 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.
+///
+/// \param SliceGroup Which group the slices from \p LookupExact form. This
+/// lookup runs its own version selection, so it is a group distinct from the
+/// broad lookup beside it even though both read the same reader. Pass
+/// parameterSelectorSliceGroup() for the reader being read.
 template <typename SpecificInfo, typename SpecificDecl>
 static void processExactAPINotes(
     Sema &S, SpecificDecl *D,
     const APINotesParameterSelectorCandidates &ParameterSelectorCandidates,
     llvm::function_ref<api_notes::APINotesReader::VersionedInfo<SpecificInfo>(
         ArrayRef<std::string>)>
-        LookupExact) {
+        LookupExact,
+    unsigned SliceGroup) {
   auto ProcessSelector = [&](const APINotesParameterSelector &Selector) {
     auto Info = LookupExact(Selector.Parameters);
     if (Info.size() == 0)
       return false;
 
-    ProcessVersionedAPINotes(S, D, Info);
+    ProcessVersionedAPINotes(S, D, Info, SliceGroup);
     return true;
   };
 
@@ -1154,10 +1193,10 @@ void Sema::ProcessAPINotes(Decl *D) {
         UnwindNamespaceContext(DC, APINotes);
     // Global variables.
     if (auto VD = dyn_cast<VarDecl>(D)) {
-      for (auto Reader : Readers) {
+      for (auto [ReaderIndex, Reader] : llvm::enumerate(Readers)) {
         auto Info =
             Reader->lookupGlobalVariable(VD->getName(), APINotesContext);
-        ProcessVersionedAPINotes(*this, VD, Info);
+        ProcessVersionedAPINotes(*this, VD, Info, 
broadSliceGroup(ReaderIndex));
       }
 
       return;
@@ -1169,10 +1208,16 @@ void Sema::ProcessAPINotes(Decl *D) {
         auto ParameterSelectorCandidates =
             getAPINotesParameterSelectorCandidates(*this, FD);
 
-        for (auto Reader : Readers) {
+        for (auto [ReaderIndexRaw, ReaderRef] : llvm::enumerate(Readers)) {
+          // Capturing a structured binding is a C++20 extension, and the
+          // lambdas below need both of these, so bind plain locals.
+          unsigned ReaderIndex = ReaderIndexRaw;
+          auto *Reader = ReaderRef;
+
           auto Info =
               Reader->lookupGlobalFunction(FD->getName(), APINotesContext);
-          ProcessVersionedAPINotes(*this, FD, Info);
+          ProcessVersionedAPINotes(*this, FD, Info,
+                                   broadSliceGroup(ReaderIndex));
 
           if (ParameterSelectorCandidates)
             processExactAPINotes<api_notes::GlobalFunctionInfo>(
@@ -1180,7 +1225,8 @@ void Sema::ProcessAPINotes(Decl *D) {
                 [&](ArrayRef<std::string> Parameters) {
                   return Reader->lookupGlobalFunction(FD->getName(), 
Parameters,
                                                       APINotesContext);
-                });
+                },
+                parameterSelectorSliceGroup(ReaderIndex));
 
           if (ParameterSelectorCandidates) {
             auto &DiagnosticState =
@@ -1204,9 +1250,10 @@ void Sema::ProcessAPINotes(Decl *D) {
 
     // Objective-C classes.
     if (auto Class = dyn_cast<ObjCInterfaceDecl>(D)) {
-      for (auto Reader : Readers) {
+      for (auto [ReaderIndex, Reader] : llvm::enumerate(Readers)) {
         auto Info = Reader->lookupObjCClassInfo(Class->getName());
-        ProcessVersionedAPINotes(*this, Class, Info);
+        ProcessVersionedAPINotes(*this, Class, Info,
+                                 broadSliceGroup(ReaderIndex));
       }
 
       return;
@@ -1214,9 +1261,10 @@ void Sema::ProcessAPINotes(Decl *D) {
 
     // Objective-C protocols.
     if (auto Protocol = dyn_cast<ObjCProtocolDecl>(D)) {
-      for (auto Reader : Readers) {
+      for (auto [ReaderIndex, Reader] : llvm::enumerate(Readers)) {
         auto Info = Reader->lookupObjCProtocolInfo(Protocol->getName());
-        ProcessVersionedAPINotes(*this, Protocol, Info);
+        ProcessVersionedAPINotes(*this, Protocol, Info,
+                                 broadSliceGroup(ReaderIndex));
       }
 
       return;
@@ -1256,11 +1304,12 @@ void Sema::ProcessAPINotes(Decl *D) {
             T.split(), getASTContext().getPrintingPolicy());
       }
 
-      for (auto Reader : Readers) {
+      for (auto [ReaderIndex, Reader] : llvm::enumerate(Readers)) {
         if (auto ParentTag = dyn_cast<TagDecl>(Tag->getDeclContext()))
           APINotesContext = UnwindTagContext(ParentTag, APINotes);
         auto Info = Reader->lookupTag(LookupName, APINotesContext);
-        ProcessVersionedAPINotes(*this, Tag, Info);
+        ProcessVersionedAPINotes(*this, Tag, Info,
+                                 broadSliceGroup(ReaderIndex));
       }
 
       return;
@@ -1268,9 +1317,10 @@ void Sema::ProcessAPINotes(Decl *D) {
 
     // Typedefs
     if (auto Typedef = dyn_cast<TypedefNameDecl>(D)) {
-      for (auto Reader : Readers) {
+      for (auto [ReaderIndex, Reader] : llvm::enumerate(Readers)) {
         auto Info = Reader->lookupTypedef(Typedef->getName(), APINotesContext);
-        ProcessVersionedAPINotes(*this, Typedef, Info);
+        ProcessVersionedAPINotes(*this, Typedef, Info,
+                                 broadSliceGroup(ReaderIndex));
       }
 
       return;
@@ -1281,9 +1331,10 @@ void Sema::ProcessAPINotes(Decl *D) {
   if (DC->getRedeclContext()->isFileContext() ||
       DC->getRedeclContext()->isExternCContext()) {
     if (auto EnumConstant = dyn_cast<EnumConstantDecl>(D)) {
-      for (auto Reader : Readers) {
+      for (auto [ReaderIndex, Reader] : llvm::enumerate(Readers)) {
         auto Info = Reader->lookupEnumConstant(EnumConstant->getName());
-        ProcessVersionedAPINotes(*this, EnumConstant, Info);
+        ProcessVersionedAPINotes(*this, EnumConstant, Info,
+                                 broadSliceGroup(ReaderIndex));
       }
 
       return;
@@ -1334,7 +1385,7 @@ void Sema::ProcessAPINotes(Decl *D) {
 
     // Objective-C methods.
     if (auto Method = dyn_cast<ObjCMethodDecl>(D)) {
-      for (auto Reader : Readers) {
+      for (auto [ReaderIndex, Reader] : llvm::enumerate(Readers)) {
         if (auto Context = GetContext(Reader)) {
           // Map the selector.
           Selector Sel = Method->getSelector();
@@ -1352,21 +1403,23 @@ void Sema::ProcessAPINotes(Decl *D) {
 
           auto Info = Reader->lookupObjCMethod(*Context, SelectorRef,
                                                Method->isInstanceMethod());
-          ProcessVersionedAPINotes(*this, Method, Info);
+          ProcessVersionedAPINotes(*this, Method, Info,
+                                   broadSliceGroup(ReaderIndex));
         }
       }
     }
 
     // Objective-C properties.
     if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) {
-      for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
+      for (auto [ReaderIndex, Reader] : llvm::enumerate(Readers)) {
         if (auto Context = GetContext(Reader)) {
           bool isInstanceProperty =
               (Property->getPropertyAttributesAsWritten() &
                ObjCPropertyAttribute::kind_class) == 0;
           auto Info = Reader->lookupObjCProperty(*Context, Property->getName(),
                                                  isInstanceProperty);
-          ProcessVersionedAPINotes(*this, Property, Info);
+          ProcessVersionedAPINotes(*this, Property, Info,
+                                   broadSliceGroup(ReaderIndex));
         }
       }
 
@@ -1381,7 +1434,12 @@ void Sema::ProcessAPINotes(Decl *D) {
           !isa<CXXConversionDecl>(CXXMethod)) {
         auto ParameterSelectorCandidates =
             getAPINotesParameterSelectorCandidates(*this, CXXMethod);
-        for (auto Reader : Readers) {
+        for (auto [ReaderIndexRaw, ReaderRef] : llvm::enumerate(Readers)) {
+          // Capturing a structured binding is a C++20 extension, and the
+          // lambdas below need both of these, so bind plain locals.
+          unsigned ReaderIndex = ReaderIndexRaw;
+          auto *Reader = ReaderRef;
+
           if (auto Context = UnwindTagContext(TagContext, APINotes)) {
             std::string MethodName;
             if (CXXMethod->isOverloadedOperator())
@@ -1392,7 +1450,8 @@ void Sema::ProcessAPINotes(Decl *D) {
               MethodName = CXXMethod->getName();
 
             auto Info = Reader->lookupCXXMethod(Context->id, MethodName);
-            ProcessVersionedAPINotes(*this, CXXMethod, Info);
+            ProcessVersionedAPINotes(*this, CXXMethod, Info,
+                                     broadSliceGroup(ReaderIndex));
 
             if (ParameterSelectorCandidates)
               processExactAPINotes<api_notes::CXXMethodInfo>(
@@ -1400,7 +1459,8 @@ void Sema::ProcessAPINotes(Decl *D) {
                   [&](ArrayRef<std::string> Parameters) {
                     return Reader->lookupCXXMethod(Context->id, MethodName,
                                                    Parameters);
-                  });
+                  },
+                  parameterSelectorSliceGroup(ReaderIndex));
 
             if (ParameterSelectorCandidates) {
               auto &DiagnosticState =
@@ -1423,20 +1483,22 @@ void Sema::ProcessAPINotes(Decl *D) {
 
     if (auto Field = dyn_cast<FieldDecl>(D)) {
       if (!Field->isUnnamedBitField() && !Field->isAnonymousStructOrUnion()) {
-        for (auto Reader : Readers) {
+        for (auto [ReaderIndex, Reader] : llvm::enumerate(Readers)) {
           if (auto Context = UnwindTagContext(TagContext, APINotes)) {
             auto Info = Reader->lookupField(Context->id, Field->getName());
-            ProcessVersionedAPINotes(*this, Field, Info);
+            ProcessVersionedAPINotes(*this, Field, Info,
+                                     broadSliceGroup(ReaderIndex));
           }
         }
       }
     }
 
     if (auto Tag = dyn_cast<TagDecl>(D)) {
-      for (auto Reader : Readers) {
+      for (auto [ReaderIndex, Reader] : llvm::enumerate(Readers)) {
         if (auto Context = UnwindTagContext(TagContext, APINotes)) {
           auto Info = Reader->lookupTag(Tag->getName(), Context);
-          ProcessVersionedAPINotes(*this, Tag, Info);
+          ProcessVersionedAPINotes(*this, Tag, Info,
+                                   broadSliceGroup(ReaderIndex));
         }
       }
     }
diff --git a/clang/test/APINotes/Inputs/Headers/ExportAs.apinotes 
b/clang/test/APINotes/Inputs/Headers/ExportAs.apinotes
index 14c77afd8c30a1..06ca173239b257 100644
--- a/clang/test/APINotes/Inputs/Headers/ExportAs.apinotes
+++ b/clang/test/APINotes/Inputs/Headers/ExportAs.apinotes
@@ -3,3 +3,18 @@ Globals:
   - Name: globalInt
     Availability: none
     AvailabilityMsg: "oh no"
+  - Name: sliceGroupProbe
+    SwiftPrivate: true
+Functions:
+  - Name: sliceGroupOrderProbe
+    SwiftName: 'exportBroad(_:)'
+  - Name: sliceGroupOrderProbe
+    Where:
+      Parameters:
+        - int
+    SwiftName: 'exportExact(_:)'
+SwiftVersions:
+  - Version: 4.0
+    Globals:
+      - Name: sliceGroupProbe
+        SwiftPrivate: false
diff --git a/clang/test/APINotes/Inputs/Headers/ExportAsCore.apinotes 
b/clang/test/APINotes/Inputs/Headers/ExportAsCore.apinotes
new file mode 100644
index 00000000000000..3bd5500f8e24e6
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Headers/ExportAsCore.apinotes
@@ -0,0 +1,20 @@
+Name: ExportAsCore
+Globals:
+  - Name: sliceGroupProbe
+    SwiftName: 'fromCoreUnversioned'
+Functions:
+  - Name: sliceGroupOrderProbe
+    SwiftName: 'coreBroad(_:)'
+  - Name: sliceGroupOrderProbe
+    Where:
+      Parameters:
+        - int
+    SwiftName: 'coreExact(_:)'
+SwiftVersions:
+  - Version: 3.0
+    Globals:
+      - Name: sliceGroupProbe
+        SwiftName: 'fromCoreV3'
+    Functions:
+      - Name: sliceGroupOrderProbe
+        SwiftName: 'coreBroadV3(_:)'
diff --git a/clang/test/APINotes/Inputs/Headers/ExportAsCore.h 
b/clang/test/APINotes/Inputs/Headers/ExportAsCore.h
index f7674c19935d64..429378d1b6092d 100644
--- a/clang/test/APINotes/Inputs/Headers/ExportAsCore.h
+++ b/clang/test/APINotes/Inputs/Headers/ExportAsCore.h
@@ -1 +1,12 @@
 static int globalInt = 123;
+
+// Annotated by two readers at once: ExportAsCore.apinotes, and 
ExportAs.apinotes
+// because ExportAsCore is export_as ExportAs. Each reader is its own slice
+// group. See slice-groups.c.
+static int sliceGroupProbe = 0;
+
+// Reached by four lookups: a broad one and an exact `Where: Parameters` one
+// against each of the two readers. This is the only fixture that combines two
+// readers with a parameter selector, so it is the only one where the group
+// numbering's ordering property is observable. See slice-groups-order.c.
+void sliceGroupOrderProbe(int x);
diff --git a/clang/test/APINotes/Inputs/Headers/SliceGroupsExact.apinotes 
b/clang/test/APINotes/Inputs/Headers/SliceGroupsExact.apinotes
new file mode 100644
index 00000000000000..e51693afbb10b3
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Headers/SliceGroupsExact.apinotes
@@ -0,0 +1,19 @@
+Name: SliceGroupsExact
+Functions:
+  - Name: sliceGroupExactProbe
+    SwiftName: 'broadUnversioned(_:)'
+  - Name: sliceGroupExactProbe
+    Where:
+      Parameters:
+        - int
+    SwiftName: 'exactUnversioned(_:)'
+SwiftVersions:
+  - Version: 3.0
+    Functions:
+      - Name: sliceGroupExactProbe
+        SwiftName: 'broadV3(_:)'
+      - Name: sliceGroupExactProbe
+        Where:
+          Parameters:
+            - int
+        SwiftName: 'exactV3(_:)'
diff --git a/clang/test/APINotes/Inputs/Headers/SliceGroupsExact.h 
b/clang/test/APINotes/Inputs/Headers/SliceGroupsExact.h
new file mode 100644
index 00000000000000..b65c5e33b34d20
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Headers/SliceGroupsExact.h
@@ -0,0 +1,3 @@
+// One declaration reached by two lookups against the same reader: the broad
+// entry, and the exact Where: Parameters entry. See slice-groups-exact.c.
+void sliceGroupExactProbe(int x);
diff --git a/clang/test/APINotes/Inputs/Headers/module.modulemap 
b/clang/test/APINotes/Inputs/Headers/module.modulemap
index a9b273ccc90e6c..0a0cbf0115dee9 100644
--- a/clang/test/APINotes/Inputs/Headers/module.modulemap
+++ b/clang/test/APINotes/Inputs/Headers/module.modulemap
@@ -84,3 +84,7 @@ module RedeclAnnotation {
   header "RedeclAnnotation.h"
   export *
 }
+
+module SliceGroupsExact {
+  header "SliceGroupsExact.h"
+}
diff --git a/clang/test/APINotes/properties.m b/clang/test/APINotes/properties.m
index 79b5e2b10c47c1..3cddab8099396c 100644
--- a/clang/test/APINotes/properties.m
+++ b/clang/test/APINotes/properties.m
@@ -15,28 +15,28 @@
 
 // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyInVersion3 'id'
 // CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <<invalid sloc>>
-// CHECK-4-NEXT: SwiftVersionedAdditionAttr {{.+}} 3.0{{$}}
+// CHECK-4-NEXT: SwiftVersionedAdditionAttr {{.+}} 3.0 0{{$}}
 // CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <<invalid sloc>>
 // CHECK-NOT: Attr
 
 // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClassInVersion3 'id'
 // CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <<invalid sloc>>
-// CHECK-4-NEXT: SwiftVersionedAdditionAttr {{.+}} 3.0{{$}}
+// CHECK-4-NEXT: SwiftVersionedAdditionAttr {{.+}} 3.0 0{{$}}
 // CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <<invalid sloc>>
 // CHECK-NOT: Attr
 
 // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyExceptInVersion3 'id'
-// CHECK-3-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 
IsReplacedByActive{{$}}
+// CHECK-3-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 
IsReplacedByActive 0{{$}}
 // CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <<invalid sloc>>
 // CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <<invalid sloc>>
-// CHECK-4-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 3.0 {{[0-9]+}}
+// CHECK-4-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 3.0 {{[0-9]+}} 
0{{$}}
 // CHECK-NOT: Attr
 
 // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClassExceptInVersion3 
'id'
-// CHECK-3-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 
IsReplacedByActive{{$}}
+// CHECK-3-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 
IsReplacedByActive 0{{$}}
 // CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <<invalid sloc>>
 // CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <<invalid sloc>>
-// CHECK-4-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 3.0 {{[0-9]+}}
+// CHECK-4-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 3.0 {{[0-9]+}} 
0{{$}}
 // CHECK-NOT: Attr
 
 // CHECK-LABEL: Decl
diff --git a/clang/test/APINotes/slice-groups-exact.c 
b/clang/test/APINotes/slice-groups-exact.c
new file mode 100644
index 00000000000000..48a7585f1d41ae
--- /dev/null
+++ b/clang/test/APINotes/slice-groups-exact.c
@@ -0,0 +1,35 @@
+// Two lookups against a single API notes reader, which is the case that makes
+// the slice group distinct from a reader index.
+//
+// Sema runs a broad lookup for a global function and, when the sidecar carries
+// a `Where: Parameters:` entry, a second exact lookup beside it. Each call 
runs
+// its own version selection and each winner is applied, so the two are 
separate
+// competitions even though they read the same file. If both stamped the same
+// group, a consumer that recomputes the selection would pool all four slices
+// into one competition, pick a single winner, and silently drop either the
+// broad annotation or the exact one.
+//
+// slice-groups.c covers the other way a second group arises, two readers via
+// export_as. This one cannot be expressed with one lookup per reader, so it is
+// the test that pins the group to the lookup rather than to the reader.
+
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: %clang_cc1 -fswift-version-independent-apinotes -fmodules 
-fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache 
-fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump 
-ast-dump-filter sliceGroupExactProbe -x c | FileCheck %s
+
+#include "SliceGroupsExact.h"
+
+// CHECK: Dumping sliceGroupExactProbe:
+// CHECK: FunctionDecl {{.+}} imported in SliceGroupsExact sliceGroupExactProbe
+
+// The broad lookup's two slices share one group.
+// CHECK: SwiftVersionedAdditionAttr {{.+}} Implicit 0 0{{$}}
+// CHECK-NEXT: SwiftNameAttr {{.+}} "broadUnversioned(_:)"
+// CHECK-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 0{{$}}
+// CHECK-NEXT: SwiftNameAttr {{.+}} "broadV3(_:)"
+
+// The exact lookup's two slices share a different group. The trailing 1 is the
+// assertion: it must not be 0, or the two competitions have been pooled.
+// CHECK-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 0 1{{$}}
+// CHECK-NEXT: SwiftNameAttr {{.+}} "exactUnversioned(_:)"
+// CHECK-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 1{{$}}
+// CHECK-NEXT: SwiftNameAttr {{.+}} "exactV3(_:)"
diff --git a/clang/test/APINotes/slice-groups-order.c 
b/clang/test/APINotes/slice-groups-order.c
new file mode 100644
index 00000000000000..4cb6b9185566bb
--- /dev/null
+++ b/clang/test/APINotes/slice-groups-order.c
@@ -0,0 +1,47 @@
+// Two API notes readers and a parameter selector on one declaration, which is
+// the only configuration where the slice group numbering's ordering property 
is
+// observable.
+//
+// Sema makes two lookups per reader: a broad one, and a parameter-selector one
+// for a `Where: Parameters:` entry. It applies them reader by reader, broad
+// first, so each reader owns an adjacent pair of groups. ExportAsCore takes 0
+// and 1, ExportAs takes 2 and 3, and that is also the order Sema applied them.
+//
+// The ordering matters because a group's winner can collide with another
+// group's winner on the same key, as all four do here on SwiftName. Clang
+// resolves that by last-applied-wins, so a consumer has to replay the groups 
in
+// ascending order. Numbering the readers 0 and 1 and the selector lookups
+// 2 and 3 would apply them in the order 0, 2, 1, 3, and a consumer sorting on
+// the ordinal would pick the wrong winner.
+//
+// slice-groups.c covers two readers with no selector, and slice-groups-exact.c
+// covers one reader with a selector. Neither pins the order, because with a
+// single pair any numbering is ascending.
+
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: %clang_cc1 -fswift-version-independent-apinotes -fmodules 
-fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache 
-fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump 
-ast-dump-filter sliceGroupOrderProbe -x c | FileCheck %s
+
+#include "ExportAs.h"
+
+// CHECK: Dumping sliceGroupOrderProbe:
+// CHECK: FunctionDecl {{.+}} imported in ExportAsCore sliceGroupOrderProbe
+
+// Group 0: ExportAsCore's broad lookup, with an unversioned and a 3.0 slice.
+// CHECK: SwiftVersionedAdditionAttr {{.+}} Implicit 0 0{{$}}
+// CHECK-NEXT: SwiftNameAttr {{.+}} "coreBroad(_:)"
+// CHECK-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 0{{$}}
+// CHECK-NEXT: SwiftNameAttr {{.+}} "coreBroadV3(_:)"
+
+// Group 1: ExportAsCore's parameter-selector lookup. Odd, and adjacent to its
+// own reader's broad group rather than pooled with the other reader's.
+// CHECK-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 0 1{{$}}
+// CHECK-NEXT: SwiftNameAttr {{.+}} "coreExact(_:)"
+
+// Group 2: ExportAs's broad lookup, reached through export_as.
+// CHECK-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 0 2{{$}}
+// CHECK-NEXT: SwiftNameAttr {{.+}} "exportBroad(_:)"
+
+// Group 3: ExportAs's parameter-selector lookup. Applied last, so under the
+// legacy selection this is the name that would win.
+// CHECK-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 0 3{{$}}
+// CHECK-NEXT: SwiftNameAttr {{.+}} "exportExact(_:)"
diff --git a/clang/test/APINotes/slice-groups.c 
b/clang/test/APINotes/slice-groups.c
new file mode 100644
index 00000000000000..b85ddd4c96ef72
--- /dev/null
+++ b/clang/test/APINotes/slice-groups.c
@@ -0,0 +1,33 @@
+// A declaration annotated by two API notes readers at once, which is what 
makes
+// the slice group observable. ExportAsCore is `export_as ExportAs`, so
+// tryAPINotes loads ExportAsCore.apinotes and then ExportAs.apinotes from the
+// same directory. Both are public readers.
+//
+// Clang runs version selection once per lookup and applies every winner, so 
the
+// two readers are two competitions. The group ordinal is what tells a consumer
+// which slices are rivals; pooling them would let one reader's slice suppress
+// the other's. Every other test in this directory has a single reader, so this
+// is the only one where a wrong or swapped group is visible at all.
+
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: %clang_cc1 -fswift-version-independent-apinotes -fmodules 
-fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache 
-fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump 
-ast-dump-filter sliceGroupProbe -x c | FileCheck %s
+
+#include "ExportAs.h"
+
+// CHECK: Dumping sliceGroupProbe:
+// CHECK: VarDecl {{.+}} imported in ExportAsCore sliceGroupProbe 'int'
+
+// Group 0 is ExportAsCore.apinotes: an unversioned slice and a 3.0 slice.
+// CHECK: SwiftVersionedAdditionAttr {{.+}} Implicit 0 0{{$}}
+// CHECK-NEXT: SwiftNameAttr {{.+}} "fromCoreUnversioned"
+// CHECK-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 0{{$}}
+// CHECK-NEXT: SwiftNameAttr {{.+}} "fromCoreV3"
+
+// Group 2 is ExportAs.apinotes, reached through export_as. Reader 1's broad
+// lookup is group 2, not 1, because each reader reserves an odd number for its
+// exact parameter-selector lookup. The nonzero group is the whole point of 
this
+// test: nothing else in the suite produces one, so a bug that collapsed the 
two
+// readers onto one group, or swapped them, would pass everywhere else.
+// CHECK-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 0 2{{$}}
+// CHECK-NEXT: SwiftPrivateAttr
+// CHECK-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 4.0 {{[0-9]+}} 2{{$}}
diff --git a/clang/test/APINotes/versioned-version-independent.m 
b/clang/test/APINotes/versioned-version-independent.m
index da8b34a1d9ba3a..e0f5bff87b3311 100644
--- a/clang/test/APINotes/versioned-version-independent.m
+++ b/clang/test/APINotes/versioned-version-independent.m
@@ -7,30 +7,34 @@
 
 #import <VersionedKit/VersionedKit.h>
 
+// Every wrapper carries the slice group it belongs to. The trailing 0 on every
+// line below is that group: one lookup here, because VersionedKit has a single
+// reader.
+
 // CHECK-VERSIONED-DUMP-LABEL: Dumping moveToPointDUMP
 // CHECK-VERSIONED-DUMP: SwiftNameAttr {{.+}} "moveTo(x:y:)"
-// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0
+// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 
0{{$}}
 // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <<invalid sloc>> 
"moveTo(a:b:)"
 
 // CHECK-VERSIONED-DUMP-LABEL: Dumping unversionedRenameDUMP
 // CHECK-VERSIONED-DUMP: SwiftNameAttr {{.+}} "unversionedRename_HEADER()"
-// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 0
+// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 0 
0{{$}}
 // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "unversionedRename_NOTES()"
 
 // CHECK-VERSIONED-DUMP-LABEL: Dumping TestGenericDUMP
-// CHECK-VERSIONED-DUMP: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0
+// CHECK-VERSIONED-DUMP: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 0{{$}}
 // CHECK-VERSIONED-DUMP-NEXT: SwiftImportAsNonGenericAttr {{.+}} <<invalid 
sloc>>
 
 // CHECK-VERSIONED-DUMP:  Swift3RenamedOnlyDUMP
-// CHECK-VERSIONED-DUMP: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0
+// CHECK-VERSIONED-DUMP: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 0{{$}}
 // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "SpecialSwift3Name"
 
 // CHECK-VERSIONED-DUMP: Swift3RenamedAlsoDUMP
 // CHECK-VERSIONED-DUMP: SwiftNameAttr {{.+}} "Swift4Name"
-// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0
+// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 
0{{$}}
 // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "SpecialSwift3Also"
 
 // CHECK-VERSIONED-DUMP: Swift4RenamedDUMP
-// CHECK-VERSIONED-DUMP: SwiftVersionedAdditionAttr {{.+}} Implicit 4
+// CHECK-VERSIONED-DUMP: SwiftVersionedAdditionAttr {{.+}} Implicit 4 0{{$}}
 // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "SpecialSwift4Name"
 
diff --git a/clang/test/APINotes/versioned.m b/clang/test/APINotes/versioned.m
index 264edde2a04fce..3b108407a49f9e 100644
--- a/clang/test/APINotes/versioned.m
+++ b/clang/test/APINotes/versioned.m
@@ -16,50 +16,50 @@
 // CHECK-VERSIONED:__attribute__((swift_name("moveTo(a:b:)"))) void 
moveToPointDUMP(double x, double y);
 
 // CHECK-DUMP-LABEL: Dumping moveToPointDUMP
-// CHECK-VERSIONED-DUMP: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 
IsReplacedByActive{{$}}
+// CHECK-VERSIONED-DUMP: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 
IsReplacedByActive 0{{$}}
 // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "moveTo(x:y:)"
 // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <<invalid sloc>> 
"moveTo(a:b:)"
 // CHECK-UNVERSIONED-DUMP: SwiftNameAttr {{.+}} "moveTo(x:y:)"
-// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 
3.0{{$}}
+// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 
0{{$}}
 // CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <<invalid sloc>> 
"moveTo(a:b:)"
 // CHECK-DUMP-NOT: Attr
 
 // CHECK-DUMP-LABEL: Dumping unversionedRenameDUMP
 // CHECK-DUMP: in VersionedKit unversionedRenameDUMP
-// CHECK-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 0 
IsReplacedByActive{{$}}
+// CHECK-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 0 
IsReplacedByActive 0{{$}}
 // CHECK-DUMP-NEXT: SwiftNameAttr {{.+}} "unversionedRename_HEADER()"
 // CHECK-DUMP-NEXT: SwiftNameAttr {{.+}} "unversionedRename_NOTES()"
 // CHECK-DUMP-NOT: Attr
 
 // CHECK-DUMP-LABEL: Dumping TestGenericDUMP
 // CHECK-VERSIONED-DUMP: SwiftImportAsNonGenericAttr {{.+}} <<invalid sloc>>
-// CHECK-UNVERSIONED-DUMP: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0{{$}}
+// CHECK-UNVERSIONED-DUMP: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 
0{{$}}
 // CHECK-UNVERSIONED-DUMP-NEXT: SwiftImportAsNonGenericAttr {{.+}} <<invalid 
sloc>>
 // CHECK-DUMP-NOT: Attr
 
 // CHECK-DUMP-LABEL: Dumping Swift3RenamedOnlyDUMP
 // CHECK-DUMP: in VersionedKit Swift3RenamedOnlyDUMP
-// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 3.0 
{{[0-9]+}} IsReplacedByActive{{$}}
+// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 3.0 
{{[0-9]+}} IsReplacedByActive 0{{$}}
 // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "SpecialSwift3Name"
-// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 
3.0{{$}}
+// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 
0{{$}}
 // CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <<invalid sloc>> 
"SpecialSwift3Name"
 // CHECK-DUMP-NOT: Attr
 
 // CHECK-DUMP-LABEL: Dumping Swift3RenamedAlsoDUMP
 // CHECK-DUMP: in VersionedKit Swift3RenamedAlsoDUMP
-// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 
IsReplacedByActive{{$}}
+// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 
IsReplacedByActive 0{{$}}
 // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <line:{{.+}}, col:{{.+}}> 
"Swift4Name"
 // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "SpecialSwift3Also"
 // CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <line:{{.+}}, col:{{.+}}> 
"Swift4Name"
-// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 
3.0{{$}}
+// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 
0{{$}}
 // CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <<invalid sloc>> 
"SpecialSwift3Also"
 // CHECK-DUMP-NOT: Attr
 
 // CHECK-DUMP-LABEL: Dumping Swift4RenamedDUMP
 // CHECK-DUMP: in VersionedKit Swift4RenamedDUMP
-// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 4 
{{[0-9]+}} IsReplacedByActive{{$}}
+// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 4 
{{[0-9]+}} IsReplacedByActive 0{{$}}
 // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "SpecialSwift4Name"
-// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 
4{{$}}
+// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 4 
0{{$}}
 // CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <<invalid sloc>> 
"SpecialSwift4Name"
 // CHECK-DUMP-NOT: Attr
 

>From 4ff92520abec9007f8db37081ce6f7ea0c2ce78b Mon Sep 17 00:00:00 2001
From: Artem Chikin <[email protected]>
Date: Mon, 21 Sep 2026 11:42:55 +0100
Subject: [PATCH 2/2] [APINotes][Unversioned] Capture presence of versioned
 slices separately

In addition to aggregating attributes with `SwiftVersionedAdditionAttr` and 
`SwiftVersionedRemovalAttr`, we may need to also capture that an APINotes file 
contained a versioned slice which may not have captured any keyed attributes.
For example:

```
Name: Bar
Functions:
  - Name: foo
    SwiftName: 'fooBase()'     # unversioned
SwiftVersions:
  - Version: 4
    Functions:
      - Name: foo
        SwiftName: 'fooFour()'  # the 4.0 slice
  - Version: 5
    Functions:
      - Name: foo
        SwiftPrivate: true      # the 5.0 slice
```

When picking which attributes/notes to apply, the algorithm is to select from 
the lowest version slice at or above the client requested one, or the 
unversioned slice otherwise. Suppose we have a client at version 3.

In this case, the client will have reconstructed the attributes as 3 options:
- Unversioned: SwiftNameAttr "fooBase()"
- 4.0: SwiftNameAttr "fooFour()"
- 5.0: SwiftPrivateAttr
According to the algorithm, slice 4 would win, so `foo` would get a name 
`fooFour()` and **not** be private.

Now suppose there is instead a versioned slice which carries no keyed 
attributes:
```
SwiftVersions:
  - Version: 4
    Functions:
      - Name: foo        # declared, no keys
```
With this slice, the client would have reconstructed just two 
versioned-attribute options:
- Unversioned: SwiftNameAttr "fooBase()"
- 5.0: SwiftPrivateAttr
And according to the algorithm selected 5 and marked `foo` as private. This is 
incorrect because the presence of the 4.0 slice still means that it is the one 
that must be selected for this declaration.

This change adds a new (no spelling) attribute called `SwiftVersionedSliceAttr` 
to capture existence of all versioned slices, which the clients will then use 
to know which slice version to select for a given declaration. It carries the 
same slice group as the addition and removal wrappers, so a client groups it 
into the same competition as its siblings.
---
 clang/include/clang/Basic/Attr.td             | 18 +++++++++++
 clang/lib/Sema/SemaAPINotes.cpp               |  9 +++++-
 .../Headers/VersionedKit.apinotes             |  4 +++
 .../Headers/VersionedKit.h                    |  2 ++
 clang/test/APINotes/slice-groups-exact.c      |  6 +++-
 clang/test/APINotes/slice-groups-order.c      |  7 +++-
 clang/test/APINotes/slice-groups.c            | 25 ++++++++++-----
 .../APINotes/versioned-version-independent.m  | 32 +++++++++++++++----
 clang/test/APINotes/versioned.m               | 14 ++++++++
 9 files changed, 100 insertions(+), 17 deletions(-)

diff --git a/clang/include/clang/Basic/Attr.td 
b/clang/include/clang/Basic/Attr.td
index 1ece6d5f34b29e..d9640f636e9204 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3387,6 +3387,24 @@ def SwiftVersionedRemoval : Attr {
   }];
 }
 
+def SwiftVersionedSlice : Attr {
+  // This attribute has no spellings as it is only ever created implicitly
+  // from API notes, and only under -fswift-version-independent-apinotes.
+  //
+  // One of these records each Swift version slice an API notes lookup supplied
+  // for the declaration, whether or not that slice set any keyed values.
+  // A slice that sets no key still takes part in version selection, selecting 
it
+  // suppresses the annotations of every other slice in its group, so a
+  // consumer that recomputes the selection has to know the slice exists.
+  //
+  // SliceGroup says which group this slice came from. See the
+  // SwiftVersionedAddition comment above.
+  let Spellings = [];
+  let Args = [VersionArgument<"Version">, UnsignedArgument<"SliceGroup">];
+  let SemaHandler = 0;
+  let Documentation = [InternalOnly];
+}
+
 def NoDeref : TypeAttr {
   let Spellings = [Clang<"noderef">];
   let Documentation = [NoDerefDocs];
diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp
index 3e5e6d56f0b4ea..86190d054d9ecd 100644
--- a/clang/lib/Sema/SemaAPINotes.cpp
+++ b/clang/lib/Sema/SemaAPINotes.cpp
@@ -961,7 +961,14 @@ static void ProcessVersionedAPINotes(
     // right one.
     if (S.captureSwiftVersionIndependentAPINotes()) {
       Active = IsActive_t::Inactive;
-      Replacement = IsSubstitution_t::Original;
+
+      // Record that this slice exists, independently of whether it goes on to
+      // set any key. A slice that sets nothing still wins selection for the
+      // versions it covers, and winning suppresses every other slice, so a
+      // client recomputing the selection cannot infer the slice set from the
+      // addition and removal wrappers alone.
+      D->addAttr(SwiftVersionedSliceAttr::CreateImplicit(S.Context, Version,
+                                                         SliceGroup));
     } else if (Active == IsActive_t::Inactive && Version.empty()) {
       Replacement = IsSubstitution_t::Replacement;
       Version = Info[Selected].first;
diff --git 
a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes
 
b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes
index 572c714b3d61a7..bbc2498a75192c 100644
--- 
a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes
+++ 
b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes
@@ -18,6 +18,8 @@ Classes:
 Functions:
   - Name: unversionedRenameDUMP
     SwiftName: 'unversionedRename_NOTES()'
+  - Name: keylessSliceDUMP
+    SwiftName: 'keylessSlice_NOTES()'
 Tags:
   - Name: APINotedFlagEnum
     FlagEnum: true
@@ -75,6 +77,8 @@ SwiftVersions:
       - Name: Swift3RenamedAlsoDUMP
         SwiftName: SpecialSwift3Also
     Functions:
+      # Names the declaration and sets no key, on purpose.
+      - Name: keylessSliceDUMP
       - Name: moveToPointDUMP
         SwiftName: 'moveTo(a:b:)'
       - Name: acceptClosure
diff --git 
a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h
 
b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h
index 9ce95633c523b7..8fbca41ee4335b 100644
--- 
a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h
+++ 
b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h
@@ -2,6 +2,8 @@ void moveToPointDUMP(double x, double y) 
__attribute__((swift_name("moveTo(x:y:)
 
 void unversionedRenameDUMP(void) 
__attribute__((swift_name("unversionedRename_HEADER()")));
 
+void keylessSliceDUMP(void);
+
 void acceptClosure(void (^ __attribute__((noescape)) block)(void));
 
 void privateFunc(void) __attribute__((swift_private));
diff --git a/clang/test/APINotes/slice-groups-exact.c 
b/clang/test/APINotes/slice-groups-exact.c
index 48a7585f1d41ae..d740723872e546 100644
--- a/clang/test/APINotes/slice-groups-exact.c
+++ b/clang/test/APINotes/slice-groups-exact.c
@@ -22,14 +22,18 @@
 // CHECK: FunctionDecl {{.+}} imported in SliceGroupsExact sliceGroupExactProbe
 
 // The broad lookup's two slices share one group.
-// CHECK: SwiftVersionedAdditionAttr {{.+}} Implicit 0 0{{$}}
+// CHECK: SwiftVersionedSliceAttr {{.+}} Implicit 0 0{{$}}
+// CHECK-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 0 0{{$}}
 // CHECK-NEXT: SwiftNameAttr {{.+}} "broadUnversioned(_:)"
+// CHECK-NEXT: SwiftVersionedSliceAttr {{.+}} Implicit 3.0 0{{$}}
 // CHECK-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 0{{$}}
 // CHECK-NEXT: SwiftNameAttr {{.+}} "broadV3(_:)"
 
 // The exact lookup's two slices share a different group. The trailing 1 is the
 // assertion: it must not be 0, or the two competitions have been pooled.
+// CHECK-NEXT: SwiftVersionedSliceAttr {{.+}} Implicit 0 1{{$}}
 // CHECK-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 0 1{{$}}
 // CHECK-NEXT: SwiftNameAttr {{.+}} "exactUnversioned(_:)"
+// CHECK-NEXT: SwiftVersionedSliceAttr {{.+}} Implicit 3.0 1{{$}}
 // CHECK-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 1{{$}}
 // CHECK-NEXT: SwiftNameAttr {{.+}} "exactV3(_:)"
diff --git a/clang/test/APINotes/slice-groups-order.c 
b/clang/test/APINotes/slice-groups-order.c
index 4cb6b9185566bb..5669b06345d76b 100644
--- a/clang/test/APINotes/slice-groups-order.c
+++ b/clang/test/APINotes/slice-groups-order.c
@@ -27,21 +27,26 @@
 // CHECK: FunctionDecl {{.+}} imported in ExportAsCore sliceGroupOrderProbe
 
 // Group 0: ExportAsCore's broad lookup, with an unversioned and a 3.0 slice.
-// CHECK: SwiftVersionedAdditionAttr {{.+}} Implicit 0 0{{$}}
+// CHECK: SwiftVersionedSliceAttr {{.+}} Implicit 0 0{{$}}
+// CHECK-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 0 0{{$}}
 // CHECK-NEXT: SwiftNameAttr {{.+}} "coreBroad(_:)"
+// CHECK-NEXT: SwiftVersionedSliceAttr {{.+}} Implicit 3.0 0{{$}}
 // CHECK-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 0{{$}}
 // CHECK-NEXT: SwiftNameAttr {{.+}} "coreBroadV3(_:)"
 
 // Group 1: ExportAsCore's parameter-selector lookup. Odd, and adjacent to its
 // own reader's broad group rather than pooled with the other reader's.
+// CHECK-NEXT: SwiftVersionedSliceAttr {{.+}} Implicit 0 1{{$}}
 // CHECK-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 0 1{{$}}
 // CHECK-NEXT: SwiftNameAttr {{.+}} "coreExact(_:)"
 
 // Group 2: ExportAs's broad lookup, reached through export_as.
+// CHECK-NEXT: SwiftVersionedSliceAttr {{.+}} Implicit 0 2{{$}}
 // CHECK-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 0 2{{$}}
 // CHECK-NEXT: SwiftNameAttr {{.+}} "exportBroad(_:)"
 
 // Group 3: ExportAs's parameter-selector lookup. Applied last, so under the
 // legacy selection this is the name that would win.
+// CHECK-NEXT: SwiftVersionedSliceAttr {{.+}} Implicit 0 3{{$}}
 // CHECK-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 0 3{{$}}
 // CHECK-NEXT: SwiftNameAttr {{.+}} "exportExact(_:)"
diff --git a/clang/test/APINotes/slice-groups.c 
b/clang/test/APINotes/slice-groups.c
index b85ddd4c96ef72..ca9f5badcd4f31 100644
--- a/clang/test/APINotes/slice-groups.c
+++ b/clang/test/APINotes/slice-groups.c
@@ -3,11 +3,16 @@
 // tryAPINotes loads ExportAsCore.apinotes and then ExportAs.apinotes from the
 // same directory. Both are public readers.
 //
-// Clang runs version selection once per lookup and applies every winner, so 
the
-// two readers are two competitions. The group ordinal is what tells a consumer
-// which slices are rivals; pooling them would let one reader's slice suppress
-// the other's. Every other test in this directory has a single reader, so this
-// is the only one where a wrong or swapped group is visible at all.
+// Clang selects one slice per group and applies every group's winner, so the
+// two readers are two groups. The group number is what tells a consumer which
+// slices were candidates against each other; pooling them would let one
+// reader's slice suppress the other's. Every other test in this directory has 
a
+// single reader, so this is the only one where a wrong or swapped group is
+// visible at all.
+//
+// This is the sibling of versioned-version-independent.m, which covers the
+// keyless-slice case on a single reader, and of slice-groups-order.c, which
+// covers two readers and a parameter selector at once.
 
 // RUN: rm -rf %t && mkdir -p %t
 // RUN: %clang_cc1 -fswift-version-independent-apinotes -fmodules 
-fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache 
-fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump 
-ast-dump-filter sliceGroupProbe -x c | FileCheck %s
@@ -18,16 +23,20 @@
 // CHECK: VarDecl {{.+}} imported in ExportAsCore sliceGroupProbe 'int'
 
 // Group 0 is ExportAsCore.apinotes: an unversioned slice and a 3.0 slice.
-// CHECK: SwiftVersionedAdditionAttr {{.+}} Implicit 0 0{{$}}
+// CHECK: SwiftVersionedSliceAttr {{.+}} Implicit 0 0{{$}}
+// CHECK-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 0 0{{$}}
 // CHECK-NEXT: SwiftNameAttr {{.+}} "fromCoreUnversioned"
+// CHECK-NEXT: SwiftVersionedSliceAttr {{.+}} Implicit 3.0 0{{$}}
 // CHECK-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 0{{$}}
 // CHECK-NEXT: SwiftNameAttr {{.+}} "fromCoreV3"
 
 // Group 2 is ExportAs.apinotes, reached through export_as. Reader 1's broad
 // lookup is group 2, not 1, because each reader reserves an odd number for its
-// exact parameter-selector lookup. The nonzero group is the whole point of 
this
-// test: nothing else in the suite produces one, so a bug that collapsed the 
two
+// parameter-selector lookup. The nonzero group is the whole point of this 
test:
+// nothing else in the suite produces one, so a bug that collapsed the two
 // readers onto one group, or swapped them, would pass everywhere else.
+// CHECK-NEXT: SwiftVersionedSliceAttr {{.+}} Implicit 0 2{{$}}
 // CHECK-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 0 2{{$}}
 // CHECK-NEXT: SwiftPrivateAttr
+// CHECK-NEXT: SwiftVersionedSliceAttr {{.+}} Implicit 4.0 2{{$}}
 // CHECK-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 4.0 {{[0-9]+}} 2{{$}}
diff --git a/clang/test/APINotes/versioned-version-independent.m 
b/clang/test/APINotes/versioned-version-independent.m
index e0f5bff87b3311..a832fed6f37581 100644
--- a/clang/test/APINotes/versioned-version-independent.m
+++ b/clang/test/APINotes/versioned-version-independent.m
@@ -7,34 +7,54 @@
 
 #import <VersionedKit/VersionedKit.h>
 
-// Every wrapper carries the slice group it belongs to. The trailing 0 on every
-// line below is that group: one lookup here, because VersionedKit has a single
-// reader.
+// Each slice an API notes lookup supplied is recorded by a
+// SwiftVersionedSliceAttr, whether or not it went on to set a key, and both it
+// and the wrappers carry the slice group they belong to. The trailing 0 on
+// every line below is that group: one lookup here, because VersionedKit has a
+// single reader.
 
 // CHECK-VERSIONED-DUMP-LABEL: Dumping moveToPointDUMP
 // CHECK-VERSIONED-DUMP: SwiftNameAttr {{.+}} "moveTo(x:y:)"
+// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedSliceAttr {{.+}} Implicit 3.0 
0{{$}}
 // CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 
0{{$}}
 // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <<invalid sloc>> 
"moveTo(a:b:)"
 
 // CHECK-VERSIONED-DUMP-LABEL: Dumping unversionedRenameDUMP
 // CHECK-VERSIONED-DUMP: SwiftNameAttr {{.+}} "unversionedRename_HEADER()"
+// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedSliceAttr {{.+}} Implicit 0 0{{$}}
 // CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 0 
0{{$}}
 // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "unversionedRename_NOTES()"
 
+// The case this attribute exists for: the 3.0 slice names the declaration and
+// sets no key. Selecting it suppresses the unversioned rename, and no addition
+// wrapper records that, so the bare slice marker is the only evidence the 
slice
+// exists. The -NEXT chain is what makes this a real assertion: it pins the 3.0
+// marker directly after the unversioned rename, so no addition wrapper for 3.0
+// can sit between them.
+// CHECK-VERSIONED-DUMP-LABEL: Dumping keylessSliceDUMP
+// CHECK-VERSIONED-DUMP: SwiftVersionedSliceAttr {{.+}} Implicit 0 0{{$}}
+// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 0 
0{{$}}
+// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "keylessSlice_NOTES()"
+// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedSliceAttr {{.+}} Implicit 3.0 
0{{$}}
+
 // CHECK-VERSIONED-DUMP-LABEL: Dumping TestGenericDUMP
-// CHECK-VERSIONED-DUMP: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 0{{$}}
+// CHECK-VERSIONED-DUMP: SwiftVersionedSliceAttr {{.+}} Implicit 3.0 0{{$}}
+// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 
0{{$}}
 // CHECK-VERSIONED-DUMP-NEXT: SwiftImportAsNonGenericAttr {{.+}} <<invalid 
sloc>>
 
 // CHECK-VERSIONED-DUMP:  Swift3RenamedOnlyDUMP
-// CHECK-VERSIONED-DUMP: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 0{{$}}
+// CHECK-VERSIONED-DUMP: SwiftVersionedSliceAttr {{.+}} Implicit 3.0 0{{$}}
+// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 
0{{$}}
 // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "SpecialSwift3Name"
 
 // CHECK-VERSIONED-DUMP: Swift3RenamedAlsoDUMP
 // CHECK-VERSIONED-DUMP: SwiftNameAttr {{.+}} "Swift4Name"
+// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedSliceAttr {{.+}} Implicit 3.0 
0{{$}}
 // CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 
0{{$}}
 // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "SpecialSwift3Also"
 
 // CHECK-VERSIONED-DUMP: Swift4RenamedDUMP
-// CHECK-VERSIONED-DUMP: SwiftVersionedAdditionAttr {{.+}} Implicit 4 0{{$}}
+// CHECK-VERSIONED-DUMP: SwiftVersionedSliceAttr {{.+}} Implicit 4 0{{$}}
+// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 4 
0{{$}}
 // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "SpecialSwift4Name"
 
diff --git a/clang/test/APINotes/versioned.m b/clang/test/APINotes/versioned.m
index 3b108407a49f9e..3f56b1204625d7 100644
--- a/clang/test/APINotes/versioned.m
+++ b/clang/test/APINotes/versioned.m
@@ -31,6 +31,20 @@
 // CHECK-DUMP-NEXT: SwiftNameAttr {{.+}} "unversionedRename_NOTES()"
 // CHECK-DUMP-NOT: Attr
 
+// A 3.0 slice that names this declaration and sets no key. This is the legacy
+// behavior that -fswift-version-independent-apinotes has to let a client
+// reproduce, so it is worth pinning here as the reference.
+// CHECK-DUMP-LABEL: Dumping keylessSliceDUMP
+// CHECK-DUMP: in VersionedKit keylessSliceDUMP
+// At the default version the keyless slice does not qualify, so the 
unversioned
+// rename applies live.
+// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "keylessSlice_NOTES()"
+// At 3.0 the keyless slice wins and suppresses the unversioned rename. What
+// survives is a superseded wrapper, and there is no live SwiftNameAttr.
+// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 
IsReplacedByActive 0{{$}}
+// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "keylessSlice_NOTES()"
+// CHECK-DUMP-NOT: Attr
+
 // CHECK-DUMP-LABEL: Dumping TestGenericDUMP
 // CHECK-VERSIONED-DUMP: SwiftImportAsNonGenericAttr {{.+}} <<invalid sloc>>
 // CHECK-UNVERSIONED-DUMP: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 
0{{$}}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to