swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.52k stars 10.35k forks source link

Any.Type issue with @Test #76637

Open Kyle-Ye opened 1 month ago

Kyle-Ye commented 1 month ago

Description

I'm using name2 pattern at first. But then I thought I should refactor to use name1 pattern. But it will give me a strange compiler error.

Is this an expected unsupported feature like swiftlang/swift-testing#202 or a bug here.

struct DemoKitTests {
    @Test(
        arguments: [
            (type: Int.self, nominalName: "Int"),
            (type: String.self, nominalName: "String"),
        ]
    )
    func name1(type: Any.Type, nominalName: String) {
        #expect(API.name(type) == nominalName)
    }

    @Test
    func name2() {
        #expect(API.name(Int.self) == "Int")
        #expect(API.name(String.self) == "String")
    }
}

Expected behavior

Compile and test successfully.

Actual behavior

Compile error on language mode v5. (On v6 it is Any is not conform to Sendable)

xx.swift:17:4 Static method '__function(named:in:xcTestCompatibleSelector:displayName:traits:arguments:sourceLocation:parameters:testFunction:)' requires the types 'Any' and '(any Any.Type, String)' be equivalent

Steps to reproduce

See description field.

Or use the following demokit package

DemoKit.zip

swift-testing version/commit hash

Xcode 16.0.0

Swift & OS version (output of swift --version ; uname -a)

Swift 6 compiler with Swift 5 language mode

Kyle-Ye commented 1 month ago

Looks like it can be workaround by explicitly mark as Any.Type.

Hope the compiler/swift-testing macro can infer it automatically here.

@Test(
    arguments: [
        (type: Int.self as Any.Type, nominalName: "Int"),
        (type: String.self as Any.Type, nominalName: "String"),
    ]
)
func name(type: Any.Type, nominalName: String)
grynspan commented 1 month ago

Type inference here is at the compiler level. Swift Testing receives an array of [Any] and doesn't have the opportunity to do its own type checking.

Kyle-Ye commented 1 month ago

Swift Testing receives an array of [Any] and doesn't have the opportunity to do its own type checking.

Since we already have the parameter signature here - (Any.Type, String), can we add some explicit type inference hint to the Test macro's arguments variable type? (the variable c in the following example)

let a = (Int.self, "A") // Inferred as (Int.Type, String)
let b1 = [
    (Int.self, "Int"),
    (String.self, "String"),
] // Inferred as [Any] with an error "Heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional"

let b2: [Any] = [
    (Int.self, "Int"),
    (String.self, "String"),
]

let c: [(Any.Type, String)] = [
    (Int.self, "Int"),
    (String.self, "String"),
]
grynspan commented 1 month ago

Type inference occurs before the @Test macro is expanded. There's nothing we can add that's in the language today that would tell the compiler to infer a type other than [Any] here, and the signature of the test function itself does not (currently) participate in type inference for arguments to the macro.

Kyle-Ye commented 2 weeks ago

Find a solution here - use as [(Any.Type, String)] after the Array Liternal

@Test(
    arguments: [
        (type: Int.self, nominalName: "Int"),
        (type: String.self, nominalName: "String"),
    ] as [(Any.Type, String)]
)
func name1(type: Any.Type, nominalName: String) {
    #expect(API.name(type) == nominalName)
}
stmontgomery commented 2 weeks ago

Although the original reporter of this issue identified a workaround (explicitly typing the expression using as), I think we should pursue some kind macros enhancement to automate this, since users of Swift Testing in particular hit it fairly often. In particular, I think it would be great if there were a way for an attached macro such as @Test to include, as part of its declaration, that the types of arguments passed to some of its parameters (arguments: ...) should use the parameter types of the declaration the macro is attached to as a hint.

grynspan commented 2 weeks ago

This is not something we can do in Swift Testing today because the failure happens before macro expansion (because the type inference on the array resolves to [Any] instead of [any Sendable] or [Any.Type].) I know I'd filed a bug against macros ages ago asking for some way to infer types more betterer but I do not know where it ended up. @DougGregor do you happen to recall that issue/radar?