diff --git a/clang/CMakeLists.txt b/clang/CMakeLists.txt index e4cb1a359620d..9a9110ebbf6c8 100644 --- a/clang/CMakeLists.txt +++ b/clang/CMakeLists.txt @@ -376,6 +376,8 @@ if (APPLE AND NOT CMAKE_LINKER MATCHES ".*lld.*") endif() include(AddClang) +include(AddDarwinPlatform) +clang_add_additional_platform() set(CMAKE_INCLUDE_CURRENT_DIR ON) diff --git a/clang/cmake/modules/AddDarwinPlatform.cmake b/clang/cmake/modules/AddDarwinPlatform.cmake new file mode 100644 index 0000000000000..a00cede522e27 --- /dev/null +++ b/clang/cmake/modules/AddDarwinPlatform.cmake @@ -0,0 +1,23 @@ +# FIXME: How to generalize this to be driven by discovered SDKs? +function(clang_add_additional_platform) + # Add a platform. + set(name "xros") + set(CLANG_PLATFORM_${name}_PLATFORM_AVAILABILITY_NAME "xros" PARENT_SCOPE) + set(platformFallBack "iphoneos") + + if (NOT "${platformFallBack}" STREQUAL "") + set(fallbackTripleName "${platformFallBack}") + # FIXME: we need a generic platform -> triple mapping. + if ("${fallbackTripleName}" STREQUAL "iphoneos") + set(fallbackTripleName "ios") + endif() + + message(STATUS "Platform ${name} has fallback platform - ${fallbackTripleName}") + set(CLANG_PLATFORM_${name}_FALLBACK_PLATFORM_AVAILABILITY_NAME "${fallbackTripleName}" PARENT_SCOPE) + + # FIXME: This is a hack for xrOS, but should be in the SDKSettings too. + set(CLANG_PLATFORM_${name}_INFER_UNAVAILABLE 1 PARENT_SCOPE) + set(CLANG_PLATFORM_${name}_PLATFORM_TRIPLE_OS_VALUE "XROS" PARENT_SCOPE) + set(CLANG_PLATFORM_${name}_FALLBACK_PLATFORM_TRIPLE_OS_VALUE "IOS" PARENT_SCOPE) + endif() +endfunction() diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index 9da50ecf1823c..a8734af580885 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -13,6 +13,38 @@ clang_tablegen(OpenCLBuiltins.inc -gen-clang-opencl-builtins TARGET ClangOpenCLBuiltinsImpl ) +# Configure additional platform-specific code that controls things like +# availability attributes. +set(TEMPLATE_INCLUDES_INFER_AVAILABILITY_ATTRIBUTE "") + +foreach(supported_platform ${LLVM_SUPPORTED_PLATFORMS}) + if (${CLANG_PLATFORM_${supported_platform}_INFER_UNAVAILABLE}) + # Generate code that infers availability(unavailable) attribute if a platform + # requires that. + set(output_file_name "InferUnavailableAvailabilityFromFallbackPlatform_${supported_platform}.def") + message(STATUS "Adding platform specific availability attribute code - ${output_file_name}") + + set(PLATFORM_TRIPLE_OS_VALUE + "${CLANG_PLATFORM_${supported_platform}_PLATFORM_TRIPLE_OS_VALUE}") + set(FALLBACK_PLATFORM_TRIPLE_OS_VALUE + "${CLANG_PLATFORM_${supported_platform}_FALLBACK_PLATFORM_TRIPLE_OS_VALUE}") + set(PLATFORM_AVAILABILITY_NAME + "${CLANG_PLATFORM_${supported_platform}_PLATFORM_AVAILABILITY_NAME}") + set(FALLBACK_PLATFORM_AVAILABILITY_NAME + "${CLANG_PLATFORM_${supported_platform}_FALLBACK_PLATFORM_AVAILABILITY_NAME}") + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/PlatformTemplates/InferUnavailableAvailabilityFromFallbackPlatform.def.in + ${CMAKE_CURRENT_BINARY_DIR}/PlatformTemplates/${output_file_name} + ) + set(TEMPLATE_INCLUDES_INFER_AVAILABILITY_ATTRIBUTE "${TEMPLATE_INCLUDES_INFER_AVAILABILITY_ATTRIBUTE}\n#include \"PlatformTemplates/${output_file_name}\"") + endif() +endforeach() + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/InferAvailabilityAttribute.cpp.in + ${CMAKE_CURRENT_BINARY_DIR}/InferAvailabilityAttribute.cpp +) + add_clang_library(clangSema AnalysisBasedWarnings.cpp # TO_UPSTREAM(BoundsSafety) ON @@ -108,6 +140,7 @@ add_clang_library(clangSema SemaWasm.cpp SemaX86.cpp TypeLocBuilder.cpp + ${CMAKE_CURRENT_BINARY_DIR}/InferAvailabilityAttribute.cpp DEPENDS ClangOpenCLBuiltinsImpl diff --git a/clang/lib/Sema/InferAvailabilityAttribute.cpp.in b/clang/lib/Sema/InferAvailabilityAttribute.cpp.in new file mode 100644 index 0000000000000..5f96c4b9643be --- /dev/null +++ b/clang/lib/Sema/InferAvailabilityAttribute.cpp.in @@ -0,0 +1,47 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/Basic/DarwinSDKInfo.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LLVM.h" +#include "llvm/Support/VersionTuple.h" +#include "llvm/TargetParser/Triple.h" + +namespace clang { + +/// Returns true if the given availability attribute should be inferred, and +/// adjusts the value of the attribute as necessary to facilitate that. +bool shouldInferAvailabilityAttribute(const llvm::Triple &TT, + const DarwinSDKInfo *SDKInfo, + const ASTContext &Context, + IdentifierInfo *&II, + bool &IsUnavailable, + VersionTuple &Introduced, + VersionTuple &Deprecated, + VersionTuple &Obsolete) { + for (const auto &f : { + // function ptr promotion + +([] (const llvm::Triple &TT, + const DarwinSDKInfo *SDKInfo, + const ASTContext &Context, + IdentifierInfo *&II, + bool &IsUnavailable, + VersionTuple &Introduced, + VersionTuple &Deprecated, + VersionTuple &Obsolete) -> bool { return false; }), + @TEMPLATE_INCLUDES_INFER_AVAILABILITY_ATTRIBUTE@ + }) { + if (f(TT, SDKInfo, Context, II, IsUnavailable, Introduced, Deprecated, + Obsolete)) + return true; + } + return false; +} + +} // end namespace clang diff --git a/clang/lib/Sema/PlatformTemplates/InferUnavailableAvailabilityFromFallbackPlatform.def.in b/clang/lib/Sema/PlatformTemplates/InferUnavailableAvailabilityFromFallbackPlatform.def.in new file mode 100644 index 0000000000000..ee72e0181667f --- /dev/null +++ b/clang/lib/Sema/PlatformTemplates/InferUnavailableAvailabilityFromFallbackPlatform.def.in @@ -0,0 +1,108 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This platform-specific template implements inferral of +// __attribute__((availability(platform, unavailable))) from a base platform +// attribute __attribute__((availability(fallback platform, unavailable))). +// +// INPUTS: +// PLATFORM_TRIPLE_OS_VALUE - the name of the target OS enum in the Triple. +// FALLBACK_PLATFORM_TRIPLE_OS_VALUE - the name of the fallback target OS +// enum in the Triple. +// PLATFORM_AVAILABILITY_NAME - the name of availability platform. +// FALLBACK_PLATFORM_AVAILABILITY_NAME - the name of availability for the +// fallback platform. +// +//===----------------------------------------------------------------------===// + +// FIXME: This is now doing more than 'unavailable', the docs have to be updated. + +// x introduced := map(ios introduced, 1.0, None) +// None -> x unavailable +// -> x introduced + +// x deprecated := map(ios deprecated, 1.0, MaximumDeploymentVersion) +// None -> None +// -> x deprecated + +// x obsoleted := map(ios obsoleted, 1.0, MaximumDeploymentVersion) +// None -> x unavailable +// -> x obsoleted + +/// Returns true if the given availability attribute should be inferred. ++([] (const llvm::Triple &TT, + const DarwinSDKInfo *SDKInfo, + const ASTContext &Context, + IdentifierInfo *&II, + bool &IsUnavailable, + VersionTuple &Introduced, + VersionTuple &Deprecated, + VersionTuple &Obsolete) -> bool { + if (TT.getOS() != llvm::Triple::@PLATFORM_TRIPLE_OS_VALUE@) + return false; + IdentifierInfo *NewII = nullptr; + if (II->getName() == "@FALLBACK_PLATFORM_AVAILABILITY_NAME@") + NewII = &Context.Idents.get("@PLATFORM_AVAILABILITY_NAME@"); + else if (II->getName() == "@FALLBACK_PLATFORM_AVAILABILITY_NAME@_app_extension") + NewII = &Context.Idents.get("@PLATFORM_AVAILABILITY_NAME@_app_extension"); + if (!NewII) + return false; + II = NewII; + + auto MakeUnavailable = [&]() { + IsUnavailable = true; + // Reset introduced, deprecated, obsoleted. + Introduced = VersionTuple(); + Deprecated = VersionTuple(); + Obsolete = VersionTuple(); + }; + + 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::@FALLBACK_PLATFORM_TRIPLE_OS_VALUE@, + llvm::Triple::UnknownEnvironment, + llvm::Triple::@PLATFORM_TRIPLE_OS_VALUE@, + llvm::Triple::UnknownEnvironment)); + if (!Mapping) { + MakeUnavailable(); + return true; + } + + if (Introduced) { + auto NewIntroduced = Mapping->mapIntroducedAvailabilityVersion(Introduced); + if (!NewIntroduced) { + MakeUnavailable(); + return true; + } + Introduced = *NewIntroduced; + } + + if (Obsolete) { + auto NewObsolete = + Mapping->mapDeprecatedObsoletedAvailabilityVersion(Obsolete); + if (!NewObsolete) { + MakeUnavailable(); + return true; + } + Obsolete = *NewObsolete; + } + + if (Deprecated) { + auto NewDeprecated = + Mapping->mapDeprecatedObsoletedAvailabilityVersion(Deprecated); + Deprecated = NewDeprecated ? *NewDeprecated : VersionTuple(); + } + + return true; +}), diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 88ef7970fa335..1175300b7ceeb 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2453,6 +2453,17 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr( return nullptr; } +namespace clang { + +/// Returns true if the given availability attribute should be inferred, and +/// adjusts the value of the attribute as necessary to facilitate that. +bool shouldInferAvailabilityAttribute( + const llvm::Triple &TT, const DarwinSDKInfo *SDKInfo, + const ASTContext &Context, IdentifierInfo *&II, bool &IsUnavailable, + VersionTuple &Introduced, VersionTuple &Deprecated, VersionTuple &Obsolete); + +} // end namespace clang + static void handleFeatureAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (S.getLangOpts().CPlusPlus) { @@ -2634,6 +2645,32 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (NewAttr) D->addAttr(NewAttr); + /* TO_UPSTREAM(XROS) ON */ + if (S.Context.getTargetInfo().getTriple().getOS() == llvm::Triple::XROS) { + // Infer availability attributes using platform-specific logic, driven by + // the SDKSettings if necessary. + IdentifierInfo *NewII = II; + bool NewIsUnavailable = IsUnavailable; + VersionTuple NewIntroduced = Introduced.Version; + VersionTuple NewDeprecated = Deprecated.Version; + VersionTuple NewObsoleted = Obsoleted.Version; + if (shouldInferAvailabilityAttribute(S.Context.getTargetInfo().getTriple(), + S.getDarwinSDKInfoForAvailabilityChecking(AL.getRange().getBegin(), "ios"), + S.Context, NewII, + NewIsUnavailable, NewIntroduced, + NewDeprecated, NewObsoleted)) { + 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); + } + } + + /* TO_UPSTREAM(XROS) OFF */ + // 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..cad7a55245751 --- /dev/null +++ b/clang/test/PlatformSpecific/xrOS/Sema/Inputs/XROS.sdk/SDKSettings.json @@ -0,0 +1,9 @@ +{ + "DefaultVariant": "xrOS", "DisplayName": "xrOS", + "Version": "1.0", + "CanonicalName": "xros1.0", + "MaximumDeploymentTarget": "1.0.99", + "VersionMap": { + "iOS_xrOS":{"15.0":"1.0"} + } +} 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..7933f9d39bfad --- /dev/null +++ b/clang/test/PlatformSpecific/xrOS/Sema/infer-ios-availability.c @@ -0,0 +1,119 @@ +// REQUIRES: platform-xros + +// 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=15))) +void ios_introduced_15(); + +__attribute__((availability(ios_app_extension, introduced=15))) +void ios_ext_introduced_15(); + +__attribute__((availability(ios, introduced=16))) +void ios_introduced_16(); // ios-note {{}} + +__attribute__((availability(ios_app_extension, introduced=16))) +void ios_ext_introduced_16(); // ext-note {{}} + +void useIntroduced() { + // introduced iOS < 10 => introduced xrOS 1 + ios_introduced_10(); + ios_ext_introduced_10(); + // introduced iOS 15 => introduced xrOS 1 + ios_introduced_15(); + ios_ext_introduced_15(); + // introduced iOS 16 => xros unavailable (no mapping) + ios_introduced_16(); // ios-error {{is unavailable: not available on }} + ios_ext_introduced_16(); // 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=15))) +void ios_deprecated_15(); // ios-note {{}} + +__attribute__((availability(ios_app_extension, deprecated=15))) +void ios_ext_deprecated_15(); // ext-note {{}} + +__attribute__((availability(ios, deprecated=16))) +void ios_deprecated_16(); +#ifdef XROS2 +// ios-note@-2 {{}} +#endif + +__attribute__((availability(ios_app_extension, deprecated=16))) +void ios_ext_deprecated_16(); + +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 15 => deprecated xrOS 1 + ios_deprecated_15(); // ios-warning {{is deprecated: first deprecated in}} + ios_ext_deprecated_15(); // ext-warning {{is deprecated: first deprecated in}} + // deprecated iOS 16 => deprecated xrOS 1.0.99 + ios_deprecated_16(); +#ifdef XROS2 + // ios-warning@-2 {{is deprecated: first deprecated in}} +#endif + ios_ext_deprecated_16(); +} + +__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=15))) +void ios_obsoleted_15(); // ios-note {{}} + +__attribute__((availability(ios_app_extension, obsoleted=15))) +void ios_ext_obsoleted_15(); // ext-note {{}} + +__attribute__((availability(ios, obsoleted=16))) +void ios_obsoleted_16(); +#ifdef XROS2 +// ios-note@-2 {{}} +#endif + +__attribute__((availability(ios_app_extension, obsoleted=16))) +void ios_ext_obsoleted_16(); + +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 15 => deprecated xrOS 1 + ios_obsoleted_15(); // ios-error {{is unavailable: obsoleted in}} + ios_ext_obsoleted_15(); // ext-error {{is unavailable: obsoleted in}} + // obsoleted iOS 16 => obsoleted xrOS 1.0.99 + ios_obsoleted_16(); +#ifdef XROS2 + // ios-error@-2 {{is unavailable: obsoleted in}} +#endif + ios_ext_obsoleted_16(); +} 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..a7b25bdb664b5 --- /dev/null +++ b/clang/test/PlatformSpecific/xrOS/Sema/infer-unavailable-from-missing-ios-availability-mapping.c @@ -0,0 +1,14 @@ +// REQUIRES: platform-xros + +// 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=17))) // 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 }} +} diff --git a/clang/test/lit.site.cfg.py.in b/clang/test/lit.site.cfg.py.in index fc1cefc19dba9..05a749a7f6a1f 100644 --- a/clang/test/lit.site.cfg.py.in +++ b/clang/test/lit.site.cfg.py.in @@ -52,6 +52,11 @@ config.substitutions.append(("%llvm-version-major", "@LLVM_VERSION_MAJOR@")) import lit.llvm lit.llvm.initialize(lit_config, config) +# Add additional features for each additional supported platform. +for platform in "@LLVM_SUPPORTED_PLATFORMS@".split(';'): + if platform: + config.available_features.add('platform-' + platform) + # Let the main config do the real work. lit_config.load_config( config, os.path.join(config.clang_src_dir, "test/lit.cfg.py")) diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt index 5be00f413aba2..be47628d84c75 100644 --- a/llvm/CMakeLists.txt +++ b/llvm/CMakeLists.txt @@ -183,6 +183,10 @@ if ("flang" IN_LIST LLVM_ENABLE_PROJECTS) endif () endif() +# This variable is referenced for generating custom platform support before it can be recognized +# in SDKSettings. It also helps drive fall-back availability inferences that is often used for platform bringup. +set(LLVM_SUPPORTED_PLATFORMS "xros" CACHE STRING "Additional supported platfroms") + if ("lldb" IN_LIST LLVM_ENABLE_PROJECTS) if (NOT "clang" IN_LIST LLVM_ENABLE_PROJECTS) message(STATUS "Enabling clang as a dependency of lldb") diff --git a/llvm/cmake/modules/LLVMConfig.cmake.in b/llvm/cmake/modules/LLVMConfig.cmake.in index 70c807abea98a..7835b3aa42266 100644 --- a/llvm/cmake/modules/LLVMConfig.cmake.in +++ b/llvm/cmake/modules/LLVMConfig.cmake.in @@ -144,6 +144,8 @@ set(LLVM_ENABLE_SHARED_LIBS @BUILD_SHARED_LIBS@) set(LLVM_DEFAULT_EXTERNAL_LIT "@LLVM_CONFIG_DEFAULT_EXTERNAL_LIT@") set(LLVM_LIT_ARGS "@LLVM_LIT_ARGS@") +set(LLVM_SUPPORTED_PLATFORMS "@LLVM_SUPPORTED_PLATFORMS@") + if(NOT TARGET LLVMSupport) @LLVM_CONFIG_INCLUDE_EXPORTS@ @llvm_config_include_buildtree_only_exports@ diff --git a/llvm/test/lit.site.cfg.py.in b/llvm/test/lit.site.cfg.py.in index db34a7812b6e2..68e87a2e1c7ad 100644 --- a/llvm/test/lit.site.cfg.py.in +++ b/llvm/test/lit.site.cfg.py.in @@ -69,6 +69,11 @@ config.have_vc_rev = @LLVM_APPEND_VC_REV@ config.force_vc_rev = "@LLVM_FORCE_VC_REVISION@" config.has_logf128 = @LLVM_HAS_LOGF128@ +# Add additional features for each additional supported platform. +for platform in "@LLVM_SUPPORTED_PLATFORMS@".split(';'): + if platform: + config.available_features.add('platform-' + platform) + import lit.llvm lit.llvm.initialize(lit_config, config)