Skip to content

Remove blanket retroactive KeyPath: Sendable conformance - #19

Merged
rozd merged 1 commit into
mainfrom
fix/remove-retroactive-keypath-sendable
Aug 14, 2026
Merged

Remove blanket retroactive KeyPath: Sendable conformance#19
rozd merged 1 commit into
mainfrom
fix/remove-retroactive-keypath-sendable

Conversation

@rozd

@rozd rozd commented Aug 14, 2026

Copy link
Copy Markdown
Owner

Ports BeaNuman/theme-kit#1 by @fdnmgh.

Problem

ThemeKit shipped extension KeyPath: @retroactive @unchecked Sendable, a global conformance on a std-lib type ThemeKit does not own. Swift has no module-scoped conformances, so it becomes law for every consumer once linked — and it collides with libraries that manage key-path sendability themselves. Notably TCA deliberately avoids such a conformance and uses scoped _Sendable*KeyPath existentials instead; the collision surfaces as a build failure in TCA-using code:

Type 'WritableKeyPath<Root, BindingState<Value>>' does not conform to the 'Sendable' protocol

Fix

The conformance only existed so the generated ThemeShapeStyle / ThemeShadowedStyle structs — which store a KeyPath<Theme, ...> and conform to ShapeStyle: Sendable — could synthesize Sendable. Those structs now declare @unchecked Sendable directly. The safety argument stays local and self-contained: the stored key path is read-only over the Sendable Theme type.

The per-property nonisolated on the key-path storage (added in 5bf9280) becomes redundant once the struct asserts sendability itself, and is dropped so the generated code builds under MainActor default isolation.

Sources/ThemeKit/KeyPath+Sendable.swift and its test are removed.

Tests

The deleted KeyPathSendableTests verified sendability at the std-lib layer, which no longer exists. Coverage moves to where it now matters:

  • ThemeFileGeneratorTests — assert the emitted declarations carry @unchecked Sendable and no longer carry nonisolated storage.
  • GeneratedCodeCompilationTests — compile-time Sendable requirement and a cross-actor hop against real generated ThemeShapeStyle / ThemeShadowedStyle values, in all four language-mode × default-isolation fixture targets (Swift 5/6 × nonisolated/MainActor).

swift build and all 220 tests pass locally.

🤖 Generated with Claude Code

ThemeKit shipped `extension KeyPath: @retroactive @unchecked Sendable`,
a global conformance on a std-lib type ThemeKit does not own. It leaks
into every consumer and collides with libraries that manage key-path
sendability themselves — notably TCA, which deliberately avoids such a
conformance and uses scoped `_Sendable*KeyPath` existentials instead.
The collision surfaces as a build failure in TCA-using code:

    Type 'WritableKeyPath<Root, BindingState<Value>>' does not conform
    to the 'Sendable' protocol

The conformance only existed to let the generated ThemeShadowedStyle /
ThemeShapeStyle structs (which store a KeyPath<Theme, ...> and conform
to ShapeStyle: Sendable) synthesize Sendable. Make those generated
structs `@unchecked Sendable` instead — sendability is asserted locally
and self-contained, since the stored key path is read-only over the
Sendable Theme type. Drop the now-redundant per-property `nonisolated`
on the key-path storage so it builds under MainActor default isolation.

Removes KeyPath+Sendable.swift and its test. The deleted test verified
sendability at the std-lib layer; replace it with coverage where it now
matters: generator assertions on the emitted declarations, plus
compile-time Sendable and cross-actor checks against the generated
styles in all four language-mode/isolation fixture targets.

Co-Authored-By: Foti Dim <foti.dimanidis@numan.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Aug 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.92%. Comparing base (785c83b) to head (0765168).

Additional details and impacted files
@@           Coverage Diff           @@
##             main      #19   +/-   ##
=======================================
  Coverage   98.91%   98.92%           
=======================================
  Files          36       35    -1     
  Lines        2213     2229   +16     
=======================================
+ Hits         2189     2205   +16     
  Misses         24       24           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rozd
rozd merged commit 2759540 into main Aug 14, 2026
3 checks passed
@rozd
rozd deleted the fix/remove-retroactive-keypath-sendable branch August 14, 2026 18:09
rozd added a commit that referenced this pull request Aug 14, 2026
Brings in the retroactive-KeyPath removal (#19) ahead of the close-out merge,
so the conflicts resolve here rather than in the umbrella PR.

Both changes edit the same two generator templates for different reasons —
main swaps the generated styles from synthesized `Sendable` (which needed the
now-deleted blanket `KeyPath: Sendable` conformance) to `@unchecked Sendable`,
while this branch splits each template into Apple and Android variants. Git
only flagged the Android arm, because main's single hunk matched the first of
the two copies; the Apple arm merged silently and still carried the old
spelling. Applied main's `@unchecked Sendable` and dropped the per-property
`nonisolated` in *both* arms, and updated the two branch-side assertions that
pinned the old declarations.

`Sources/ThemeKit/KeyPath+Sendable.swift` is gone; nothing on this branch
referenced it outside the generated styles it existed to serve.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant