https://github.com/ian-twilightcoder created 
https://github.com/llvm/llvm-project/pull/226041

Swift carries a few API notes in its resource directory that moved to the SDK 
several releases ago. Swift can't delete its obsolete API notes because it has 
to support older SDKs. The SDK API notes have since been converted to 
attributes in the headers themselves. The SDK can't delete the API notes though 
because they're currently overriding the obsolete Swift API notes.

Add a ValidSDKs map to the API notes format so that the API notes file can 
state the versions where it's obsoleted and should be ignored.

Assisted-by: Claude Opus 5 (1M context) <[email protected]>

rdar://188239796

>From 690d4dfad48d40e8057e7ace8fac09c044ad9222 Mon Sep 17 00:00:00 2001
From: Ian Anderson <[email protected]>
Date: Thu, 24 Sep 2026 00:06:06 -0700
Subject: [PATCH] [APINotes] Add a mechanism to ignore API notes after a
 certain SDK version

Swift carries a few API notes in its resource directory that moved to the SDK 
several releases ago. Swift can't delete its obsolete API notes because it has 
to support older SDKs. The SDK API notes have since been converted to 
attributes in the headers themselves. The SDK can't delete the API notes though 
because they're currently overriding the obsolete Swift API notes.

Add a ValidSDKs map to the API notes format so that the API notes file can 
state the versions where it's obsoleted and should be ignored.

Assisted-by: Claude Opus 5 (1M context) <[email protected]>

rdar://188239796
---
 clang/docs/APINotes.md                        | 27 +++++
 .../include/clang/APINotes/APINotesManager.h  | 21 ++--
 .../clang/APINotes/APINotesYAMLCompiler.h     | 23 ++++-
 clang/include/clang/Basic/DarwinSDKInfo.h     |  2 +
 clang/include/clang/Sema/Sema.h               |  2 +-
 clang/lib/APINotes/APINotesManager.cpp        | 71 ++++++++------
 clang/lib/APINotes/APINotesYAMLCompiler.cpp   | 68 +++++++++++--
 clang/lib/Basic/DarwinSDKInfo.cpp             |  7 ++
 clang/lib/Frontend/CompilerInstance.cpp       |  4 +
 clang/lib/Sema/Sema.cpp                       |  4 +-
 clang/lib/Sema/SemaDeclAttr.cpp               |  4 +-
 .../Inputs/APINotesAged/SomeOtherKit.apinotes | 14 +++
 .../Headers/SomeOtherKit.apinotes             |  5 +
 .../Headers/SomeOtherKit.h                    |  1 +
 clang/test/APINotes/Inputs/Headers/AgingLib.h |  8 ++
 .../Inputs/Headers/PublicPrivateLib.apinotes  | 14 +++
 .../Inputs/Headers/PublicPrivateLib.h         | 10 ++
 .../Headers/PublicPrivateLib_private.apinotes | 13 +++
 .../APINotes/Inputs/Headers/module.modulemap  |  8 ++
 .../Inputs/Headers/module.private.modulemap   |  4 +
 .../ValidSDKs/device-only/AgingLib.apinotes   |  9 ++
 .../AgingLib.apinotes                         | 11 +++
 .../ValidSDKs/multiple-sdks/AgingLib.apinotes | 11 +++
 clang/test/APINotes/search-order.m            | 26 ++++-
 .../APINotes/valid-sdks-implicit-search.c     | 60 ++++++++++++
 .../test/APINotes/valid-sdks-private-notes.c  | 45 +++++++++
 clang/test/APINotes/valid-sdks.c              | 98 +++++++++++++++++++
 .../APINotes/yaml-roundtrip-valid-sdks.test   | 17 ++++
 28 files changed, 531 insertions(+), 56 deletions(-)
 create mode 100644 
clang/test/APINotes/Inputs/APINotesAged/SomeOtherKit.apinotes
 create mode 100644 clang/test/APINotes/Inputs/Headers/AgingLib.h
 create mode 100644 clang/test/APINotes/Inputs/Headers/PublicPrivateLib.apinotes
 create mode 100644 clang/test/APINotes/Inputs/Headers/PublicPrivateLib.h
 create mode 100644 
clang/test/APINotes/Inputs/Headers/PublicPrivateLib_private.apinotes
 create mode 100644 
clang/test/APINotes/Inputs/ValidSDKs/device-only/AgingLib.apinotes
 create mode 100644 
clang/test/APINotes/Inputs/ValidSDKs/different-simulator-version/AgingLib.apinotes
 create mode 100644 
clang/test/APINotes/Inputs/ValidSDKs/multiple-sdks/AgingLib.apinotes
 create mode 100644 clang/test/APINotes/valid-sdks-implicit-search.c
 create mode 100644 clang/test/APINotes/valid-sdks-private-notes.c
 create mode 100644 clang/test/APINotes/valid-sdks.c
 create mode 100644 clang/test/APINotes/yaml-roundtrip-valid-sdks.test

diff --git a/clang/docs/APINotes.md b/clang/docs/APINotes.md
index 4d8646ec36c27..473199ea6fdd7 100644
--- a/clang/docs/APINotes.md
+++ b/clang/docs/APINotes.md
@@ -71,6 +71,33 @@ entries:
 
     Name: MyFramework
 
+:ValidSDKs:
+
+  An exhaustive list of the SDKs this file is still valid on, each with the SDK
+  version at which it stops being valid. A file that declares 'ValidSDKs' is
+  used only when the SDK being compiled against is listed *and* older than that
+  entry's 'ValidUntil'; otherwise the whole file is ignored. This lets an API
+  notes file outside of the SDK be phased out for new SDKs while still
+  supporting old SDKs. The SDK list is exhaustive so that new SDKs don't need 
to
+  be added to an API notes file that is being phased out already.
+
+  ::
+
+    ValidSDKs:
+    - Name: macosx
+      ValidUntil: 14.0
+    - Name: iphoneos
+      ValidUntil: 17.0
+
+  'Name' is an Xcode SDK name as might be passed to `xcrun --sdk`, e.g. macosx,
+  iphoneos, iphonesimulator, et al.
+
+  'ValidUntil' is exclusive, and is compared against the SDK's version, *not*
+  against the deployment target.
+
+  If Clang cannot identify an SDK at all -- no '-isysroot', missing
+  'SDKSettings.json', etc. -- the file applies.
+
 :Classes, Protocols, Tags, Typedefs, Globals, Enumerators, Functions, 
Namespaces:
 
   Arrays of top-level declarations. Each entry in the array must have a
diff --git a/clang/include/clang/APINotes/APINotesManager.h 
b/clang/include/clang/APINotes/APINotesManager.h
index aaf48706fb26b..93e3e23d0fa37 100644
--- a/clang/include/clang/APINotes/APINotesManager.h
+++ b/clang/include/clang/APINotes/APINotesManager.h
@@ -12,6 +12,7 @@
 #include "clang/Basic/SourceLocation.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/FunctionExtras.h"
 #include "llvm/ADT/PointerUnion.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/VersionTuple.h"
@@ -20,6 +21,7 @@
 
 namespace clang {
 
+class DarwinSDKInfo;
 class DirectoryEntry;
 class FileEntry;
 class LangOptions;
@@ -46,6 +48,11 @@ class APINotesManager {
 
   SourceManager &SM;
 
+  /// Resolves the SDK being compiled against when an API notes file declares
+  /// 'ValidSDKs'. Unset, or returning null, means there is no SDK to compare
+  /// against, in which case no file is skipped.
+  llvm::unique_function<const DarwinSDKInfo *()> SDKInfoProvider;
+
   /// Whether to implicitly search for API notes files based on the
   /// source file from which an entity was declared.
   bool ImplicitAPINotes;
@@ -135,6 +142,12 @@ class APINotesManager {
     this->SwiftVersion = Version;
   }
 
+  /// Set the callback used to resolve the SDK being compiled against, for API
+  /// notes files that declare 'ValidSDKs'.
+  void setSDKInfoProvider(llvm::unique_function<const DarwinSDKInfo *()> P) {
+    SDKInfoProvider = std::move(P);
+  }
+
   /// Load the API notes for the current module.
   ///
   /// \param M The current module.
@@ -150,15 +163,9 @@ class APINotesManager {
   /// compiled.
   ///
   /// \param M The current module.
-  /// \param LookInModule Whether to look inside the directory of the current
-  /// module.
-  /// \param SearchPaths The paths in which we should search for API
-  /// notes for the current module.
   ///
   /// \returns a vector of FileEntry where APINotes files are.
-  llvm::SmallVector<FileEntryRef, 2>
-  getCurrentModuleAPINotes(Module *M, bool LookInModule,
-                           ArrayRef<std::string> SearchPaths);
+  llvm::SmallVector<FileEntryRef, 2> getCurrentModuleAPINotes(Module *M);
 
   /// Load Compiled API notes for current module.
   ///
diff --git a/clang/include/clang/APINotes/APINotesYAMLCompiler.h 
b/clang/include/clang/APINotes/APINotesYAMLCompiler.h
index 9c24ed85b6a12..891832c237b43 100644
--- a/clang/include/clang/APINotes/APINotesYAMLCompiler.h
+++ b/clang/include/clang/APINotes/APINotesYAMLCompiler.h
@@ -9,11 +9,13 @@
 #ifndef LLVM_CLANG_APINOTES_APINOTESYAMLCOMPILER_H
 #define LLVM_CLANG_APINOTES_APINOTESYAMLCOMPILER_H
 
+#include "llvm/ADT/STLFunctionalExtras.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/SourceMgr.h"
 #include "llvm/Support/raw_ostream.h"
 
 namespace clang {
+class DarwinSDKInfo;
 class FileEntry;
 } // namespace clang
 
@@ -24,11 +26,24 @@ namespace api_notes {
 /// the APINotes format.
 bool parseAndDumpAPINotes(llvm::StringRef YI, llvm::raw_ostream &OS);
 
+/// Resolves the SDK being compiled against, on demand. Returns null when the
+/// SDK cannot be identified (no sysroot, no SDKSettings.json, ...), which 
means
+/// "apply the API notes" rather than "skip them".
+using DarwinSDKInfoProviderRef = llvm::function_ref<const DarwinSDKInfo *()>;
+
+enum class CompileResult {
+  Success,
+  Error,
+  /// The file declares 'ValidSDKs' and the SDK being compiled against isn't 
one
+  /// of them, so nothing was written.
+  Skipped,
+};
+
 /// Converts API notes from YAML format to binary format.
-bool compileAPINotes(llvm::StringRef YAMLInput, const FileEntry *SourceFile,
-                     llvm::raw_ostream &OS,
-                     llvm::SourceMgr::DiagHandlerTy DiagHandler = nullptr,
-                     void *DiagHandlerCtxt = nullptr);
+CompileResult compileAPINotes(
+    llvm::StringRef YAMLInput, const FileEntry *SourceFile,
+    llvm::raw_ostream &OS, llvm::SourceMgr::DiagHandlerTy DiagHandler = 
nullptr,
+    void *DiagHandlerCtxt = nullptr, DarwinSDKInfoProviderRef GetSDKInfo = {});
 } // namespace api_notes
 } // namespace clang
 
diff --git a/clang/include/clang/Basic/DarwinSDKInfo.h 
b/clang/include/clang/Basic/DarwinSDKInfo.h
index 3c8297569180f..fe750734f58e4 100644
--- a/clang/include/clang/Basic/DarwinSDKInfo.h
+++ b/clang/include/clang/Basic/DarwinSDKInfo.h
@@ -203,6 +203,8 @@ class DarwinSDKInfo {
 
   llvm::Triple::EnvironmentType getEnvironment() const { return Environment; }
 
+  bool matchesSDKName(StringRef Name) const;
+
   const llvm::VersionTuple &getVersion() const { return Version; }
 
   const llvm::VersionTuple &getDefaultDeploymentTarget() const {
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 5f24adbf9e9cf..3a7bc5d820f3c 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -939,7 +939,7 @@ class Sema final : public SemaBase {
 
   DarwinSDKInfo *getDarwinSDKInfoForAvailabilityChecking(SourceLocation Loc,
                                                          StringRef Platform);
-  DarwinSDKInfo *getDarwinSDKInfoForAvailabilityChecking();
+  DarwinSDKInfo *getDarwinSDKInfo();
 
   /// Registers an external source. If an external source already exists,
   ///  creates a multiplex external source and appends to it.
diff --git a/clang/lib/APINotes/APINotesManager.cpp 
b/clang/lib/APINotes/APINotesManager.cpp
index 2cc801d5415b8..7ae04973c9757 100644
--- a/clang/lib/APINotes/APINotesManager.cpp
+++ b/clang/lib/APINotes/APINotesManager.cpp
@@ -48,6 +48,15 @@ class PrettyStackTraceDoubleString : public 
llvm::PrettyStackTraceEntry {
 };
 } // namespace
 
+/// Adapts an SDK info provider for passing down to the API notes YAML
+/// compiler, or an empty reference if no provider has been set.
+static DarwinSDKInfoProviderRef
+asProviderRef(llvm::unique_function<const DarwinSDKInfo *()> &Provider) {
+  if (!Provider)
+    return {};
+  return Provider;
+}
+
 APINotesManager::APINotesManager(SourceManager &SM, const LangOptions 
&LangOpts)
     : SM(SM), ImplicitAPINotes(LangOpts.APINotes),
       HasAPINotes(LangOpts.APINotes),
@@ -89,7 +98,8 @@ APINotesManager::loadAPINotes(FileEntryRef APINotesFile) {
     llvm::raw_svector_ostream OS(APINotesBuffer);
     if (api_notes::compileAPINotes(
             SourceBuffer->getBuffer(), SM.getFileEntryForID(SourceFileID), OS,
-            SMAdapter.getDiagHandler(), SMAdapter.getDiagContext()))
+            SMAdapter.getDiagHandler(), SMAdapter.getDiagContext(),
+            asProviderRef(SDKInfoProvider)) != CompileResult::Success)
       return nullptr;
 
     // Make a copy of the compiled form into the buffer.
@@ -115,9 +125,10 @@ APINotesManager::loadAPINotes(StringRef Buffer) {
       diag::warn_apinotes_message, diag::note_apinotes_message, std::nullopt);
   llvm::raw_svector_ostream OS(APINotesBuffer);
 
-  if (api_notes::compileAPINotes(Buffer, nullptr, OS,
-                                 SMAdapter.getDiagHandler(),
-                                 SMAdapter.getDiagContext()))
+  if (api_notes::compileAPINotes(
+          Buffer, nullptr, OS, SMAdapter.getDiagHandler(),
+          SMAdapter.getDiagContext(),
+          asProviderRef(SDKInfoProvider)) != CompileResult::Success)
     return nullptr;
 
   CompiledBuffer = llvm::MemoryBuffer::getMemBufferCopy(
@@ -225,15 +236,13 @@ static bool hasPrivateSubmodules(const Module *M) {
 }
 
 llvm::SmallVector<FileEntryRef, 2>
-APINotesManager::getCurrentModuleAPINotes(Module *M, bool LookInModule,
-                                          ArrayRef<std::string> SearchPaths) {
+APINotesManager::getCurrentModuleAPINotes(Module *M) {
   FileManager &FM = SM.getFileManager();
   auto ModuleName = M->getTopLevelModuleName();
   auto ExportedModuleName = M->getTopLevelModule()->ExportAsModule;
   llvm::SmallVector<FileEntryRef, 2> APINotes;
 
-  // First, look relative to the module itself.
-  if (LookInModule && M->Directory) {
+  if (M->Directory) {
     // Local function to try loading an API notes file in the given directory.
     auto tryAPINotes = [&](DirectoryEntryRef Dir, bool WantPublic) {
       if (auto File = findAPINotesFile(Dir, ModuleName, WantPublic)) {
@@ -287,23 +296,8 @@ APINotesManager::getCurrentModuleAPINotes(Module *M, bool 
LookInModule,
       if (!M->ModuleMapIsPrivate && hasPrivateSubmodules(M))
         tryAPINotes(*M->Directory, /*wantPublic=*/false);
     }
-
-    if (!APINotes.empty())
-      return APINotes;
-  }
-
-  // Second, look for API notes for this module in the module API
-  // notes search paths.
-  for (const auto &SearchPath : SearchPaths) {
-    if (auto SearchDir = FM.getOptionalDirectoryRef(SearchPath)) {
-      if (auto File = findAPINotesFile(*SearchDir, ModuleName)) {
-        APINotes.push_back(*File);
-        return APINotes;
-      }
-    }
   }
 
-  // Didn't find any API notes.
   return APINotes;
 }
 
@@ -312,12 +306,29 @@ bool APINotesManager::loadCurrentModuleAPINotes(
   assert(!CurrentModuleReaders[ReaderKind::Public] &&
          "Already loaded API notes for the current module?");
 
-  auto APINotes = getCurrentModuleAPINotes(M, LookInModule, SearchPaths);
   unsigned NumReaders = 0;
-  for (auto File : APINotes) {
-    CurrentModuleReaders[NumReaders++] = loadAPINotes(File).release();
-    if (!getCurrentModuleReaders().empty())
+  auto tryLoad = [&](FileEntryRef File) {
+    if (auto Reader = loadAPINotes(File)) {
+      CurrentModuleReaders[NumReaders++] = Reader.release();
       M->APINotesFile = File.getName().str();
+    }
+  };
+
+  // First, look relative to the module itself.
+  if (LookInModule)
+    for (FileEntryRef File : getCurrentModuleAPINotes(M))
+      tryLoad(File);
+
+  // Second, look for API notes for this module in the module API
+  // notes search paths.
+  FileManager &FM = SM.getFileManager();
+  for (const auto &SearchPath : SearchPaths) {
+    if (NumReaders > 0)
+      break;
+
+    if (auto SearchDir = FM.getOptionalDirectoryRef(SearchPath))
+      if (auto File = findAPINotesFile(*SearchDir, M->getTopLevelModuleName()))
+        tryLoad(*File);
   }
 
   if (NumReaders > 0)
@@ -329,10 +340,8 @@ bool APINotesManager::loadCurrentModuleAPINotesFromBuffer(
     ArrayRef<StringRef> Buffers) {
   unsigned NumReader = 0;
   for (auto Buf : Buffers) {
-    auto Reader = loadAPINotes(Buf);
-    assert(Reader && "Could not load the API notes we just generated?");
-
-    CurrentModuleReaders[NumReader++] = Reader.release();
+    if (auto Reader = loadAPINotes(Buf))
+      CurrentModuleReaders[NumReader++] = Reader.release();
   }
   if (NumReader > 0)
     HasAPINotes = true;
diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp 
b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
index 4079675228a21..81d211c64d296 100644
--- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp
+++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -16,6 +16,7 @@
 #include "clang/APINotes/APINotesYAMLCompiler.h"
 #include "clang/APINotes/APINotesWriter.h"
 #include "clang/APINotes/Types.h"
+#include "clang/Basic/DarwinSDKInfo.h"
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/Specifiers.h"
 #include "llvm/ADT/STLExtras.h"
@@ -731,10 +732,35 @@ template <> struct MappingTraits<Versioned> {
 } // namespace yaml
 } // namespace llvm
 
+namespace {
+struct ValidSDK {
+  StringRef Name;
+  VersionTuple ValidUntil;
+};
+
+typedef std::vector<ValidSDK> ValidSDKsSeq;
+} // namespace
+
+LLVM_YAML_IS_SEQUENCE_VECTOR(ValidSDK)
+
+namespace llvm {
+namespace yaml {
+template <> struct MappingTraits<ValidSDK> {
+  static void mapping(IO &IO, ValidSDK &S) {
+    // Name is a plain string rather than an enumeration. Unrecognized SDK 
names
+    // are handled by clients.
+    IO.mapRequired("Name", S.Name);
+    IO.mapRequired("ValidUntil", S.ValidUntil);
+  }
+};
+} // namespace yaml
+} // namespace llvm
+
 namespace {
 struct Module {
   StringRef Name;
   AvailabilityItem Availability;
+  ValidSDKsSeq ValidSDKs;
   TopLevelItems TopLevel;
   VersionedSeq SwiftVersions;
 
@@ -755,6 +781,7 @@ template <> struct MappingTraits<Module> {
                    APIAvailability::Available);
     IO.mapOptional("AvailabilityMsg", M.Availability.Msg, StringRef(""));
     IO.mapOptional("SwiftInferImportAsMember", M.SwiftInferImportAsMember);
+    IO.mapOptional("ValidSDKs", M.ValidSDKs);
     mapTopLevelItems(IO, M.TopLevel);
     IO.mapOptional("SwiftVersions", M.SwiftVersions);
   }
@@ -1392,18 +1419,45 @@ static void printDiagnostic(const llvm::SMDiagnostic 
&Diag, void *Context) {
   Diag.print(nullptr, llvm::errs());
 }
 
-bool api_notes::compileAPINotes(StringRef YAMLInput,
-                                const FileEntry *SourceFile,
-                                llvm::raw_ostream &OS,
-                                llvm::SourceMgr::DiagHandlerTy DiagHandler,
-                                void *DiagHandlerCtxt) {
+static bool isValidForSDK(const ValidSDKsSeq &ValidSDKs,
+                          DarwinSDKInfoProviderRef GetSDKInfo) {
+  if (ValidSDKs.empty() || !GetSDKInfo)
+    return true;
+
+  // If there's no SDK info, assume an old SDK and apply the API notes.
+  const DarwinSDKInfo *SDK = GetSDKInfo();
+  if (!SDK)
+    return true;
+
+  const auto Entry = llvm::find_if(ValidSDKs, [SDK](const ValidSDK &S) {
+    return SDK->matchesSDKName(S.Name);
+  });
+
+  // If the SDK doesn't match any of the valid SDKs, assume a new SDK that
+  // should skip the obsolete API notes.
+  if (Entry == ValidSDKs.end())
+    return false;
+
+  return SDK->getVersion() < Entry->ValidUntil;
+}
+
+CompileResult api_notes::compileAPINotes(
+    StringRef YAMLInput, const FileEntry *SourceFile, llvm::raw_ostream &OS,
+    llvm::SourceMgr::DiagHandlerTy DiagHandler, void *DiagHandlerCtxt,
+    DarwinSDKInfoProviderRef GetSDKInfo) {
   Module TheModule;
 
   if (!DiagHandler)
     DiagHandler = &printDiagnostic;
 
   if (parseAPINotes(YAMLInput, TheModule, DiagHandler, DiagHandlerCtxt))
-    return true;
+    return CompileResult::Error;
+
+  if (!isValidForSDK(TheModule.ValidSDKs, GetSDKInfo))
+    return CompileResult::Skipped;
+
+  if (compile(TheModule, SourceFile, OS, DiagHandler, DiagHandlerCtxt))
+    return CompileResult::Error;
 
-  return compile(TheModule, SourceFile, OS, DiagHandler, DiagHandlerCtxt);
+  return CompileResult::Success;
 }
diff --git a/clang/lib/Basic/DarwinSDKInfo.cpp 
b/clang/lib/Basic/DarwinSDKInfo.cpp
index 63608e3e4ad8b..5004e5b165ab5 100644
--- a/clang/lib/Basic/DarwinSDKInfo.cpp
+++ b/clang/lib/Basic/DarwinSDKInfo.cpp
@@ -397,6 +397,13 @@ findPlatformInfo(const 
DarwinSDKInfo::PlatformInfoStorageType &PlatformInfos,
   return PlatformInfoIt;
 }
 
+bool DarwinSDKInfo::matchesSDKName(StringRef Name) const {
+  std::pair<llvm::Triple::OSType, llvm::Triple::EnvironmentType>
+      OSAndEnvironment = parseOSAndEnvironment(Name);
+  return (OSAndEnvironment.first == OS) &&
+         (OSAndEnvironment.second == Environment);
+}
+
 bool DarwinSDKInfo::supportsTriple(const llvm::Triple &Triple) const {
   return findPlatformInfo(PlatformInfos, Triple) != PlatformInfos.end();
 }
diff --git a/clang/lib/Frontend/CompilerInstance.cpp 
b/clang/lib/Frontend/CompilerInstance.cpp
index 87abcd38c1a92..0dce1e86e560e 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -775,6 +775,10 @@ void CompilerInstance::createSema(TranslationUnitKind 
TUKind,
 
   // Set up API notes.
   TheSema->APINotes.setSwiftVersion(getAPINotesOpts().SwiftVersion);
+  // Resolve the SDK lazily, since only an API notes file that declares
+  // 'ValidSDKs' needs it.
+  TheSema->APINotes.setSDKInfoProvider(
+      [&S = *TheSema]() { return S.getDarwinSDKInfo(); });
 
   // Attach the external sema source if there is any.
   if (ExternalSemaSrc) {
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 21f71d7f8b40e..3b997f478769a 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -112,7 +112,7 @@ ModuleLoader &Sema::getModuleLoader() const { return 
PP.getModuleLoader(); }
 DarwinSDKInfo *
 Sema::getDarwinSDKInfoForAvailabilityChecking(SourceLocation Loc,
                                               StringRef Platform) {
-  auto *SDKInfo = getDarwinSDKInfoForAvailabilityChecking();
+  auto *SDKInfo = getDarwinSDKInfo();
   if (!SDKInfo && !WarnedDarwinSDKInfoMissing) {
     Diag(Loc, diag::warn_missing_sdksettings_for_availability_checking)
         << Platform;
@@ -121,7 +121,7 @@ 
Sema::getDarwinSDKInfoForAvailabilityChecking(SourceLocation Loc,
   return SDKInfo;
 }
 
-DarwinSDKInfo *Sema::getDarwinSDKInfoForAvailabilityChecking() {
+DarwinSDKInfo *Sema::getDarwinSDKInfo() {
   if (CachedDarwinSDKInfo)
     return CachedDarwinSDKInfo->get();
   auto SDKInfo = parseDarwinSDKInfo(
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index eb4a8c2ab9ae0..2d06d0ec51521 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -2904,7 +2904,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, 
const ParsedAttr &AL) {
       NewII = &S.Context.Idents.get("watchos_app_extension");
 
     if (NewII) {
-      const auto *SDKInfo = S.getDarwinSDKInfoForAvailabilityChecking();
+      const auto *SDKInfo = S.getDarwinSDKInfo();
       const auto *IOSToWatchOSMapping =
           SDKInfo ? SDKInfo->getVersionMapping(
                         DarwinSDKInfo::OSEnvPair::iOStoWatchOSPair())
@@ -2965,7 +2965,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, 
const ParsedAttr &AL) {
       NewII = &S.Context.Idents.get("tvos_app_extension");
 
     if (NewII) {
-      const auto *SDKInfo = S.getDarwinSDKInfoForAvailabilityChecking();
+      const auto *SDKInfo = S.getDarwinSDKInfo();
       const auto *IOSToTvOSMapping =
           SDKInfo ? SDKInfo->getVersionMapping(
                         DarwinSDKInfo::OSEnvPair::iOStoTvOSPair())
diff --git a/clang/test/APINotes/Inputs/APINotesAged/SomeOtherKit.apinotes 
b/clang/test/APINotes/Inputs/APINotesAged/SomeOtherKit.apinotes
new file mode 100644
index 0000000000000..a782f439b247c
--- /dev/null
+++ b/clang/test/APINotes/Inputs/APINotesAged/SomeOtherKit.apinotes
@@ -0,0 +1,14 @@
+Name: SomeOtherKit
+# Stands in for a search path whose notes are no longer valid: ages out on the
+# macOS 15.0 SDK. Annotates methodC rather than methodB so that it is 
observable
+# which of several search paths was used.
+ValidSDKs:
+  - Name: macosx
+    ValidUntil: 15.0
+Classes:
+  - Name: A
+    Methods:
+      - Selector:        "methodC"
+        MethodKind:      Instance
+        Availability:    none
+        AvailabilityMsg: "anything but this"
diff --git 
a/clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.apinotes
 
b/clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.apinotes
index 2ad546b8f8bcc..6ee7a258cb590 100644
--- 
a/clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.apinotes
+++ 
b/clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.apinotes
@@ -1,4 +1,9 @@
 Name: SomeOtherKit
+# Ages out on the macOS 15.0 SDK. Tests that pass no -isysroot see no SDK, so
+# this still applies for them.
+ValidSDKs:
+  - Name: macosx
+    ValidUntil: 15.0
 Classes:
   - Name: A
     Methods:
diff --git 
a/clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.h
 
b/clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.h
index 3911d765230c6..b702cfc5fb9e4 100644
--- 
a/clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.h
+++ 
b/clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.h
@@ -4,6 +4,7 @@ __attribute__((objc_root_class))
 @interface A
 -(void)methodA;
 -(void)methodB;
+-(void)methodC;
 @end
 
 #endif
diff --git a/clang/test/APINotes/Inputs/Headers/AgingLib.h 
b/clang/test/APINotes/Inputs/Headers/AgingLib.h
new file mode 100644
index 0000000000000..15a863cac7084
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Headers/AgingLib.h
@@ -0,0 +1,8 @@
+#ifndef AGING_LIB_H
+#define AGING_LIB_H
+
+// Annotated as unavailable by AgingLib.apinotes. Whether that note applies
+// depends on the SDK being compiled against; see test/APINotes/valid-sdks.c.
+void aged_out_function(void);
+
+#endif
diff --git a/clang/test/APINotes/Inputs/Headers/PublicPrivateLib.apinotes 
b/clang/test/APINotes/Inputs/Headers/PublicPrivateLib.apinotes
new file mode 100644
index 0000000000000..1e1a5b5beffd9
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Headers/PublicPrivateLib.apinotes
@@ -0,0 +1,14 @@
+---
+Name: PublicPrivateLib
+# Valid below macOS 11.0 but well into the iOS SDKs, which is the opposite of 
the
+# private file below. That lets one module cover every combination of the two
+# in-module readers surviving or ageing out, just by picking the SDK.
+ValidSDKs:
+  - Name: macosx
+    ValidUntil: 11.0
+  - Name: iphoneos
+    ValidUntil: 99.0
+Functions:
+  - Name: public_note_fn
+    Availability: none
+    AvailabilityMsg: "from the public API notes"
diff --git a/clang/test/APINotes/Inputs/Headers/PublicPrivateLib.h 
b/clang/test/APINotes/Inputs/Headers/PublicPrivateLib.h
new file mode 100644
index 0000000000000..c945c5d944ede
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Headers/PublicPrivateLib.h
@@ -0,0 +1,10 @@
+#ifndef PUBLIC_PRIVATE_LIB_H
+#define PUBLIC_PRIVATE_LIB_H
+
+// Annotated by PublicPrivateLib.apinotes.
+void public_note_fn(void);
+
+// Annotated by PublicPrivateLib_private.apinotes.
+void private_note_fn(void);
+
+#endif
diff --git 
a/clang/test/APINotes/Inputs/Headers/PublicPrivateLib_private.apinotes 
b/clang/test/APINotes/Inputs/Headers/PublicPrivateLib_private.apinotes
new file mode 100644
index 0000000000000..e36b2e83a6796
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Headers/PublicPrivateLib_private.apinotes
@@ -0,0 +1,13 @@
+---
+Name: PublicPrivateLib
+# Deliberately the mirror of the public file: valid much further into the macOS
+# SDKs, but already expired for any iOS SDK.
+ValidSDKs:
+  - Name: macosx
+    ValidUntil: 15.0
+  - Name: iphoneos
+    ValidUntil: 1.0
+Functions:
+  - Name: private_note_fn
+    Availability: none
+    AvailabilityMsg: "from the private API notes"
diff --git a/clang/test/APINotes/Inputs/Headers/module.modulemap 
b/clang/test/APINotes/Inputs/Headers/module.modulemap
index a9b273ccc90e6..9f102ef3456b4 100644
--- a/clang/test/APINotes/Inputs/Headers/module.modulemap
+++ b/clang/test/APINotes/Inputs/Headers/module.modulemap
@@ -84,3 +84,11 @@ module RedeclAnnotation {
   header "RedeclAnnotation.h"
   export *
 }
+
+module AgingLib {
+  header "AgingLib.h"
+}
+
+module PublicPrivateLib {
+  header "PublicPrivateLib.h"
+}
diff --git a/clang/test/APINotes/Inputs/Headers/module.private.modulemap 
b/clang/test/APINotes/Inputs/Headers/module.private.modulemap
index 2ecf322ed18d9..91472ff8c847f 100644
--- a/clang/test/APINotes/Inputs/Headers/module.private.modulemap
+++ b/clang/test/APINotes/Inputs/Headers/module.private.modulemap
@@ -3,3 +3,7 @@ module PrivateLib {
 }
 
 module ModuleWithWrongCasePrivate.Inner {}
+
+// Gives PublicPrivateLib a private submodule, which is what makes its
+// _private.apinotes file be looked for at all.
+module PublicPrivateLib.Inner {}
diff --git a/clang/test/APINotes/Inputs/ValidSDKs/device-only/AgingLib.apinotes 
b/clang/test/APINotes/Inputs/ValidSDKs/device-only/AgingLib.apinotes
new file mode 100644
index 0000000000000..57659b53e8d25
--- /dev/null
+++ b/clang/test/APINotes/Inputs/ValidSDKs/device-only/AgingLib.apinotes
@@ -0,0 +1,9 @@
+---
+Name: AgingLib
+ValidSDKs:
+  - Name: xros
+    ValidUntil: 99.0
+Functions:
+  - Name: aged_out_function
+    Availability: none
+    AvailabilityMsg: "the SDK now declares this itself"
diff --git 
a/clang/test/APINotes/Inputs/ValidSDKs/different-simulator-version/AgingLib.apinotes
 
b/clang/test/APINotes/Inputs/ValidSDKs/different-simulator-version/AgingLib.apinotes
new file mode 100644
index 0000000000000..07591e348c0d0
--- /dev/null
+++ 
b/clang/test/APINotes/Inputs/ValidSDKs/different-simulator-version/AgingLib.apinotes
@@ -0,0 +1,11 @@
+---
+Name: AgingLib
+ValidSDKs:
+  - Name: xros
+    ValidUntil: 1.0
+  - Name: xrsimulator
+    ValidUntil: 2.0
+Functions:
+  - Name: aged_out_function
+    Availability: none
+    AvailabilityMsg: "the SDK now declares this itself"
diff --git 
a/clang/test/APINotes/Inputs/ValidSDKs/multiple-sdks/AgingLib.apinotes 
b/clang/test/APINotes/Inputs/ValidSDKs/multiple-sdks/AgingLib.apinotes
new file mode 100644
index 0000000000000..5e8f5524c77a8
--- /dev/null
+++ b/clang/test/APINotes/Inputs/ValidSDKs/multiple-sdks/AgingLib.apinotes
@@ -0,0 +1,11 @@
+---
+Name: AgingLib
+ValidSDKs:
+  - Name: iphoneos
+    ValidUntil: 12.0
+  - Name: macosx
+    ValidUntil: 15.0
+Functions:
+  - Name: aged_out_function
+    Availability: none
+    AvailabilityMsg: "the SDK now declares this itself"
diff --git a/clang/test/APINotes/search-order.m 
b/clang/test/APINotes/search-order.m
index 17e81d5eb2d69..23c1affb54496 100644
--- a/clang/test/APINotes/search-order.m
+++ b/clang/test/APINotes/search-order.m
@@ -2,23 +2,45 @@
 
 // RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -I 
%S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_FRAMEWORK=1 -verify
 
-// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes  
-fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s 
-DFROM_SEARCH_PATH=1 -verify
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes  
-fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_APINOTES=1 
-verify
 
 // RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache -fapinotes-modules -iapinotes-modules 
%S/Inputs/APINotes  -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks 
%s -DFROM_FRAMEWORK=1 -verify
 
+// First search path wins.
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/APINotesFirst -iapinotes-modules %S/Inputs/APINotes 
-iapinotes-modules %S/Inputs/APINotesAged -fsyntax-only -I %S/Inputs/Headers -F 
%S/Inputs/Frameworks %s -DFROM_APINOTES=1 -verify
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/APINotesAgedFirst -iapinotes-modules 
%S/Inputs/APINotesAged -iapinotes-modules %S/Inputs/APINotes -fsyntax-only -I 
%S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_APINOTES_AGED=1 -verify
+
+// Use -isysroot to selectively skip the API notes which use 
ValidSDKs/ValidUntil.
+// Skipped API notes should behave as if they weren't present at all. If 
non-skipped
+// API notes are found, they should be used even if skipped ones are later in 
the
+// search path. If skipped API notes are found, the search should continue 
through
+// the search path for non-skipped API notes.
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModuleNotesAgedOut -fapinotes-modules 
-iapinotes-modules %S/Inputs/APINotes -isysroot 
%S/../Driver/Inputs/MacOSX15.0.sdk -fsyntax-only -I %S/Inputs/Headers -F 
%S/Inputs/Frameworks %s -DFROM_APINOTES=1 -verify
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/SearchPathNotConsulted -fapinotes-modules 
-iapinotes-modules %S/Inputs/APINotesAged -isysroot 
%S/../Driver/Inputs/MacOSX10.15.sdk -fsyntax-only -I %S/Inputs/Headers -F 
%S/Inputs/Frameworks %s -DFROM_FRAMEWORK=1 -verify
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/APINotesAgedFirstWithSDK -iapinotes-modules 
%S/Inputs/APINotesAged -iapinotes-modules %S/Inputs/APINotes -isysroot 
%S/../Driver/Inputs/MacOSX15.0.sdk -fsyntax-only -I %S/Inputs/Headers -F 
%S/Inputs/Frameworks %s -DFROM_APINOTES=1 -verify
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/APINotesFirstWithSDK -iapinotes-modules 
%S/Inputs/APINotes -iapinotes-modules %S/Inputs/APINotesAged -isysroot 
%S/../Driver/Inputs/MacOSX15.0.sdk -fsyntax-only -I %S/Inputs/Headers -F 
%S/Inputs/Frameworks %s -DFROM_APINOTES=1 -verify
+
 @import SomeOtherKit;
 
 void test(A *a) {
 #if FROM_FRAMEWORK
   [a methodA]; // expected-error{{unavailable}}
   [a methodB];
+  [a methodC];
 
   // expected-note@SomeOtherKit/SomeOtherKit.h:5{{'methodA' has been 
explicitly marked unavailable here}}
-#elif FROM_SEARCH_PATH
+#elif FROM_APINOTES
   [a methodA];
   [a methodB]; // expected-error{{unavailable}}
+  [a methodC];
 
   // expected-note@SomeOtherKit/SomeOtherKit.h:6{{'methodB' has been 
explicitly marked unavailable here}}
+#elif FROM_APINOTES_AGED
+  [a methodA];
+  [a methodB];
+  [a methodC]; // expected-error{{unavailable}}
+
+  // expected-note@SomeOtherKit/SomeOtherKit.h:7{{'methodC' has been 
explicitly marked unavailable here}}
 #else
 #  error Not something we need to test
 #endif
diff --git a/clang/test/APINotes/valid-sdks-implicit-search.c 
b/clang/test/APINotes/valid-sdks-implicit-search.c
new file mode 100644
index 0000000000000..ae23858337644
--- /dev/null
+++ b/clang/test/APINotes/valid-sdks-implicit-search.c
@@ -0,0 +1,60 @@
+// RUN: rm -rf %t && split-file %s %t
+
+// On macOS 10.15 the inner notes apply, so the outer notes are not used.
+// RUN: %clang_cc1 -triple arm64-apple-macosx10.15 -fapinotes \
+// RUN:   -isysroot %S/../Driver/Inputs/MacOSX10.15.sdk \
+// RUN:   -fsyntax-only -I %t/outer/inner %t/test.c -verify=inner
+
+// On macOS 15.0 the inner notes are skipped and the outer notes are used.
+// RUN: %clang_cc1 -triple arm64-apple-macosx15.0 -fapinotes \
+// RUN:   -isysroot %S/../Driver/Inputs/MacOSX15.0.sdk \
+// RUN:   -fsyntax-only -I %t/outer/inner %t/test.c -verify=outer
+
+//--- outer/APINotes.apinotes
+---
+Name: NestedNotes
+# No ValidSDKs, so this always applies. Only reached when the inner file does 
not.
+Functions:
+  - Name: outer_fn
+    Availability: none
+    AvailabilityMsg: "from the outer directory"
+
+//--- outer/inner/APINotes.apinotes
+---
+Name: NestedNotes
+# Ages out on the macOS 15.0 SDK, at which point the walk has to carry on 
upwards.
+ValidSDKs:
+  - Name: macosx
+    ValidUntil: 15.0
+Functions:
+  - Name: inner_fn
+    Availability: none
+    AvailabilityMsg: "from the inner directory"
+
+//--- outer/inner/nested.h
+#ifndef NESTED_H
+#define NESTED_H
+
+// Annotated by outer/inner/APINotes.apinotes.
+void inner_fn(void);
+
+// Annotated by outer/APINotes.apinotes.
+void outer_fn(void);
+
+#endif
+
+//--- test.c
+#include "nested.h"
+
+// Exactly one of the two is annotated in each case, so the other acts as a
+// negative control: reading both files, or the wrong one, shows up as an
+// unexpected diagnostic.
+void test(void) {
+  inner_fn();
+  // inner-error@-1{{'inner_fn' is unavailable: from the inner directory}}
+  // [email protected]:5{{'inner_fn' has been explicitly marked unavailable 
here}}
+
+  outer_fn();
+  // outer-error@-1{{'outer_fn' is unavailable: from the outer directory}}
+  // [email protected]:8{{'outer_fn' has been explicitly marked unavailable 
here}}
+}
diff --git a/clang/test/APINotes/valid-sdks-private-notes.c 
b/clang/test/APINotes/valid-sdks-private-notes.c
new file mode 100644
index 0000000000000..d2be63a790e7a
--- /dev/null
+++ b/clang/test/APINotes/valid-sdks-private-notes.c
@@ -0,0 +1,45 @@
+// RUN: rm -rf %t
+
+// Both API notes apply on macOS 10.15.
+// RUN: %clang_cc1 -triple arm64-apple-macosx10.15 -fmodules 
-fimplicit-module-maps \
+// RUN:   -fmodules-cache-path=%t/both -fapinotes-modules \
+// RUN:   -isysroot %S/../Driver/Inputs/MacOSX10.15.sdk \
+// RUN:   -fsyntax-only -I %S/Inputs/Headers %s -verify=both
+
+// The public API notes are skipped on macOS 13.0, but the private API notes 
should
+// still apply.
+// RUN: %clang_cc1 -triple arm64-apple-macosx13.0 -fmodules 
-fimplicit-module-maps \
+// RUN:   -fmodules-cache-path=%t/private-only -fapinotes-modules \
+// RUN:   -isysroot %S/../InstallAPI/Inputs/MacOSX13.0.sdk \
+// RUN:   -fsyntax-only -I %S/Inputs/Headers %s -verify=privateonly
+
+// The private API notes are skipped on iOS 13.0, but the public API notes 
should
+// still apply.
+// RUN: %clang_cc1 -triple arm64-apple-ios13.0 -fmodules 
-fimplicit-module-maps \
+// RUN:   -fmodules-cache-path=%t/public-only -fapinotes-modules \
+// RUN:   -isysroot %S/../Driver/Inputs/iPhoneOS13.0.sdk \
+// RUN:   -fsyntax-only -I %S/Inputs/Headers %s -verify=publiconly
+
+// Both API notes are skipped on macOS 15.0.
+// RUN: %clang_cc1 -triple arm64-apple-macosx15.0 -fmodules 
-fimplicit-module-maps \
+// RUN:   -fmodules-cache-path=%t/neither -fapinotes-modules \
+// RUN:   -isysroot %S/../Driver/Inputs/MacOSX15.0.sdk \
+// RUN:   -fsyntax-only -I %S/Inputs/Headers %s -verify=neither
+
+// neither-no-diagnostics
+
+#include "PublicPrivateLib.h"
+
+void test(void) {
+  public_note_fn();
+  // both-error@-1{{'public_note_fn' is unavailable: from the public API 
notes}}
+  // [email protected]:5{{'public_note_fn' has been explicitly 
marked unavailable here}}
+  // publiconly-error@-3{{'public_note_fn' is unavailable: from the public API 
notes}}
+  // [email protected]:5{{'public_note_fn' has been 
explicitly marked unavailable here}}
+
+  private_note_fn();
+  // both-error@-1{{'private_note_fn' is unavailable: from the private API 
notes}}
+  // [email protected]:8{{'private_note_fn' has been explicitly 
marked unavailable here}}
+  // privateonly-error@-3{{'private_note_fn' is unavailable: from the private 
API notes}}
+  // [email protected]:8{{'private_note_fn' has been 
explicitly marked unavailable here}}
+}
diff --git a/clang/test/APINotes/valid-sdks.c b/clang/test/APINotes/valid-sdks.c
new file mode 100644
index 0000000000000..2b9887cdfbcae
--- /dev/null
+++ b/clang/test/APINotes/valid-sdks.c
@@ -0,0 +1,98 @@
+// RUN: rm -rf %t
+
+// API notes without ValidSDKs/ValidUntil always apply.
+// RUN: %clang_cc1 -triple arm64-apple-macosx15.0 -fmodules 
-fimplicit-module-maps \
+// RUN:   -fmodules-cache-path=%t/no-key -fapinotes-modules \
+// RUN:   -isysroot %S/../Driver/Inputs/MacOSX15.0.sdk \
+// RUN:   -fsyntax-only -I %S/Inputs/Headers %s -verify=nokey
+
+// Old SDK versions get the API notes, even when a new delopment version is 
used.
+// RUN: %clang_cc1 -triple arm64-apple-macosx15.0 -fmodules 
-fimplicit-module-maps \
+// RUN:   -fmodules-cache-path=%t/below -iapinotes-modules 
%S/Inputs/ValidSDKs/multiple-sdks \
+// RUN:   -isysroot %S/../Driver/Inputs/MacOSX10.15.sdk \
+// RUN:   -fsyntax-only -I %S/Inputs/Headers %s -verify=applies
+
+// API notes are skipped when the SDK reaches the ValidUntil version.
+// RUN: %clang_cc1 -triple arm64-apple-macosx10.15 -fmodules 
-fimplicit-module-maps \
+// RUN:   -fmodules-cache-path=%t/at-boundary -iapinotes-modules 
%S/Inputs/ValidSDKs/multiple-sdks \
+// RUN:   -isysroot %S/../Driver/Inputs/MacOSX15.0.sdk \
+// RUN:   -fsyntax-only -I %S/Inputs/Headers %s -verify=skipped
+
+// New SDK versions skip the API notes, even when an old deployment version is 
used.
+// RUN: %clang_cc1 -triple arm64-apple-ios11.0 -fmodules 
-fimplicit-module-maps \
+// RUN:   -fmodules-cache-path=%t/newer-than-cutoff \
+// RUN:   -iapinotes-modules %S/Inputs/ValidSDKs/multiple-sdks \
+// RUN:   -isysroot %S/../Driver/Inputs/iPhoneOS13.0.sdk \
+// RUN:   -fsyntax-only -I %S/Inputs/Headers %s -verify=skipped
+
+// Totally new SDKs skip the API notes.
+// RUN: %clang_cc1 -triple arm64-apple-xros1.0 -fmodules 
-fimplicit-module-maps \
+// RUN:   -fmodules-cache-path=%t/unlisted-platform \
+// RUN:   -iapinotes-modules %S/Inputs/ValidSDKs/multiple-sdks \
+// RUN:   -isysroot %S/../Driver/Inputs/XROS1.0.sdk \
+// RUN:   -fsyntax-only -I %S/Inputs/Headers %s -verify=skipped
+
+// Particularly old SDK versions have no SDKSettings, the API notes apply even 
if the
+// SDK wasn't listed.
+// RUN: %clang_cc1 -triple x86_64-apple-driverkit19.0 -fmodules 
-fimplicit-module-maps \
+// RUN:   -fmodules-cache-path=%t/no-sdksettings \
+// RUN:   -iapinotes-modules %S/Inputs/ValidSDKs/multiple-sdks \
+// RUN:   -isysroot %S/../Driver/Inputs/DriverKit19.0.sdk \
+// RUN:   -fsyntax-only -I %S/Inputs/Headers %s -verify=applies
+
+// An -isysroot pointing inside the SDK is a gray area, err on the side of 
applying
+// the API notes.
+// RUN: %clang_cc1 -triple arm64-apple-xros1.0-simulator -fmodules 
-fimplicit-module-maps \
+// RUN:   -fmodules-cache-path=%t/nested-sysroot \
+// RUN:   -iapinotes-modules %S/Inputs/ValidSDKs/multiple-sdks \
+// RUN:   -isysroot %S/../Driver/Inputs/XRSimulator1.0.sdk/usr/include/libxml \
+// RUN:   -fsyntax-only -I %S/Inputs/Headers %s -verify=applies
+
+// No SDK provided, the triple doesn't get used as a fallback.
+// RUN: %clang_cc1 -triple arm64-apple-macosx15.0 -fmodules 
-fimplicit-module-maps \
+// RUN:   -fmodules-cache-path=%t/no-sdk -iapinotes-modules 
%S/Inputs/ValidSDKs/multiple-sdks \
+// RUN:   -fsyntax-only -I %S/Inputs/Headers %s -verify=applies
+
+// Non-Darwin targets have nothing to fall back on, API notes always apply.
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fmodules 
-fimplicit-module-maps \
+// RUN:   -fmodules-cache-path=%t/non-darwin -iapinotes-modules 
%S/Inputs/ValidSDKs/multiple-sdks \
+// RUN:   -fsyntax-only -I %S/Inputs/Headers %s -verify=applies
+
+// Device and simulator SDKs have to both be specified.
+// RUN: %clang_cc1 -triple arm64-apple-xros1.0 -fmodules 
-fimplicit-module-maps \
+// RUN:   -fmodules-cache-path=%t/device -iapinotes-modules 
%S/Inputs/ValidSDKs/device-only \
+// RUN:   -isysroot %S/../Driver/Inputs/XROS1.0.sdk \
+// RUN:   -fsyntax-only -I %S/Inputs/Headers %s -verify=applies
+// RUN: %clang_cc1 -triple arm64-apple-xros1.0-simulator -fmodules 
-fimplicit-module-maps \
+// RUN:   -fmodules-cache-path=%t/simulator -iapinotes-modules 
%S/Inputs/ValidSDKs/device-only \
+// RUN:   -isysroot %S/../Driver/Inputs/XRSimulator1.0.sdk \
+// RUN:   -fsyntax-only -I %S/Inputs/Headers %s -verify=skipped
+
+// Device and simulator SDKs can have different versions.
+// RUN: %clang_cc1 -triple arm64-apple-xros1.0 -fmodules 
-fimplicit-module-maps \
+// RUN:   -fmodules-cache-path=%t/device-versioned \
+// RUN:   -iapinotes-modules %S/Inputs/ValidSDKs/different-simulator-version \
+// RUN:   -isysroot %S/../Driver/Inputs/XROS1.0.sdk \
+// RUN:   -fsyntax-only -I %S/Inputs/Headers %s -verify=skipped
+// RUN: %clang_cc1 -triple arm64-apple-xros2.0-simulator -fmodules 
-fimplicit-module-maps \
+// RUN:   -fmodules-cache-path=%t/simulator-versioned \
+// RUN:   -iapinotes-modules %S/Inputs/ValidSDKs/different-simulator-version \
+// RUN:   -isysroot %S/../Driver/Inputs/XRSimulator1.0.sdk \
+// RUN:   -fsyntax-only -I %S/Inputs/Headers %s -verify=applies
+
+// skipped-no-diagnostics
+
+#include "AgingLib.h"
+// HeaderLib's existing API notes declare no ValidSDKs; only the first RUN 
above
+// enables them, so the other cases see this declaration unannotated.
+#include "HeaderLib.h"
+
+void test(void) {
+  aged_out_function();
+  // applies-error@-1{{'aged_out_function' is unavailable: the SDK now 
declares this itself}}
+  // [email protected]:6{{'aged_out_function' has been explicitly marked 
unavailable here}}
+
+  unavailable_function();
+  // nokey-error@-1{{'unavailable_function' is unavailable: I beg you not to 
use this}}
+  // [email protected]:8{{'unavailable_function' has been explicitly 
marked unavailable here}}
+}
diff --git a/clang/test/APINotes/yaml-roundtrip-valid-sdks.test 
b/clang/test/APINotes/yaml-roundtrip-valid-sdks.test
new file mode 100644
index 0000000000000..7e7a7e86a901c
--- /dev/null
+++ b/clang/test/APINotes/yaml-roundtrip-valid-sdks.test
@@ -0,0 +1,17 @@
+Checks that 'ValidSDKs' survives a YAML -> YAML round trip, i.e. that
+parseAndDumpAPINotes does not silently drop the key or any of its entries.
+
+RUN: apinotes-test %S/Inputs/ValidSDKs/multiple-sdks/AgingLib.apinotes | 
FileCheck %s
+
+CHECK:      Name:{{ +}}AgingLib
+CHECK:      ValidSDKs:
+CHECK-NEXT: {{ +}}- Name:{{ +}}iphoneos
+CHECK-NEXT: {{ +}}ValidUntil:{{ +}}12.0
+CHECK-NEXT: {{ +}}- Name:{{ +}}macosx
+CHECK-NEXT: {{ +}}ValidUntil:{{ +}}15.0
+
+The rest of the file must still round trip -- a malformed ValidSDKs mapping
+would otherwise swallow the following keys.
+
+CHECK:      Functions:
+CHECK-NEXT: {{ +}}- Name:{{ +}}aged_out_function

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

Reply via email to