From 5aa4b2f305d6d39170a0c757eaf5bde54bfe1bf5 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Tue, 14 Mar 2023 09:52:16 -0700 Subject: [PATCH 1/3] Tests: Add some missing CHECKs to IRGen/weak_import_availability.swift. --- test/IRGen/weak_import_availability.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/IRGen/weak_import_availability.swift b/test/IRGen/weak_import_availability.swift index 2dc7f3f8f0ec2..d783518b6774e 100644 --- a/test/IRGen/weak_import_availability.swift +++ b/test/IRGen/weak_import_availability.swift @@ -56,6 +56,9 @@ public func useConditionallyAvailableGlobal() { // CHECK-OLD-LABEL: declare extern_weak swiftcc void @"$s31weak_import_availability_helper28conditionallyAvailableGlobalSivs"(i64) // CHECK-NEW-LABEL: declare swiftcc void @"$s31weak_import_availability_helper28conditionallyAvailableGlobalSivs"(i64) +// CHECK-OLD-LABEL: declare extern_weak swiftcc { i8*, %TSi* } @"$s31weak_import_availability_helper28conditionallyAvailableGlobalSivM"(i8* noalias dereferenceable(32)) +// CHECK-NEW-LABEL: declare swiftcc { i8*, %TSi* } @"$s31weak_import_availability_helper28conditionallyAvailableGlobalSivM"(i8* noalias dereferenceable(32)) + func blackHole(_: T) {} @available(macOS 10.50, *) @@ -63,6 +66,9 @@ public func useConditionallyAvailableStruct() { blackHole(ConditionallyAvailableStruct.self) } +// CHECK-OLD-LABEL: declare extern_weak swiftcc %swift.metadata_response @"$s31weak_import_availability_helper28ConditionallyAvailableStructVMa"(i64) +// CHECK-NEW-LABEL: declare swiftcc %swift.metadata_response @"$s31weak_import_availability_helper28ConditionallyAvailableStructVMa"(i64) + @available(macOS 10.50, *) public func useConditionallyAvailableMethod(s: ConditionallyAvailableStruct) { s.conditionallyAvailableMethod() From ab0f7c19f50d6322c601eaa444ce517cbae0e54f Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Tue, 14 Mar 2023 09:52:31 -0700 Subject: [PATCH 2/3] IRGen: Weakly link symbols for unavailable declarations. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When computing linkage, the compiler would treat unavailable declarations as if they were "always available" when they lack an `introduced:` version: ``` // Library @available(macOS, unavailable) public func foo() { // … } // Client import Library @available(macOS, unavailable) func bar() { // Even though foo() and bar() are unavalable on macOS the compiler still // strongly links foo(). foo() } ``` This created an unnecessary dependency between libraries and their clients and also can interfere with back deployment, since unavailable declarations may not be present in a library on all OS versions. Developers could work around these issues by conditionally compiling the code that references an unavailable declaration, but they shouldn't have to given that unavailable code is meant to be provably unreachable at runtime. Additionally, it could improve library code size if we allowed the compiler to strip unavailable declarations from a binary completely. Resolves rdar://106673713 --- lib/AST/Decl.cpp | 3 + .../weak_import_availability_helper.swift | 26 +++++++- test/IRGen/weak_import_availability.swift | 62 +++++++++++++++++-- test/SILGen/objc_init_unavailable.swift | 3 +- 4 files changed, 86 insertions(+), 8 deletions(-) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 384c0450b1034..fa9b7e0818d9e 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1126,6 +1126,9 @@ bool Decl::isAlwaysWeakImported() const { if (getAttrs().hasAttribute()) return true; + if (getSemanticUnavailableAttr()) + return true; + if (auto *accessor = dyn_cast(this)) return accessor->getStorage()->isAlwaysWeakImported(); diff --git a/test/IRGen/Inputs/weak_import_availability_helper.swift b/test/IRGen/Inputs/weak_import_availability_helper.swift index 3a74e9651b8a3..a5d1f1db0e126 100644 --- a/test/IRGen/Inputs/weak_import_availability_helper.swift +++ b/test/IRGen/Inputs/weak_import_availability_helper.swift @@ -1,17 +1,35 @@ @available(macOS 10.50, *) public func conditionallyAvailableFunction() {} +@available(macOS, unavailable) +public func unavailableFunction() {} + @available(macOS 10.50, *) public var conditionallyAvailableGlobal: Int { get {return 0} set {} } +@available(macOS, unavailable) +public var unavailableGlobal: Int { + get {return 0} + set {} +} + @available(macOS 10.50, *) public struct ConditionallyAvailableStruct { public func conditionallyAvailableMethod() {} } +extension ConditionallyAvailableStruct { + public struct NestedStruct {} +} + +@available(macOS, unavailable) +public struct UnvailableStruct { + public func unavailableMethod() {} +} + public protocol AlwaysAvailableProtocol {} public struct AlwaysAvailableStruct {} @@ -19,9 +37,15 @@ public struct AlwaysAvailableStruct {} @available(macOS 10.50, *) extension AlwaysAvailableStruct : AlwaysAvailableProtocol {} +@available(macOS, unavailable) +public protocol UnavailableProtocol {} + +@available(macOS, unavailable) +extension AlwaysAvailableStruct : UnavailableProtocol {} + public enum AlwaysAvailableEnum { case alwaysAvailableCase @available(macOS 10.50, *) case conditionallyAvailableCase -} \ No newline at end of file +} diff --git a/test/IRGen/weak_import_availability.swift b/test/IRGen/weak_import_availability.swift index d783518b6774e..abf0f0d01aa8d 100644 --- a/test/IRGen/weak_import_availability.swift +++ b/test/IRGen/weak_import_availability.swift @@ -1,12 +1,12 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -emit-module-path %t/weak_import_availability_helper.swiftmodule -parse-as-library %S/Inputs/weak_import_availability_helper.swift -enable-library-evolution +// RUN: %target-swift-frontend -target %target-cpu-apple-macosx10.50 -emit-module -emit-module-path %t/weak_import_availability_helper.swiftmodule -parse-as-library %S/Inputs/weak_import_availability_helper.swift -enable-library-evolution // -// RUN: %target-swift-frontend -primary-file %s -I %t -emit-ir | %FileCheck %s --check-prefix=CHECK-OLD -// RUN: %target-swift-frontend -primary-file %s -I %t -emit-ir -target %target-cpu-apple-macosx10.50 | %FileCheck %s --check-prefix=CHECK-NEW -// RUN: %target-swift-frontend -primary-file %s -I %t -emit-ir -target %target-cpu-apple-macosx10.60 | %FileCheck %s --check-prefix=CHECK-NEW +// RUN: %target-swift-frontend -primary-file %s -I %t -emit-ir | %FileCheck %s --check-prefixes=CHECK,CHECK-OLD +// RUN: %target-swift-frontend -primary-file %s -I %t -emit-ir -target %target-cpu-apple-macosx10.50 | %FileCheck %s --check-prefixes=CHECK,CHECK-NEW +// RUN: %target-swift-frontend -primary-file %s -I %t -emit-ir -target %target-cpu-apple-macosx10.60 | %FileCheck %s --check-prefixes=CHECK,CHECK-NEW -// RUN: %target-swift-frontend -primary-file %s -I %t -emit-ir -target %target-cpu-apple-macosx10.50 -weak-link-at-target | %FileCheck %s --check-prefix=CHECK-OLD -// RUN: %target-swift-frontend -primary-file %s -I %t -emit-ir -target %target-cpu-apple-macosx10.60 -weak-link-at-target | %FileCheck %s --check-prefix=CHECK-NEW +// RUN: %target-swift-frontend -primary-file %s -I %t -emit-ir -target %target-cpu-apple-macosx10.50 -weak-link-at-target | %FileCheck %s --check-prefixes=CHECK,CHECK-OLD +// RUN: %target-swift-frontend -primary-file %s -I %t -emit-ir -target %target-cpu-apple-macosx10.60 -weak-link-at-target | %FileCheck %s --check-prefixes=CHECK,CHECK-NEW // REQUIRES: OS=macosx @@ -35,6 +35,16 @@ public func useConditionallyAvailableConformance() { // CHECK-OLD-LABEL: @"$s31weak_import_availability_helper21AlwaysAvailableStructVAA0eF8ProtocolAAWP" = extern_weak global i8* // CHECK-NEW-LABEL: @"$s31weak_import_availability_helper21AlwaysAvailableStructVAA0eF8ProtocolAAWP" = external global i8* +@available(macOS, unavailable) +func useUnavailableConformance(_: T.Type) {} + +@available(macOS, unavailable) +public func useUnavailableConformance() { + useUnavailableConformance(AlwaysAvailableStruct.self) +} + +// CHECK-LABEL: @"$s31weak_import_availability_helper21AlwaysAvailableStructVAA19UnavailableProtocolAAWP" = extern_weak global i8*, align 8 + @available(macOS 10.50, *) public func callConditionallyAvailableFunction() { conditionallyAvailableFunction() @@ -43,6 +53,13 @@ public func callConditionallyAvailableFunction() { // CHECK-OLD-LABEL: declare extern_weak swiftcc void @"$s31weak_import_availability_helper30conditionallyAvailableFunctionyyF"() // CHECK-NEW-LABEL: declare swiftcc void @"$s31weak_import_availability_helper30conditionallyAvailableFunctionyyF"() +@available(macOS, unavailable) +public func callUnavailableFunction() { + unavailableFunction() +} + +// CHECK-LABEL: declare extern_weak swiftcc void @"$s31weak_import_availability_helper19unavailableFunctionyyF"() + @available(macOS 10.50, *) public func useConditionallyAvailableGlobal() { _ = conditionallyAvailableGlobal @@ -59,6 +76,17 @@ public func useConditionallyAvailableGlobal() { // CHECK-OLD-LABEL: declare extern_weak swiftcc { i8*, %TSi* } @"$s31weak_import_availability_helper28conditionallyAvailableGlobalSivM"(i8* noalias dereferenceable(32)) // CHECK-NEW-LABEL: declare swiftcc { i8*, %TSi* } @"$s31weak_import_availability_helper28conditionallyAvailableGlobalSivM"(i8* noalias dereferenceable(32)) +@available(macOS, unavailable) +public func useUnavailableGlobal() { + _ = unavailableGlobal + unavailableGlobal = 0 + unavailableGlobal += 1 +} + +// CHECK-LABEL: declare extern_weak swiftcc i64 @"$s31weak_import_availability_helper17unavailableGlobalSivg"() +// CHECK-LABEL: declare extern_weak swiftcc void @"$s31weak_import_availability_helper17unavailableGlobalSivs"(i64) +// CHECK-LABEL: declare extern_weak swiftcc { i8*, %TSi* } @"$s31weak_import_availability_helper17unavailableGlobalSivM"(i8* noalias dereferenceable(32)) + func blackHole(_: T) {} @available(macOS 10.50, *) @@ -69,6 +97,14 @@ public func useConditionallyAvailableStruct() { // CHECK-OLD-LABEL: declare extern_weak swiftcc %swift.metadata_response @"$s31weak_import_availability_helper28ConditionallyAvailableStructVMa"(i64) // CHECK-NEW-LABEL: declare swiftcc %swift.metadata_response @"$s31weak_import_availability_helper28ConditionallyAvailableStructVMa"(i64) +@available(macOS 10.50, *) +public func useNestedConditionallyAvailableStruct() { + blackHole(ConditionallyAvailableStruct.NestedStruct.self) +} + +// CHECK-OLD-LABEL: declare extern_weak swiftcc %swift.metadata_response @"$s31weak_import_availability_helper28ConditionallyAvailableStructV06NestedG0VMa"(i64) +// CHECK-NEW-LABEL: declare swiftcc %swift.metadata_response @"$s31weak_import_availability_helper28ConditionallyAvailableStructV06NestedG0VMa"(i64) + @available(macOS 10.50, *) public func useConditionallyAvailableMethod(s: ConditionallyAvailableStruct) { s.conditionallyAvailableMethod() @@ -76,3 +112,17 @@ public func useConditionallyAvailableMethod(s: ConditionallyAvailableStruct) { // CHECK-OLD-LABEL: declare extern_weak swiftcc void @"$s31weak_import_availability_helper28ConditionallyAvailableStructV013conditionallyF6MethodyyF"(%swift.opaque* noalias nocapture swiftself) // CHECK-NEW-LABEL: declare swiftcc void @"$s31weak_import_availability_helper28ConditionallyAvailableStructV013conditionallyF6MethodyyF"(%swift.opaque* noalias nocapture swiftself) + +@available(macOS, unavailable) +public func useUnavailableStruct() { + blackHole(UnvailableStruct.self) +} + +// CHECK-LABEL: declare extern_weak swiftcc %swift.metadata_response @"$s31weak_import_availability_helper16UnvailableStructVMa"(i64) + +@available(macOS, unavailable) +public func useUnavailableMethod(s: UnvailableStruct) { + s.unavailableMethod() +} + +// CHECK-LABEL: declare extern_weak swiftcc void @"$s31weak_import_availability_helper16UnvailableStructV17unavailableMethodyyF"(%swift.opaque* noalias nocapture swiftself) diff --git a/test/SILGen/objc_init_unavailable.swift b/test/SILGen/objc_init_unavailable.swift index 7d3f30accff67..04f791ebfd7df 100644 --- a/test/SILGen/objc_init_unavailable.swift +++ b/test/SILGen/objc_init_unavailable.swift @@ -1,12 +1,13 @@ // RUN: %target-swift-emit-silgen(mock-sdk: %clang-importer-sdk) -enable-objc-interop -import-objc-header %S/Inputs/objc_init_unavailable.h %s | %FileCheck %s // REQUIRES: objc_interop +// REQUIRES: OS=macosx @available(macOS, unavailable) public func callUnavailableInit(name: String) -> ClassWithUnavailableInit { return ClassWithUnavailableInit(bundleID: name) } -// CHECK-LABEL: sil [ossa] @$s21objc_init_unavailable19callUnavailableInit4nameSo09ClassWitheF0CSS_tF : $@convention(thin) (@guaranteed String) -> @owned ClassWithUnavailableInit { +// CHECK-LABEL: sil [weak_imported] [ossa] @$s21objc_init_unavailable19callUnavailableInit4nameSo09ClassWitheF0CSS_tF : $@convention(thin) (@guaranteed String) -> @owned ClassWithUnavailableInit { // CHECK: function_ref @$sSo24ClassWithUnavailableInitC8bundleIDABSgSSSg_tcfC : $@convention(method) (@owned Optional, @thick ClassWithUnavailableInit.Type) -> @owned Optional // CHECK: return From 084ef39981a9cfc27e91d13e73d5ded307b28f47 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Tue, 14 Mar 2023 15:24:03 -0700 Subject: [PATCH 3/3] IRGen: Strongly link unavailable declarations with `introduced:` versions. If a declaration is unavailable but also has an `introduced:` availability version, treat that as an indication that it is considered ABI and should be linked as if it were available. Part of rdar://106673713 --- lib/AST/Decl.cpp | 7 ++++++- test/IRGen/Inputs/weak_import_availability_helper.swift | 4 ++++ test/IRGen/weak_import_availability.swift | 8 ++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index fa9b7e0818d9e..8bea5b9f43c95 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1126,7 +1126,12 @@ bool Decl::isAlwaysWeakImported() const { if (getAttrs().hasAttribute()) return true; - if (getSemanticUnavailableAttr()) + // Declarations that are unavailable should be weak linked since they are + // meant to be unreachable at runtime and their removal should not affect + // clients. However, make an exception for unavailable declarations with + // explicit introduction versions, which are considered required ABI. + if (getSemanticUnavailableAttr() && + getAvailabilityForLinkage().isAlwaysAvailable()) return true; if (auto *accessor = dyn_cast(this)) diff --git a/test/IRGen/Inputs/weak_import_availability_helper.swift b/test/IRGen/Inputs/weak_import_availability_helper.swift index a5d1f1db0e126..a4c549fc75b3f 100644 --- a/test/IRGen/Inputs/weak_import_availability_helper.swift +++ b/test/IRGen/Inputs/weak_import_availability_helper.swift @@ -4,6 +4,10 @@ public func conditionallyAvailableFunction() {} @available(macOS, unavailable) public func unavailableFunction() {} +@available(macOS 10.50, *) +@available(macOS, unavailable) +public func unavailableButIntroducedFunction() {} + @available(macOS 10.50, *) public var conditionallyAvailableGlobal: Int { get {return 0} diff --git a/test/IRGen/weak_import_availability.swift b/test/IRGen/weak_import_availability.swift index abf0f0d01aa8d..85216cd4d4815 100644 --- a/test/IRGen/weak_import_availability.swift +++ b/test/IRGen/weak_import_availability.swift @@ -60,6 +60,14 @@ public func callUnavailableFunction() { // CHECK-LABEL: declare extern_weak swiftcc void @"$s31weak_import_availability_helper19unavailableFunctionyyF"() +@available(macOS, unavailable) +public func callUnavailableButIntroducedFunction() { + unavailableButIntroducedFunction() +} + +// CHECK-OLD-LABEL: declare extern_weak swiftcc void @"$s31weak_import_availability_helper32unavailableButIntroducedFunctionyyF"() +// CHECK-NEW-LABEL: declare swiftcc void @"$s31weak_import_availability_helper32unavailableButIntroducedFunctionyyF"() + @available(macOS 10.50, *) public func useConditionallyAvailableGlobal() { _ = conditionallyAvailableGlobal