This solves a problem that users who pass array literals to @Test(arguments: ...) may encounter if the array's elements are heterogeneous. For example:
@Test(arguments: [ // ❌ error: type 'Any' does not conform to the 'Sendable' protocol
(Int.self, nominalName: "Int"),
(String.self, nominalName: "String"),
])
func name1(type: Any.Type, nominalName: String) {
#expect(API.name(type) == nominalName)
}
Due to the use of heterogeneous elements Int.self and String.self, the overall array's type is inferred as [Any]. This leads to two problems:
The @Test(arguments:) macro requires a Collection which is Sendable, and [Any] is not Sendable. This prevents passing the array to the macro at all, since arguments to a macro must typecheck successfully before the macro is expanded.
The test function's parameters are of type Any.Type and String, respectively. For the macro expansion to produce valid code, the array literal needs to have type [(Any.Type, String)].
This PR makes two changes to address those problems:
First, it introduces overloads of some of the @Test macro declarations without their Sendable requirements. This solves problem 1 above. (See the considerations below for discussion about why I believe this is safe and reasonable.)
Second, it modifies the macro expansion logic to add an as ... cast to array literal expressions passed to arguments: ... (unless they already have one) to provide an explicit type.
Continuing the example above, this results in the expanded code behaving as though the array expression had as [(Any.Type, String)] at the end, and the original code now compiles successfully.
Thank you to @ApolloZhu for suggesting the fix to use as ... in the macro expansion!
Concurrency safety
The expanded code from the new @Test overloads is no less concurrency safe than before, because it still calls APIs from the testing library which require Sendable. This means that passing a non-Sendable collection will still result in a compiler diagnostic, just with a different source location than before:
// With the changes in this PR:
struct NonSendable {}
@Test(arguments: [NonSendable()]) // ❌: @__swiftmacro_12TestingTests6unsafe4TestfMp_.swift:14:4: error: type 'NonSendable' does not conform to the 'Sendable' protocol
func unsafe(_ x: NonSendable) {}
Documentation
The new @Test overloads are necessarily public but are hidden from rendered DocC documentation using @_documentation(visibility: private). From an end user's perspective, @Test(arguments:) did, and still does, require Sendable; the only thing changing is where that enforcement occurs. So in terms of documentation, the only overloads we need to document are those that do require Sendable. In fact, for similar reasons, we already hide many of our @Test overloads from documentation so this has precedent.
Checklist:
[x] Code and documentation should follow the style of the Style Guide.
[x] If public symbols are renamed or modified, DocC references should be updated.
This solves a problem that users who pass array literals to
@Test(arguments: ...)
may encounter if the array's elements are heterogeneous. For example:Due to the use of heterogeneous elements
Int.self
andString.self
, the overall array's type is inferred as[Any]
. This leads to two problems:@Test(arguments:)
macro requires aCollection
which isSendable
, and[Any]
is notSendable
. This prevents passing the array to the macro at all, since arguments to a macro must typecheck successfully before the macro is expanded.Any.Type
andString
, respectively. For the macro expansion to produce valid code, the array literal needs to have type[(Any.Type, String)]
.This PR makes two changes to address those problems:
@Test
macro declarations without theirSendable
requirements. This solves problem 1 above. (See the considerations below for discussion about why I believe this is safe and reasonable.)as ...
cast to array literal expressions passed toarguments: ...
(unless they already have one) to provide an explicit type.Continuing the example above, this results in the expanded code behaving as though the array expression had
as [(Any.Type, String)]
at the end, and the original code now compiles successfully.Concurrency safety
The expanded code from the new
@Test
overloads is no less concurrency safe than before, because it still calls APIs from the testing library which requireSendable
. This means that passing a non-Sendable
collection will still result in a compiler diagnostic, just with a different source location than before:Documentation
The new
@Test
overloads are necessarilypublic
but are hidden from rendered DocC documentation using@_documentation(visibility: private)
. From an end user's perspective,@Test(arguments:)
did, and still does, requireSendable
; the only thing changing is where that enforcement occurs. So in terms of documentation, the only overloads we need to document are those that do requireSendable
. In fact, for similar reasons, we already hide many of our@Test
overloads from documentation so this has precedent.Checklist:
Fixes swiftlang/swift#76637