Skip to content

Infer visionOS availability from iOS availability#176990

Merged
ahatanak merged 1 commit into
llvm:mainfrom
ahatanak:infer-visionos-availability
Jan 23, 2026
Merged

Infer visionOS availability from iOS availability#176990
ahatanak merged 1 commit into
llvm:mainfrom
ahatanak:infer-visionos-availability

Conversation

@ahatanak
Copy link
Copy Markdown
Contributor

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

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
@ahatanak ahatanak marked this pull request as ready for review January 20, 2026 20:22
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Jan 20, 2026
@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Jan 20, 2026

@llvm/pr-subscribers-clang

Author: Akira Hatanaka (ahatanak)

Changes

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


Full diff: https://github.com/llvm/llvm-project/pull/176990.diff

4 Files Affected:

  • (modified) clang/lib/Sema/SemaDeclAttr.cpp (+93)
  • (added) clang/test/PlatformSpecific/xrOS/Sema/Inputs/XROS.sdk/SDKSettings.json (+21)
  • (added) clang/test/PlatformSpecific/xrOS/Sema/infer-ios-availability.c (+117)
  • (added) clang/test/PlatformSpecific/xrOS/Sema/infer-unavailable-from-missing-ios-availability-mapping.c (+12)
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 }}
+}

@ahatanak ahatanak merged commit 8a94708 into llvm:main Jan 23, 2026
16 checks passed
@ahatanak ahatanak deleted the infer-visionos-availability branch January 23, 2026 01:35
@llvm-ci
Copy link
Copy Markdown

llvm-ci commented Jan 23, 2026

LLVM Buildbot has detected a new failure on builder arc-builder running on arc-worker while building clang at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/3/builds/27622

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-all) failure: 1200 seconds without output running [b'ninja', b'check-all'], attempting to kill
...
8.279 [38/18/16] Linking CXX executable unittests/CGData/CGDataTests
8.426 [37/18/17] Linking CXX executable bin/OrcV2CBindingsRemovableCode
8.900 [36/18/18] Linking CXX executable unittests/Bitcode/BitcodeTests
9.187 [35/18/19] Linking CXX executable bin/Kaleidoscope-Ch6
9.388 [34/18/20] Linking CXX executable tools/lld/unittests/AsLibELF/LLDAsLibELFTests
9.558 [33/18/21] Linking CXX executable bin/Kaleidoscope-Ch5
9.624 [32/18/22] Linking CXX executable unittests/DebugInfo/BTF/DebugInfoBTFTests
9.723 [31/18/23] Linking CXX executable unittests/DebugInfo/GSYM/DebugInfoGSYMTests
9.794 [30/18/24] Linking CXX executable tools/lld/unittests/AsLibAll/LLDAsLibAllTests
10.035 [29/18/25] Linking CXX executable bin/OrcV2CBindingsLazy
command timed out: 1200 seconds without output running [b'ninja', b'check-all'], attempting to kill
process killed by signal 9
program finished with exit code -1
elapsedTime=1210.841463

ahatanaka pushed a commit to swiftlang/llvm-project that referenced this pull request Jan 23, 2026
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
ahatanaka pushed a commit to swiftlang/llvm-project that referenced this pull request Jan 26, 2026
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants