https://github.com/ahatanak created https://github.com/llvm/llvm-project/pull/176990
Automatically infer and apply availability or unavailable attributes for visionOS based on the corresponding iOS availability of the same declaration using the version mapping information provided in SDKSettings.json. rdar://162148639 >From ba2e1a8a68f2daa1118b475626a15d078bf50aff Mon Sep 17 00:00:00 2001 From: Akira Hatanaka <[email protected]> Date: Thu, 15 Jan 2026 16:04:00 -0800 Subject: [PATCH] Infer visionOS availability from iOS availability Automatically infer and apply availability or unavailable attributes for visionOS based on the corresponding iOS availability of the same declaration using the version mapping information provided in SDKSettings.json. rdar://162148639 --- clang/lib/Sema/SemaDeclAttr.cpp | 93 ++++++++++++++ .../Sema/Inputs/XROS.sdk/SDKSettings.json | 21 ++++ .../xrOS/Sema/infer-ios-availability.c | 117 ++++++++++++++++++ ...le-from-missing-ios-availability-mapping.c | 12 ++ 4 files changed, 243 insertions(+) create mode 100644 clang/test/PlatformSpecific/xrOS/Sema/Inputs/XROS.sdk/SDKSettings.json create mode 100644 clang/test/PlatformSpecific/xrOS/Sema/infer-ios-availability.c create mode 100644 clang/test/PlatformSpecific/xrOS/Sema/infer-unavailable-from-missing-ios-availability-mapping.c diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index d762bcd789bf5..dc954c3925596 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2518,6 +2518,80 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr( return nullptr; } +/// Returns true if the given availability attribute should be inferred, and +/// adjusts the value of the attribute as necessary to facilitate that. +static bool shouldInferAvailabilityAttribute(const ParsedAttr &AL, + IdentifierInfo *&II, + bool &IsUnavailable, + VersionTuple &Introduced, + VersionTuple &Deprecated, + VersionTuple &Obsolete, Sema &S) { + const llvm::Triple &TT = S.Context.getTargetInfo().getTriple(); + const ASTContext &Context = S.Context; + if (TT.getOS() != llvm::Triple::XROS) + return false; + IdentifierInfo *NewII = nullptr; + if (II->getName() == "ios") + NewII = &Context.Idents.get("xros"); + else if (II->getName() == "ios_app_extension") + NewII = &Context.Idents.get("xros_app_extension"); + if (!NewII) + return false; + II = NewII; + + auto MakeUnavailable = [&]() { + IsUnavailable = true; + // Reset introduced, deprecated, obsoleted. + Introduced = VersionTuple(); + Deprecated = VersionTuple(); + Obsolete = VersionTuple(); + }; + + const DarwinSDKInfo *SDKInfo = S.getDarwinSDKInfoForAvailabilityChecking( + AL.getRange().getBegin(), "ios"); + + if (!SDKInfo) { + MakeUnavailable(); + return true; + } + // Map from the fallback platform availability to the current platform + // availability. + const auto *Mapping = SDKInfo->getVersionMapping(DarwinSDKInfo::OSEnvPair( + llvm::Triple::IOS, llvm::Triple::UnknownEnvironment, llvm::Triple::XROS, + llvm::Triple::UnknownEnvironment)); + if (!Mapping) { + MakeUnavailable(); + return true; + } + + if (!Introduced.empty()) { + auto NewIntroduced = Mapping->mapIntroducedAvailabilityVersion(Introduced); + if (!NewIntroduced) { + MakeUnavailable(); + return true; + } + Introduced = *NewIntroduced; + } + + if (!Obsolete.empty()) { + auto NewObsolete = + Mapping->mapDeprecatedObsoletedAvailabilityVersion(Obsolete); + if (!NewObsolete) { + MakeUnavailable(); + return true; + } + Obsolete = *NewObsolete; + } + + if (!Deprecated.empty()) { + auto NewDeprecated = + Mapping->mapDeprecatedObsoletedAvailabilityVersion(Deprecated); + Deprecated = NewDeprecated ? *NewDeprecated : VersionTuple(); + } + + return true; +} + static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (isa<UsingDecl, UnresolvedUsingTypenameDecl, UnresolvedUsingValueDecl>( D)) { @@ -2630,6 +2704,25 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (NewAttr) D->addAttr(NewAttr); + if (S.Context.getTargetInfo().getTriple().getOS() == llvm::Triple::XROS) { + IdentifierInfo *NewII = II; + bool NewIsUnavailable = IsUnavailable; + VersionTuple NewIntroduced = Introduced.Version; + VersionTuple NewDeprecated = Deprecated.Version; + VersionTuple NewObsoleted = Obsoleted.Version; + if (shouldInferAvailabilityAttribute(AL, NewII, NewIsUnavailable, + NewIntroduced, NewDeprecated, + NewObsoleted, S)) { + AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr( + ND, AL, NewII, true /*Implicit*/, NewIntroduced, NewDeprecated, + NewObsoleted, NewIsUnavailable, Str, IsStrict, Replacement, + AvailabilityMergeKind::None, + PriorityModifier + Sema::AP_InferredFromOtherPlatform, IIEnvironment); + if (NewAttr) + D->addAttr(NewAttr); + } + } + // Transcribe "ios" to "watchos" (and add a new attribute) if the versioning // matches before the start of the watchOS platform. if (S.Context.getTargetInfo().getTriple().isWatchOS()) { diff --git a/clang/test/PlatformSpecific/xrOS/Sema/Inputs/XROS.sdk/SDKSettings.json b/clang/test/PlatformSpecific/xrOS/Sema/Inputs/XROS.sdk/SDKSettings.json new file mode 100644 index 0000000000000..e4f7c87519805 --- /dev/null +++ b/clang/test/PlatformSpecific/xrOS/Sema/Inputs/XROS.sdk/SDKSettings.json @@ -0,0 +1,21 @@ +{ + "DefaultVariant": "xrOS", "DisplayName": "xrOS", + "Version": "1.0", + "CanonicalName": "xros1.0", + "MaximumDeploymentTarget": "1.0.99", + "SupportedTargets": { + "xros": { + "Archs": ["arm64e", "arm64"], + "LLVMTargetTripleVendor": "apple", + "LLVMTargetTripleSys": "xros", + "LLVMTargetTripleEnvironment": "", + "SystemPrefix": "" + } + }, + "VersionMap": { + "iOS_visionOS": {"17.1": "1.0"}, + "iOS_xrOS": {"17.1": "1.0"}, + "visionOS_iOS": {"1.0": "17.1"}, + "xrOS_iOS": {"1.0": "17.1"} + } +} diff --git a/clang/test/PlatformSpecific/xrOS/Sema/infer-ios-availability.c b/clang/test/PlatformSpecific/xrOS/Sema/infer-ios-availability.c new file mode 100644 index 0000000000000..76be32370e745 --- /dev/null +++ b/clang/test/PlatformSpecific/xrOS/Sema/infer-ios-availability.c @@ -0,0 +1,117 @@ +// RUN: %clang_cc1 -triple arm64-apple-xros1 -verify=ios -isysroot %S/Inputs/XROS.sdk %s 2>&1 +// RUN: %clang_cc1 -triple arm64-apple-xros1 -fapplication-extension -verify=ios,ext -isysroot %S/Inputs/XROS.sdk %s 2>&1 + +// RUN: %clang_cc1 -triple arm64-apple-xros2 -DXROS2 -verify=ios -isysroot %S/Inputs/XROS.sdk %s 2>&1 + +__attribute__((availability(ios, unavailable))) +void ios_unavail(); // ios-note {{}} + +__attribute__((availability(ios_app_extension, unavailable))) +void ios_ext_unavail(); // ext-note {{}} + +void use() { + ios_unavail(); // ios-error {{'ios_unavail' is unavailable: not available on }} + ios_ext_unavail(); // ext-error {{'ios_ext_unavail' is unavailable: not available on }} +} + +__attribute__((availability(ios, introduced=10))) +void ios_introduced_10(); + +__attribute__((availability(ios_app_extension, introduced=10))) +void ios_ext_introduced_10(); + +__attribute__((availability(ios, introduced=17.1))) +void ios_introduced_17(); + +__attribute__((availability(ios_app_extension, introduced=17.1))) +void ios_ext_introduced_17(); + +__attribute__((availability(ios, introduced=18))) +void ios_introduced_18(); // ios-note {{}} + +__attribute__((availability(ios_app_extension, introduced=18))) +void ios_ext_introduced_18(); // ext-note {{}} + +void useIntroduced() { + // introduced iOS < 10 => introduced xrOS 1 + ios_introduced_10(); + ios_ext_introduced_10(); + // introduced iOS 17.1 => introduced xrOS 1 + ios_introduced_17(); + ios_ext_introduced_17(); + // introduced iOS 18 => xros unavailable (no mapping) + ios_introduced_18(); // ios-error {{is unavailable: not available on }} + ios_ext_introduced_18(); // ext-error {{is unavailable: not available on }} +} + +__attribute__((availability(ios, deprecated=10))) +void ios_deprecated_10(); // ios-note {{}} + +__attribute__((availability(ios_app_extension, deprecated=10))) +void ios_ext_deprecated_10(); // ext-note {{}} + +__attribute__((availability(ios, deprecated=17.1))) +void ios_deprecated_17(); // ios-note {{}} + +__attribute__((availability(ios_app_extension, deprecated=17.1))) +void ios_ext_deprecated_17(); // ext-note {{}} + +__attribute__((availability(ios, deprecated=18))) +void ios_deprecated_18(); +#ifdef XROS2 +// ios-note@-2 {{}} +#endif + +__attribute__((availability(ios_app_extension, deprecated=18))) +void ios_ext_deprecated_18(); + +void useDeprecated() { + // deprecated iOS < 10 => deprecated xrOS 1 + ios_deprecated_10(); // ios-warning {{is deprecated: first deprecated in}} + ios_ext_deprecated_10(); // ext-warning {{is deprecated: first deprecated in}} + // deprecated iOS 17.1 => deprecated xrOS 1 + ios_deprecated_17(); // ios-warning {{is deprecated: first deprecated in}} + ios_ext_deprecated_17(); // ext-warning {{is deprecated: first deprecated in}} + // deprecated iOS 18 => deprecated xrOS 1.0.99 + ios_deprecated_18(); +#ifdef XROS2 + // ios-warning@-2 {{is deprecated: first deprecated in}} +#endif + ios_ext_deprecated_18(); +} + +__attribute__((availability(ios, obsoleted=10))) +void ios_obsoleted_10(); // ios-note {{}} + +__attribute__((availability(ios_app_extension, obsoleted=10))) +void ios_ext_obsoleted_10(); // ext-note {{}} + +__attribute__((availability(ios, obsoleted=17.1))) +void ios_obsoleted_17(); // ios-note {{}} + +__attribute__((availability(ios_app_extension, obsoleted=17.1))) +void ios_ext_obsoleted_17(); // ext-note {{}} + +__attribute__((availability(ios, obsoleted=18))) +void ios_obsoleted_18(); +#ifdef XROS2 +// ios-note@-2 {{}} +#endif + +__attribute__((availability(ios_app_extension, obsoleted=18))) +void ios_ext_obsoleted_18(); + +void useObsoleted() { + // deprecated iOS < 10 => deprecated xrOS 1 + ios_obsoleted_10(); // ios-error {{is unavailable: obsoleted in}} + ios_ext_obsoleted_10(); // ext-error {{is unavailable: obsoleted in}} + // deprecated iOS 17.1 => deprecated xrOS 1 + ios_obsoleted_17(); // ios-error {{is unavailable: obsoleted in}} + ios_ext_obsoleted_17(); // ext-error {{is unavailable: obsoleted in}} + // obsoleted iOS 18 => obsoleted xrOS 1.0.99 + ios_obsoleted_18(); +#ifdef XROS2 + // ios-error@-2 {{is unavailable: obsoleted in}} +#endif + ios_ext_obsoleted_18(); +} diff --git a/clang/test/PlatformSpecific/xrOS/Sema/infer-unavailable-from-missing-ios-availability-mapping.c b/clang/test/PlatformSpecific/xrOS/Sema/infer-unavailable-from-missing-ios-availability-mapping.c new file mode 100644 index 0000000000000..2645b487ffd72 --- /dev/null +++ b/clang/test/PlatformSpecific/xrOS/Sema/infer-unavailable-from-missing-ios-availability-mapping.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -triple arm64-apple-xros1 -verify=ios -DNOSDK %s 2>&1 +// RUN: %clang_cc1 -triple arm64-apple-xros1 -verify=ios -isysroot %S/Inputs/XROS.sdk %s 2>&1 + +#ifdef NOSDK +// ios-warning@+2 {{ios availability is ignored without a valid 'SDKSettings.json' in the SDK}} +#endif +__attribute__((availability(ios, introduced=18))) // note the version introduced has to be higher than the versions in SDKSettings +void ios_introduced_10(); // ios-note {{}} + +void useIntroduced() { + ios_introduced_10(); // ios-error {{is unavailable: not available on }} +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
