Skip to content

Clang does not process visionOS (xros) availability correctly #10782

@stuartcarnie

Description

@stuartcarnie

The following snippet of Objective-C code, which declares the typedef as available on watchOS, and unavailable on iOS, macOS and tvOS platforms:

#import <Foundation/Foundation.h>

API_UNAVAILABLE(visionos) @interface Foo
@end

typedef void(^Bar)(Foo *foo) API_AVAILABLE(watchos(5.0)) API_UNAVAILABLE(ios,macos,tvos);

Given iOS is unavailable, it also includes derivatives, which means visionOS.

Compiling this snippet with Apple's clang compiler works as expected:

echo "#import <Foundation/Foundation.h>\nAPI_UNAVAILABLE(visionos) @interface Foo\n@end\n typedef void(^Bar)(Foo *foo) API_AVAILABLE(watchos(5.0)) API_UNAVAILABLE(ios,macos,tvos);" | xcrun -sdk xros clang -mtargetos=xros2.5 -O2 -x objective-c -c -o test.o -

However, compiling with clang from here fails:

echo -e "#import <Foundation/Foundation.h>\nAPI_UNAVAILABLE(visionos) @interface Foo\n@end\ntypedef void(^Bar)(Foo *foo) API_AVAILABLE(watchos(5.0)) API_UNAVAILABLE(ios,macos,tvos);" | arm-apple-darwin11-clang -isysroot /root/SDKs/XROS2.5.sdk/ -mtargetos=xros2.5 -O2 -x objective-c -c -o test.o -

Output:

<stdin>:4:20: error: 'Foo' is unavailable: not available on visionOS
    4 | typedef void(^Bar)(Foo *foo) API_AVAILABLE(watchos(5.0)) API_UNAVAILABLE(ios,macos,tvos);
      |                    ^                                                                                                                                          <stdin>:2:38: note: 'Foo' has been explicitly marked unavailable here                                                                                                     2 | API_UNAVAILABLE(visionos) @interface Foo
      |                                      ^
1 error generated.

If I explicitly add visionos to the typedef declaration, it passes.

Unfortunately, this problem occurs when compiling Apple's SDKs, such as CoreMotion with llvm / clang, that has declarations such as:

/*!
 * @typedef CMDyskineticSymptomResultHandler
 * @brief Completion handler for CMDyskineticSymptomResult values.
 */
typedef void(^CMDyskineticSymptomResultHandler)(NSArray<CMDyskineticSymptomResult *> * _Nonnull dyskineticSymptomResult, NSError * _Nullable error) API_AVAILABLE(watchos(5.0)) API_UNAVAILABLE(ios, macos, tvos);

Note

This is a copy of llvm#142502, as I realise I am using a build from the Apple fork of llvm via https://github.com/tpoechtrager/osxcross.

Patch to fix release/19.x
diff --git a/clang/include/clang/Basic/DarwinSDKInfo.h b/clang/include/clang/Basic/DarwinSDKInfo.h
index db20b968a..33b36f53c 100644
--- a/clang/include/clang/Basic/DarwinSDKInfo.h
+++ b/clang/include/clang/Basic/DarwinSDKInfo.h
@@ -72,6 +72,13 @@ public:
                        llvm::Triple::TvOS, llvm::Triple::UnknownEnvironment);
     }
 
+    /// Returns the os-environment mapping pair that's used to represent the
+    /// iOS -> visionOS version mapping.
+    static inline constexpr OSEnvPair iOStoXROSPair() {
+      return OSEnvPair(llvm::Triple::IOS, llvm::Triple::UnknownEnvironment,
+                       llvm::Triple::XROS, llvm::Triple::UnknownEnvironment);
+    }
+
   private:
     StorageType Value;
 
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index e2eada24f..26290b3ba 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -2415,6 +2415,48 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
       auto NewDeprecated = AdjustTvOSVersion(Deprecated.Version);
       auto NewObsoleted = AdjustTvOSVersion(Obsoleted.Version);
 
+      AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr(
+          ND, AL, NewII, true /*Implicit*/, NewIntroduced, NewDeprecated,
+          NewObsoleted, IsUnavailable, Str, IsStrict, Replacement,
+          Sema::AMK_None, PriorityModifier + Sema::AP_InferredFromOtherPlatform,
+          IIEnvironment);
+      if (NewAttr)
+        D->addAttr(NewAttr);
+    }
+  } else if (S.Context.getTargetInfo().getTriple().isXROS()) {
+    // Transcribe "ios" to "visionos" (and add a new attribute) if the versioning
+    // matches before the start of the visionOS platform.
+    IdentifierInfo *NewII = nullptr;
+    if (II->getName() == "ios")
+      NewII = &S.Context.Idents.get("xros");
+    else if (II->getName() == "ios_app_extension")
+      NewII = &S.Context.Idents.get("xros_app_extension");
+
+    if (NewII) {
+      const auto *SDKInfo = S.getDarwinSDKInfoForAvailabilityChecking();
+      const auto *IOSToXROSMapping =
+          SDKInfo ? SDKInfo->getVersionMapping(
+                        DarwinSDKInfo::OSEnvPair::iOStoXROSPair())
+                  : nullptr;
+
+      auto AdjustTvOSVersion =
+          [IOSToXROSMapping](VersionTuple Version) -> VersionTuple {
+        if (Version.empty())
+          return Version;
+
+        if (IOSToXROSMapping) {
+          if (auto MappedVersion = IOSToXROSMapping->map(
+                  Version, VersionTuple(1, 0), std::nullopt)) {
+            return *MappedVersion;
+          }
+        }
+        return Version;
+      };
+
+      auto NewIntroduced = AdjustTvOSVersion(Introduced.Version);
+      auto NewDeprecated = AdjustTvOSVersion(Deprecated.Version);
+      auto NewObsoleted = AdjustTvOSVersion(Obsoleted.Version);
+
       AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr(
           ND, AL, NewII, true /*Implicit*/, NewIntroduced, NewDeprecated,
           NewObsoleted, IsUnavailable, Str, IsStrict, Replacement,
Patch to fix release/21.x
--- llvm-project-stable-20250402/clang/include/clang/Basic/DarwinSDKInfo.orig.h	2025-06-07 04:17:57
+++ llvm-project-stable-20250402/clang/include/clang/Basic/DarwinSDKInfo.h	2025-10-04 09:35:43
@@ -72,6 +72,13 @@
                        llvm::Triple::TvOS, llvm::Triple::UnknownEnvironment);
     }
 
+    /// Returns the os-environment mapping pair that's used to represent the
+    /// iOS -> visionOS version mapping.
+    static inline constexpr OSEnvPair iOStoXROSPair() {
+      return OSEnvPair(llvm::Triple::IOS, llvm::Triple::UnknownEnvironment,
+                       llvm::Triple::XROS, llvm::Triple::UnknownEnvironment);
+    }
+
   private:
     StorageType Value;
 
--- llvm-project-stable-20250402/clang/lib/Sema/SemaDeclAttr.orig.cpp	2025-06-07 04:17:57
+++ llvm-project-stable-20250402/clang/lib/Sema/SemaDeclAttr.cpp	2025-10-04 09:35:28
@@ -2636,6 +2636,48 @@
         if (IOSToTvOSMapping) {
           if (auto MappedVersion = IOSToTvOSMapping->map(
                   Version, VersionTuple(0, 0), std::nullopt)) {
+            return *MappedVersion;
+          }
+        }
+        return Version;
+      };
+
+      auto NewIntroduced = AdjustTvOSVersion(Introduced.Version);
+      auto NewDeprecated = AdjustTvOSVersion(Deprecated.Version);
+      auto NewObsoleted = AdjustTvOSVersion(Obsoleted.Version);
+
+      AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr(
+          ND, AL, NewII, true /*Implicit*/, NewIntroduced, NewDeprecated,
+          NewObsoleted, IsUnavailable, Str, IsStrict, Replacement,
+          Sema::AMK_None, PriorityModifier + Sema::AP_InferredFromOtherPlatform,
+          IIEnvironment);
+      if (NewAttr)
+        D->addAttr(NewAttr);
+    }
+  } else if (S.Context.getTargetInfo().getTriple().isXROS()) {
+    // Transcribe "ios" to "visionos" (and add a new attribute) if the versioning
+    // matches before the start of the visionOS platform.
+    IdentifierInfo *NewII = nullptr;
+    if (II->getName() == "ios")
+      NewII = &S.Context.Idents.get("xros");
+    else if (II->getName() == "ios_app_extension")
+      NewII = &S.Context.Idents.get("xros_app_extension");
+
+    if (NewII) {
+      const auto *SDKInfo = S.getDarwinSDKInfoForAvailabilityChecking();
+      const auto *IOSToXROSMapping =
+          SDKInfo ? SDKInfo->getVersionMapping(
+                        DarwinSDKInfo::OSEnvPair::iOStoXROSPair())
+                  : nullptr;
+
+      auto AdjustTvOSVersion =
+          [IOSToXROSMapping](VersionTuple Version) -> VersionTuple {
+        if (Version.empty())
+          return Version;
+
+        if (IOSToXROSMapping) {
+          if (auto MappedVersion = IOSToXROSMapping->map(
+                  Version, VersionTuple(1, 0), std::nullopt)) {
             return *MappedVersion;
           }
         }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions