swift-testing icon indicating copy to clipboard operation
swift-testing copied to clipboard

Add support for non-copyable types as expectation arguments

Open grynspan opened this issue 1 year ago • 8 comments

Add support for non-copyable types as expectation arguments, e.g.:

struct S: ~Copyable, ... {}

let a: S = ...
let b: S = ...
#expect(a != b)
#expect(a.foo())
#expect(a.isBar)
// etc.

grynspan avatar Jul 16 '24 20:07 grynspan

Known issues:

  • String(describing:) does not take move-only values, so it cannot be used to describe a value that's part of a failed expectation expression
  • type(of:) does not take move-only values, so we can't get the runtime type of a value
  • String(reflecting:) does not take move-only metatypes, so we cannot capture the FQN of a move-only type
  • Variadic generics don't support move-only types (or at least, I can't get the compiler to accept them) which precludes the expansion of function calls that take any move-only arguments
  • We cannot syntactically distinguish consuming from borrowing arguments at call sites, so we may not be able to correctly expand expressions with move-only parts (and we don't have enough information during macro expansion to know to suppress these expansions, unlike with try and await.)
  • @autoclosure () -> U is treated as escaping, which means syntactically we can't borrow the right-hand argument of a binary operator.
  • Result needs to support non-copyable Success values (although we could build our own monad type if needed)
    • Attempting to implement my own Result analogue with the signature:
    enum __Result<Success, Failure>: ~Copyable where Success: ~Copyable, Failure: Error
    
    Seems to confuse the compiler, which still complains:

    🛑 Noncopyable type 'S' cannot be substituted for copyable generic parameter 'Success' in '__Result'

  • For our current implementation of binary operator expansion, we need non-copyable tuples.
  • Conditional casts to/from move-only types and existentials are needed for us to be able to implement String(describingForTest:). Even if we don't support the full gamut of possible stringification techniques, we need to be able to write something like if let value = value as? Copyable { ... } so we can opt into the full set for types that allow it.

grynspan avatar Jul 16 '24 20:07 grynspan

Result needs to support non-copyable Success values (although we could build our own monad type if needed)

FWIW Result will support non-copyable Success values in Swift 6.0

Azoy avatar Jul 17 '24 01:07 Azoy

Good to know—that knocks one point off the list, at least!

grynspan avatar Jul 17 '24 01:07 grynspan

  • @autoclosure () -> U is treated as escaping, which means syntactically we can't borrow the right-hand argument of a binary operator.

To avoid this problem, could we change our macro expansion code, and the associated __check.... functions it calls, to use an explicit closure and then stop using @autoclosure?

stmontgomery avatar Jul 17 '24 02:07 stmontgomery

That causes other compilation issues (I'd have to dig into the pre-OSS commit history to determine what, exactly, the problems were—maybe they're no longer relevant?)

grynspan avatar Jul 17 '24 11:07 grynspan

Thinking about it, I think it was that it forced evaluation of the RHS argument at capture time, defeating the purpose of a short-circuiting operator like &&.

grynspan avatar Jul 26 '24 23:07 grynspan

String(reflecting:) does not take move-only metatypes, so we cannot capture the FQN of a move-only type

This appears to be resolved in the latest toolchains.

grynspan avatar Aug 19 '24 20:08 grynspan

Tracked internally as rdar://131866222.

grynspan avatar Mar 12 '25 18:03 grynspan