From 94e9f918977d238011215ebc5635631e80c6e01b Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Thu, 16 Oct 2025 11:57:55 -0400 Subject: [PATCH 1/2] Make `Attachment` always conform to `CustomStringConvertible`. This PR ensures that `Attachment` conforms to `CustomStringConvertible` even if the value it wraps is non-copyable. We accomplish this by creating a new helper function that is able to box an arbitrary `~Copyable` value in `Any` if its type is actually `Copyable`. (This requires a newer runtime on Apple platforms, so we fall back to a generic (`~Copyable`) implementation on older macOS/iOS/etc.) We then call this helper function in `Attachment.description` and if it returns a value, we can stringify it. --- Package.swift | 1 + Sources/Testing/Attachments/Attachment.swift | 16 +++---- Sources/Testing/CMakeLists.txt | 1 + .../Support/Additions/CopyableAdditions.swift | 47 +++++++++++++++++++ .../shared/AvailabilityDefinitions.cmake | 1 + 5 files changed, 56 insertions(+), 10 deletions(-) create mode 100644 Sources/Testing/Support/Additions/CopyableAdditions.swift diff --git a/Package.swift b/Package.swift index e910a67b8..2788502c3 100644 --- a/Package.swift +++ b/Package.swift @@ -406,6 +406,7 @@ extension Array where Element == PackageDescription.SwiftSetting { .enableExperimentalFeature("AvailabilityMacro=_regexAPI:macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0"), .enableExperimentalFeature("AvailabilityMacro=_swiftVersionAPI:macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0"), .enableExperimentalFeature("AvailabilityMacro=_typedThrowsAPI:macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0"), + .enableExperimentalFeature("AvailabilityMacro=_castingWithNonCopyableGenerics:macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0"), .enableExperimentalFeature("AvailabilityMacro=_distantFuture:macOS 99.0, iOS 99.0, watchOS 99.0, tvOS 99.0, visionOS 99.0"), ] diff --git a/Sources/Testing/Attachments/Attachment.swift b/Sources/Testing/Attachments/Attachment.swift index a17130176..1313b0d41 100644 --- a/Sources/Testing/Attachments/Attachment.swift +++ b/Sources/Testing/Attachments/Attachment.swift @@ -193,21 +193,17 @@ public struct AnyAttachable: AttachableWrapper, Sendable, Copyable { // MARK: - Describing an attachment -extension Attachment where AttachableValue: ~Copyable { - @_documentation(visibility: private) - public var description: String { - let typeInfo = TypeInfo(describing: AttachableValue.self) - return #""\#(preferredName)": instance of '\#(typeInfo.unqualifiedName)'"# - } -} - -extension Attachment: CustomStringConvertible { +extension Attachment: CustomStringConvertible where AttachableValue: ~Copyable { /// @Metadata { /// @Available(Swift, introduced: 6.2) /// @Available(Xcode, introduced: 26.0) /// } public var description: String { - #""\#(preferredName)": \#(String(describingForTest: attachableValue))"# + if #available(_castingWithNonCopyableGenerics, *), let attachableValue = boxCopyableValue(attachableValue) { + return #""\#(preferredName)": \#(String(describingForTest: attachableValue))"# + } + let typeInfo = TypeInfo(describing: AttachableValue.self) + return #""\#(preferredName)": instance of '\#(typeInfo.unqualifiedName)'"# } } diff --git a/Sources/Testing/CMakeLists.txt b/Sources/Testing/CMakeLists.txt index fca4fcae4..bebf05eb9 100644 --- a/Sources/Testing/CMakeLists.txt +++ b/Sources/Testing/CMakeLists.txt @@ -76,6 +76,7 @@ add_library(Testing Support/Additions/ArrayAdditions.swift Support/Additions/CollectionDifferenceAdditions.swift Support/Additions/CommandLineAdditions.swift + Support/Additions/CopyableAdditions.swift Support/Additions/NumericAdditions.swift Support/Additions/ResultAdditions.swift Support/Additions/TaskAdditions.swift diff --git a/Sources/Testing/Support/Additions/CopyableAdditions.swift b/Sources/Testing/Support/Additions/CopyableAdditions.swift new file mode 100644 index 000000000..7b3751c63 --- /dev/null +++ b/Sources/Testing/Support/Additions/CopyableAdditions.swift @@ -0,0 +1,47 @@ +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for Swift project authors +// + +#if !hasFeature(Embedded) +/// A helper protocol for ``boxCopyableValue(_:)``. +private protocol _CopyablePointer { + /// Load the value at this address into an existential box. + /// + /// - Returns: The value at this address. + func load() -> Any +} + +extension UnsafePointer: _CopyablePointer where Pointee: Copyable { + func load() -> Any { + pointee + } +} +#endif + +/// Copy a value to an existential box if its type conforms to `Copyable`. +/// +/// - Parameters: +/// - value: The value to copy. +/// +/// - Returns: A copy of `value` in an existential box, or `nil` if the type of +/// `value` does not conform to `Copyable`. +/// +/// When using Embedded Swift, this function always returns `nil`. +#if !hasFeature(Embedded) +@available(_castingWithNonCopyableGenerics, *) +func boxCopyableValue(_ value: borrowing some ~Copyable) -> Any? { + withUnsafePointer(to: value) { address in + return (address as? any _CopyablePointer)?.load() + } +} +#else +func boxCopyableValue(_ value: borrowing some ~Copyable) -> Never? { + nil +} +#endif diff --git a/cmake/modules/shared/AvailabilityDefinitions.cmake b/cmake/modules/shared/AvailabilityDefinitions.cmake index 472b2b929..e6b716657 100644 --- a/cmake/modules/shared/AvailabilityDefinitions.cmake +++ b/cmake/modules/shared/AvailabilityDefinitions.cmake @@ -17,4 +17,5 @@ add_compile_options( "SHELL:$<$:-Xfrontend -define-availability -Xfrontend \"_regexAPI:macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0\">" "SHELL:$<$:-Xfrontend -define-availability -Xfrontend \"_swiftVersionAPI:macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0\">" "SHELL:$<$:-Xfrontend -define-availability -Xfrontend \"_typedThrowsAPI:macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0\">" + "SHELL:$<$:-Xfrontend -define-availability -Xfrontend \"_castingWithNonCopyableGenerics:macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0\">" "SHELL:$<$:-Xfrontend -define-availability -Xfrontend \"_distantFuture:macOS 99.0, iOS 99.0, watchOS 99.0, tvOS 99.0, visionOS 99.0\">") From ccebf0bdb244dfe4dfad9b53885ec1caf5cc4734 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Thu, 16 Oct 2025 12:19:34 -0400 Subject: [PATCH 2/2] Use Void instead of Never to suppress a warning that may occur in Embedded Swift --- Sources/Testing/Support/Additions/CopyableAdditions.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Testing/Support/Additions/CopyableAdditions.swift b/Sources/Testing/Support/Additions/CopyableAdditions.swift index 7b3751c63..ea192b1ef 100644 --- a/Sources/Testing/Support/Additions/CopyableAdditions.swift +++ b/Sources/Testing/Support/Additions/CopyableAdditions.swift @@ -41,7 +41,7 @@ func boxCopyableValue(_ value: borrowing some ~Copyable) -> Any? { } } #else -func boxCopyableValue(_ value: borrowing some ~Copyable) -> Never? { +func boxCopyableValue(_ value: borrowing some ~Copyable) -> Void? { nil } #endif