diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -856,6 +856,8 @@ .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 @@ .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 @@ .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 --- a/clang/lib/Basic/Targets/OSTargets.cpp +++ b/clang/lib/Basic/Targets/OSTargets.cpp @@ -55,6 +55,8 @@ } 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 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2556,6 +2556,36 @@ 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 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -19816,16 +19816,26 @@ ExprResult Sema::ActOnObjCAvailabilityCheckExpr( llvm::ArrayRef 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 { + 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 --- /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 --- /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)) +@interface NewClass +@end + +@interface OldButOfferFixit +@property(copy) NewClass *prop; +// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:1-[[@LINE-2]]:1}:"API_AVAILABLE(maccatalyst(13.2))\n" + +@end diff --git a/clang/test/Sema/attr-availability-maccatalyst.c b/clang/test/Sema/attr-availability-maccatalyst.c new file mode 100644 --- /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@-2 {{'f0' is deprecated: first deprecated in macCatalyst 9.1}} +#else + // expected-warning@-4 {{'f0' is deprecated: first deprecated in macCatalyst (App Extension) 9.1}} +#endif + f1(0); + f2(0); +#ifndef APPEXT + // expected-warning@-2 {{'f2' is deprecated: first deprecated in macCatalyst 9.0}} +#else + // expected-warning@-4 {{'f2' is deprecated: first deprecated in macCatalyst (App Extension) 9.0}} +#endif + f3(0); +#ifndef APPEXT + // expected-error@-2 {{'f3' is unavailable: obsoleted in macCatalyst 9.0}} +#else + // expected-error@-4 {{'f3' is unavailable: obsoleted in macCatalyst (App Extension) 9.0}} +#endif + f32(0); +#ifndef APPEXT + // expected-error@-2 {{'f32' is unavailable: obsoleted in macCatalyst 9.0}} +#else + // expected-error@-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@-2 {{'f7' is deprecated: first deprecated in macCatalyst 4.0}} +#else + // expected-warning@-4 {{'f7' is deprecated: first deprecated in macCatalyst (App Extension) 4.0}} +#endif + f8(); +#ifndef APPEXT + // expected-error@-2 {{'f8' is unavailable: obsoleted in macCatalyst 4.0}} +#else + // expected-error@-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@-2 {{'f202' is deprecated: first deprecated in macCatalyst 13.1}} +#else +// expected-warning@-4 {{'f202' is deprecated: first deprecated in macCatalyst (App Extension) 13.1}} +#endif + f203(); +#ifndef APPEXT + // expected-error@-2 {{'f203' is unavailable: obsoleted in macCatalyst 13.1}} +#else + // expected-error@-4 {{'f203' is unavailable: obsoleted in macCatalyst (App Extension) 13.1}} +#endif + f204(); +#ifndef APPEXT + // expected-error@-2 {{'f204' is unavailable: not available on macCatalyst}} +#else + // expected-error@-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 --- /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@-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@-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@-2 {{'Record' is only available on macCatalyst 14.1 or newer}} +// expected-note@-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@-2 {{'willBeAvailabile' is only available on macCatalyst 14.1 or newer}} + // expected-note@-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@-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@-2 {{'willBeAvailabileIOS' is only available on macCatalyst 14.1 or newer}} + // expected-note@-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@-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@-2 {{'Record2' is only available on macCatalyst 14.1 or newer}} +// expected-note@-3 {{annotate 'var11' with an availability attribute to silence this warnin}} +#endif + +__attribute__((availability(ios, introduced = 14.1))) +Record2 var12;