From af76f1440c60d73f98ec98a33fef029c5925befd Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Tue, 10 Jun 2025 16:00:50 -0700 Subject: [PATCH 1/6] Convert DispatchGlobalExecutor deadline casts to a decltype based cast (which should hold on all platforms) --- .../Concurrency/DispatchGlobalExecutor.cpp | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp b/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp index 0afa0a445a0ed..cba912dbd8e9f 100644 --- a/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp +++ b/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp @@ -314,22 +314,10 @@ clock_and_value_to_time(int clock, long long sec, long long nsec) { case swift_clock_id_continuous: return value | DISPATCH_UP_OR_MONOTONIC_TIME_MASK; case swift_clock_id_wall: { -#if defined(_WIN32) - struct timespec ts = { - .tv_sec = sec, - .tv_nsec = static_cast(nsec) - }; -#elif defined(__APPLE__) - struct timespec ts = { - .tv_sec = static_cast<__darwin_time_t>(sec), - .tv_nsec = nsec + struct timespec ts = { + .tv_sec = static_cast(sec), + .tv_nsec = static_cast(nsec) }; -#else - struct timespec ts = { - .tv_sec = static_cast(sec), - .tv_nsec = nsec - }; -#endif return dispatch_walltime(&ts, 0); } } From 3f305a48c81d262efe1173052e9bdfd3c3508b12 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Sun, 8 Jun 2025 12:03:55 -0700 Subject: [PATCH 2/6] AST: Re-map macOS 16 aligned availability versions to 26. - watchOS 12 -> 26 - visionOS 3 -> 26 - macos 16 -> 26 - iOS 19 -> 26 - tvOS 19 -> 26 The version numbers for `if #available(...)` queries are intentionally not re-mapped. --- lib/AST/PlatformKind.cpp | 43 +++++++++++++---- .../MacOSVersionCanonicalization.h | 12 +++++ ...ailability_macosx_canonical_versions.swift | 8 ++++ ...ity_macosx_canonical_versions_appext.swift | 8 ++++ ...rint_ast_tc_decls_canonical_versions.swift | 21 +++++++++ ...t_tc_decls_macosx_canonical_versions.swift | 16 ------- test/IRGen/osx-targets.swift | 2 +- .../availability-canonical-version.swift | 34 ++++++++++++++ ...availability-macos-canonical-version.swift | 19 -------- .../availability_canonical_versions.swift | 47 +++++++++++++++++++ ...ailability_macosx_canonical_versions.swift | 20 -------- ...catalyst_zippered_canonical_versions.swift | 41 +++++++++++++--- ...lity_query_macosx_canonical_versions.swift | 33 ++++++++++--- .../availability_versions_canonical.swift | 25 ++++++++++ ...vailability_versions_canonical_macos.swift | 14 ------ ...availability_canonical_macos_version.swift | 30 ++++++++++-- ...canonical_macos_version_introduction.swift | 30 +++++++++++- ...al_macos_version_introduction_appext.swift | 12 ++++- 18 files changed, 317 insertions(+), 98 deletions(-) create mode 100644 test/IDE/print_ast_tc_decls_canonical_versions.swift delete mode 100644 test/IDE/print_ast_tc_decls_macosx_canonical_versions.swift create mode 100644 test/ModuleInterface/availability-canonical-version.swift delete mode 100644 test/ModuleInterface/availability-macos-canonical-version.swift create mode 100644 test/PrintAsObjC/availability_canonical_versions.swift delete mode 100644 test/PrintAsObjC/availability_macosx_canonical_versions.swift create mode 100644 test/Sema/availability_versions_canonical.swift delete mode 100644 test/Sema/availability_versions_canonical_macos.swift diff --git a/lib/AST/PlatformKind.cpp b/lib/AST/PlatformKind.cpp index 03b227b3aa411..348dc647190b6 100644 --- a/lib/AST/PlatformKind.cpp +++ b/lib/AST/PlatformKind.cpp @@ -263,16 +263,41 @@ bool swift::inheritsAvailabilityFromPlatform(PlatformKind Child, return false; } -llvm::VersionTuple swift::canonicalizePlatformVersion( - PlatformKind platform, const llvm::VersionTuple &version) { - - // Canonicalize macOS version for macOS Big Sur to treat - // 10.16 as 11.0. - if (platform == PlatformKind::macOS || - platform == PlatformKind::macOSApplicationExtension) { - return llvm::Triple::getCanonicalVersionForOS(llvm::Triple::MacOSX, - version); +static std::optional +tripleOSTypeForPlatform(PlatformKind platform) { + switch (platform) { + case PlatformKind::macOS: + case PlatformKind::macOSApplicationExtension: + return llvm::Triple::MacOSX; + case PlatformKind::iOS: + case PlatformKind::iOSApplicationExtension: + case PlatformKind::macCatalyst: + case PlatformKind::macCatalystApplicationExtension: + return llvm::Triple::IOS; + case PlatformKind::tvOS: + case PlatformKind::tvOSApplicationExtension: + return llvm::Triple::TvOS; + case PlatformKind::watchOS: + case PlatformKind::watchOSApplicationExtension: + return llvm::Triple::WatchOS; + case PlatformKind::visionOS: + case PlatformKind::visionOSApplicationExtension: + return llvm::Triple::XROS; + case PlatformKind::OpenBSD: + return llvm::Triple::OpenBSD; + case PlatformKind::Windows: + return llvm::Triple::Win32; + case PlatformKind::none: + return std::nullopt; } + llvm_unreachable("bad PlatformKind"); +} + +llvm::VersionTuple +swift::canonicalizePlatformVersion(PlatformKind platform, + const llvm::VersionTuple &version) { + if (auto osType = tripleOSTypeForPlatform(platform)) + return llvm::Triple::getCanonicalVersionForOS(*osType, version); return version; } diff --git a/test/ClangImporter/Inputs/custom-modules/MacOSVersionCanonicalization.h b/test/ClangImporter/Inputs/custom-modules/MacOSVersionCanonicalization.h index b0b02c20e999b..c7e345a632eab 100644 --- a/test/ClangImporter/Inputs/custom-modules/MacOSVersionCanonicalization.h +++ b/test/ClangImporter/Inputs/custom-modules/MacOSVersionCanonicalization.h @@ -4,8 +4,20 @@ void FunctionIntroducedIn10_16(); __attribute__((availability(macosx,introduced=11.0))) void FunctionIntroducedIn11_0(); +__attribute__((availability(macosx,introduced=16.0))) +void FunctionIntroducedIn16_0(); + +__attribute__((availability(macosx,introduced=26.0))) +void FunctionIntroducedIn26_0(); + __attribute__((availability(macosx_app_extension,introduced=10.16))) void FunctionIntroducedIn10_16AppExt(); __attribute__((availability(macosx_app_extension,introduced=11.0))) void FunctionIntroducedIn11_0AppExt(); + +__attribute__((availability(macosx_app_extension,introduced=16.0))) +void FunctionIntroducedIn16_0AppExt(); + +__attribute__((availability(macosx_app_extension,introduced=26.0))) +void FunctionIntroducedIn26_0AppExt(); diff --git a/test/ClangImporter/availability_macosx_canonical_versions.swift b/test/ClangImporter/availability_macosx_canonical_versions.swift index da2d8d3ab0fd5..b777eba211024 100644 --- a/test/ClangImporter/availability_macosx_canonical_versions.swift +++ b/test/ClangImporter/availability_macosx_canonical_versions.swift @@ -11,3 +11,11 @@ FunctionIntroducedIn10_16() FunctionIntroducedIn11_0() // expected-error@-1 {{'FunctionIntroducedIn11_0()' is only available in macOS 11.0 or newer}} // expected-note@-2 {{add 'if #available' version check}} + +FunctionIntroducedIn16_0() +// expected-error@-1 {{'FunctionIntroducedIn16_0()' is only available in macOS 26.0 or newer}} +// expected-note@-2 {{add 'if #available' version check}} + +FunctionIntroducedIn26_0() +// expected-error@-1 {{'FunctionIntroducedIn26_0()' is only available in macOS 26.0 or newer}} +// expected-note@-2 {{add 'if #available' version check}} diff --git a/test/ClangImporter/availability_macosx_canonical_versions_appext.swift b/test/ClangImporter/availability_macosx_canonical_versions_appext.swift index e0d655164debc..37d5bb51a1588 100644 --- a/test/ClangImporter/availability_macosx_canonical_versions_appext.swift +++ b/test/ClangImporter/availability_macosx_canonical_versions_appext.swift @@ -11,3 +11,11 @@ FunctionIntroducedIn10_16AppExt() FunctionIntroducedIn11_0AppExt() // expected-error@-1 {{'FunctionIntroducedIn11_0AppExt()' is only available in application extensions for macOS 11.0 or newer}} // expected-note@-2 {{add 'if #available' version check}} + +FunctionIntroducedIn16_0AppExt() +// expected-error@-1 {{'FunctionIntroducedIn16_0AppExt()' is only available in application extensions for macOS 26.0 or newer}} +// expected-note@-2 {{add 'if #available' version check}} + +FunctionIntroducedIn26_0AppExt() +// expected-error@-1 {{'FunctionIntroducedIn26_0AppExt()' is only available in application extensions for macOS 26.0 or newer}} +// expected-note@-2 {{add 'if #available' version check}} diff --git a/test/IDE/print_ast_tc_decls_canonical_versions.swift b/test/IDE/print_ast_tc_decls_canonical_versions.swift new file mode 100644 index 0000000000000..2571c593f8c42 --- /dev/null +++ b/test/IDE/print_ast_tc_decls_canonical_versions.swift @@ -0,0 +1,21 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -typecheck -verify %s +// RUN: %target-swift-ide-test -skip-deinit=false -print-ast-typechecked -source-filename %s -function-definitions=false -prefer-type-repr=false -print-implicit-attrs=true > %t.printed.txt +// RUN: %FileCheck %s -check-prefix=PASS_COMMON -strict-whitespace < %t.printed.txt + +@available(iOS 10.16, OSX 10.16, *) +func introduced10_16() {} +// PASS_COMMON: {{^}}@available(iOS 10.16, macOS 11.0, *){{$}} +// PASS_COMMON-NEXT: {{^}}func introduced10_16(){{$}} + +@available(macOS 16.0, iOS 19.0, macCatalyst 19.0, watchOS 12.0, tvOS 19.0, visionOS 3.0, *) +func introducedInVersionsMappingTo26_0() {} +// FIXME: visionOS and macCatalyst are missing +// PASS_COMMON: {{^}}@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *){{$}} +// PASS_COMMON-NEXT: {{^}}func introducedInVersionsMappingTo26_0(){{$}} + +@available(macOS 18.0, iOS 21.0, macCatalyst 21.0, watchOS 14.0, tvOS 21.0, visionOS 5.0, *) +func introducedInVersionsMappingTo28_0() {} +// FIXME: visionOS and macCatalyst are missing +// PASS_COMMON: {{^}}@available(macOS 28.0, iOS 28.0, watchOS 28.0, tvOS 28.0, *){{$}} +// PASS_COMMON-NEXT: {{^}}func introducedInVersionsMappingTo28_0(){{$}} diff --git a/test/IDE/print_ast_tc_decls_macosx_canonical_versions.swift b/test/IDE/print_ast_tc_decls_macosx_canonical_versions.swift deleted file mode 100644 index f51f8f2e173ca..0000000000000 --- a/test/IDE/print_ast_tc_decls_macosx_canonical_versions.swift +++ /dev/null @@ -1,16 +0,0 @@ -// RUN: %empty-directory(%t) -// -// -// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -swift-version 4 -typecheck -verify %s -F %S/Inputs/mock-sdk -enable-objc-interop -disable-objc-attr-requires-foundation-module -// -// RUN: %target-swift-ide-test(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -swift-version 4 -skip-deinit=false -print-ast-typechecked -source-filename %s -F %S/Inputs/mock-sdk -function-definitions=false -prefer-type-repr=false -print-implicit-attrs=true -enable-objc-interop -disable-objc-attr-requires-foundation-module > %t.printed.txt -// RUN: %FileCheck %s -check-prefix=PASS_COMMON -strict-whitespace < %t.printed.txt - - -// FIXME: rdar://problem/19648117 Needs splitting objc parts out -// REQUIRES: objc_interop - -@available(iOS 10.16, OSX 10.16, *) -func introduced10_16() {} -// PASS_COMMON: {{^}}@available(iOS 10.16, macOS 11.0, *){{$}} -// PASS_COMMON-NEXT: {{^}}func introduced10_16(){{$}} diff --git a/test/IRGen/osx-targets.swift b/test/IRGen/osx-targets.swift index b967650dc2f9c..4a9013b1b9f14 100644 --- a/test/IRGen/osx-targets.swift +++ b/test/IRGen/osx-targets.swift @@ -6,7 +6,7 @@ // CHECK: target triple = "{{.*}}-apple-macosx{{[0-9][0-9]}}. // CHECK-SPECIFIC-MAC-10-X: target triple = "{{.*}}-apple-macosx10.51.0" -// CHECK-DARWIN-OVER-11: target triple = "{{.*}}-apple-macosx46.0.0" +// CHECK-DARWIN-OVER-11: target triple = "{{.*}}-apple-macosx56.0.0" public func anchor() {} anchor() diff --git a/test/ModuleInterface/availability-canonical-version.swift b/test/ModuleInterface/availability-canonical-version.swift new file mode 100644 index 0000000000000..4f08f601c9118 --- /dev/null +++ b/test/ModuleInterface/availability-canonical-version.swift @@ -0,0 +1,34 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-emit-module-interface(%t/Module.swiftinterface) %s +// RUN: %target-swift-typecheck-module-from-interface(%t/Module.swiftinterface) +// RUN: %FileCheck %s < %t/Module.swiftinterface + +@available(macOS 10.16, iOS 10.16, watchOS 10.16, tvOS 10.16, *) +public func introduced10_16() { } +// CHECK: @available(macOS 11.0, iOS 10.16, watchOS 10.16, tvOS 10.16, *) +// CHECK-NEXT: public func introduced10_16() + +@available(OSX 11.0, iOS 11.0, watchOS 11.0, tvOS 11.0, *) +public func introduced11_0() { } +// CHECK-NEXT: @available(macOS 11.0, iOS 11.0, watchOS 11.0, tvOS 11.0, *) +// CHECK-NEXT: public func introduced11_0() + +@available(macOS 16.0, iOS 19.0, macCatalyst 19.0, watchOS 12.0, tvOS 19.0, visionOS 3.0, *) +public func introducedInVersionsMappingTo26_0() { } +// CHECK-NEXT: @available(macOS 26.0, iOS 26.0, macCatalyst 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) +// CHECK-NEXT: public func introducedInVersionsMappingTo26_0() + +@available(macOS 17.0, iOS 20.0, macCatalyst 20.0, watchOS 13.0, tvOS 20.0, visionOS 4.0, *) +public func introducedInInvalidVersionsMappingTo27_0() { } +// CHECK-NEXT: @available(macOS 27.0, iOS 27.0, macCatalyst 27.0, watchOS 27.0, tvOS 27.0, visionOS 27.0, *) +// CHECK-NEXT: public func introducedInInvalidVersionsMappingTo27_0() + +@available(macOS 19.1.1, iOS 21, macCatalyst 21.0, watchOS 14.5.1, tvOS 21.2.3, visionOS 4.0.1, *) +public func introducedInInvalidVersionsWithVaryingComponents() { } +// CHECK-NEXT: @available(macOS 29.1.1, iOS 28, macCatalyst 28.0, watchOS 28.5.1, tvOS 28.2.3, visionOS 27.0.1, *) +// CHECK-NEXT: public func introducedInInvalidVersionsWithVaryingComponents() + +@available(macOS 26.0, iOS 26.0, macCatalyst 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) +public func introduced26_0() { } +// CHECK-NEXT: @available(macOS 26.0, iOS 26.0, macCatalyst 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) +// CHECK-NEXT: public func introduced26_0() diff --git a/test/ModuleInterface/availability-macos-canonical-version.swift b/test/ModuleInterface/availability-macos-canonical-version.swift deleted file mode 100644 index 0c73c2cc43068..0000000000000 --- a/test/ModuleInterface/availability-macos-canonical-version.swift +++ /dev/null @@ -1,19 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: %target-swift-emit-module-interface(%t/Module.swiftinterface) %s -// RUN: %target-swift-typecheck-module-from-interface(%t/Module.swiftinterface) -// RUN: %FileCheck %s < %t/Module.swiftinterface - -// REQUIRES: OS=macosx - -@available(macOS 10.16, *) -public func introduced10_16() { } -// CHECK: @available(macOS 11.0, *) -// CHECK-NEXT: public func introduced10_16() - - -@available(OSX 11.0, *) -public func introduced11_0() { } -// CHECK-NEXT: @available(macOS 11.0, *) -// CHECK-NEXT: public func introduced11_0() - - diff --git a/test/PrintAsObjC/availability_canonical_versions.swift b/test/PrintAsObjC/availability_canonical_versions.swift new file mode 100644 index 0000000000000..ab21b5caca97d --- /dev/null +++ b/test/PrintAsObjC/availability_canonical_versions.swift @@ -0,0 +1,47 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t %s -disable-objc-attr-requires-foundation-module +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -parse-as-library %t/availability_canonical_versions.swiftmodule -typecheck -verify -emit-objc-header-path %t/availability.h -import-objc-header %S/../Inputs/empty.h -disable-objc-attr-requires-foundation-module +// RUN: %FileCheck %s < %t/availability.h +// RUN: %check-in-clang %t/availability.h + +// REQUIRES: objc_interop + +// CHECK-LABEL: @interface Availability{{$}} +@objc class Availability { + // CHECK-NEXT: - (void)alwaysAvailable; + @objc func alwaysAvailable() {} + + // CHECK-NEXT: - (void)introducedOn10_16 + // CHECK-SAME: SWIFT_AVAILABILITY(ios,introduced=10.16) + // CHECK-SAME: SWIFT_AVAILABILITY(macos,introduced=11.0) + @available(macOS 10.16, *) + @available(iOS, introduced: 10.16) + @objc func introducedOn10_16() {} + + // CHECK-NEXT: - (void)introducedOn11_0 + // CHECK-SAME: SWIFT_AVAILABILITY(ios,introduced=11.0) + // CHECK-SAME: SWIFT_AVAILABILITY(macos,introduced=11.0) + @available(macOS 11.0, *) + @available(iOS, introduced: 11.0) + @objc func introducedOn11_0() {} + + // CHECK-NEXT: - (void)introducedOnVersionsMappingTo26_0 + // CHECK-SAME: SWIFT_AVAILABILITY(visionos,introduced=26.0) + // CHECK-SAME: SWIFT_AVAILABILITY(tvos,introduced=26.0) + // CHECK-SAME: SWIFT_AVAILABILITY(watchos,introduced=26.0) + // CHECK-SAME: SWIFT_AVAILABILITY(maccatalyst,introduced=26.0) + // CHECK-SAME: SWIFT_AVAILABILITY(ios,introduced=26.0) + // CHECK-SAME: SWIFT_AVAILABILITY(macos,introduced=26.0) + @available(macOS 16.0, iOS 19.0, macCatalyst 19.0, watchOS 12.0, tvOS 19.0, visionOS 3.0, *) + @objc func introducedOnVersionsMappingTo26_0() {} + + // CHECK-NEXT: - (void)introducedOn26_0 + // CHECK-SAME: SWIFT_AVAILABILITY(visionos,introduced=26.0) + // CHECK-SAME: SWIFT_AVAILABILITY(watchos,introduced=26.0) + // CHECK-SAME: SWIFT_AVAILABILITY(tvos,introduced=26.0) + // CHECK-SAME: SWIFT_AVAILABILITY(maccatalyst,introduced=26.0) + // CHECK-SAME: SWIFT_AVAILABILITY(ios,introduced=26.0) + // CHECK-SAME: SWIFT_AVAILABILITY(macos,introduced=26.0) + @available(macOS 26.0, iOS 26.0, macCatalyst 26.0, tvOS 26.0, watchOS 26.0, visionOS 26.0, *) + @objc func introducedOn26_0() {} +} diff --git a/test/PrintAsObjC/availability_macosx_canonical_versions.swift b/test/PrintAsObjC/availability_macosx_canonical_versions.swift deleted file mode 100644 index 164cbed53b109..0000000000000 --- a/test/PrintAsObjC/availability_macosx_canonical_versions.swift +++ /dev/null @@ -1,20 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t %s -disable-objc-attr-requires-foundation-module -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -parse-as-library %t/availability_macosx_canonical_versions.swiftmodule -typecheck -verify -emit-objc-header-path %t/availability.h -import-objc-header %S/../Inputs/empty.h -disable-objc-attr-requires-foundation-module -// RUN: %FileCheck %s < %t/availability.h -// RUN: %check-in-clang %t/availability.h - -// REQUIRES: objc_interop - -// CHECK-LABEL: @interface Availability{{$}} -// CHECK-NEXT: - (void)alwaysAvailable; -// CHECK-NEXT: - (void)introducedOn10_16 -// CHECK-DAG: SWIFT_AVAILABILITY(macos,introduced=11.0) -// CHECK-DAG: SWIFT_AVAILABILITY(ios,introduced=10.16) - -@objc class Availability { - @objc func alwaysAvailable() {} - @available(macOS 10.16, *) - @available(iOS, introduced: 10.16) - @objc func introducedOn10_16() {} -} diff --git a/test/SILGen/availability_query_maccatalyst_zippered_canonical_versions.swift b/test/SILGen/availability_query_maccatalyst_zippered_canonical_versions.swift index 56a73749d847b..be8eb5ab40d42 100644 --- a/test/SILGen/availability_query_maccatalyst_zippered_canonical_versions.swift +++ b/test/SILGen/availability_query_maccatalyst_zippered_canonical_versions.swift @@ -1,15 +1,11 @@ -// RUN: %target-swift-emit-silgen %s -target x86_64-apple-macosx10.52 -target-variant x86_64-apple-ios50.0-macabi | %FileCheck %s -// RUN: %target-swift-emit-silgen %s -target x86_64-apple-ios50.0-macabi -target-variant x86_64-apple-macosx10.52 | %FileCheck %s +// RUN: %target-swift-emit-silgen %s -target %target-cpu-apple-macosx10.15 -target-variant %target-cpu-apple-ios13.0-macabi | %FileCheck %s +// RUN: %target-swift-emit-silgen %s -target %target-cpu-apple-ios13.0-macabi -target-variant %target-cpu-apple-macosx10.15 | %FileCheck %s // REQUIRES: OS=macosx || OS=maccatalyst // CHECK-LABEL: sil{{.+}}@main{{.*}} { - -// Test for the runtime non-canonical version hack for canonical macOS versioning. -// This will eventually change to be the correctly canonicalized version. - // CHECK: [[MACOS_MAJOR:%.*]] = integer_literal $Builtin.Word, 10 // CHECK: [[MACOS_MINOR:%.*]] = integer_literal $Builtin.Word, 16 // CHECK: [[MACOS_PATCH:%.*]] = integer_literal $Builtin.Word, 0 @@ -20,3 +16,36 @@ // CHECK: [[QUERY_RESULT:%.*]] = apply [[FUNC]]([[MACOS_MAJOR]], [[MACOS_MINOR]], [[MACOS_PATCH]], [[IOS_MAJOR]], [[IOS_MINOR]], [[IOS_PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word, Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 if #available(OSX 10.16, iOS 51.1.2, *) { } + +// CHECK: [[MACOS_MAJOR:%.*]] = integer_literal $Builtin.Word, 11 +// CHECK: [[MACOS_MINOR:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[MACOS_PATCH:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[IOS_MAJOR:%.*]] = integer_literal $Builtin.Word, 51 +// CHECK: [[IOS_MINOR:%.*]] = integer_literal $Builtin.Word, 1 +// CHECK: [[IOS_PATCH:%.*]] = integer_literal $Builtin.Word, 2 +// CHECK: [[FUNC:%.*]] = function_ref @$ss042_stdlib_isOSVersionAtLeastOrVariantVersiondE0yBi1_Bw_BwBwBwBwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word, Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: [[QUERY_RESULT:%.*]] = apply [[FUNC]]([[MACOS_MAJOR]], [[MACOS_MINOR]], [[MACOS_PATCH]], [[IOS_MAJOR]], [[IOS_MINOR]], [[IOS_PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word, Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +if #available(OSX 11.0, iOS 51.1.2, *) { +} + +// CHECK: [[MACOS_MAJOR:%.*]] = integer_literal $Builtin.Word, 16 +// CHECK: [[MACOS_MINOR:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[MACOS_PATCH:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[IOS_MAJOR:%.*]] = integer_literal $Builtin.Word, 19 +// CHECK: [[IOS_MINOR:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[IOS_PATCH:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[FUNC:%.*]] = function_ref @$ss042_stdlib_isOSVersionAtLeastOrVariantVersiondE0yBi1_Bw_BwBwBwBwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word, Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: [[QUERY_RESULT:%.*]] = apply [[FUNC]]([[MACOS_MAJOR]], [[MACOS_MINOR]], [[MACOS_PATCH]], [[IOS_MAJOR]], [[IOS_MINOR]], [[IOS_PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word, Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +if #available(macOS 16.0, iOS 19.0, *) { +} + +// CHECK: [[MACOS_MAJOR:%.*]] = integer_literal $Builtin.Word, 26 +// CHECK: [[MACOS_MINOR:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[MACOS_PATCH:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[IOS_MAJOR:%.*]] = integer_literal $Builtin.Word, 26 +// CHECK: [[IOS_MINOR:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[IOS_PATCH:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[FUNC:%.*]] = function_ref @$ss042_stdlib_isOSVersionAtLeastOrVariantVersiondE0yBi1_Bw_BwBwBwBwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word, Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: [[QUERY_RESULT:%.*]] = apply [[FUNC]]([[MACOS_MAJOR]], [[MACOS_MINOR]], [[MACOS_PATCH]], [[IOS_MAJOR]], [[IOS_MINOR]], [[IOS_PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word, Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +if #available(macOS 26.0, iOS 26.0, *) { +} diff --git a/test/SILGen/availability_query_macosx_canonical_versions.swift b/test/SILGen/availability_query_macosx_canonical_versions.swift index becbe903eb6b1..c72e31bb5e95b 100644 --- a/test/SILGen/availability_query_macosx_canonical_versions.swift +++ b/test/SILGen/availability_query_macosx_canonical_versions.swift @@ -1,17 +1,38 @@ -// RUN: %target-swift-emit-sil %s -target %target-cpu-apple-macosx10.50 -verify -// RUN: %target-swift-emit-silgen %s -target %target-cpu-apple-macosx10.50 | %FileCheck %s +// RUN: %target-swift-emit-sil %s -target %target-cpu-apple-macosx10.15 -verify +// RUN: %target-swift-emit-silgen %s -target %target-cpu-apple-macosx10.15 | %FileCheck %s // REQUIRES: OS=macosx // CHECK-LABEL: sil{{.+}}@main{{.*}} { -// Test for the runtime non-canonical version hack for canonical macOS versioning. -// This will eventually change to be the correctly canonicalized version. - // CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 10 // CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 16 // CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0 // CHECK: [[FUNC:%.*]] = function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 // CHECK: [[QUERY_RESULT:%.*]] = apply [[FUNC]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 if #available(OSX 10.16, iOS 7.1, *) { -} \ No newline at end of file +} + +// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 11 +// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[FUNC:%.*]] = function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: [[QUERY_RESULT:%.*]] = apply [[FUNC]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +if #available(OSX 11.0, *) { +} + +// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 16 +// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[FUNC:%.*]] = function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: [[QUERY_RESULT:%.*]] = apply [[FUNC]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +if #available(macOS 16.0, *) { +} + +// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 26 +// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[FUNC:%.*]] = function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: [[QUERY_RESULT:%.*]] = apply [[FUNC]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +if #available(macOS 26.0, *) { +} diff --git a/test/Sema/availability_versions_canonical.swift b/test/Sema/availability_versions_canonical.swift new file mode 100644 index 0000000000000..0f80ad205de26 --- /dev/null +++ b/test/Sema/availability_versions_canonical.swift @@ -0,0 +1,25 @@ +// RUN: %target-typecheck-verify-swift -target %target-swift-5.1-abi-triple + +@available(OSX 10.16, *) +func introducedOnMacOS10_16() { } + +@available(OSX 11.0, *) +func introducedOnMacOS11_0() { } + +@available(macOS 16.0, iOS 19.0, watchOS 12.0, tvOS 19.0, visionOS 3.0, *) +func introducedInVersionsMappingTo26_0() { } + +@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) +func introducedIn26_0() { } + +func useUnderPoundAvailable() { + if #available(OSX 10.16, *) { + introducedOnMacOS10_16() + introducedOnMacOS11_0() + } + + if #available(macOS 16.0, iOS 19.0, watchOS 12.0, tvOS 19.0, visionOS 3.0, *) { + introducedInVersionsMappingTo26_0() + introducedIn26_0() + } +} diff --git a/test/Sema/availability_versions_canonical_macos.swift b/test/Sema/availability_versions_canonical_macos.swift deleted file mode 100644 index fb18d412aa22f..0000000000000 --- a/test/Sema/availability_versions_canonical_macos.swift +++ /dev/null @@ -1,14 +0,0 @@ -// RUN: %target-typecheck-verify-swift -target %target-cpu-apple-macosx10.15 -disable-objc-attr-requires-foundation-module - -// REQUIRES: OS=macosx - -func markUsed(_ t: T) {} - -@available(OSX 10.16, *) -func introducedOn10_16() { } - -func useUnderPoundAvailable() { - if #available(OSX 10.16, *) { - introducedOn10_16() // no-error - } -} diff --git a/test/attr/attr_availability_canonical_macos_version.swift b/test/attr/attr_availability_canonical_macos_version.swift index 10cf92286e255..1fadb1e045558 100644 --- a/test/attr/attr_availability_canonical_macos_version.swift +++ b/test/attr/attr_availability_canonical_macos_version.swift @@ -1,4 +1,4 @@ -// RUN: %swift -typecheck -verify -parse-stdlib -module-name Swift -target x86_64-apple-macosx11.0 %s +// RUN: %swift -typecheck -verify -parse-stdlib -module-name Swift -target %target-cpu-apple-macosx26.0 %s @available(OSX, introduced: 10.5, deprecated: 10.8, obsoleted: 11.0, @@ -16,6 +16,19 @@ func obsoletedIn10_16() { } obsoletedIn10_16() // expected-error{{'obsoletedIn10_16()' is unavailable in macOS: you don't want to do that anyway}} +@available(OSX, introduced: 10.5, deprecated: 10.8, obsoleted: 16.0, + message: "you don't want to do that anyway") +func obsoletedIn16() { } +// expected-note @-1{{'obsoletedIn16()' was obsoleted in macOS 26.0}} + +obsoletedIn16() // expected-error{{'obsoletedIn16()' is unavailable in macOS: you don't want to do that anyway}} + +@available(OSX, introduced: 10.5, deprecated: 10.8, obsoleted: 26.0, + message: "you don't want to do that anyway") +func obsoletedIn26() { } +// expected-note @-1{{'obsoletedIn26()' was obsoleted in macOS 26.0}} + +obsoletedIn26() // expected-error{{'obsoletedIn26()' is unavailable in macOS: you don't want to do that anyway}} @available(OSX, deprecated: 10.16) func deprecatedIn10_16() { } @@ -26,8 +39,14 @@ func deprecatedIn10_18() { } @available(OSX, deprecated: 11.0) func deprecatedIn11_0() { } -@available(OSX, deprecated: 13.0) -func deprecatedIn13_0() { } +@available(OSX, deprecated: 16.0) +func deprecatedIn16_0() { } + +@available(OSX, deprecated: 26.0) +func deprecatedIn26_0() { } + +@available(OSX, deprecated: 27.0) +func deprecatedIn27_0() { } @available(OSXApplicationExtension, deprecated: 10.16) func deprecatedIn10_16AppExtension() { } @@ -36,6 +55,9 @@ func useDeprecated() { deprecatedIn10_16() // expected-warning {{deprecatedIn10_16()' was deprecated in macOS 11.0}} deprecatedIn10_18() // expected-warning {{'deprecatedIn10_18()' was deprecated in macOS 10.18}} deprecatedIn11_0() // expected-warning {{'deprecatedIn11_0()' was deprecated in macOS 11.0}} + deprecatedIn16_0() // expected-warning {{'deprecatedIn16_0()' was deprecated in macOS 26.0}} + deprecatedIn26_0() // expected-warning {{'deprecatedIn26_0()' was deprecated in macOS 26.0}} - deprecatedIn13_0() // no-warning + deprecatedIn27_0() + deprecatedIn10_16AppExtension() } diff --git a/test/attr/attr_availability_canonical_macos_version_introduction.swift b/test/attr/attr_availability_canonical_macos_version_introduction.swift index 766a54255ac07..0cebd00d7c6f4 100644 --- a/test/attr/attr_availability_canonical_macos_version_introduction.swift +++ b/test/attr/attr_availability_canonical_macos_version_introduction.swift @@ -1,4 +1,4 @@ -// RUN: %swift -typecheck -verify -parse-stdlib -module-name Swift -target x86_64-apple-macosx10.15 %s +// RUN: %swift -typecheck -verify -parse-stdlib -module-name Swift -target %target-cpu-apple-macosx10.15 %s @available(OSX, introduced: 10.16) func longFormIntroducedIn10_16() { } @@ -12,6 +12,12 @@ func longFormIntroducedIn11_0() { } @available(OSX, introduced: 13.0) func longFormIntroducedIn13_0() { } +@available(OSX, introduced: 16.0) +func longFormIntroducedIn16_0() { } + +@available(OSX, introduced: 26.0) +func longFormIntroducedIn26_0() { } + // expected-note@+1 *{{add '@available' attribute to enclosing global function}} func useLongFromIntroduced() { longFormIntroducedIn10_16() @@ -29,6 +35,14 @@ func useLongFromIntroduced() { longFormIntroducedIn13_0() // expected-error@-1{{'longFormIntroducedIn13_0()' is only available in macOS 13.0 or newer}} // expected-note@-2{{add 'if #available' version check}} + + longFormIntroducedIn16_0() + // expected-error@-1{{'longFormIntroducedIn16_0()' is only available in macOS 26.0 or newer}} + // expected-note@-2{{add 'if #available' version check}} + + longFormIntroducedIn26_0() + // expected-error@-1{{'longFormIntroducedIn26_0()' is only available in macOS 26.0 or newer}} + // expected-note@-2{{add 'if #available' version check}} } @available(OSX 10.16, *) @@ -43,6 +57,12 @@ func shortFormIntroducedIn11_0() { } @available(OSX 13.0, *) func shortFormIntroducedIn13_0() { } +@available(OSX 16.0, *) +func shortFormIntroducedIn16_0() { } + +@available(OSX 26.0, *) +func shortFormIntroducedIn26_0() { } + // expected-note@+1 *{{add '@available' attribute to enclosing global function}} func useShortIntroduced() { shortFormIntroducedIn10_16() @@ -58,4 +78,12 @@ func useShortIntroduced() { shortFormIntroducedIn13_0() // expected-error@-1{{'shortFormIntroducedIn13_0()' is only available in macOS 13.0 or newer}} // expected-note@-2{{add 'if #available' version check}} + + shortFormIntroducedIn16_0() + // expected-error@-1{{'shortFormIntroducedIn16_0()' is only available in macOS 26.0 or newer}} + // expected-note@-2{{add 'if #available' version check}} + + shortFormIntroducedIn26_0() + // expected-error@-1{{'shortFormIntroducedIn26_0()' is only available in macOS 26.0 or newer}} + // expected-note@-2{{add 'if #available' version check}} } diff --git a/test/attr/attr_availability_canonical_macos_version_introduction_appext.swift b/test/attr/attr_availability_canonical_macos_version_introduction_appext.swift index fa4ff5ad960f0..cf0011e5769a5 100644 --- a/test/attr/attr_availability_canonical_macos_version_introduction_appext.swift +++ b/test/attr/attr_availability_canonical_macos_version_introduction_appext.swift @@ -1,9 +1,17 @@ -// RUN: %swift -typecheck -verify -parse-stdlib -module-name Swift -target x86_64-apple-macosx10.15 %s -application-extension +// RUN: %swift -typecheck -verify -parse-stdlib -module-name Swift -target %target-cpu-apple-macosx10.15 %s -application-extension @available(OSXApplicationExtension 11, *) func introducedInAppExtension11_0() { } @available(OSXApplicationExtension 10.16, *) -func useAppExtension() { +func useAppExtension10_16() { introducedInAppExtension11_0() // no-warning } + +@available(OSXApplicationExtension 26, *) +func introducedInAppExtension26_0() { } + +@available(OSXApplicationExtension 16, *) +func useAppExtension16_0() { + introducedInAppExtension26_0() // no-warning +} From e358cd6309cf76792b98869c318b97be07fd25fe Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Sun, 8 Jun 2025 12:15:53 -0700 Subject: [PATCH 3/6] Tests: Correct invalid platform versions in @available attributes. --- test/Constraints/operator_availability.swift | 4 +-- test/ModuleInterface/conformances.swift | 6 ++-- ...catalyst_zippered_canonical_versions.swift | 4 +-- .../Symbols/Mixins/Availability/Basic.swift | 4 +-- .../Inherited/ObsoletedReplaced.swift | 28 +++++++++---------- test/attr/attr_availability_vision.swift | 6 ++-- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/test/Constraints/operator_availability.swift b/test/Constraints/operator_availability.swift index f0246a1d67634..67ae640f28d11 100644 --- a/test/Constraints/operator_availability.swift +++ b/test/Constraints/operator_availability.swift @@ -37,12 +37,12 @@ public func ~> (lhs: T, rhs: (T) -> Void) -> T { } struct S { - @available(macOS 20, *) + @available(macOS 50, *) func f() {} } let s = S() ~> { - if #available(macOS 20.0, *) { + if #available(macOS 50.0, *) { $0.f() } } diff --git a/test/ModuleInterface/conformances.swift b/test/ModuleInterface/conformances.swift index 4d3691452c0ca..e584eabd9f189 100644 --- a/test/ModuleInterface/conformances.swift +++ b/test/ModuleInterface/conformances.swift @@ -230,11 +230,11 @@ extension PrivateProtoConformer : PrivateProto { // NEGATIVE-NOT: extension {{(Swift.)?}}Bool{{.+}}Equatable -@available(macOS 10.97, iOS 22, *) +@available(macOS 10.97, iOS 13.22, *) @available(tvOS, unavailable) @available(swift 4.2.123) public struct NestedAvailabilityOuter { - @available(iOS 23, *) + @available(iOS 13.23, *) public struct Inner: PrivateSubProto {} } @@ -244,7 +244,7 @@ public struct NestedAvailabilityOuter { // CHECK: @available(*, unavailable) // CHECK-NEXT: extension conformances.NonSendable : @unchecked Swift.Sendable { -// CHECK-END: @available(macOS 10.97, iOS 23, *) +// CHECK-END: @available(macOS 10.97, iOS 13.23, *) // CHECK-END: @available(tvOS, unavailable) // CHECK-END: extension conformances.NestedAvailabilityOuter.Inner : conformances.PublicBaseProto {} diff --git a/test/SILGen/availability_query_maccatalyst_zippered_canonical_versions.swift b/test/SILGen/availability_query_maccatalyst_zippered_canonical_versions.swift index be8eb5ab40d42..9d24c0aa20657 100644 --- a/test/SILGen/availability_query_maccatalyst_zippered_canonical_versions.swift +++ b/test/SILGen/availability_query_maccatalyst_zippered_canonical_versions.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-emit-silgen %s -target %target-cpu-apple-macosx10.15 -target-variant %target-cpu-apple-ios13.0-macabi | %FileCheck %s -// RUN: %target-swift-emit-silgen %s -target %target-cpu-apple-ios13.0-macabi -target-variant %target-cpu-apple-macosx10.15 | %FileCheck %s +// RUN: %target-swift-emit-silgen %s -target %target-cpu-apple-macosx10.15 -target-variant %target-cpu-apple-ios13.1-macabi | %FileCheck %s +// RUN: %target-swift-emit-silgen %s -target %target-cpu-apple-ios13.1-macabi -target-variant %target-cpu-apple-macosx10.15 | %FileCheck %s // REQUIRES: OS=macosx || OS=maccatalyst diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Basic.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Basic.swift index 6e3e294b249b7..00a1d53757620 100644 --- a/test/SymbolGraph/Symbols/Mixins/Availability/Basic.swift +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Basic.swift @@ -5,7 +5,7 @@ // REQUIRES: OS=macosx -@available(macOS, introduced: 10.9, deprecated: 11.0, obsoleted: 18.1, message: "Everyone makes mistakes", renamed: "S2") +@available(macOS, introduced: 10.9, deprecated: 11.0, obsoleted: 15.1, message: "Everyone makes mistakes", renamed: "S2") public struct S {} // CHECK: "domain": "macOS" @@ -19,7 +19,7 @@ public struct S {} // CHECK-NEXT: "minor": 0 // CHECK: obsoleted -// CHECK-NEXT: "major": 18 +// CHECK-NEXT: "major": 15 // CHECK-NEXT: "minor": 1 // CHECK: "message": "Everyone makes mistakes" diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/ObsoletedReplaced.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/ObsoletedReplaced.swift index c9f7c86039748..1dd9f5b92b259 100644 --- a/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/ObsoletedReplaced.swift +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/ObsoletedReplaced.swift @@ -10,19 +10,19 @@ // REQUIRES: OS=macosx -@available(macOS, obsoleted: 18.14) +@available(macOS, obsoleted: 98.14) public struct S { // LESSTHAN-LABEL: "precise": "s:17ObsoletedReplaced1SV8lessThanyyF", // LESSTHAN: "availability": [ // LESSTHAN-NEXT: { // LESSTHAN-NEXT: "domain": "macOS", // LESSTHAN-NEXT: "obsoleted": { - // LESSTHAN-NEXT: "major": 18, + // LESSTHAN-NEXT: "major": 98, // LESSTHAN-NEXT: "minor": 10 // LESSTHAN-NEXT: } // LESSTHAN-NEXT: } // LESSTHAN-NEXT: ] - @available(macOS, obsoleted: 18.10) + @available(macOS, obsoleted: 98.10) public func lessThan() {} // GREATERTHAN-LABEL: "precise": "s:17ObsoletedReplaced1SV11greaterThanyyF", @@ -30,12 +30,12 @@ public struct S { // GREATERTHAN-NEXT: { // GREATERTHAN-NEXT: "domain": "macOS", // GREATERTHAN-NEXT: "obsoleted": { - // GREATERTHAN-NEXT: "major": 18, + // GREATERTHAN-NEXT: "major": 98, // GREATERTHAN-NEXT: "minor": 14 // GREATERTHAN-NEXT: } // GREATERTHAN-NEXT: } // GREATERTHAN-NEXT: ] - @available(macOS, obsoleted: 18.15) + @available(macOS, obsoleted: 98.15) public func greaterThan() {} // EQUAL-LABEL: "precise": "s:17ObsoletedReplaced1SV5equalyyF", @@ -43,16 +43,16 @@ public struct S { // EQUAL-NEXT: { // EQUAL-NEXT: "domain": "macOS", // EQUAL-NEXT: "obsoleted": { - // EQUAL-NEXT: "major": 18, + // EQUAL-NEXT: "major": 98, // EQUAL-NEXT: "minor": 14 // EQUAL-NEXT: } // EQUAL-NEXT: } // EQUAL-NEXT: ] - @available(macOS, obsoleted: 18.14) + @available(macOS, obsoleted: 98.14) public func equal() {} } -@available(macOS, obsoleted: 18.14) +@available(macOS, obsoleted: 98.14) public struct Outer { public struct Inner { // TRANSITIVELESSTHAN-LABEL: "precise": "s:17ObsoletedReplaced5OuterV5InnerV8lessThanyyF" @@ -60,12 +60,12 @@ public struct Outer { // TRANSITIVELESSTHAN-NEXT: { // TRANSITIVELESSTHAN-NEXT: "domain": "macOS", // TRANSITIVELESSTHAN-NEXT: "obsoleted": { - // TRANSITIVELESSTHAN-NEXT: "major": 18, + // TRANSITIVELESSTHAN-NEXT: "major": 98, // TRANSITIVELESSTHAN-NEXT: "minor": 10 // TRANSITIVELESSTHAN-NEXT: } // TRANSITIVELESSTHAN-NEXT: } // TRANSITIVELESSTHAN-NEXT: ] - @available(macOS, obsoleted: 18.10) + @available(macOS, obsoleted: 98.10) public func lessThan() {} // TRANSITIVEGREATERTHAN-LABEL:"precise": "s:17ObsoletedReplaced5OuterV5InnerV11greaterThanyyF" @@ -73,12 +73,12 @@ public struct Outer { // TRANSITIVEGREATERTHAN-NEXT: { // TRANSITIVEGREATERTHAN-NEXT: "domain": "macOS", // TRANSITIVEGREATERTHAN-NEXT: "obsoleted": { - // TRANSITIVEGREATERTHAN-NEXT: "major": 18, + // TRANSITIVEGREATERTHAN-NEXT: "major": 98, // TRANSITIVEGREATERTHAN-NEXT: "minor": 14 // TRANSITIVEGREATERTHAN-NEXT: } // TRANSITIVEGREATERTHAN-NEXT: } // TRANSITIVEGREATERTHAN-NEXT: ] - @available(macOS, obsoleted: 18.15) + @available(macOS, obsoleted: 98.15) public func greaterThan() {} // TRANSITIVEEQUAL-LABEL:"precise": "s:17ObsoletedReplaced5OuterV5InnerV5equalyyF" @@ -86,12 +86,12 @@ public struct Outer { // TRANSITIVEEQUAL-NEXT: { // TRANSITIVEEQUAL-NEXT: "domain": "macOS", // TRANSITIVEEQUAL-NEXT: "obsoleted": { - // TRANSITIVEEQUAL-NEXT: "major": 18, + // TRANSITIVEEQUAL-NEXT: "major": 98, // TRANSITIVEEQUAL-NEXT: "minor": 14 // TRANSITIVEEQUAL-NEXT: } // TRANSITIVEEQUAL-NEXT: } // TRANSITIVEEQUAL-NEXT: ] - @available(macOS, obsoleted: 18.14) + @available(macOS, obsoleted: 98.14) public func equal() {} } } diff --git a/test/attr/attr_availability_vision.swift b/test/attr/attr_availability_vision.swift index 7e41d936ba43a..d7fb905a6f735 100644 --- a/test/attr/attr_availability_vision.swift +++ b/test/attr/attr_availability_vision.swift @@ -41,12 +41,12 @@ class DeprecatedClass { } func functionWithDeprecatedParameter(p: DeprecatedClass) { } // expected-warning{{'DeprecatedClass' was deprecated in visionOS 2.0: Use BetterClass instead}} -@available(visionOS, introduced: 2.0, deprecated: 4.0, +@available(visionOS, introduced: 2.0, deprecated: 2.1, message: "Use BetterClass instead") -class DeprecatedClassIn3_0 { } +class DeprecatedClassIn2_1 { } // Elements deprecated later than the minimum deployment target (which is 2.0, in this case) should not generate warnings -func functionWithDeprecatedLaterParameter(p: DeprecatedClassIn3_0) { } +func functionWithDeprecatedLaterParameter(p: DeprecatedClassIn2_1) { } // Treat visionOS as an alias for iOS in availability queries From e16c638fc3b28c03b9898960bb273200691814db Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Sun, 8 Jun 2025 12:20:27 -0700 Subject: [PATCH 4/6] AST: Warn for non-existent platform versions in @available attributes. --- include/swift/AST/AvailabilityDomain.h | 4 + include/swift/AST/DiagnosticsSema.def | 3 + include/swift/AST/PlatformKind.h | 5 ++ lib/AST/Availability.cpp | 15 +++- lib/AST/AvailabilityDomain.cpp | 20 +++++ lib/AST/PlatformKind.cpp | 11 ++- ...rint_ast_tc_decls_canonical_versions.swift | 2 +- .../availability_versions_canonical.swift | 77 ++++++++++++++++++- ...ailability_invalid_platform_versions.swift | 34 ++++++++ 9 files changed, 164 insertions(+), 7 deletions(-) create mode 100644 test/attr/attr_availability_invalid_platform_versions.swift diff --git a/include/swift/AST/AvailabilityDomain.h b/include/swift/AST/AvailabilityDomain.h index 87b5fb46c429a..e16f5c6b8057a 100644 --- a/include/swift/AST/AvailabilityDomain.h +++ b/include/swift/AST/AvailabilityDomain.h @@ -209,6 +209,10 @@ class AvailabilityDomain final { /// version ranges. bool isVersioned() const; + /// Returns true if the given version is a valid version number for this + /// domain. It is an error to call this on an un-versioned domain. + bool isVersionValid(const llvm::VersionTuple &version) const; + /// Returns true if availability of the domain can be refined using /// `@available` attributes and `if #available` queries. If not, then the /// domain's availability is fixed by compilation settings. For example, diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index e907814f7c455..e9922c7c33459 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -6929,6 +6929,9 @@ GROUPED_ERROR(availability_suggest_platform_name, (Identifier, StringRef)) WARNING(availability_unsupported_version_number, none, "'%0' is not a supported version number", (llvm::VersionTuple)) +WARNING(availability_invalid_version_number_for_domain, none, + "'%0' is not a valid version number for %1", + (llvm::VersionTuple, AvailabilityDomain)) WARNING(attr_availability_expected_deprecated_version, none, "expected version number with 'deprecated' in '%0' attribute for %1", diff --git a/include/swift/AST/PlatformKind.h b/include/swift/AST/PlatformKind.h index a8a869a36777a..34323bae9ba36 100644 --- a/include/swift/AST/PlatformKind.h +++ b/include/swift/AST/PlatformKind.h @@ -21,6 +21,7 @@ #include "swift/Config.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/VersionTuple.h" +#include "llvm/TargetParser/Triple.h" #include namespace swift { @@ -91,6 +92,10 @@ PlatformKind targetVariantPlatform(const LangOptions &LangOpts); /// an explicit attribute for the child. bool inheritsAvailabilityFromPlatform(PlatformKind Child, PlatformKind Parent); +/// Returns the LLVM triple OS type for the given platform, if there is one. +std::optional +tripleOSTypeForPlatform(PlatformKind platform); + llvm::VersionTuple canonicalizePlatformVersion( PlatformKind platform, const llvm::VersionTuple &version); diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index 2f4c07b294e91..e8a6fc7d76d8a 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -832,7 +832,10 @@ SemanticAvailableAttrRequest::evaluate(swift::Evaluator &evaluator, auto checkVersion = [&](std::optional version, SourceRange sourceRange) { - if (version && !VersionRange::isValidVersion(*version)) { + if (!version) + return false; + + if (!VersionRange::isValidVersion(*version)) { diags .diagnose(attrLoc, diag::availability_unsupported_version_number, *version) @@ -840,6 +843,16 @@ SemanticAvailableAttrRequest::evaluate(swift::Evaluator &evaluator, return true; } + // Warn if the version is not a valid one for the domain. For example, macOS + // 17 will never exist. + if (domain->isVersioned() && !domain->isVersionValid(*version)) { + diags + .diagnose(attrLoc, + diag::availability_invalid_version_number_for_domain, + *version, *domain) + .highlight(sourceRange); + } + return false; }; diff --git a/lib/AST/AvailabilityDomain.cpp b/lib/AST/AvailabilityDomain.cpp index bc2e0a2fa9272..feffdb338c76e 100644 --- a/lib/AST/AvailabilityDomain.cpp +++ b/lib/AST/AvailabilityDomain.cpp @@ -109,6 +109,26 @@ bool AvailabilityDomain::isVersioned() const { } } +bool AvailabilityDomain::isVersionValid( + const llvm::VersionTuple &version) const { + ASSERT(isVersioned()); + + switch (getKind()) { + case Kind::Universal: + case Kind::Embedded: + llvm_unreachable("unexpected domain kind"); + case Kind::SwiftLanguage: + case Kind::PackageDescription: + return true; + case Kind::Platform: + if (auto osType = tripleOSTypeForPlatform(getPlatformKind())) + return llvm::Triple::isValidVersionForOS(*osType, version); + return true; + case Kind::Custom: + return true; + } +} + bool AvailabilityDomain::supportsContextRefinement() const { switch (getKind()) { case Kind::Universal: diff --git a/lib/AST/PlatformKind.cpp b/lib/AST/PlatformKind.cpp index 348dc647190b6..b8026af51a091 100644 --- a/lib/AST/PlatformKind.cpp +++ b/lib/AST/PlatformKind.cpp @@ -263,8 +263,8 @@ bool swift::inheritsAvailabilityFromPlatform(PlatformKind Child, return false; } -static std::optional -tripleOSTypeForPlatform(PlatformKind platform) { +std::optional +swift::tripleOSTypeForPlatform(PlatformKind platform) { switch (platform) { case PlatformKind::macOS: case PlatformKind::macOSApplicationExtension: @@ -296,8 +296,11 @@ tripleOSTypeForPlatform(PlatformKind platform) { llvm::VersionTuple swift::canonicalizePlatformVersion(PlatformKind platform, const llvm::VersionTuple &version) { - if (auto osType = tripleOSTypeForPlatform(platform)) - return llvm::Triple::getCanonicalVersionForOS(*osType, version); + if (auto osType = tripleOSTypeForPlatform(platform)) { + bool isInValidRange = llvm::Triple::isValidVersionForOS(*osType, version); + return llvm::Triple::getCanonicalVersionForOS(*osType, version, + isInValidRange); + } return version; } diff --git a/test/IDE/print_ast_tc_decls_canonical_versions.swift b/test/IDE/print_ast_tc_decls_canonical_versions.swift index 2571c593f8c42..ca30d5d63e57c 100644 --- a/test/IDE/print_ast_tc_decls_canonical_versions.swift +++ b/test/IDE/print_ast_tc_decls_canonical_versions.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -typecheck -verify %s +// RUN: %target-swift-frontend -typecheck %s // RUN: %target-swift-ide-test -skip-deinit=false -print-ast-typechecked -source-filename %s -function-definitions=false -prefer-type-repr=false -print-implicit-attrs=true > %t.printed.txt // RUN: %FileCheck %s -check-prefix=PASS_COMMON -strict-whitespace < %t.printed.txt diff --git a/test/Sema/availability_versions_canonical.swift b/test/Sema/availability_versions_canonical.swift index 0f80ad205de26..e825766096871 100644 --- a/test/Sema/availability_versions_canonical.swift +++ b/test/Sema/availability_versions_canonical.swift @@ -1,4 +1,8 @@ -// RUN: %target-typecheck-verify-swift -target %target-swift-5.1-abi-triple +// RUN: %swift -typecheck %s -verify -parse-stdlib -module-name Swift -target x86_64-apple-macosx10.15 -verify-additional-prefix macos- +// RUN: %swift -typecheck %s -verify -parse-stdlib -module-name Swift -target arm64-apple-ios13 -verify-additional-prefix ios- +// RUN: %swift -typecheck %s -verify -parse-stdlib -module-name Swift -target arm64-apple-watchos6 -verify-additional-prefix watchos- +// RUN: %swift -typecheck %s -verify -parse-stdlib -module-name Swift -target arm64-apple-tvos13 -verify-additional-prefix tvos- +// RUN: %swift -typecheck %s -verify -parse-stdlib -module-name Swift -target arm64-apple-xros1 -verify-additional-prefix visionos- @available(OSX 10.16, *) func introducedOnMacOS10_16() { } @@ -12,7 +16,59 @@ func introducedInVersionsMappingTo26_0() { } @available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) func introducedIn26_0() { } +@available(macOS 17.0, iOS 20.0, watchOS 13.0, tvOS 20.0, visionOS 4.0, *) +// expected-warning@-1 {{'17.0' is not a valid version number for macOS}} +// expected-warning@-2 {{'20.0' is not a valid version number for iOS}} +// expected-warning@-3 {{'13.0' is not a valid version number for watchOS}} +// expected-warning@-4 {{'20.0' is not a valid version number for tvOS}} +// expected-warning@-5 {{'4.0' is not a valid version number for visionOS}} +func introducedInVersionsMappingTo27_0() { } + +@available(macOS 27.0, iOS 27.0, watchOS 27.0, tvOS 27.0, visionOS 27.0, *) +func introducedIn27_0() { } + func useUnderPoundAvailable() { + // expected-note@-1 * {{add '@available' attribute to enclosing global function}} + introducedOnMacOS10_16() + // expected-macos-error@-1 {{'introducedOnMacOS10_16()' is only available in macOS 11.0 or newer}} + // expected-macos-note@-2 {{add 'if #available' version check}} + + introducedOnMacOS11_0() + // expected-macos-error@-1 {{'introducedOnMacOS11_0()' is only available in macOS 11.0 or newer}} + // expected-macos-note@-2 {{add 'if #available' version check}} + + introducedInVersionsMappingTo26_0() + // expected-macos-error@-1 {{'introducedInVersionsMappingTo26_0()' is only available in macOS 26.0 or newer}} + // expected-ios-error@-2 {{'introducedInVersionsMappingTo26_0()' is only available in iOS 26.0 or newer}} + // expected-watchos-error@-3 {{'introducedInVersionsMappingTo26_0()' is only available in watchOS 26.0 or newer}} + // expected-tvos-error@-4 {{'introducedInVersionsMappingTo26_0()' is only available in tvOS 26.0 or newer}} + // expected-visionos-error@-5 {{'introducedInVersionsMappingTo26_0()' is only available in visionOS 26.0 or newer}} + // expected-note@-6 {{add 'if #available' version check}} + + introducedIn26_0() + // expected-macos-error@-1 {{'introducedIn26_0()' is only available in macOS 26.0 or newer}} + // expected-ios-error@-2 {{'introducedIn26_0()' is only available in iOS 26.0 or newer}} + // expected-watchos-error@-3 {{'introducedIn26_0()' is only available in watchOS 26.0 or newer}} + // expected-tvos-error@-4 {{'introducedIn26_0()' is only available in tvOS 26.0 or newer}} + // expected-visionos-error@-5 {{'introducedIn26_0()' is only available in visionOS 26.0 or newer}} + // expected-note@-6 {{add 'if #available' version check}} + + introducedInVersionsMappingTo27_0() + // expected-macos-error@-1 {{'introducedInVersionsMappingTo27_0()' is only available in macOS 27.0 or newer}} + // expected-ios-error@-2 {{'introducedInVersionsMappingTo27_0()' is only available in iOS 27.0 or newer}} + // expected-watchos-error@-3 {{'introducedInVersionsMappingTo27_0()' is only available in watchOS 27.0 or newer}} + // expected-tvos-error@-4 {{'introducedInVersionsMappingTo27_0()' is only available in tvOS 27.0 or newer}} + // expected-visionos-error@-5 {{'introducedInVersionsMappingTo27_0()' is only available in visionOS 27.0 or newer}} + // expected-note@-6 {{add 'if #available' version check}} + + introducedIn27_0() + // expected-macos-error@-1 {{'introducedIn27_0()' is only available in macOS 27.0 or newer}} + // expected-ios-error@-2 {{'introducedIn27_0()' is only available in iOS 27.0 or newer}} + // expected-watchos-error@-3 {{'introducedIn27_0()' is only available in watchOS 27.0 or newer}} + // expected-tvos-error@-4 {{'introducedIn27_0()' is only available in tvOS 27.0 or newer}} + // expected-visionos-error@-5 {{'introducedIn27_0()' is only available in visionOS 27.0 or newer}} + // expected-note@-6 {{add 'if #available' version check}} + if #available(OSX 10.16, *) { introducedOnMacOS10_16() introducedOnMacOS11_0() @@ -21,5 +77,24 @@ func useUnderPoundAvailable() { if #available(macOS 16.0, iOS 19.0, watchOS 12.0, tvOS 19.0, visionOS 3.0, *) { introducedInVersionsMappingTo26_0() introducedIn26_0() + introducedInVersionsMappingTo27_0() + // expected-macos-error@-1 {{'introducedInVersionsMappingTo27_0()' is only available in macOS 27.0 or newer}} + // expected-ios-error@-2 {{'introducedInVersionsMappingTo27_0()' is only available in iOS 27.0 or newer}} + // expected-watchos-error@-3 {{'introducedInVersionsMappingTo27_0()' is only available in watchOS 27.0 or newer}} + // expected-tvos-error@-4 {{'introducedInVersionsMappingTo27_0()' is only available in tvOS 27.0 or newer}} + // expected-visionos-error@-5 {{'introducedInVersionsMappingTo27_0()' is only available in visionOS 27.0 or newer}} + // expected-note@-6 {{add 'if #available' version check}} + introducedIn27_0() + // expected-macos-error@-1 {{'introducedIn27_0()' is only available in macOS 27.0 or newer}} + // expected-ios-error@-2 {{'introducedIn27_0()' is only available in iOS 27.0 or newer}} + // expected-watchos-error@-3 {{'introducedIn27_0()' is only available in watchOS 27.0 or newer}} + // expected-tvos-error@-4 {{'introducedIn27_0()' is only available in tvOS 27.0 or newer}} + // expected-visionos-error@-5 {{'introducedIn27_0()' is only available in visionOS 27.0 or newer}} + // expected-note@-6 {{add 'if #available' version check}} + } + + if #available(macOS 17.0, iOS 20.0, watchOS 13.0, tvOS 20.0, visionOS 4.0, *) { + introducedInVersionsMappingTo27_0() + introducedIn27_0() } } diff --git a/test/attr/attr_availability_invalid_platform_versions.swift b/test/attr/attr_availability_invalid_platform_versions.swift new file mode 100644 index 0000000000000..8ddb3ce1ac4be --- /dev/null +++ b/test/attr/attr_availability_invalid_platform_versions.swift @@ -0,0 +1,34 @@ +// RUN: %target-swift-frontend -typecheck -verify -parse-stdlib -module-name Swift %s + +@available(macOS, introduced: 17) // expected-warning {{'17' is not a valid version number for macOS}} +@available(iOS, introduced: 20) // expected-warning {{'20' is not a valid version number for iOS}} +@available(macCatalyst, introduced: 20) // expected-warning {{'20' is not a valid version number for Mac Catalyst}} +@available(watchOS, introduced: 13) // expected-warning {{'13' is not a valid version number for watchOS}} +@available(tvOS, introduced: 20) // expected-warning {{'20' is not a valid version number for tvOS}} +@available(visionOS, introduced: 4) // expected-warning {{'4' is not a valid version number for visionOS}} +func invalidIntroduced() { } + +@available(macOS, deprecated: 17) // expected-warning {{'17' is not a valid version number for macOS}} +@available(iOS, deprecated: 20) // expected-warning {{'20' is not a valid version number for iOS}} +@available(macCatalyst, deprecated: 20) // expected-warning {{'20' is not a valid version number for Mac Catalyst}} +@available(watchOS, deprecated: 13) // expected-warning {{'13' is not a valid version number for watchOS}} +@available(tvOS, deprecated: 20) // expected-warning {{'20' is not a valid version number for tvOS}} +@available(visionOS, deprecated: 4) // expected-warning {{'4' is not a valid version number for visionOS}} +func invalidDeprecated() { } + +@available(macOS, obsoleted: 17) // expected-warning {{'17' is not a valid version number for macOS}} +@available(iOS, obsoleted: 20) // expected-warning {{'20' is not a valid version number for iOS}} +@available(macCatalyst, obsoleted: 20) // expected-warning {{'20' is not a valid version number for Mac Catalyst}} +@available(watchOS, obsoleted: 13) // expected-warning {{'13' is not a valid version number for watchOS}} +@available(tvOS, obsoleted: 20) // expected-warning {{'20' is not a valid version number for tvOS}} +@available(visionOS, obsoleted: 4) // expected-warning {{'4' is not a valid version number for visionOS}} +func invalidObsoleted() { } + +@available(macOS 18, iOS 21, macCatalyst 22, watchOS 14, tvOS 23, visionOS 7, *) +// expected-warning@-1 {{'18' is not a valid version number for macOS}} +// expected-warning@-2 {{'21' is not a valid version number for iOS}} +// expected-warning@-3 {{'22' is not a valid version number for Mac Catalyst}} +// expected-warning@-4 {{'14' is not a valid version number for watchOS}} +// expected-warning@-5 {{'23' is not a valid version number for tvOS}} +// expected-warning@-6 {{'7' is not a valid version number for visionOS}} +func invalidIntroducedShort() { } From 1a8d8413b4ac6767fefa50a7f265727e387f2b42 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Sun, 8 Jun 2025 12:23:49 -0700 Subject: [PATCH 5/6] AST: Canonicalize version numbers in @_originallyDefinedIn attributes. --- lib/AST/Attr.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 0b1f903f19e6f..744b7c3b65a8e 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -2375,16 +2375,13 @@ static StringRef getLinkerModuleName(StringRef OriginalModuleName) { } OriginallyDefinedInAttr::OriginallyDefinedInAttr( - SourceLoc AtLoc, SourceRange Range, - StringRef OriginalModuleName, - PlatformKind Platform, - const llvm::VersionTuple MovedVersion, bool Implicit) - : DeclAttribute(DeclAttrKind::OriginallyDefinedIn, AtLoc, Range, - Implicit), - ManglingModuleName(getManglingModuleName(OriginalModuleName)), - LinkerModuleName(getLinkerModuleName(OriginalModuleName)), - Platform(Platform), - MovedVersion(MovedVersion) {} + SourceLoc AtLoc, SourceRange Range, StringRef OriginalModuleName, + PlatformKind Platform, const llvm::VersionTuple MovedVersion, bool Implicit) + : DeclAttribute(DeclAttrKind::OriginallyDefinedIn, AtLoc, Range, Implicit), + ManglingModuleName(getManglingModuleName(OriginalModuleName)), + LinkerModuleName(getLinkerModuleName(OriginalModuleName)), + Platform(Platform), + MovedVersion(canonicalizePlatformVersion(Platform, MovedVersion)) {} std::optional OriginallyDefinedInAttr::isActivePlatform(const ASTContext &ctx) const { From 6e3a2d22c81e9ac364a3349ec7ef0f0dd3b8d6b3 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Sun, 8 Jun 2025 12:59:44 -0700 Subject: [PATCH 6/6] ModuleInterface: Canonicalize OS version numbers in loaded swiftinterfaces. --- include/swift/AST/DiagnosticsSema.def | 8 +++ .../Serialization/SerializedModuleLoader.h | 3 +- lib/Frontend/ModuleInterfaceLoader.cpp | 2 +- lib/Serialization/SerializedModuleLoader.cpp | 72 ++++++++++++++----- 4 files changed, 67 insertions(+), 18 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index e9922c7c33459..fe2aad6ec4235 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -891,6 +891,14 @@ ERROR(serialization_failed,none, WARNING(can_import_invalid_swiftmodule,none, "canImport() evaluated to false due to invalid swiftmodule: %0", (StringRef)) +ERROR(map_os_version_from_textual_interface_failed,none, + "failed to map OS version from %0 to %1 in %2", + (StringRef, StringRef, StringRef)) + +ERROR(target_os_version_from_textual_interface_invalid,none, + "invalid target triple %0 in %1", + (StringRef, StringRef)) + ERROR(serialization_load_failed,Fatal, "failed to load module '%0'", (StringRef)) ERROR(module_interface_build_failed,Fatal, diff --git a/include/swift/Serialization/SerializedModuleLoader.h b/include/swift/Serialization/SerializedModuleLoader.h index 200fcd20a7b23..137a9dcd24783 100644 --- a/include/swift/Serialization/SerializedModuleLoader.h +++ b/include/swift/Serialization/SerializedModuleLoader.h @@ -580,7 +580,8 @@ class SerializedASTFile final : public LoadedFile { bool extractCompilerFlagsFromInterface( StringRef interfacePath, StringRef buffer, llvm::StringSaver &ArgSaver, SmallVectorImpl &SubArgs, - std::optional PreferredTarget = std::nullopt); + std::optional PreferredTarget = std::nullopt, + DiagnosticEngine *diagEngine = nullptr); /// Extract the user module version number from an interface file. llvm::VersionTuple extractUserModuleVersionFromInterface(StringRef moduleInterfacePath); diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index 1c28f550e68d6..ad9db89855a90 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -1547,7 +1547,7 @@ static bool readSwiftInterfaceVersionAndArgs( if (extractCompilerFlagsFromInterface(interfacePath, SB, ArgSaver, interfaceInfo.Arguments, - preferredTarget)) { + preferredTarget, &Diags)) { InterfaceSubContextDelegateImpl::diagnose( interfacePath, diagnosticLoc, SM, &Diags, diag::error_extracting_version_from_module_interface); diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index ab37e7c368020..c9dbedbe48684 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -1470,30 +1470,70 @@ static std::optional getFlagsFromInterfaceFile(StringRef &file, bool swift::extractCompilerFlagsFromInterface( StringRef interfacePath, StringRef buffer, llvm::StringSaver &ArgSaver, SmallVectorImpl &SubArgs, - std::optional PreferredTarget) { + std::optional PreferredTarget, DiagnosticEngine *Diag) { auto FlagMatch = getFlagsFromInterfaceFile(buffer, SWIFT_MODULE_FLAGS_KEY); if (!FlagMatch) return true; llvm::cl::TokenizeGNUCommandLine(*FlagMatch, ArgSaver, SubArgs); - // If the target triple parsed from the Swift interface file differs - // only in subarchitecture from the compatible target triple, then - // we have loaded a Swift interface from a different-but-compatible - // architecture slice. Use the compatible subarchitecture. - if (PreferredTarget) { - for (unsigned I = 1; I < SubArgs.size(); ++I) { - if (strcmp(SubArgs[I - 1], "-target") != 0 && - strcmp(SubArgs[I - 1], "-target-variant") != 0) - continue; + for (unsigned I = 1; I < SubArgs.size(); ++I) { + if (strcmp(SubArgs[I - 1], "-target") != 0 && + strcmp(SubArgs[I - 1], "-target-variant") != 0) + continue; - llvm::Triple triple(SubArgs[I]); - if (triple.getArch() != PreferredTarget->getArch()) - continue; - if (triple.getSubArch() == PreferredTarget->getSubArch()) - continue; + llvm::Triple triple(SubArgs[I]); + bool shouldModify = false; + // If the target triple parsed from the swiftinterface file differs + // only in subarchitecture from the compatible target triple, then + // we have loaded a Swift interface from a different-but-compatible + // architecture slice. Use the compatible subarchitecture. + if (PreferredTarget && triple.getArch() == PreferredTarget->getArch() && + triple.getSubArch() != PreferredTarget->getSubArch()) { triple.setArch(PreferredTarget->getArch(), PreferredTarget->getSubArch()); - SubArgs[I] = ArgSaver.save(triple.str()).data(); + shouldModify = true; } + + // Diagnose if the version in the target triple parsed from the + // swiftinterface is invalid for the OS. + const llvm::VersionTuple originalVer = triple.getOSVersion(); + bool isValidVersion = + llvm::Triple::isValidVersionForOS(triple.getOS(), originalVer); + if (!isValidVersion) { + if (Diag) { + Diag->diagnose(SourceLoc(), + diag::target_os_version_from_textual_interface_invalid, + triple.str(), interfacePath); + } + break; + } + + // Canonicalize the version in the target triple parsed from the + // swiftinterface. + llvm::VersionTuple newVer = llvm::Triple::getCanonicalVersionForOS( + triple.getOS(), originalVer, isValidVersion); + if (originalVer != newVer) { + std::string originalOSName = triple.getOSName().str(); + std::string originalVerStr = originalVer.getAsString(); + std::string newVerStr = newVer.getAsString(); + const int OSNameWithoutVersionLength = + originalOSName.size() - originalVerStr.size(); + if (!StringRef(originalOSName).ends_with(originalVerStr) || + (OSNameWithoutVersionLength <= 0)) { + if (Diag) { + Diag->diagnose(SourceLoc(), + diag::map_os_version_from_textual_interface_failed, + originalVerStr, newVerStr, interfacePath); + } + break; + } + llvm::SmallString<64> buffer( + originalOSName.substr(0, OSNameWithoutVersionLength)); + buffer.append(newVerStr); + triple.setOSName(buffer.str()); + shouldModify = true; + } + if (shouldModify) + SubArgs[I] = ArgSaver.save(triple.str()).data(); } auto IgnFlagMatch =