[clang] a8262a3 - [clang][darwin] add support for Mac Catalyst availability

Alex Lorenz via cfe-commits cfe-commits at lists.llvm.org
Tue Jul 20 12:52:05 PDT 2021


Author: Alex Lorenz
Date: 2021-07-20T12:51:57-07:00
New Revision: a8262a383bc627cef3de78e60bbbedb788157a50

URL: https://github.com/llvm/llvm-project/commit/a8262a383bc627cef3de78e60bbbedb788157a50
DIFF: https://github.com/llvm/llvm-project/commit/a8262a383bc627cef3de78e60bbbedb788157a50.diff

LOG: [clang][darwin] add support for Mac Catalyst availability

This commit adds support for Mac Catalyst availability attribute, as
supported by the Apple clang compiler. A follow-up commit will provide
additional support for inferring Mac Catalyst availability from macOS
availability using the mapping in the SDKSettings.json.

Differential Revision: https://reviews.llvm.org/D105052

Added: 
    clang/test/CodeGenObjC/availability-check-maccatalyst.m
    clang/test/FixIt/fixit-availability-maccatalyst.m
    clang/test/Sema/attr-availability-maccatalyst.c
    clang/test/SemaObjC/unguarded-availability-maccatalyst.m

Modified: 
    clang/include/clang/Basic/Attr.td
    clang/lib/Basic/Targets/OSTargets.cpp
    clang/lib/Sema/SemaDeclAttr.cpp
    clang/lib/Sema/SemaExpr.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index e27efd404c218..12d09181a2ea8 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -856,6 +856,8 @@ def Availability : InheritableAttr {
              .Case("macos_app_extension", "macOS (App Extension)")
              .Case("tvos_app_extension", "tvOS (App Extension)")
              .Case("watchos_app_extension", "watchOS (App Extension)")
+             .Case("maccatalyst", "macCatalyst")
+             .Case("maccatalyst_app_extension", "macCatalyst (App Extension)")
              .Case("swift", "Swift")
              .Default(llvm::StringRef());
 }
@@ -869,6 +871,8 @@ static llvm::StringRef getPlatformNameSourceSpelling(llvm::StringRef Platform) {
              .Case("macos_app_extension", "macOSApplicationExtension")
              .Case("tvos_app_extension", "tvOSApplicationExtension")
              .Case("watchos_app_extension", "watchOSApplicationExtension")
+             .Case("maccatalyst", "macCatalyst")
+             .Case("maccatalyst_app_extension", "macCatalystApplicationExtension")
              .Case("zos", "z/OS")
              .Default(Platform);
 }
@@ -882,6 +886,8 @@ static llvm::StringRef canonicalizePlatformName(llvm::StringRef Platform) {
              .Case("macOSApplicationExtension", "macos_app_extension")
              .Case("tvOSApplicationExtension", "tvos_app_extension")
              .Case("watchOSApplicationExtension", "watchos_app_extension")
+             .Case("macCatalyst", "maccatalyst")
+             .Case("macCatalystApplicationExtension", "maccatalyst_app_extension")
              .Default(Platform);
 } }];
   let HasCustomParsing = 1;

diff  --git a/clang/lib/Basic/Targets/OSTargets.cpp b/clang/lib/Basic/Targets/OSTargets.cpp
index 15e475a31d644..7cd4a51901205 100644
--- a/clang/lib/Basic/Targets/OSTargets.cpp
+++ b/clang/lib/Basic/Targets/OSTargets.cpp
@@ -55,6 +55,8 @@ void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts,
   } else {
     Triple.getOSVersion(Maj, Min, Rev);
     PlatformName = llvm::Triple::getOSTypeName(Triple.getOS());
+    if (PlatformName == "ios" && Triple.isMacCatalystEnvironment())
+      PlatformName = "maccatalyst";
   }
 
   // If -target arch-pc-win32-macho option specified, we're

diff  --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 9aac9d2156445..c61afa750bc14 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -2556,6 +2556,36 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
       if (NewAttr)
         D->addAttr(NewAttr);
       }
+  } else if (S.Context.getTargetInfo().getTriple().getOS() ==
+                 llvm::Triple::IOS &&
+             S.Context.getTargetInfo().getTriple().isMacCatalystEnvironment()) {
+    // Transcribe "ios" to "maccatalyst" (and add a new attribute).
+    IdentifierInfo *NewII = nullptr;
+    auto MinMacCatalystVersion = [](const VersionTuple &V) {
+      if (V.empty())
+        return V;
+      if (V.getMajor() < 13 ||
+          (V.getMajor() == 13 && V.getMinor() && *V.getMinor() < 1))
+        return VersionTuple(13, 1); // The minimum Mac Catalyst version is 13.1.
+      return V;
+    };
+    if (II->getName() == "ios")
+      NewII = &S.Context.Idents.get("maccatalyst");
+    else if (II->getName() == "ios_app_extension")
+      NewII = &S.Context.Idents.get("maccatalyst_app_extension");
+    // FIXME: Add support for transcribing macOS availability using mapping from
+    // SDKSettings.json.
+    if (NewII) {
+      AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr(
+          ND, AL.getRange(), NewII, true /*Implicit*/,
+          MinMacCatalystVersion(Introduced.Version),
+          MinMacCatalystVersion(Deprecated.Version),
+          MinMacCatalystVersion(Obsoleted.Version), IsUnavailable, Str,
+          IsStrict, Replacement, Sema::AMK_None,
+          PriorityModifier + Sema::AP_InferredFromOtherPlatform);
+      if (NewAttr)
+        D->addAttr(NewAttr);
+    }
   }
 }
 

diff  --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index dcb162952d137..80f9c1e1b372a 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -19816,16 +19816,26 @@ Sema::ActOnObjCBoolLiteral(SourceLocation OpLoc, tok::TokenKind Kind) {
 ExprResult Sema::ActOnObjCAvailabilityCheckExpr(
     llvm::ArrayRef<AvailabilitySpec> AvailSpecs, SourceLocation AtLoc,
     SourceLocation RParen) {
-
-  StringRef Platform = getASTContext().getTargetInfo().getPlatformName();
-
-  auto Spec = llvm::find_if(AvailSpecs, [&](const AvailabilitySpec &Spec) {
-    return Spec.getPlatform() == Platform;
-  });
+  auto FindSpecVersion = [&](StringRef Platform) -> Optional<VersionTuple> {
+    auto Spec = llvm::find_if(AvailSpecs, [&](const AvailabilitySpec &Spec) {
+      return Spec.getPlatform() == Platform;
+    });
+    // Transcribe the "ios" availability check to "maccatalyst" when compiling
+    // for "maccatalyst" if "maccatalyst" is not specified.
+    if (Spec == AvailSpecs.end() && Platform == "maccatalyst") {
+      Spec = llvm::find_if(AvailSpecs, [&](const AvailabilitySpec &Spec) {
+        return Spec.getPlatform() == "ios";
+      });
+    }
+    if (Spec == AvailSpecs.end())
+      return None;
+    return Spec->getVersion();
+  };
 
   VersionTuple Version;
-  if (Spec != AvailSpecs.end())
-    Version = Spec->getVersion();
+  if (auto MaybeVersion =
+          FindSpecVersion(Context.getTargetInfo().getPlatformName()))
+    Version = *MaybeVersion;
 
   // The use of `@available` in the enclosing context should be analyzed to
   // warn when it's used inappropriately (i.e. not if(@available)).

diff  --git a/clang/test/CodeGenObjC/availability-check-maccatalyst.m b/clang/test/CodeGenObjC/availability-check-maccatalyst.m
new file mode 100644
index 0000000000000..03ec8edb634f2
--- /dev/null
+++ b/clang/test/CodeGenObjC/availability-check-maccatalyst.m
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -triple x86_64-apple-ios13.1-macabi -emit-llvm -o - %s | FileCheck %s
+
+void use_at_available() {
+  // CHECK: call i32 @__isPlatformVersionAtLeast(i32 2, i32 14, i32 0, i32 0)
+  // CHECK-NEXT: icmp ne i32
+  if (__builtin_available(ios 14, *))
+    ;
+
+  // CHECK: call i32 @__isPlatformVersionAtLeast(i32 2, i32 13, i32 2, i32 0)
+  // CHECK-NEXT: icmp ne i32
+  if (@available(macCatalyst 13.2, *))
+    ;
+
+  // CHECK: call i32 @__isPlatformVersionAtLeast(i32 2, i32 13, i32 2, i32 0)
+  // CHECK-NEXT: icmp ne i32
+  if (__builtin_available(macCatalyst 13.2, macos 10.15.2, *))
+    ;
+}

diff  --git a/clang/test/FixIt/fixit-availability-maccatalyst.m b/clang/test/FixIt/fixit-availability-maccatalyst.m
new file mode 100644
index 0000000000000..3c370992c14d6
--- /dev/null
+++ b/clang/test/FixIt/fixit-availability-maccatalyst.m
@@ -0,0 +1,24 @@
+// RUN: %clang_cc1 -fsyntax-only -Wunguarded-availability -fdiagnostics-parseable-fixits -triple x86_64-apple-ios13.1-macabi %s 2>&1 | FileCheck %s
+
+__attribute__((availability(macCatalyst, introduced=13.2))) __attribute__((availability(ios, introduced=13.1)))
+int function(void);
+
+void anotherFunction(int function);
+
+int use() {
+  function();
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:3-[[@LINE-1]]:3}:"if (@available(macCatalyst 13.2, *)) {\n      "
+// CHECK-NEXT: fix-it:{{.*}}:{[[@LINE-2]]:14-[[@LINE-2]]:14}:"\n  } else {\n      // Fallback on earlier versions\n  }"
+}
+
+#define API_AVAILABLE(X) __attribute__((availability(macCatalyst, introduced=13.2))) __attribute__((availability(ios, introduced=13.1))) // dummy macro
+
+API_AVAILABLE(macos(10.12))
+ at interface NewClass
+ at end
+
+ at interface OldButOfferFixit
+ at property(copy) NewClass *prop;
+// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:1-[[@LINE-2]]:1}:"API_AVAILABLE(maccatalyst(13.2))\n"
+
+ at end

diff  --git a/clang/test/Sema/attr-availability-maccatalyst.c b/clang/test/Sema/attr-availability-maccatalyst.c
new file mode 100644
index 0000000000000..c345443f24114
--- /dev/null
+++ b/clang/test/Sema/attr-availability-maccatalyst.c
@@ -0,0 +1,131 @@
+// RUN: %clang_cc1 "-triple" "x86_64-apple-ios13.1-macabi" -fsyntax-only -verify %s
+// RUN: %clang_cc1 "-triple" "x86_64-apple-ios13.1-macabi" -fapplication-extension -D APPEXT -fsyntax-only -verify %s
+
+#ifdef APPEXT
+
+#define maccatalyst maccatalyst_app_extension
+#define macCatalyst maccatalyst_app_extension
+#define ios ios_app_extension
+
+#endif
+
+void f0(int) __attribute__((availability(maccatalyst,introduced=2.0,deprecated=9.1))); // expected-note {{'f0' has been explicitly marked deprecated here}}
+void f1(int) __attribute__((availability(maccatalyst,introduced=2.1)));
+void f2(int) __attribute__((availability(macCatalyst,introduced=2.0,deprecated=9.0))); // expected-note {{'f2' has been explicitly marked deprecated here}}
+void f3(int) __attribute__((availability(macosx,introduced=10.1),  availability(maccatalyst,introduced=3.0, obsoleted=9.0))); // expected-note {{'f3' has been explicitly marked unavailable here}}
+void f32(int) __attribute__((availability(macosx,introduced=10.1,deprecated=10.3,obsoleted=10.5),  availability(maccatalyst,introduced=3.0, obsoleted=9.0))); // expected-note {{'f32' has been explicitly marked unavailable here}}
+
+
+void f5(int) __attribute__((availability(maccatalyst,introduced=2.0))) __attribute__((availability(maccatalyst,deprecated=9.0))); // expected-note {{'f5' has been explicitly marked deprecated here}}
+void f6(int) __attribute__((availability(maccatalyst,deprecated=9.0))); // expected-note {{'f6' has been explicitly marked deprecated here}}
+void f6(int) __attribute__((availability(macCatalyst,introduced=2.0)));
+
+void f7(void) // expected-note {{'f7' has been explicitly marked deprecated here}}
+__attribute__((availability(maccatalyst,introduced=3.0, deprecated=4.0)))
+__attribute__((availability(ios,introduced=2.0, deprecated=5.0)));
+
+void f8(void) // expected-note {{'f8' has been explicitly marked unavailable here}}
+__attribute__((availability(maccatalyst,introduced=3.0, obsoleted=4.0)))
+__attribute__((availability(ios,introduced=2.0, obsoleted=5.0)));
+
+void f9(void) // expected-note {{'f9' has been explicitly marked unavailable here}}
+__attribute__((availability(maccatalyst,unavailable)))
+__attribute__((availability(ios,introduced=2.0)));
+
+void test() {
+  f0(0);
+#ifndef APPEXT
+  // expected-warning at -2 {{'f0' is deprecated: first deprecated in macCatalyst 9.1}}
+#else
+  // expected-warning at -4 {{'f0' is deprecated: first deprecated in macCatalyst (App Extension) 9.1}}
+#endif
+  f1(0);
+  f2(0);
+#ifndef APPEXT
+  // expected-warning at -2 {{'f2' is deprecated: first deprecated in macCatalyst 9.0}}
+#else
+  // expected-warning at -4 {{'f2' is deprecated: first deprecated in macCatalyst (App Extension) 9.0}}
+#endif
+  f3(0);
+#ifndef APPEXT
+  // expected-error at -2 {{'f3' is unavailable: obsoleted in macCatalyst 9.0}}
+#else
+  // expected-error at -4 {{'f3' is unavailable: obsoleted in macCatalyst (App Extension) 9.0}}
+#endif
+  f32(0);
+#ifndef APPEXT
+  // expected-error at -2 {{'f32' is unavailable: obsoleted in macCatalyst 9.0}}
+#else
+  // expected-error at -4 {{'f32' is unavailable: obsoleted in macCatalyst (App Extension) 9.0}}
+#endif
+  f5(0); // expected-warning{{'f5' is deprecated: first deprecated in macCatalyst}}
+  f6(0); // expected-warning{{'f6' is deprecated: first deprecated in macCatalyst}}
+
+  f7();
+#ifndef APPEXT
+  // expected-warning at -2 {{'f7' is deprecated: first deprecated in macCatalyst 4.0}}
+#else
+  // expected-warning at -4 {{'f7' is deprecated: first deprecated in macCatalyst (App Extension) 4.0}}
+#endif
+  f8();
+#ifndef APPEXT
+  // expected-error at -2 {{'f8' is unavailable: obsoleted in macCatalyst 4.0}}
+#else
+  // expected-error at -4 {{'f8' is unavailable: obsoleted in macCatalyst (App Extension) 4.0}}
+#endif
+  f9(); // expected-error {{'f9' is unavailable}}
+}
+
+// Don't inherit "deprecated"/"obsoleted" from iOS for Mac Catalyst.
+
+void f100(void)
+__attribute__((availability(maccatalyst,introduced=3.0)))
+__attribute__((availability(ios,introduced=2.0, deprecated=5.0)));
+
+void f101(void)
+__attribute__((availability(maccatalyst,introduced=3.0)))
+__attribute__((availability(ios,introduced=2.0, obsoleted=5.0)));
+
+void f102(void)
+__attribute__((availability(maccatalyst,introduced=3.0)))
+__attribute__((availability(ios,unavailable)));
+
+void f103(void)
+__attribute__((availability(ios,unavailable)));
+
+void f103(void)
+__attribute__((availability(maccatalyst,introduced=3.0)));
+
+void dontInheritObsoletedDeprecated() {
+  f100();
+  f101();
+  f102();
+  f103();
+}
+
+// Inherit the ios availability when Mac Catalyst isn't given.
+
+void f202(void) __attribute__((availability(ios,introduced=2.0, deprecated=5.0))); // expected-note {{here}}
+void f203(void) __attribute__((availability(ios,introduced=2.0, obsoleted=5.0))); // expected-note {{here}}
+void f204(void) __attribute__((availability(ios,unavailable))); // expected-note {{here}}
+
+void inheritIosAvailability() {
+  f202();
+#ifndef APPEXT
+// expected-warning at -2 {{'f202' is deprecated: first deprecated in macCatalyst 13.1}}
+#else
+// expected-warning at -4 {{'f202' is deprecated: first deprecated in macCatalyst (App Extension) 13.1}}
+#endif
+  f203();
+#ifndef APPEXT
+  // expected-error at -2 {{'f203' is unavailable: obsoleted in macCatalyst 13.1}}
+#else
+  // expected-error at -4 {{'f203' is unavailable: obsoleted in macCatalyst (App Extension) 13.1}}
+#endif
+  f204();
+#ifndef APPEXT
+  // expected-error at -2 {{'f204' is unavailable: not available on macCatalyst}}
+#else
+  // expected-error at -4 {{'f204' is unavailable: not available on macCatalyst (App Extension)}}
+#endif
+}

diff  --git a/clang/test/SemaObjC/unguarded-availability-maccatalyst.m b/clang/test/SemaObjC/unguarded-availability-maccatalyst.m
new file mode 100644
index 0000000000000..590304dda505b
--- /dev/null
+++ b/clang/test/SemaObjC/unguarded-availability-maccatalyst.m
@@ -0,0 +1,91 @@
+// RUN: %clang_cc1 -triple x86_64-apple-ios14-macabi -fblocks -fsyntax-only -verify %s
+// RUN: %clang_cc1 -xobjective-c++ -triple x86_64-apple-ios14-macabi -fblocks -fsyntax-only -verify %s
+
+// RUN: %clang_cc1 -triple x86_64-apple-ios14.1-macabi -DNO_WARNING -fblocks -fsyntax-only -verify %s
+
+#ifdef NO_WARNING
+  // expected-no-diagnostics
+#endif
+
+#define AVAILABLE_PREV __attribute__((availability(macCatalyst, introduced = 13.1)))
+#define AVAILABLE_CURRENT __attribute__((availability(macCatalyst, introduced = 14)))
+#define AVAILABLE_NEXT __attribute__((availability(macCatalyst, introduced = 14.1)))
+
+void previouslyAvailable() AVAILABLE_PREV;
+void currentlyAvailable() AVAILABLE_CURRENT;
+void willBeAvailabile() AVAILABLE_NEXT;
+#ifndef NO_WARNING
+// expected-note at -2 {{'willBeAvailabile' has been marked as being introduced in macCatalyst 14.1 here, but the deployment target is macCatalyst 14.0.0}}
+#endif
+
+
+typedef struct {
+
+} Record AVAILABLE_NEXT;
+#ifndef NO_WARNING
+// expected-note at -2 {{'Record' has been marked as being introduced in macCatalyst 14.1 here, but the deployment target is macCatalyst 14.0.0}}
+#endif
+
+AVAILABLE_PREV
+Record var;
+#ifndef NO_WARNING
+// expected-warning at -2 {{'Record' is only available on macCatalyst 14.1 or newer}}
+// expected-note at -3 {{annotate 'var' with an availability attribute to silence this warnin}}
+#endif
+
+AVAILABLE_NEXT
+Record var2;
+
+void test() {
+  previouslyAvailable();
+  currentlyAvailable();
+  willBeAvailabile();
+#ifndef NO_WARNING
+  // expected-warning at -2 {{'willBeAvailabile' is only available on macCatalyst 14.1 or newer}}
+  // expected-note at -3 {{enclose 'willBeAvailabile' in an @available check to silence this warning}}
+#endif
+  if (@available(maccatalyst 14.1, *))
+    willBeAvailabile(); // OK
+  if (@available(ios 14.1, *))
+    willBeAvailabile(); // Also OK
+  if (@available(macCatalyst 14.1, *))
+    willBeAvailabile(); // OK
+}
+
+void previouslyAvailableIOS() __attribute__((availability(ios, introduced = 10)));
+void currentlyAvailableIOS() __attribute__((availability(ios, introduced = 14)));
+void willBeAvailabileIOS() __attribute__((availability(ios, introduced = 14.1)));
+#ifndef NO_WARNING
+// expected-note at -2 {{'willBeAvailabileIOS' has been marked as being introduced in macCatalyst 14.1 here, but the deployment target is macCatalyst 14.0.0}}
+#endif
+
+void testIOSAvailabilityAlsoWorks() {
+  previouslyAvailableIOS();
+  currentlyAvailableIOS();
+  willBeAvailabileIOS();
+#ifndef NO_WARNING
+  // expected-warning at -2 {{'willBeAvailabileIOS' is only available on macCatalyst 14.1 or newer}}
+  // expected-note at -3 {{enclose 'willBeAvailabileIOS' in an @available check to silence this warning}}
+#endif
+  if (@available(macCatalyst 14.1, *))
+    willBeAvailabileIOS(); // OK
+  if (@available(ios 14.1, *))
+    willBeAvailabile(); // Also OK
+}
+
+typedef struct {
+
+} Record2 __attribute__((availability(ios, introduced = 14.1)));
+#ifndef NO_WARNING
+// expected-note at -2 {{'Record2' has been marked as being introduced in macCatalyst 14.1 here, but the deployment target is macCatalyst 14.0.0}}
+#endif
+
+__attribute__((availability(ios, introduced = 10)))
+Record2 var11;
+#ifndef NO_WARNING
+// expected-warning at -2 {{'Record2' is only available on macCatalyst 14.1 or newer}}
+// expected-note at -3 {{annotate 'var11' with an availability attribute to silence this warnin}}
+#endif
+
+__attribute__((availability(ios, introduced = 14.1)))
+Record2 var12;


        


More information about the cfe-commits mailing list