Skip to content

Implement Sema iosmac support - #218843

Closed
ian-twilightcoder wants to merge 1 commit into
llvm:mainfrom
ian-twilightcoder:sema-iosmac
Closed

ian-twilightcoder wants to merge 1 commit into
llvm:mainfrom
ian-twilightcoder:sema-iosmac

Conversation

@ian-twilightcoder

@ian-twilightcoder ian-twilightcoder commented Aug 26, 2026 •

Copy link
Copy Markdown
Contributor

First commit in a series of upstreaming -target-variant support in Sema.

rdar://38885683

apple-llvm-split-commit: 40aab84d831ddfc623de95fbbca06de6f0baf0a6
apple-llvm-split-dir: clang/

@llvmorg-github-actions llvmorg-github-actions Bot added clang Clang issues not falling into any other category backend:ARM clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:modules C++20 modules and Clang Header Modules clang:codegen IR generation bugs: mangling, exceptions, etc. clang:as-a-library libclang and C++ API labels Aug 26, 2026
@llvmorg-github-actions

llvmorg-github-actions Bot commented Aug 26, 2026 •

Copy link
Copy Markdown

@llvm/pr-subscribers-clangd
@llvm/pr-subscribers-clang
@llvm/pr-subscribers-backend-arm

@llvm/pr-subscribers-clang-codegen

Author: Ian Anderson (ian-twilightcoder)

Changes

rdar://38885683

apple-llvm-split-commit: 40aab84d831ddfc623de95fbbca06de6f0baf0a6
apple-llvm-split-dir: clang/


Patch is 121.04 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/218843.diff

33 Files Affected:

  • (modified) clang/include/clang/AST/DeclBase.h (+14-4)
  • (modified) clang/include/clang/AST/ExprObjC.h (+12-3)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+3)
  • (modified) clang/include/clang/Basic/TargetInfo.h (+24)
  • (modified) clang/include/clang/Sema/DelayedDiagnostic.h (+12-8)
  • (modified) clang/include/clang/Sema/Sema.h (+7-1)
  • (modified) clang/lib/AST/DeclBase.cpp (+45-9)
  • (modified) clang/lib/Basic/Targets/ARM.cpp (+3-1)
  • (modified) clang/lib/Basic/Targets/OSTargets.cpp (+12-2)
  • (modified) clang/lib/Basic/Targets/OSTargets.h (+18-4)
  • (modified) clang/lib/CodeGen/CGExprScalar.cpp (+8-2)
  • (modified) clang/lib/CodeGen/CGObjC.cpp (+44-11)
  • (modified) clang/lib/CodeGen/CodeGenFunction.h (+2-1)
  • (modified) clang/lib/CodeGen/CodeGenModule.h (+2)
  • (modified) clang/lib/Edit/RewriteObjCFoundationAPI.cpp (+1-1)
  • (modified) clang/lib/Sema/CodeCompleteConsumer.cpp (+10-2)
  • (modified) clang/lib/Sema/DelayedDiagnostic.cpp (+7-9)
  • (modified) clang/lib/Sema/SemaAvailability.cpp (+817-381)
  • (modified) clang/lib/Sema/SemaDecl.cpp (+3-2)
  • (modified) clang/lib/Sema/SemaDeclAttr.cpp (+21-4)
  • (modified) clang/lib/Sema/SemaDeclObjC.cpp (+31-14)
  • (modified) clang/lib/Sema/SemaExpr.cpp (+2-2)
  • (modified) clang/lib/Sema/SemaExprObjC.cpp (+12-2)
  • (modified) clang/lib/Sema/SemaLookup.cpp (+1-1)
  • (modified) clang/lib/Sema/SemaObjCProperty.cpp (+4-5)
  • (modified) clang/lib/Sema/SemaTemplateInstantiate.cpp (+6-1)
  • (modified) clang/lib/Serialization/ASTReaderStmt.cpp (+1)
  • (modified) clang/lib/Serialization/ASTWriterStmt.cpp (+1)
  • (added) clang/test/CodeGenObjC/availability-check-zippered.m (+41)
  • (added) clang/test/FixIt/fixit-availability-zippered.m (+23)
  • (added) clang/test/Sema/attr-availability-zippered.c (+133)
  • (added) clang/test/SemaObjC/unguarded-availability-zippered.m (+42)
  • (modified) clang/tools/libclang/CIndex.cpp (+4-1)
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index 9d233be282dbb..bab32734519c9 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -758,6 +758,13 @@ class alignas(8) Decl {
                   VersionTuple EnclosingVersion = VersionTuple(),
                   StringRef *RealizedPlatform = nullptr) const;
 
+  /// Determine the availability of the given declaration for the given
+  /// target platform and its minimum version.
+  AvailabilityResult getAvailability(StringRef Platform,
+                                     const VersionTuple &PlatformMinVersion,
+                                     std::string *Message = nullptr,
+                                     StringRef *RealizedPlatform = nullptr) const;
+
   /// Retrieve the version of the target platform in which this
   /// declaration was introduced.
   ///
@@ -766,14 +773,13 @@ class alignas(8) Decl {
   /// attribute otherwise.
   VersionTuple getVersionIntroduced() const;
 
-  /// Determine whether this declaration is marked 'deprecated'.
+  /// \brief Determine whether this declaration is marked 'deprecated' in any
+  /// target platform that we're compiling for.
   ///
   /// \param Message If non-NULL and the declaration is deprecated,
   /// this will be set to the message describing why the declaration
   /// was deprecated (which may be empty).
-  bool isDeprecated(std::string *Message = nullptr) const {
-    return getAvailability(Message) == AR_Deprecated;
-  }
+  bool isDeprecatedInAnyTargetPlatform(std::string *Message = nullptr) const;
 
   /// Determine whether this declaration is marked 'unavailable'.
   ///
@@ -784,6 +790,10 @@ class alignas(8) Decl {
     return getAvailability(Message) == AR_Unavailable;
   }
 
+  /// \brief Determine whether this declaration is marked 'deprecated' for
+  /// all target platforms that we're compiling for.
+  bool isUnavailabledForAllTargetPlatforms() const;
+
   /// Determine whether this is a weak-imported symbol.
   ///
   /// Weak-imported symbols are typically marked with the
diff --git a/clang/include/clang/AST/ExprObjC.h b/clang/include/clang/AST/ExprObjC.h
index b424530c8447c..39f22c961ce96 100644
--- a/clang/include/clang/AST/ExprObjC.h
+++ b/clang/include/clang/AST/ExprObjC.h
@@ -1736,13 +1736,18 @@ class ObjCAvailabilityCheckExpr : public Expr {
   friend class ASTStmtReader;
 
   VersionTuple VersionToCheck;
+  VersionTuple VariantVersionToCheck;
   SourceLocation AtLoc, RParen;
 
 public:
-  ObjCAvailabilityCheckExpr(VersionTuple VersionToCheck, SourceLocation AtLoc,
-                            SourceLocation RParen, QualType Ty)
+  ObjCAvailabilityCheckExpr(VersionTuple VersionToCheck,
+                            VersionTuple VariantVersionToCheck,
+                            SourceLocation AtLoc, SourceLocation RParen,
+                            QualType Ty)
       : Expr(ObjCAvailabilityCheckExprClass, Ty, VK_PRValue, OK_Ordinary),
-        VersionToCheck(VersionToCheck), AtLoc(AtLoc), RParen(RParen) {
+        VersionToCheck(VersionToCheck),
+        VariantVersionToCheck(VariantVersionToCheck), AtLoc(AtLoc),
+        RParen(RParen) {
     setDependence(ExprDependence::None);
   }
 
@@ -1757,6 +1762,10 @@ class ObjCAvailabilityCheckExpr : public Expr {
   bool hasVersion() const { return !VersionToCheck.empty(); }
   VersionTuple getVersion() const { return VersionToCheck; }
 
+  bool hasVariantVersion() const { return !VariantVersionToCheck.empty(); }
+
+  VersionTuple getVariantVersion() const { return VariantVersionToCheck; }
+
   child_range children() {
     return child_range(child_iterator(), child_iterator());
   }
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index cfb2ee3368201..c6b237c883a2b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4300,6 +4300,9 @@ def warn_unguarded_availability_new :
 def warn_unguarded_availability_unavailable_new :
   Warning<warn_unguarded_availability_unavailable.Summary>,
   InGroup<UnguardedAvailabilityNew>;
+def warn_zippered_unguarded_availability :
+  Warning<"%0 is only available on %1 %2 and %3 %4 or newer">,
+  InGroup<UnguardedAvailabilityNew>;
 def note_decl_unguarded_availability_silence : Note<
   "annotate %select{%1|anonymous %1}0 with an availability attribute to silence this warning">;
 def note_unguarded_available_silence : Note<
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index 5a541aaf797fe..66a7ba2bd096f 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -260,6 +260,8 @@ class TargetInfo : public TransferrableTargetInfo,
 
   mutable StringRef PlatformName;
   mutable VersionTuple PlatformMinVersion;
+  mutable StringRef TargetVariantPlatform;
+  mutable VersionTuple TargetVariantPlatformMinVersion;
 
   LLVM_PREFERRED_TYPE(bool)
   unsigned HasAlignMac68kSupport : 1;
@@ -1737,6 +1739,28 @@ class TargetInfo : public TransferrableTargetInfo,
   /// which the program should be compiled.
   VersionTuple getPlatformMinVersion() const { return PlatformMinVersion; }
 
+  /// Returns true when are building for an auxillary target variant platform.
+  bool hasTargetVariantPlatform() const {
+    return !TargetVariantPlatform.empty();
+  }
+
+  /// Retrieve the name of the target variant platform as it is used in the
+  /// availability attribute.
+  StringRef getTargetVariantPlatform() const {
+    assert(hasTargetVariantPlatform() &&
+           "no target variant platform specified");
+    return TargetVariantPlatform;
+  }
+
+  /// Retrieve the minimum desired deployment version of the target variant
+  /// platform,
+  /// for which the program should be compiled.
+  VersionTuple getTargetVariantPlatformMinVersion() const {
+    assert(hasTargetVariantPlatform() &&
+           "no target variant platform specified");
+    return TargetVariantPlatformMinVersion;
+  }
+
   bool isBigEndian() const { return BigEndian; }
   bool isLittleEndian() const { return !BigEndian; }
 
diff --git a/clang/include/clang/Sema/DelayedDiagnostic.h b/clang/include/clang/Sema/DelayedDiagnostic.h
index 0105089a393f1..5e45c3459f151 100644
--- a/clang/include/clang/Sema/DelayedDiagnostic.h
+++ b/clang/include/clang/Sema/DelayedDiagnostic.h
@@ -134,14 +134,13 @@ class DelayedDiagnostic {
 
   void Destroy();
 
-  static DelayedDiagnostic makeAvailability(AvailabilityResult AR,
-                                            ArrayRef<SourceLocation> Locs,
-                                            const NamedDecl *ReferringDecl,
-                                            const NamedDecl *OffendingDecl,
-                                            const ObjCInterfaceDecl *UnknownObjCClass,
-                                            const ObjCPropertyDecl  *ObjCProperty,
-                                            StringRef Msg,
-                                            bool ObjCPropertyAccess);
+  static DelayedDiagnostic
+  makeAvailability(AvailabilityResult AR, ArrayRef<SourceLocation> Locs,
+                   const NamedDecl *ReferringDecl,
+                   const NamedDecl *OffendingDecl,
+                   const ObjCInterfaceDecl *UnknownObjCClass,
+                   const ObjCPropertyDecl *ObjCProperty, StringRef Msg,
+                   bool ObjCPropertyAccess, bool IsTargetVariantPlatform);
 
   static DelayedDiagnostic makeAccess(SourceLocation Loc,
                                       const AccessedEntity &Entity) {
@@ -232,6 +231,10 @@ class DelayedDiagnostic {
     return AvailabilityData.ObjCPropertyAccess;
   }
 
+  bool isTargetVariantPlatform() const {
+    return AvailabilityData.IsTargetVariantPlatform;
+  }
+
 private:
   struct AD {
     const NamedDecl *ReferringDecl;
@@ -244,6 +247,7 @@ class DelayedDiagnostic {
     size_t NumSelectorLocs;
     AvailabilityResult AR;
     bool ObjCPropertyAccess;
+    bool IsTargetVariantPlatform;
   };
 
   struct FTD {
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index dcf112fd8eaa4..c7e4db0f49a30 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2466,6 +2466,10 @@ class Sema final : public SemaBase {
 
   void handleDelayedAvailabilityCheck(sema::DelayedDiagnostic &DD, Decl *Ctx);
 
+  void handleZipperedDelayedAvailabilityCheck(
+      sema::DelayedDiagnostic &DD, sema::DelayedDiagnostic &VariantDD,
+      Decl *Ctx);
+
   /// Retrieve the current function, if any, that should be analyzed for
   /// potential availability violations.
   sema::FunctionScopeInfo *getCurFunctionAvailabilityContext();
@@ -2479,7 +2483,9 @@ class Sema final : public SemaBase {
   void DiagnoseAvailabilityOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs);
 
   std::pair<AvailabilityResult, const NamedDecl *>
-  ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, std::string *Message,
+  ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, StringRef Platform,
+                                   const VersionTuple &PlatformVersion,
+                                   std::string *Message,
                                    ObjCInterfaceDecl *ClassReceiver);
   ///@}
 
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index 70f61fa57a682..b8dd2772977bd 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -674,15 +674,12 @@ static StringRef getRealizedPlatform(const AvailabilityAttr *A,
 static AvailabilityResult CheckAvailability(ASTContext &Context,
                                             const AvailabilityAttr *A,
                                             std::string *Message,
+                                            StringRef TargetPlatform,
                                             VersionTuple EnclosingVersion) {
-  if (EnclosingVersion.empty())
-    EnclosingVersion = Context.getTargetInfo().getPlatformMinVersion();
-
   if (EnclosingVersion.empty())
     return AR_Available;
 
   StringRef ActualPlatform = A->getPlatform()->getName();
-  StringRef TargetPlatform = Context.getTargetInfo().getPlatformName();
 
   // Match the platform name.
   if (getRealizedPlatform(A, Context) != TargetPlatform)
@@ -779,9 +776,21 @@ static AvailabilityResult CheckAvailability(ASTContext &Context,
 AvailabilityResult Decl::getAvailability(std::string *Message,
                                          VersionTuple EnclosingVersion,
                                          StringRef *RealizedPlatform) const {
+  const TargetInfo &TI = getASTContext().getTargetInfo();
+  return getAvailability(TI.getPlatformName(),
+                         EnclosingVersion.empty() ? TI.getPlatformMinVersion()
+                                                  : EnclosingVersion,
+                         Message, RealizedPlatform);
+}
+
+AvailabilityResult Decl::getAvailability(StringRef Platform,
+                                         const VersionTuple &PlatformMinVersion,
+                                         std::string *Message,
+                                         StringRef *RealizedPlatform) const {
   if (auto *FTD = dyn_cast<FunctionTemplateDecl>(this))
-    return FTD->getTemplatedDecl()->getAvailability(Message, EnclosingVersion,
-                                                    RealizedPlatform);
+    return FTD->getTemplatedDecl()->getAvailability(
+        Platform, PlatformMinVersion, Message, RealizedPlatform);
+
 
   AvailabilityResult Result = AR_Available;
   std::string ResultMessage;
@@ -806,8 +815,8 @@ AvailabilityResult Decl::getAvailability(std::string *Message,
 
     if (const auto *Availability = dyn_cast<AvailabilityAttr>(A)) {
       Availability = Availability->getEffectiveAttr();
-      AvailabilityResult AR = CheckAvailability(getASTContext(), Availability,
-                                                Message, EnclosingVersion);
+      AvailabilityResult AR = CheckAvailability(
+          getASTContext(), Availability, Message, Platform, PlatformMinVersion);
 
       if (AR == AR_Unavailable) {
         if (RealizedPlatform)
@@ -844,6 +853,26 @@ VersionTuple Decl::getVersionIntroduced() const {
   return {};
 }
 
+bool Decl::isDeprecatedInAnyTargetPlatform(std::string *Message) const {
+  const TargetInfo &TI = getASTContext().getTargetInfo();
+  return getAvailability(TI.getPlatformName(), TI.getPlatformMinVersion()) ==
+             AR_Deprecated ||
+         (TI.hasTargetVariantPlatform() &&
+          getAvailability(TI.getTargetVariantPlatform(),
+                          TI.getTargetVariantPlatformMinVersion()) ==
+              AR_Deprecated);
+}
+
+bool Decl::isUnavailabledForAllTargetPlatforms() const {
+  const TargetInfo &TI = getASTContext().getTargetInfo();
+  return getAvailability(TI.getPlatformName(), TI.getPlatformMinVersion()) ==
+             AR_Unavailable &&
+         (!getASTContext().getTargetInfo().hasTargetVariantPlatform() ||
+          getAvailability(TI.getTargetVariantPlatform(),
+                          TI.getTargetVariantPlatformMinVersion()) ==
+              AR_Unavailable);
+}
+
 bool Decl::canBeWeakImported(bool &IsDefinition) const {
   IsDefinition = false;
 
@@ -878,6 +907,7 @@ bool Decl::isWeakImported() const {
   if (!canBeWeakImported(IsDefinition))
     return false;
 
+  const TargetInfo &TI = getASTContext().getTargetInfo();
   for (const auto *A : getMostRecentDecl()->attrs()) {
     if (isa<WeakImportAttr>(A))
       return true;
@@ -885,7 +915,13 @@ bool Decl::isWeakImported() const {
     if (const auto *Availability = dyn_cast<AvailabilityAttr>(A)) {
       Availability = Availability->getEffectiveAttr();
       if (CheckAvailability(getASTContext(), Availability, nullptr,
-                            VersionTuple()) == AR_NotYetIntroduced)
+                            TI.getPlatformName(), TI.getPlatformMinVersion()) ==
+              AR_NotYetIntroduced ||
+          (TI.hasTargetVariantPlatform() &&
+           CheckAvailability(getASTContext(), Availability, nullptr,
+                             TI.getTargetVariantPlatform(),
+                             TI.getTargetVariantPlatformMinVersion()) ==
+               AR_NotYetIntroduced))
         return true;
     }
   }
diff --git a/clang/lib/Basic/Targets/ARM.cpp b/clang/lib/Basic/Targets/ARM.cpp
index 8d6e8d6990e80..0cd4b705bbcb9 100644
--- a/clang/lib/Basic/Targets/ARM.cpp
+++ b/clang/lib/Basic/Targets/ARM.cpp
@@ -1558,5 +1558,7 @@ DarwinARMTargetInfo::DarwinARMTargetInfo(const llvm::Triple &Triple,
 void DarwinARMTargetInfo::getOSDefines(const LangOptions &Opts,
                                        const llvm::Triple &Triple,
                                        MacroBuilder &Builder) const {
-  getDarwinDefines(Builder, Opts, Triple, PlatformName, PlatformMinVersion);
+  getDarwinDefines(Builder, Opts, Triple, /*TargetVariantTriple=*/"",
+                   PlatformName, PlatformMinVersion, TargetVariantPlatform,
+                   TargetVariantPlatformMinVersion);
 }
diff --git a/clang/lib/Basic/Targets/OSTargets.cpp b/clang/lib/Basic/Targets/OSTargets.cpp
index 04d9e13d3c9a6..91f98595dbbf5 100644
--- a/clang/lib/Basic/Targets/OSTargets.cpp
+++ b/clang/lib/Basic/Targets/OSTargets.cpp
@@ -55,8 +55,10 @@ void getAppleMachODefines(MacroBuilder &Builder, const LangOptions &Opts,
 }
 
 void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts,
-                      const llvm::Triple &Triple, StringRef &PlatformName,
-                      VersionTuple &PlatformMinVersion) {
+                      const llvm::Triple &Triple, StringRef TargetVariantTriple,
+                      StringRef &PlatformName, VersionTuple &PlatformMinVersion,
+                      StringRef &TargetVariantPlatformName,
+                      VersionTuple &TargetVariantPlatformMinVersion) {
   getAppleMachODefines(Builder, Opts, Triple);
 
   // Darwin's libc doesn't have threads.h
@@ -67,6 +69,14 @@ void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts,
   if (Triple.isMacOSX()) {
     Triple.getMacOSXVersion(OsVersion);
     PlatformName = "macos";
+    if (!TargetVariantTriple.empty()) {
+      llvm::Triple TVT(TargetVariantTriple);
+      if (TVT.getOS() == llvm::Triple::IOS &&
+          TVT.getEnvironment() == llvm::Triple::MacABI) {
+        TargetVariantPlatformName = "maccatalyst";
+        TargetVariantPlatformMinVersion = TVT.getiOSVersion();
+      }
+    }
   } else {
     OsVersion = Triple.getOSVersion();
     PlatformName = llvm::Triple::getOSTypeName(Triple.getOS());
diff --git a/clang/lib/Basic/Targets/OSTargets.h b/clang/lib/Basic/Targets/OSTargets.h
index fb5c17cefc004..d5aa067d9eabf 100644
--- a/clang/lib/Basic/Targets/OSTargets.h
+++ b/clang/lib/Basic/Targets/OSTargets.h
@@ -38,8 +38,10 @@ void getAppleMachODefines(MacroBuilder &Builder, const LangOptions &Opts,
                           const llvm::Triple &Triple);
 
 void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts,
-                      const llvm::Triple &Triple, StringRef &PlatformName,
-                      VersionTuple &PlatformMinVersion);
+                      const llvm::Triple &Triple, StringRef TargetVariantTriple,
+                      StringRef &PlatformName, VersionTuple &PlatformMinVersion,
+                      StringRef &TargetVariantPlatformName,
+                      VersionTuple &TargetVariantPlatform);
 
 template <typename Target>
 class LLVM_LIBRARY_VISIBILITY AppleMachOTargetInfo
@@ -77,8 +79,10 @@ class LLVM_LIBRARY_VISIBILITY DarwinTargetInfo
 protected:
   void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple,
                     MacroBuilder &Builder) const override {
-    getDarwinDefines(Builder, Opts, Triple, this->PlatformName,
-                     this->PlatformMinVersion);
+    getDarwinDefines(Builder, Opts, Triple, TargetVariantTriple,
+                     this->PlatformName, this->PlatformMinVersion,
+                     this->TargetVariantPlatform,
+                     this->TargetVariantPlatformMinVersion);
   }
 
 public:
@@ -87,6 +91,7 @@ class LLVM_LIBRARY_VISIBILITY DarwinTargetInfo
     // By default, no TLS, and we list permitted architecture/OS
     // combinations.
     this->TLSSupported = false;
+    TargetVariantTriple = Opts.DarwinTargetVariantTriple;
 
     if (Triple.isMacOSX())
       this->TLSSupported = !Triple.isMacOSXVersionLT(10, 7);
@@ -185,6 +190,15 @@ class LLVM_LIBRARY_VISIBILITY DarwinTargetInfo
   bool areDefaultedSMFStillPOD(const LangOptions &) const override {
     return false;
   }
+
+  /// Darwin does not support protected visibility.  Darwin's "default"
+  /// is very similar to ELF's "protected";  Darwin requires a "weak"
+  /// attribute on declarations that can be dynamically replaced.
+  bool hasProtectedVisibility() const override { return false; }
+
+private:
+  std::string TargetVariantTriple;
+
 };
 
 // DragonFlyBSD Target
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 173931213ce68..5f46849ccc956 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -635,13 +635,19 @@ class ScalarExprEmitter
 
   Value *VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) {
     VersionTuple Version = E->getVersion();
+    VersionTuple VariantVersion = E->getVariantVersion();
 
     // If we're checking for a platform older than our minimum deployment
     // target, we can fold the check away.
-    if (Version <= CGF.CGM.getTarget().getPlatformMinVersion())
+    if ((Version.empty() && VariantVersion.empty()) || (!Version.empty() &&
+        Version <= CGF.CGM.getTarget().getPlatformMinVersion() &&
+        (!CGF.CGM.getTarget().hasTargetVariantPlatform() ||
+         (!VariantVersion.empty() &&
+          VariantVersion <=
+              CGF.CGM.getTarget().getTargetVariantPlatformMinVersion()))))
       return llvm::ConstantInt::get(Builder.getInt1Ty(), 1);
 
-    return CGF.EmitBuiltinAvailable(Version);
+    return CGF.EmitBuiltinAvailable(Version, VariantVersion);
   }
 
   Value *VisitArraySubscriptExpr(ArraySubscriptExpr *E);
diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp
index b9cbf593fb1c4..73b1cdba09514 100644
--- a/clang/lib/CodeGen/CGObjC.cpp
+++ b/clang/lib/CodeGen/CGObjC.cpp
@@ -40...
[truncated]

@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-clang-modules

Author: Ian Anderson (ian-twilightcoder)

Changes

rdar://38885683

apple-llvm-split-commit: 40aab84d831ddfc623de95fbbca06de6f0baf0a6
apple-llvm-split-dir: clang/


Patch is 121.04 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/218843.diff

33 Files Affected:

  • (modified) clang/include/clang/AST/DeclBase.h (+14-4)
  • (modified) clang/include/clang/AST/ExprObjC.h (+12-3)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+3)
  • (modified) clang/include/clang/Basic/TargetInfo.h (+24)
  • (modified) clang/include/clang/Sema/DelayedDiagnostic.h (+12-8)
  • (modified) clang/include/clang/Sema/Sema.h (+7-1)
  • (modified) clang/lib/AST/DeclBase.cpp (+45-9)
  • (modified) clang/lib/Basic/Targets/ARM.cpp (+3-1)
  • (modified) clang/lib/Basic/Targets/OSTargets.cpp (+12-2)
  • (modified) clang/lib/Basic/Targets/OSTargets.h (+18-4)
  • (modified) clang/lib/CodeGen/CGExprScalar.cpp (+8-2)
  • (modified) clang/lib/CodeGen/CGObjC.cpp (+44-11)
  • (modified) clang/lib/CodeGen/CodeGenFunction.h (+2-1)
  • (modified) clang/lib/CodeGen/CodeGenModule.h (+2)
  • (modified) clang/lib/Edit/RewriteObjCFoundationAPI.cpp (+1-1)
  • (modified) clang/lib/Sema/CodeCompleteConsumer.cpp (+10-2)
  • (modified) clang/lib/Sema/DelayedDiagnostic.cpp (+7-9)
  • (modified) clang/lib/Sema/SemaAvailability.cpp (+817-381)
  • (modified) clang/lib/Sema/SemaDecl.cpp (+3-2)
  • (modified) clang/lib/Sema/SemaDeclAttr.cpp (+21-4)
  • (modified) clang/lib/Sema/SemaDeclObjC.cpp (+31-14)
  • (modified) clang/lib/Sema/SemaExpr.cpp (+2-2)
  • (modified) clang/lib/Sema/SemaExprObjC.cpp (+12-2)
  • (modified) clang/lib/Sema/SemaLookup.cpp (+1-1)
  • (modified) clang/lib/Sema/SemaObjCProperty.cpp (+4-5)
  • (modified) clang/lib/Sema/SemaTemplateInstantiate.cpp (+6-1)
  • (modified) clang/lib/Serialization/ASTReaderStmt.cpp (+1)
  • (modified) clang/lib/Serialization/ASTWriterStmt.cpp (+1)
  • (added) clang/test/CodeGenObjC/availability-check-zippered.m (+41)
  • (added) clang/test/FixIt/fixit-availability-zippered.m (+23)
  • (added) clang/test/Sema/attr-availability-zippered.c (+133)
  • (added) clang/test/SemaObjC/unguarded-availability-zippered.m (+42)
  • (modified) clang/tools/libclang/CIndex.cpp (+4-1)
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index 9d233be282dbb..bab32734519c9 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -758,6 +758,13 @@ class alignas(8) Decl {
                   VersionTuple EnclosingVersion = VersionTuple(),
                   StringRef *RealizedPlatform = nullptr) const;
 
+  /// Determine the availability of the given declaration for the given
+  /// target platform and its minimum version.
+  AvailabilityResult getAvailability(StringRef Platform,
+                                     const VersionTuple &PlatformMinVersion,
+                                     std::string *Message = nullptr,
+                                     StringRef *RealizedPlatform = nullptr) const;
+
   /// Retrieve the version of the target platform in which this
   /// declaration was introduced.
   ///
@@ -766,14 +773,13 @@ class alignas(8) Decl {
   /// attribute otherwise.
   VersionTuple getVersionIntroduced() const;
 
-  /// Determine whether this declaration is marked 'deprecated'.
+  /// \brief Determine whether this declaration is marked 'deprecated' in any
+  /// target platform that we're compiling for.
   ///
   /// \param Message If non-NULL and the declaration is deprecated,
   /// this will be set to the message describing why the declaration
   /// was deprecated (which may be empty).
-  bool isDeprecated(std::string *Message = nullptr) const {
-    return getAvailability(Message) == AR_Deprecated;
-  }
+  bool isDeprecatedInAnyTargetPlatform(std::string *Message = nullptr) const;
 
   /// Determine whether this declaration is marked 'unavailable'.
   ///
@@ -784,6 +790,10 @@ class alignas(8) Decl {
     return getAvailability(Message) == AR_Unavailable;
   }
 
+  /// \brief Determine whether this declaration is marked 'deprecated' for
+  /// all target platforms that we're compiling for.
+  bool isUnavailabledForAllTargetPlatforms() const;
+
   /// Determine whether this is a weak-imported symbol.
   ///
   /// Weak-imported symbols are typically marked with the
diff --git a/clang/include/clang/AST/ExprObjC.h b/clang/include/clang/AST/ExprObjC.h
index b424530c8447c..39f22c961ce96 100644
--- a/clang/include/clang/AST/ExprObjC.h
+++ b/clang/include/clang/AST/ExprObjC.h
@@ -1736,13 +1736,18 @@ class ObjCAvailabilityCheckExpr : public Expr {
   friend class ASTStmtReader;
 
   VersionTuple VersionToCheck;
+  VersionTuple VariantVersionToCheck;
   SourceLocation AtLoc, RParen;
 
 public:
-  ObjCAvailabilityCheckExpr(VersionTuple VersionToCheck, SourceLocation AtLoc,
-                            SourceLocation RParen, QualType Ty)
+  ObjCAvailabilityCheckExpr(VersionTuple VersionToCheck,
+                            VersionTuple VariantVersionToCheck,
+                            SourceLocation AtLoc, SourceLocation RParen,
+                            QualType Ty)
       : Expr(ObjCAvailabilityCheckExprClass, Ty, VK_PRValue, OK_Ordinary),
-        VersionToCheck(VersionToCheck), AtLoc(AtLoc), RParen(RParen) {
+        VersionToCheck(VersionToCheck),
+        VariantVersionToCheck(VariantVersionToCheck), AtLoc(AtLoc),
+        RParen(RParen) {
     setDependence(ExprDependence::None);
   }
 
@@ -1757,6 +1762,10 @@ class ObjCAvailabilityCheckExpr : public Expr {
   bool hasVersion() const { return !VersionToCheck.empty(); }
   VersionTuple getVersion() const { return VersionToCheck; }
 
+  bool hasVariantVersion() const { return !VariantVersionToCheck.empty(); }
+
+  VersionTuple getVariantVersion() const { return VariantVersionToCheck; }
+
   child_range children() {
     return child_range(child_iterator(), child_iterator());
   }
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index cfb2ee3368201..c6b237c883a2b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4300,6 +4300,9 @@ def warn_unguarded_availability_new :
 def warn_unguarded_availability_unavailable_new :
   Warning<warn_unguarded_availability_unavailable.Summary>,
   InGroup<UnguardedAvailabilityNew>;
+def warn_zippered_unguarded_availability :
+  Warning<"%0 is only available on %1 %2 and %3 %4 or newer">,
+  InGroup<UnguardedAvailabilityNew>;
 def note_decl_unguarded_availability_silence : Note<
   "annotate %select{%1|anonymous %1}0 with an availability attribute to silence this warning">;
 def note_unguarded_available_silence : Note<
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index 5a541aaf797fe..66a7ba2bd096f 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -260,6 +260,8 @@ class TargetInfo : public TransferrableTargetInfo,
 
   mutable StringRef PlatformName;
   mutable VersionTuple PlatformMinVersion;
+  mutable StringRef TargetVariantPlatform;
+  mutable VersionTuple TargetVariantPlatformMinVersion;
 
   LLVM_PREFERRED_TYPE(bool)
   unsigned HasAlignMac68kSupport : 1;
@@ -1737,6 +1739,28 @@ class TargetInfo : public TransferrableTargetInfo,
   /// which the program should be compiled.
   VersionTuple getPlatformMinVersion() const { return PlatformMinVersion; }
 
+  /// Returns true when are building for an auxillary target variant platform.
+  bool hasTargetVariantPlatform() const {
+    return !TargetVariantPlatform.empty();
+  }
+
+  /// Retrieve the name of the target variant platform as it is used in the
+  /// availability attribute.
+  StringRef getTargetVariantPlatform() const {
+    assert(hasTargetVariantPlatform() &&
+           "no target variant platform specified");
+    return TargetVariantPlatform;
+  }
+
+  /// Retrieve the minimum desired deployment version of the target variant
+  /// platform,
+  /// for which the program should be compiled.
+  VersionTuple getTargetVariantPlatformMinVersion() const {
+    assert(hasTargetVariantPlatform() &&
+           "no target variant platform specified");
+    return TargetVariantPlatformMinVersion;
+  }
+
   bool isBigEndian() const { return BigEndian; }
   bool isLittleEndian() const { return !BigEndian; }
 
diff --git a/clang/include/clang/Sema/DelayedDiagnostic.h b/clang/include/clang/Sema/DelayedDiagnostic.h
index 0105089a393f1..5e45c3459f151 100644
--- a/clang/include/clang/Sema/DelayedDiagnostic.h
+++ b/clang/include/clang/Sema/DelayedDiagnostic.h
@@ -134,14 +134,13 @@ class DelayedDiagnostic {
 
   void Destroy();
 
-  static DelayedDiagnostic makeAvailability(AvailabilityResult AR,
-                                            ArrayRef<SourceLocation> Locs,
-                                            const NamedDecl *ReferringDecl,
-                                            const NamedDecl *OffendingDecl,
-                                            const ObjCInterfaceDecl *UnknownObjCClass,
-                                            const ObjCPropertyDecl  *ObjCProperty,
-                                            StringRef Msg,
-                                            bool ObjCPropertyAccess);
+  static DelayedDiagnostic
+  makeAvailability(AvailabilityResult AR, ArrayRef<SourceLocation> Locs,
+                   const NamedDecl *ReferringDecl,
+                   const NamedDecl *OffendingDecl,
+                   const ObjCInterfaceDecl *UnknownObjCClass,
+                   const ObjCPropertyDecl *ObjCProperty, StringRef Msg,
+                   bool ObjCPropertyAccess, bool IsTargetVariantPlatform);
 
   static DelayedDiagnostic makeAccess(SourceLocation Loc,
                                       const AccessedEntity &Entity) {
@@ -232,6 +231,10 @@ class DelayedDiagnostic {
     return AvailabilityData.ObjCPropertyAccess;
   }
 
+  bool isTargetVariantPlatform() const {
+    return AvailabilityData.IsTargetVariantPlatform;
+  }
+
 private:
   struct AD {
     const NamedDecl *ReferringDecl;
@@ -244,6 +247,7 @@ class DelayedDiagnostic {
     size_t NumSelectorLocs;
     AvailabilityResult AR;
     bool ObjCPropertyAccess;
+    bool IsTargetVariantPlatform;
   };
 
   struct FTD {
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index dcf112fd8eaa4..c7e4db0f49a30 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2466,6 +2466,10 @@ class Sema final : public SemaBase {
 
   void handleDelayedAvailabilityCheck(sema::DelayedDiagnostic &DD, Decl *Ctx);
 
+  void handleZipperedDelayedAvailabilityCheck(
+      sema::DelayedDiagnostic &DD, sema::DelayedDiagnostic &VariantDD,
+      Decl *Ctx);
+
   /// Retrieve the current function, if any, that should be analyzed for
   /// potential availability violations.
   sema::FunctionScopeInfo *getCurFunctionAvailabilityContext();
@@ -2479,7 +2483,9 @@ class Sema final : public SemaBase {
   void DiagnoseAvailabilityOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs);
 
   std::pair<AvailabilityResult, const NamedDecl *>
-  ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, std::string *Message,
+  ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, StringRef Platform,
+                                   const VersionTuple &PlatformVersion,
+                                   std::string *Message,
                                    ObjCInterfaceDecl *ClassReceiver);
   ///@}
 
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index 70f61fa57a682..b8dd2772977bd 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -674,15 +674,12 @@ static StringRef getRealizedPlatform(const AvailabilityAttr *A,
 static AvailabilityResult CheckAvailability(ASTContext &Context,
                                             const AvailabilityAttr *A,
                                             std::string *Message,
+                                            StringRef TargetPlatform,
                                             VersionTuple EnclosingVersion) {
-  if (EnclosingVersion.empty())
-    EnclosingVersion = Context.getTargetInfo().getPlatformMinVersion();
-
   if (EnclosingVersion.empty())
     return AR_Available;
 
   StringRef ActualPlatform = A->getPlatform()->getName();
-  StringRef TargetPlatform = Context.getTargetInfo().getPlatformName();
 
   // Match the platform name.
   if (getRealizedPlatform(A, Context) != TargetPlatform)
@@ -779,9 +776,21 @@ static AvailabilityResult CheckAvailability(ASTContext &Context,
 AvailabilityResult Decl::getAvailability(std::string *Message,
                                          VersionTuple EnclosingVersion,
                                          StringRef *RealizedPlatform) const {
+  const TargetInfo &TI = getASTContext().getTargetInfo();
+  return getAvailability(TI.getPlatformName(),
+                         EnclosingVersion.empty() ? TI.getPlatformMinVersion()
+                                                  : EnclosingVersion,
+                         Message, RealizedPlatform);
+}
+
+AvailabilityResult Decl::getAvailability(StringRef Platform,
+                                         const VersionTuple &PlatformMinVersion,
+                                         std::string *Message,
+                                         StringRef *RealizedPlatform) const {
   if (auto *FTD = dyn_cast<FunctionTemplateDecl>(this))
-    return FTD->getTemplatedDecl()->getAvailability(Message, EnclosingVersion,
-                                                    RealizedPlatform);
+    return FTD->getTemplatedDecl()->getAvailability(
+        Platform, PlatformMinVersion, Message, RealizedPlatform);
+
 
   AvailabilityResult Result = AR_Available;
   std::string ResultMessage;
@@ -806,8 +815,8 @@ AvailabilityResult Decl::getAvailability(std::string *Message,
 
     if (const auto *Availability = dyn_cast<AvailabilityAttr>(A)) {
       Availability = Availability->getEffectiveAttr();
-      AvailabilityResult AR = CheckAvailability(getASTContext(), Availability,
-                                                Message, EnclosingVersion);
+      AvailabilityResult AR = CheckAvailability(
+          getASTContext(), Availability, Message, Platform, PlatformMinVersion);
 
       if (AR == AR_Unavailable) {
         if (RealizedPlatform)
@@ -844,6 +853,26 @@ VersionTuple Decl::getVersionIntroduced() const {
   return {};
 }
 
+bool Decl::isDeprecatedInAnyTargetPlatform(std::string *Message) const {
+  const TargetInfo &TI = getASTContext().getTargetInfo();
+  return getAvailability(TI.getPlatformName(), TI.getPlatformMinVersion()) ==
+             AR_Deprecated ||
+         (TI.hasTargetVariantPlatform() &&
+          getAvailability(TI.getTargetVariantPlatform(),
+                          TI.getTargetVariantPlatformMinVersion()) ==
+              AR_Deprecated);
+}
+
+bool Decl::isUnavailabledForAllTargetPlatforms() const {
+  const TargetInfo &TI = getASTContext().getTargetInfo();
+  return getAvailability(TI.getPlatformName(), TI.getPlatformMinVersion()) ==
+             AR_Unavailable &&
+         (!getASTContext().getTargetInfo().hasTargetVariantPlatform() ||
+          getAvailability(TI.getTargetVariantPlatform(),
+                          TI.getTargetVariantPlatformMinVersion()) ==
+              AR_Unavailable);
+}
+
 bool Decl::canBeWeakImported(bool &IsDefinition) const {
   IsDefinition = false;
 
@@ -878,6 +907,7 @@ bool Decl::isWeakImported() const {
   if (!canBeWeakImported(IsDefinition))
     return false;
 
+  const TargetInfo &TI = getASTContext().getTargetInfo();
   for (const auto *A : getMostRecentDecl()->attrs()) {
     if (isa<WeakImportAttr>(A))
       return true;
@@ -885,7 +915,13 @@ bool Decl::isWeakImported() const {
     if (const auto *Availability = dyn_cast<AvailabilityAttr>(A)) {
       Availability = Availability->getEffectiveAttr();
       if (CheckAvailability(getASTContext(), Availability, nullptr,
-                            VersionTuple()) == AR_NotYetIntroduced)
+                            TI.getPlatformName(), TI.getPlatformMinVersion()) ==
+              AR_NotYetIntroduced ||
+          (TI.hasTargetVariantPlatform() &&
+           CheckAvailability(getASTContext(), Availability, nullptr,
+                             TI.getTargetVariantPlatform(),
+                             TI.getTargetVariantPlatformMinVersion()) ==
+               AR_NotYetIntroduced))
         return true;
     }
   }
diff --git a/clang/lib/Basic/Targets/ARM.cpp b/clang/lib/Basic/Targets/ARM.cpp
index 8d6e8d6990e80..0cd4b705bbcb9 100644
--- a/clang/lib/Basic/Targets/ARM.cpp
+++ b/clang/lib/Basic/Targets/ARM.cpp
@@ -1558,5 +1558,7 @@ DarwinARMTargetInfo::DarwinARMTargetInfo(const llvm::Triple &Triple,
 void DarwinARMTargetInfo::getOSDefines(const LangOptions &Opts,
                                        const llvm::Triple &Triple,
                                        MacroBuilder &Builder) const {
-  getDarwinDefines(Builder, Opts, Triple, PlatformName, PlatformMinVersion);
+  getDarwinDefines(Builder, Opts, Triple, /*TargetVariantTriple=*/"",
+                   PlatformName, PlatformMinVersion, TargetVariantPlatform,
+                   TargetVariantPlatformMinVersion);
 }
diff --git a/clang/lib/Basic/Targets/OSTargets.cpp b/clang/lib/Basic/Targets/OSTargets.cpp
index 04d9e13d3c9a6..91f98595dbbf5 100644
--- a/clang/lib/Basic/Targets/OSTargets.cpp
+++ b/clang/lib/Basic/Targets/OSTargets.cpp
@@ -55,8 +55,10 @@ void getAppleMachODefines(MacroBuilder &Builder, const LangOptions &Opts,
 }
 
 void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts,
-                      const llvm::Triple &Triple, StringRef &PlatformName,
-                      VersionTuple &PlatformMinVersion) {
+                      const llvm::Triple &Triple, StringRef TargetVariantTriple,
+                      StringRef &PlatformName, VersionTuple &PlatformMinVersion,
+                      StringRef &TargetVariantPlatformName,
+                      VersionTuple &TargetVariantPlatformMinVersion) {
   getAppleMachODefines(Builder, Opts, Triple);
 
   // Darwin's libc doesn't have threads.h
@@ -67,6 +69,14 @@ void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts,
   if (Triple.isMacOSX()) {
     Triple.getMacOSXVersion(OsVersion);
     PlatformName = "macos";
+    if (!TargetVariantTriple.empty()) {
+      llvm::Triple TVT(TargetVariantTriple);
+      if (TVT.getOS() == llvm::Triple::IOS &&
+          TVT.getEnvironment() == llvm::Triple::MacABI) {
+        TargetVariantPlatformName = "maccatalyst";
+        TargetVariantPlatformMinVersion = TVT.getiOSVersion();
+      }
+    }
   } else {
     OsVersion = Triple.getOSVersion();
     PlatformName = llvm::Triple::getOSTypeName(Triple.getOS());
diff --git a/clang/lib/Basic/Targets/OSTargets.h b/clang/lib/Basic/Targets/OSTargets.h
index fb5c17cefc004..d5aa067d9eabf 100644
--- a/clang/lib/Basic/Targets/OSTargets.h
+++ b/clang/lib/Basic/Targets/OSTargets.h
@@ -38,8 +38,10 @@ void getAppleMachODefines(MacroBuilder &Builder, const LangOptions &Opts,
                           const llvm::Triple &Triple);
 
 void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts,
-                      const llvm::Triple &Triple, StringRef &PlatformName,
-                      VersionTuple &PlatformMinVersion);
+                      const llvm::Triple &Triple, StringRef TargetVariantTriple,
+                      StringRef &PlatformName, VersionTuple &PlatformMinVersion,
+                      StringRef &TargetVariantPlatformName,
+                      VersionTuple &TargetVariantPlatform);
 
 template <typename Target>
 class LLVM_LIBRARY_VISIBILITY AppleMachOTargetInfo
@@ -77,8 +79,10 @@ class LLVM_LIBRARY_VISIBILITY DarwinTargetInfo
 protected:
   void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple,
                     MacroBuilder &Builder) const override {
-    getDarwinDefines(Builder, Opts, Triple, this->PlatformName,
-                     this->PlatformMinVersion);
+    getDarwinDefines(Builder, Opts, Triple, TargetVariantTriple,
+                     this->PlatformName, this->PlatformMinVersion,
+                     this->TargetVariantPlatform,
+                     this->TargetVariantPlatformMinVersion);
   }
 
 public:
@@ -87,6 +91,7 @@ class LLVM_LIBRARY_VISIBILITY DarwinTargetInfo
     // By default, no TLS, and we list permitted architecture/OS
     // combinations.
     this->TLSSupported = false;
+    TargetVariantTriple = Opts.DarwinTargetVariantTriple;
 
     if (Triple.isMacOSX())
       this->TLSSupported = !Triple.isMacOSXVersionLT(10, 7);
@@ -185,6 +190,15 @@ class LLVM_LIBRARY_VISIBILITY DarwinTargetInfo
   bool areDefaultedSMFStillPOD(const LangOptions &) const override {
     return false;
   }
+
+  /// Darwin does not support protected visibility.  Darwin's "default"
+  /// is very similar to ELF's "protected";  Darwin requires a "weak"
+  /// attribute on declarations that can be dynamically replaced.
+  bool hasProtectedVisibility() const override { return false; }
+
+private:
+  std::string TargetVariantTriple;
+
 };
 
 // DragonFlyBSD Target
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 173931213ce68..5f46849ccc956 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -635,13 +635,19 @@ class ScalarExprEmitter
 
   Value *VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) {
     VersionTuple Version = E->getVersion();
+    VersionTuple VariantVersion = E->getVariantVersion();
 
     // If we're checking for a platform older than our minimum deployment
     // target, we can fold the check away.
-    if (Version <= CGF.CGM.getTarget().getPlatformMinVersion())
+    if ((Version.empty() && VariantVersion.empty()) || (!Version.empty() &&
+        Version <= CGF.CGM.getTarget().getPlatformMinVersion() &&
+        (!CGF.CGM.getTarget().hasTargetVariantPlatform() ||
+         (!VariantVersion.empty() &&
+          VariantVersion <=
+              CGF.CGM.getTarget().getTargetVariantPlatformMinVersion()))))
       return llvm::ConstantInt::get(Builder.getInt1Ty(), 1);
 
-    return CGF.EmitBuiltinAvailable(Version);
+    return CGF.EmitBuiltinAvailable(Version, VariantVersion);
   }
 
   Value *VisitArraySubscriptExpr(ArraySubscriptExpr *E);
diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp
index b9cbf593fb1c4..73b1cdba09514 100644
--- a/clang/lib/CodeGen/CGObjC.cpp
+++ b/clang/lib/CodeGen/CGObjC.cpp
@@ -40...
[truncated]

@github-actions

github-actions Bot commented Aug 26, 2026 •

Copy link
Copy Markdown

✅ With the latest revision this PR passed the C/C++ code formatter.

@ian-twilightcoder

ian-twilightcoder commented Aug 26, 2026 •

Copy link
Copy Markdown
Contributor Author

First commit in a series of upstreaming -target-variant support in Sema, which I've been meaning to do since discovering how much was missing upstream when I did #105855. I leaned on Claude to help with this one, as a lot of refactors have happened since the original commit (66073d6143b60e46a43dac714c4f1d768cdba319 in the Apple internal repo) has happened. Especially there were large swaths of code that got moved into different files, and replaying those by hand was turning into a mess. I went over the diffs as best I could, but they aren't a perfect match due to later open source llvm changes that touched some of this code in the internal merge. e.g. Decl::getAvailability(...) has an extra parameter that was added in f4d4cfb when it merged downstream.

@github-actions

github-actions Bot commented Aug 26, 2026 •

Copy link
Copy Markdown

🪟 Windows x64 Test Results

  • 57941 tests passed
  • 2910 tests skipped

✅ The build succeeded and all tests passed.

@cor3ntin cor3ntin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make sure the description is a minimum useful.
https://llvm.org/docs/DeveloperPolicy.html#commit-messages

@ian-twilightcoder

ian-twilightcoder commented Aug 26, 2026 •

Copy link
Copy Markdown
Contributor Author

Please make sure the description is a minimum useful. https://llvm.org/docs/DeveloperPolicy.html#commit-messages

Sure, I'll add the summary of what I'm trying to do: upstream some Mac Catalyst support that was missed and has been causing merge conflicts for awhile now.

rdar://38885683

apple-llvm-split-commit: 40aab84d831ddfc623de95fbbca06de6f0baf0a6
apple-llvm-split-dir: clang/
@Fznamznon

Copy link
Copy Markdown
Contributor

Does that need an RFC?

@ian-twilightcoder

Copy link
Copy Markdown
Contributor Author

Does that need an RFC?

I don't think so, it's 8 years relied upon Mac Catalyst behavior, but @ahatanak what do you think?

@cor3ntin

Copy link
Copy Markdown
Contributor

@Fznamznon I do agree.

The community do try to be accommodating but there is certainly no blanket policy to accept arbitrary downstream extensions without discussion. That it has been deployed downstream for some years isn't really relevant.

So an RFC that describes the behavior of this PR would be useful.
Same thing if you want to upstream further extensions.

This PR would further need to come with a sensible description, documentation (and a release note) before we can meaningfully review it.

Thanks

@shafik

shafik commented Aug 26, 2026 •

Copy link
Copy Markdown
Contributor

Please make sure the description is a minimum useful. https://llvm.org/docs/DeveloperPolicy.html#commit-messages

I really have to reinforce this message. As a reviewer I expect the summary to explain the problem, what the approach is, anything that may warrant attention and minimize the amount of context switching I need to do to understand the code I am about to review. Especially for large sets of changes.

@ojhunt ojhunt left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, a few change requests, but I think the PR as a whole should be split up. A large amount of the change is churn around diagnostic generation and code location. I think splitting that out into a separate PR will make it easier to reason about the behavioural changes.

It's a significant enough change that we should update the documentation around availability and targets.

Finally, this is functionally a feature addition so the PR/commit message does need to be quite detailed as to what is being changed and why.

Someone said an RFC may be appropriate (@shafik maybe?). I'm not sure that that is actually necessary here, but to avoid the appearance of favouritism I'll defer to the maintainers.

More details about what this change is, what it supports, and why it is structured how it is will help folk without the downstream context will help folk determine whether or not an RFC is warranted.

Comment on lines +643 to +648
(!Version.empty() &&
Version <= CGF.CGM.getTarget().getPlatformMinVersion() &&
(!CGF.CGM.getTarget().hasTargetVariantPlatform() ||
(!VariantVersion.empty() &&
VariantVersion <=
CGF.CGM.getTarget().getTargetVariantPlatformMinVersion()))))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really think the complexity of this condition warrants breaking it into separate flags, e.g.

bool FoldCheck = false;
if (Version.empty() && VariantVersion.empty())
  FoldCheck = true;
else if (....) FoldCheck = true;
else if (...) FoldCzech = true;
...
if (FoldhCeck) 
 return llvm::ConstantInt::get(Builder.getInt1Ty(), 1);

Comment on lines +4087 to +4089
if (!Args.empty()) {
Check = EmitNounwindRuntimeCall(CGM.IsOSVersionAtLeastFn, Args);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: no braces

Comment on lines +4091 to +4094
if (!VariantArgs.empty()) {
VariantCheck = EmitNounwindRuntimeCall(
CGM.IsTargetVariantOSVersionAtLeastFn, VariantArgs);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: again -- will refrain from more :D

VariantCheck = EmitNounwindRuntimeCall(
CGM.IsTargetVariantOSVersionAtLeastFn, VariantArgs);
}
llvm::Value *IsNativeCheck = nullptr;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be outside the if block?

Comment on lines +769 to +770
// FIXME: Take min of target and target-variant AR for better Xcode
// experience?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unsure if this comment is relevant to llvm.org? Would depend on existing precedence

Comment on lines +126 to +128
/* TO_UPSTREAM(iosmac) ON*/
Result = D->getAvailability(Platform, PlatformVersion, Message);
/* TO_UPSTREAM(iosmac) OFF*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left overs?

Comment on lines -774 to -776
bool isDeprecated(std::string *Message = nullptr) const {
return getAvailability(Message) == AR_Deprecated;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isDeprecated should be able to just do the right thing - it has access to the target info via the ASTContext. There does not appear to be a strong reason to change its behavior - there are places where we want to be more lax and say "is deprecated on any platform", but that seems to be atypical. The common case should remain isDeprecated.

The analogous isUnavailable has not been removed, but should also remain and continue to do the correct thing by default.

// at one of them.
const AvailabilityAttr *A =
getAttrForPlatform(S.Context, Platform, OffendingDecl);
if (A && A->isInherited()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider writing as

SourceLocation OriginalOffendingLocation = OffendingDecl->getLocation();
if (!A || !A->isInherited())
  return OriginalOffendingLocation;
// for loop here
 ...
//
return OriginalOffendingLocation;

Not a blocker in any way - I simply prefer early returns in these cases

@ian-twilightcoder

Copy link
Copy Markdown
Contributor Author

Alright, let me circle back my teammates on how to best approach this.

My end goal is to upstream some missing Mac Catalyst code, which comprises 21 commits downstream. I wanted to preserve the Git history as close as possible so that it lines up with the downstream repo, and because it's mostly not my code. It's from 8 years ago and the original author isn't at Apple anymore, which is the main reason I did it myself.

I didn't want to do a giant mega review with 21 commits for people who did want to look at some of this beyond "yep it's what the Xcode clang has been doing for many years and it works fine". But it's also not very helpful to have people review code that the next 20 commits is just going to change anyway.

So for now I'm going to close the review and come up with a better strategy so this isn't terrible for everyone who's looking at it, sorry for the commotion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend:ARM clang:as-a-library libclang and C++ API clang:codegen IR generation bugs: mangling, exceptions, etc. clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:modules C++20 modules and Clang Header Modules clang Clang issues not falling into any other category clang-tools-extra clangd

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants