Skip to content

Make Test.Attachment generic. #814

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Nov 11, 2024
Merged

Make Test.Attachment generic. #814

merged 23 commits into from
Nov 11, 2024

Conversation

grynspan
Copy link
Contributor

@grynspan grynspan commented Nov 8, 2024

This PR makes Test.Attachment generic over its attachable value type. It gains conditional conformance to Copyable and Sendable depending on the attachable value and if you call attach() on a move-only or non-sendable attachment, will eagerly serialize the attachable value at that point (rather than during initialization.)

There are a few benefits here:

  1. Callers can statically know the type of the attachable value in an attachment rather than needing to always deal with an existential box;
  2. We can add associated types to Test.Attachable that will be readily accessible in withUnsafeBufferPointer(for:_:) again without needing an existential; and
  3. When we eventually add support for image attachments, we won't need a bunch of additional initializers or intermediate box types or what-have-you; and
  4. For Embedded Swift or other environments where existentials are problematic, we can eagerly serialize all attachments and pass a consistent type (Test.Attachment<[UInt8]> Test.Attachment<Test.AnyAttachable>) to the event handler.

There are also some drawbacks:

  1. Because conformance to Copyable and Sendable is conditional, we lose a bit of flexibility if you have a non-sendable Test.Attachment instance or whatnot;

  2. We still need a lazy, type-erased attachment type that can be passed to the event handler. I played around with Test.Attachment<Any> but that causes as many problems as it solves.

    We end up with Test.Attachment<any Test.Attachable & Sendable & Copyable> but, because that's an existential type that doesn't conform to itself, the generic parameter AttachableValue is not constrained to Test.Attachable. We only provide initializers for types that do conform though (plus the existential one internally) so in practice it's not a huge issue.

    I replaced any Test.Attachable & Sendable & Copyable with a dedicated type-erasing box type, Test.AnyAttachable, that wraps the existential. In Embedded Swift, it wraps a [UInt8] instead.

  3. There is some code duplication necessary (i.e. multiple implementations of attach() and write().)

  4. Non-final classes cannot conform to Test.Attachable. You can work around this with a box type that's generic over the class and conforms to Test.Attachable: the Test.AttachableContainer protocol now exists to make this easier.

Checklist:

  • Code and documentation should follow the style of the Style Guide.
  • If public symbols are renamed or modified, DocC references should be updated.

@grynspan grynspan added enhancement New feature or request attachments/activities 🖇️ Work related to attachments and/or activities labels Nov 8, 2024
@grynspan grynspan self-assigned this Nov 8, 2024
@grynspan grynspan added the embedded-swift 📟 Embedded Swift issues label Nov 8, 2024
@grynspan
Copy link
Contributor Author

grynspan commented Nov 8, 2024

@swift-ci test

@grynspan grynspan added this to the Swift 6.1 milestone Nov 8, 2024
@grynspan
Copy link
Contributor Author

grynspan commented Nov 8, 2024

@swift-ci test

1 similar comment
@grynspan
Copy link
Contributor Author

grynspan commented Nov 8, 2024

@swift-ci test

_attachableValue
}
#endif
public var attachableValue: AttachableValue

/// The path to which the this attachment was written, if any.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// The path to which the this attachment was written, if any.
/// The path to which the attachment was written, if any.

@grynspan
Copy link
Contributor Author

grynspan commented Nov 8, 2024

@swift-ci test

1 similar comment
@grynspan
Copy link
Contributor Author

grynspan commented Nov 8, 2024

@swift-ci test

@grynspan grynspan requested a review from plemarquand November 9, 2024 16:36
@grynspan
Copy link
Contributor Author

@swift-ci test

5 similar comments
@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan
Copy link
Contributor Author

@swift-ci test Windows

This PR makes `Test.Attachment` generic over its attachable value type. It gains
conditional conformance to `Copyable` and `Sendable` depending on the attachable
value and if you call `attach()` on a move-only or non-sendable attachment, will
eagerly serialize the attachable value at that point (rather than during
initialization.)

There are a few benefits here:

1. Callers can statically know the type of the attachable value in an attachment
   rather than needing to always deal with an existential box;
2. We can add associated types to `Test.Attachable` that will be readily
   accessible in `withUnsafeBufferPointer(for:_:)` again without needing an
   existential; and
3. When we eventually add support for image attachments, we won't need a bunch
   of additional initializers or intermediate box types or what-have-you; and
4. For Embedded Swift or other environments where existentials are problematic,
   we can eagerly serialize all attachments and pass a consistent type
   (`Test.Attachment<[UInt8]>`) to the event handler.

There are also some drawbacks:

1. Because conformance to `Copyable` and `Sendable` is conditional, we lose a
   bit of flexibility if you have a non-sendable `Test.Attachment` instance or
   whatnot;
2. We still need a lazy, type-erased attachment type that can be passed to the
   event handler. I played around with `Test.Attachment<Any>` but that causes as
   many problems as it solves.

   We end up with `Test.Attachment<any Test.Attachable & Sendable & Copyable>`
   but, because that's an existential type that doesn't conform to itself, the
   generic parameter `AttachableValue` is not constrained to `Test.Attachable`.
   We only provide initializers for types that do conform though (plus the
   existential one internally) so in practice it's not a huge issue.
3. There is some code duplication necessary (i.e. multiple implementations of
   `attach()` and `write()`.)
@grynspan grynspan force-pushed the jgrynspan/generic-attachment branch from 6d28bcc to ec9542d Compare November 11, 2024 15:56
@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan grynspan merged commit 35f2618 into main Nov 11, 2024
3 checks passed
@grynspan grynspan deleted the jgrynspan/generic-attachment branch November 11, 2024 21:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
attachments/activities 🖇️ Work related to attachments and/or activities embedded-swift 📟 Embedded Swift issues enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants