From 7949916cc4f9bfedf075852db97cd84b843d502c Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Tue, 14 Mar 2023 09:52:16 -0700 Subject: [PATCH 1/2] 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 dabf0cee1762de9bca20101475bbd9f2ed2c0970 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Tue, 14 Mar 2023 09:52:31 -0700 Subject: [PATCH 2/2] 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 | 6 ++ .../weak_import_availability_helper.swift | 26 +++++++- test/IRGen/float16_macos.swift | 7 +-- test/IRGen/weak_import_availability.swift | 62 +++++++++++++++++-- test/SILGen/objc_init_unavailable.swift | 3 +- .../execution/unavailable_decls.swift | 30 +++++++++ 6 files changed, 122 insertions(+), 12 deletions(-) create mode 100644 validation-test/execution/unavailable_decls.swift diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index b065d6d82ade0..1cb4ab7b3a341 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1126,6 +1126,12 @@ bool Decl::isAlwaysWeakImported() const { if (getAttrs().hasAttribute()) return true; + // FIXME: Weak linking on Windows is not yet supported + // https://github.com/apple/swift/issues/53303 + if (getSemanticUnavailableAttr() && + !getASTContext().LangOpts.Target.isOSWindows()) + 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/float16_macos.swift b/test/IRGen/float16_macos.swift index 7293254fe495f..e9e518a09013b 100644 --- a/test/IRGen/float16_macos.swift +++ b/test/IRGen/float16_macos.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -emit-ir %s -target x86_64-apple-macos10.15 | %FileCheck %s --check-prefix=CHECK10 -// RUN: %target-swift-frontend -emit-ir %s -target x86_64-apple-macos11 | %FileCheck %s --check-prefix=CHECK11 +// RUN: %target-swift-frontend -emit-ir %s -target x86_64-apple-macos10.15 | %FileCheck %s +// RUN: %target-swift-frontend -emit-ir %s -target x86_64-apple-macos11 | %FileCheck %s // REQUIRES: OS=macosx // REQUIRES: CPU=x86_64 @@ -11,5 +11,4 @@ public struct Float16Wrapper { var x: Float16 } -// CHECK10-LABEL: @"$ss7Float16VMn" = extern_weak global %swift.type_descriptor -// CHECK11-LABEL: @"$ss7Float16VMn" = external global %swift.type_descriptor +// CHECK-LABEL: @"$ss7Float16VMn" = extern_weak global %swift.type_descriptor 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 diff --git a/validation-test/execution/unavailable_decls.swift b/validation-test/execution/unavailable_decls.swift new file mode 100644 index 0000000000000..244045d14c72b --- /dev/null +++ b/validation-test/execution/unavailable_decls.swift @@ -0,0 +1,30 @@ +// RUN: %empty-directory(%t) +// RUN: split-file %s %t +// RUN: %target-build-swift-dylib(%t/%target-library-name(Library)) -emit-module -emit-module-path %t/Library.swiftmodule -module-name Library -parse-as-library %t/Library.swift -swift-version 5 -enable-library-evolution +// RUN: %target-build-swift-dylib(%t/%target-library-name(ClientLibrary)) %t/ClientA.swift %t/ClientB.swift -I%t -L%t -swift-version 5 -lLibrary + +//--- Library.swift + +@available(*, unavailable) +public struct UnavailableStruct {} + +//--- ClientA.swift + +import Library + +@inline(never) +func blackHole(_ t: T) {} + +@available(*, unavailable) +public func foo() { + blackHole(UnavailableStruct.self) +} + +//--- ClientB.swift + +import Library + +@available(*, unavailable) +public func bar() { + blackHole(UnavailableStruct.self) +}