feat(SwiftInterface): GenericSpecializer with concurrency-safe MachOCaches#86
feat(SwiftInterface): GenericSpecializer with concurrency-safe MachOCaches#86
Conversation
Direct generic params and nested member references were being conflated because `collectRequirements` matched on any `DependentGenericParamType` in the demangled tree, so `A.Element: P` was credited to A. Layout constraints were also dropped before reaching the requirement model because the top-level `hasKeyArgument` guard skipped them. Walk the demangled `DependentMemberType` chain to recover the full access path (e.g. `A.Element.Element`) plus per-step protocol context, and resolve the leaf metadata step by step through the runtime so chains deeper than one level produce the correct witness tables. Add tests for unconstrained, single/multi-protocol, layout, mixed, dual-chain, and multi-level associated-type scenarios.
Move the Test*Struct fixtures into GenericSpecializationTests so each type sits directly above the test that exercises it, and switch the descriptor lookup from an exact name match to a substring match so the nested-type qualified names continue to resolve.
The plan tracked work that has since landed; CLAUDE.md now references the feature directly, so the standalone plan is no longer load-bearing.
Six lightweight gaps surfaced by the Swift Runtime ABI diff: expose ~Copyable/~Escapable on Parameter, merge conditional invertible requirements, split combined nil-return branches, fail fast on generic candidates, accept MetadataRequest, drop dead convertLayoutKind. Out of scope: typePack/value generics, isPack/isValue requirement flags, validate substantive checks, PWT caching.
Six TDD tasks (drop dead helper, split nil-return arms, accept MetadataRequest, merge conditional invertible requirements, fail fast on generic candidates, surface invertibleProtocols on Parameter), each with bite-sized steps and a final commit.
Document that the parameter governs only the returned metadata's freshness; internal accessor calls (resolveCandidate, resolveAssociatedTypeStep) deliberately use fixed requests, matching swift_getGenericMetadata semantics.
Add private static `mergedRequirements(from:)` helper on GenericSpecializer
that combines `genericContext.allRequirements.flatMap { $0 }` with
`genericContext.conditionalInvertibleProtocolsRequirements`, then route the
three call sites in `buildParameters`, `buildAssociatedTypeRequirements`, and
`resolveAssociatedTypeWitnesses` through it. Behaviour-neutral: all existing
candidates remain Copyable+Escapable so conditional requirements always
evaluate active.
Add `isGeneric: Bool` to `SpecializationRequest.Candidate` (populated in `findCandidates` from the descriptor's generic-context flags) and a new `SpecializerError.candidateRequiresNestedSpecialization` case thrown by `resolveCandidate` when the selected candidate is itself generic, rather than letting the no-argument accessor call produce a generic failure.
Replace `first { $0.isGeneric }` / `first { !$0.isGeneric }` with
explicit lookups for Swift.Array (generic) and Swift.Int (non-generic)
so a regression of the fail-fast logic and a stdlib candidate-set shift
produce distinct, actionable failure messages.
Add `invertibleProtocols: InvertibleProtocolSet?` to `SpecializationRequest.Parameter` so callers can see whether a generic parameter declares `~Copyable` / `~Escapable`. The field is `nil` for ordinary parameters and non-nil (with the inverted-protocol bits set) for parameters that suppress a capability. Backed by a new `collectInvertibleProtocols` static helper in `GenericSpecializer` that walks `mergedRequirements`, matches on `.invertedProtocols` requirements by flat parameter index, and intersects duplicates. Includes a `TestInvertedCopyableStruct<A: ~Copyable>: ~Copyable` fixture and `invertedProtocolsExposed` test that verifies the field is populated, `.copyable` is set in the returned set (matching the binary's encoding where the set records suppressed protocols), and that specialization with `Int` succeeds via the conditional `Copyable` extension.
Bring the doc comment in line with the binary encoding (and with the existing `InvertibleProtocolSet.hasCopyable` / `dumpInvertedProtocolNames` convention in `SwiftDump`): set bits indicate which protocols the parameter *suppresses*, not which ones it preserves. `<A: ~Copyable>` produces a set *containing* `.copyable`.
`collectInvertibleProtocols` was intersecting `InvertibleProtocolSet`s when multiple `.invertedProtocols` requirements targeted the same flat parameter index. Per Swift IRGen (`GenMeta.cpp` `suppressed[index].insert(...)`), suppressions accumulate per parameter — a parent context's suppressions union with the current context's. Intersection would silently drop suppressions whenever parent and child encodings disagreed. Also tighten `invertedProtocolsExposed` from `contains(.copyable)` to `== .copyable` so extra bits leaking into the set fail the test.
…e error message Add two regression tests for the recent GenericSpecializer cleanup: - `noInvertedRequirementYieldsNil`: re-uses the existing `TestGenericStruct<A, B, C>` fixture (no `~Copyable` / `~Escapable` declarations) and asserts every parameter's `invertibleProtocols` is `nil`, locking in the documented "typical Swift case" path that the existing single- and multi-bit fixtures don't exercise. - `candidateErrorMessageMentionsSpecialized`: confirms that `candidateRequiresNestedSpecialization.errorDescription` mentions `Argument.specialized`, names the offending candidate, and uses the word "generic" — guards against silent rewording that would break any UI surfacing the localized description. A `~Copyable & ~Escapable` fixture for the multi-bit case was attempted but rolled back: Swift 6.2 rejects the implicit initializer for a `~Escapable` struct, and conditional `Copyable` / `Escapable` conformance can't cross-depend on the other invertible. The single-bit `invertedProtocolsExposed` test together with the union-vs-intersect fix in 0b0c03f covers the relevant code paths.
`GenericSpecializationTests` re-prepared one of three `SwiftInterfaceIndexer` shapes (local / +libswiftCore / +Foundation+libswiftCore) inside every `@Test` body — 18 hand-rolled copies of the same 2–4 line setup, each re-running `prepare()` from scratch. Hoist the setup into an `override init() async throws` that pulls the three shapes from a file-private `SharedIndexerCache` actor, which builds each shape at most once per process and returns it on subsequent test instances. `currentMachO`, `localIndexer`, `coreIndexer`, and `fullIndexer` become stored properties; test bodies reduce to one or two `let` aliases. `async let` runs the three preparations concurrently on the cold-start path so the first `init` waits on `max(prepare)` rather than the sum. After the first test populates the cache, every subsequent `init` is an immediate actor lookup. All 18 tests still pass.
Replace the file-private `SharedIndexerCache` actor and its three indexer shapes (local / core / full) with a single `fullIndexer` stored property constructed directly in `init`. Every test that previously used `localIndexer` or `coreIndexer` now reads `fullIndexer` — a strict superset that exposes the same types plus the unused-but-harmless extra sub-indexers. Each `@Test` now pays one full prepare per test instance instead of sharing across the suite, but the test setup is far simpler: 28 lines vs. 104 for the actor + cache + three shapes.
The 17 `allXxx` getters that fan out across sub-indexers were recomputing on every access. `IndexerConformanceProvider` treated `allAllTypeDefinitions` as a constant dictionary — iterating `allTypeNames` and calling `typeDefinition(for:)` / `imagePath(for:)` per element triggered a full recursive merge each time, turning candidate discovery into O(n²) in the combined type count of the indexer tree. With `GenericSpecializationTests` running against Foundation + libswiftCore + the test image, the suite took ~13 minutes; sampling showed `allAllTypeDefinitions.getter` self time alone exceeded 7 minutes. Each indexer now memoizes the merged result of its 17 aggregates in a single `@Mutex`-wrapped struct. Prepare / addSubIndexer / removeSubIndexer invalidate the cache by replacing the struct wholesale; lookups stay correct without per-field lock reasoning thanks to the macro's `_modify` accessor. After `prepare()` the indexer is treated as effectively read-only, which the file-level comment makes explicit. `GenericSpecializationTests` now runs in ~22s end-to-end (≈35× faster) and all 18 tests pass.
Sixteen test cases repeated the same descriptor lookup
(`try #require(try machO.swift.typeContextDescriptors.first { ... }?.struct)`),
half of them paired with a follow-up `genericContext` `#require`. The
boilerplate buried which fixture each `@Test` actually targeted.
`structDescriptor(named:)` and `genericStructFixture(named:)` collapse the
per-test setup to one line, so the fixture name is the most prominent thing
in each body. The line-78 `asPointerWrapper` form stays inline since it has
a different shape.
…lizer
Three nested-generic bugs (depth ≥ 2) are addressed in this commit.
Single-level nesting kept working only because parentParameters has
just one entry, so the buggy `flatMap.count` and the correct
`last?.count` happen to coincide.
P0.1 — `GenericContext.currentRequirements` used
`parentRequirements.flatMap { $0 }.count` to skip inherited entries.
Since `parentRequirements` itself is cumulative at every level (the
Swift compiler emits the full canonical signature; see
swift/lib/IRGen/GenMeta.cpp:7263 / 7342), summing the levels
double-counts inherited requirements at depth ≥ 2 and silently drops
the entries introduced at the current scope. Mirrors
`currentParameters` and now drops only `parentRequirements.last?.count`.
P0.2 — `GenericSpecializer.buildParameters` walked
`genericContext.allParameters.enumerated()` directly, treating each
outer-loop index as the parameter's depth. Parent levels in
`allParameters` are cumulative, however, so a three-level nesting
yields `[[A], [A, B], [C]]`; iterating it produces phantom entries
(`A` reappears at depth 1, `B` is renamed to canonical "B1") and
drops the demangler-canonical "A1" / "A2" mapping. Replaced with a
per-level new-count walk derived from `parentParameters` cumulative
diffs so each parameter is visited exactly once at its declaration
depth.
P0.3 — `collectInvertibleProtocols` derived the flat ordinal as
`allParameters.prefix(depth).reduce(+, count) + index`. The same
cumulative-parent over-count bug applied. The flat
`genericParamIndex` is now passed in directly by the caller (see
IRGen `getGenericParamOrdinal`).
Auxiliary cleanup: `mergedRequirements` is rebuilt from
`requirements + conditionalInvertibleProtocolsRequirements` instead
of re-flattening `allRequirements`, which would otherwise re-introduce
the same cumulative-parent doubling for downstream consumers.
GenericSpecializationTests grows four reproduction tests:
- `nestedTwoLevelBaseline` — verifies single-level nesting (the
accidental sweet spot) keeps passing.
- `nestedThreeLevelCurrentRequirementsLosesInner` — pins the P0.1
invariant directly on `currentRequirements`.
- `nestedThreeLevelMakeRequestProducesWrongParameters` — pins the
P0.2 canonical names ("A", "A1", "A2") and per-parameter
requirement counts.
- `nestedThreeLevelInvertedProtocolsPerLevel` — pins the P0.3 flat
ordinal recovery using `~Copyable` on every level.
Adds a deliberately deep fixture and unit tests that catch the P0
class of regressions in `GenericContext` derived properties. The
existing fixture coverage was structurally blind to these bugs:
- Every generic fixture in `GenericFieldLayout` /
`GenericRequirementVariants` was single-level (parentParameters is
empty), so `parentRequirements.flatMap.count` accidentally agreed
with `parentRequirements.last?.count`.
- `BaselineFixturePicker` never referenced the existing
`NestedGenerics.OuterGenericTest…` chain, so `nestedGenericsSnapshot`
was the only place that ever loaded a multi-level generic.
- `GenericContextBaselineGenerator` snapshots
`context.currentRequirements.count` as-is, which makes the unit
assertion `actual == baseline` self-validate against the buggy
implementation. Even adding a deeper fixture into the snapshot
workflow would not have caught the bug.
Changes:
* `NestedGenerics.NestedGenericThreeLevelConstraintTest<Outer:
Hashable>.MiddleConstrainedTest<Middle: Equatable>.InnerMostConstrainedTest<
InnerMost: Comparable>` — three levels, each constrained by a
different protocol, so every depth contributes one direct
conformance requirement to the cumulative signature.
* `BaselineFixturePicker.struct_NestedThreeLevelInnerMostConstrainedTest`
— locates the leaf `InnerMostConstrainedTest` descriptor by name so
fixture suites can address it without hardcoding offsets.
* `GenericContextTests` gains a `nestedThreeLevelContexts()` helper and
ten counter-example tests with **hardcoded** expected values that
do not flow through the baseline emitter:
nestedThreeLevelParametersIsCumulative
nestedThreeLevelRequirementsIsCumulative
nestedThreeLevelParentParametersAreCumulative
nestedThreeLevelParentRequirementsAreCumulative
nestedThreeLevelCurrentParametersHardcoded
nestedThreeLevelCurrentRequirementsHardcoded ← P0.1 regression guard
nestedThreeLevelFlatMapVsLastDivergence ← invariant guard
nestedThreeLevelUniqueCurrentRequirements
nestedThreeLevelAllParametersLevelCount
nestedThreeLevelAllRequirementsLevelCount
Mechanical regeneration after introducing
`NestedGenericThreeLevelConstraintTest` in SymbolTestsCore. The new
struct shifts every subsequent symbol's binary offset, so the offset
literals captured by RegenerateBaselinesPlugin in __Baseline__ all
drift by the same amount; no field semantics change.
- 59 `Tests/MachOSwiftSectionTests/Fixtures/__Baseline__/*Baseline.swift`
files have their `offset:` / `size:` literals refreshed via
`swift package --allow-writing-to-package-directory regen-baselines`.
- `nestedGenericsSnapshot.1.txt` and `interfaceSnapshot.1.txt`
re-recorded with the new struct's dump output. Both snapshots
exercise three-level nesting and serve as a live regression
artifact for the P0.2 canonical-naming fix — the new entries
correctly read `<A>` / `<A1>` / `<A2>` rather than the buggy
`<A1> / <B1>` cumulative-leak names.
dumpGenericParameters(isDumpCurrentLevel: false) iterated over allParameters, but parentParameters is stored cumulatively at every level. For ≥ 3-level nesting this re-emitted each inherited level — e.g. NestedGenericThreeLevelInner produced "<A, A1, B1, A2>" (Outer's A re-named A1, Middle's B mis-numbered B1) instead of the demangler-canonical "<A, A1, A2>". Walk per-level "newly introduced" slices via parentParameters cumulative diffs, mirroring GenericSpecializer.perLevelNewParameterCounts. This dump path is currently exercised only via tests, so the bug was latent in production callers. Documented behaviour now matches swift/lib/IRGen/GenMeta.cpp:7263 forEachParam ordering.
Four independent fixes surfaced by a Swift-source-cross-checked review plus their reproduction tests. Each test was confirmed to fail under the pre-fix code by temporarily reverting the corresponding change. P3 — makeRequest now rejects TypePack / Value parameters up front with a typed SpecializerError.unsupportedGenericParameter. The old guard silently skipped them in buildParameters, leaving request.parameters short of numKeyArguments and crashing the metadata accessor downstream. TypePack / Value generics remain explicitly out of scope. P6 — adds runtimePreflight(selection:for:) on MachOImage that surfaces protocol-conformance mismatches as protocolRequirementNotSatisfied and class-layout violations as layoutRequirementNotSatisfied before the accessor call. Previously these only emerged inside the runtime as an opaque witnessTableNotFound. validate() stays purely static and now documents the split. P7 — SpecializationRequest.CandidateOptions (OptionSet) lets callers ask for "directly specializable" candidates via .excludeGenerics, filtering out generic descriptors that would otherwise trip candidateRequiresNestedSpecialization deep inside specialize. P8 — buildAssociatedTypeRequirements aggregates requirements by (parameterName, path), matching the field's `requirements: [Requirement]` plural type. Three constraints on A.Element collapse from three single-element entries into one entry carrying all three, in canonical binary order. Plus a coverage test (nestedThreeLevelSpecializeEndToEnd) that drives the full request → specialize → metadata → fieldOffsets pipeline on a three-level nested fixture, closing the gap aa07d74 left between makeRequest assertions and the runtime accessor call.
Captures the full audit trail behind the previous two commits: - which findings were real bugs (P3, P5, P6, P8) and the failure mode each reproduction test catches when the fix is reverted; - which were rolled back as not-actually-bugs (P1 — Swift sema rejects the input; P4 — the 2-phase PWT walk is byte-equivalent to canonical order on current Swift), with the rationale and source-line citations; - the single-pass canonical PWT iteration sketched out in full so a future PR can switch to it when type-pack / value-generic support lands or Swift's compareDependentTypesRec changes; - API surface deltas and follow-up items. Lives next to docs/superpowers/plans/* / specs/* under the existing date-prefixed convention.
…document mode contracts Follow-up cleanup from the GenericSpecializer review (review report tracked in REVIEW_FIXUPS.md alongside this commit): - C2: drop SpecializationResult.fieldOffsets() / fieldOffsets(in:) — not the result type's responsibility; existing callers already go through StructMetadata.fieldOffsets() directly. - C5: remove never-thrown SpecializerError cases (invalidParameterIndex, requirementParsingFailed, missingGenericContext) and never-emitted SpecializationValidation.Error cases (sameTypeRequirementNotSatisfied, baseClassRequirementNotSatisfied, candidateResolutionFailed, associatedTypeResolutionFailed, requiresFurtherSpecialization, unknown) plus never-emitted Warning cases (potentialPerformanceIssue, deprecatedType). - C4: drop the redundant SpecializationSelection initializer overloads (variadic and unlabeled-dict forms); init(arguments:) and ExpressibleByDictionaryLiteral cover every existing call site. - C1: rewrite the mergedRequirements comment so it accurately reflects the conditional-invertible-section content. The conditional section can carry both .protocol and .invertedProtocols records (per GenMeta.cpp:1381 + the inverse_cannot_be_conditional_on_requirement diagnostic). Marker .protocol records are filtered by the hasKeyArgument check in collectRequirements, so merging stays safe. - C7: document IndexerConformanceProvider's "must prepare() first" contract. - C8: document GenericSpecializer's MachO-mode constraint — specialize, runtimePreflight, and resolveAssociatedTypeWitnesses require MachO == MachOImage. Also exclude the markdown notes from the SwiftInterface SPM target so the build no longer warns about unhandled resources.
…ss, ~Escapable, and remaining Argument cases Follow-up test additions from the GenericSpecializer review (see REVIEW_FIXUPS.md). Each addition pins a path that had been ostensibly supported but never exercised: - M10: makeRequestRejectsNonGenericType — the typed `notGenericType` error path on a non-generic struct fixture. - M8: validateEmitsExtraArgumentWarning — the `.extraArgument` warning branch of `validate(selection:for:)`, including the invariant that extras are warnings (not errors) and leave `isValid` unchanged. - M2a: argumentMetadataPathProducesSameMetadata — `.metadata(...)` resolves to the same generic-metadata cache slot as the equivalent `.metatype(...)` selection. - M2b: argumentCandidatePathSpecializesNonGenericCandidate — the success path through `.candidate(...)`, scoped to non-generic candidates via `.excludeGenerics`. - M2c: argumentSpecializedPathFeedsNestedSpecialization — `.specialized` feeds a previously-built `SpecializationResult` back into the specializer, producing distinct metadata for inner vs. outer parameterizations. - M3: nestedThreeLevelInvertedSpecializeEndToEnd — three-level nested `~Copyable` fixture (NestedInvertedInner) now goes through the full specialize pipeline, not just `makeRequest`. Layout matches the non-inverted three-level baseline (a@0, b@8, c@16). - M5: invertedEscapableExposed + invertedDualCopyableAndEscapable — empty-enum fixtures cover the `~Escapable` and `~Copyable & ~Escapable` corners of `InvertibleProtocolSet`. Empty-struct fixtures rejected by the frontend (synthesized init returns ~Escapable Self), so empty enum is the only way to get a generic-context descriptor with the dual flags. The dual case ships without conditional Copyable / Escapable extensions due to a toolchain issue producing malformed descriptors. - M1a/M1b: enumGenericMakeRequestAndSpecialize + classGenericMakeRequestAndSpecialize — the `TypeContextDescriptorWrapper.enum` and `.class` cases route through `makeRequest` and `specialize` without any struct-specific assumptions.
…y reporting Provides task_vm_info-based metrics (physical footprint, RSS, compressed, virtual size) and a formatted report. Used by IntegrationTests to surface memory cost during exploratory runs.
…le>/ Tests in Tests/IntegrationTests/ are exploratory dumps without assertions; keeping them under per-module subfolders (MachOSwiftSection, MachOSymbols, SwiftDump, SwiftInspection, SwiftInterface, TypeIndexing) makes the split between unit tests and manual-inspection tests obvious. Drop a few demangling/symbol dumps superseded by fixture-based coverage, and move the real LayoutTests unit test into Tests/MachOSwiftSectionTests/ where it belongs. CLAUDE.md records the convention so agents skip IntegrationTests in --skip runs.
The file was removed in d3f731a; the exclude entry has been silently dead since then and SwiftPM warns about the missing path.
…indings Second-pass review surfaced five latent issues, each reproduced by a unit test before being fixed: - runtimePreflight now validates .specialized arguments (the result already carries resolved metadata; no accessor needed). Mismatches surface as protocolRequirementNotSatisfied instead of vague witnessTableNotFound deep in specialize. - specialize asserts (metadatas + witnessTables) == numKeyArguments before invoking the accessor, converting opaque runtime crashes into a typed mismatch error attributable to the regression that introduced it. - runtimePreflight emits a protocolNotInIndexer warning when a required protocol's defining image isn't sub-indexed (was silently skipped, leaving callers with no signal that validation was a no-op). - validate distinguishes associated-type paths in the selection (e.g. "A.Element") from typo'd keys via a dedicated associatedTypePathInSelection warning. - extractAssociatedPath accepts bare .protocolSymbolicReference / .objectiveCProtocolSymbolicReference children; the Demangler emits these when its resolver returns nil, and the previous nil-fallthrough split two callers into inconsistent silent-drop vs typed-error paths. Also locks an ABI invariant via dualProtocolSameNamedAssociatedTypeIs- Canonicalized: GenericSignature minimization picks one canonical declaring protocol when two unrelated protocols share an associated-type name, justifying the (parameterName, [stepName]) aggregation key in buildAssociatedTypeRequirements.
… promise Previously `SharedCache.storage(in:buildUsing:)` ran the entire `build` closure inside `withLockUnchecked`, so a long build (e.g. `SymbolIndexStore.prepareWithProgress`) blocked every other key for its full duration. Replace the value map with an `Entry` enum (`completed` | `inFlight`) and a `SharedCacheBuildPromise` that lets the cache lock route a caller to either: return the finished value, attach to someone else's in-flight build, or install a fresh promise and run the build outside the lock. Concurrent builders for distinct keys now run in parallel; two builders for the same key share one execution. Also registers `MachOCachesTests` and temporarily disables the stale `MachOSymbolsTests` / `TypeIndexingTests` targets that no longer build under the current toolchain.
…and prune broken APIs `runtimePreflight` previously swallowed four `try?` sites silently, so a caller with a malformed metatype, a corrupt specialized result, or an unresolvable protocol descriptor saw "valid" preflight followed by an opaque accessor crash. Replace each with a typed error or warning: - `metadataResolutionFailed` (error) for `Metadata.createInProcess` and `SpecializationResult.metadata()` failures — `specialize` runs the same path, so the failure is guaranteed to surface. - `protocolDescriptorResolutionFailed` (error) for indexer entries that can't be materialized as a `Protocol`. - `conformanceCheckFailed` (warning) for `RuntimeFunctions.conformsToProtocol` throws — distinct from `protocolRequirementNotSatisfied` which is "ran the check, type doesn't conform." Document the `AssociatedTypeRequirementKey` invariant: omitting `protocolNode` is safe because the Swift compiler's RequirementMachine canonicalizes any `A.[P:Element]` / `A.[Q:Element]` collisions before emission (see MinimalConformances.cpp + compareDependentTypesRec). Delete the broken-by-design `SpecializationResult.valueWitnessTable(in:)` overload — specialized metadata is allocated by `swift_getGenericMetadata` into a runtime-owned heap that belongs to no MachO image, so the file-context overload would crash with SIGBUS. Harden the type's docs to reflect that constraint. Drop the unused `StandardLibraryConformanceProvider` that duplicated indexer logic with hard-coded conformance lists.
… suites and broaden coverage Split the monolithic 47-test file into 9 nested @suite groups (MakeRequest / Specialize / NestedGenerics / Validation / RuntimePreflight / Invariants / ErrorPaths / Models, plus the outer namespace) bound by an `Environment` protocol. The protocol's default implementations route every suite through a shared `static let sharedMachO = MachOImage.current()` and an actor-cached indexer, so per-suite setup runs once per process instead of per @test — overall runtime drops from ~82s to ~58s with 62 tests. Centralize fixture lookup in four helpers (`structDescriptor`, `inProcessStructDescriptor`, `enumDescriptor`, `classDescriptor`) so test bodies stop inlining `try machO.swift.typeContextDescriptors.first { ... }` ladders, removing 8 such duplications. Add coverage for the new typed preflight diagnostics (`metadataResolutionFailed`, `protocolDescriptorResolutionFailed`, `conformanceCheckFailed`), the deleted `valueWitnessTable(in:)` removal (only the no-arg accessor remains), `CompositeConformanceProvider` dedup behavior, the bare ObjC protocol symbolic-reference path, and a belt-and-suspenders pin on the upstream RequirementMachine canonicalization that justifies omitting `protocolNode` from `AssociatedTypeRequirementKey`.
There was a problem hiding this comment.
Code Review
This pull request refactors SharedCache to support concurrent builds using a promise-based mechanism and significantly cleans up the GenericSpecializer to improve handling of nested generic contexts, associated types, and invertible protocols. It also introduces memoization for aggregate properties in SwiftInterfaceIndexer. Review feedback identifies a critical thread-safety issue in the memoization logic where non-atomic updates to the cache struct could lead to lost data. Furthermore, performance improvements are recommended to reduce redundant demangling during requirement collection and to cache resolved metadata during associated type path traversal.
| public var allTypes: [MachOIndexedValue<MachO, TypeContextWrapper>] { | ||
| currentStorage.types.map { .init(machO: machO, value: $0) } + subIndexers.flatMap { $0.allTypes } | ||
| if let cached = allStorageCache.allTypes { return cached } | ||
| let result = currentStorage.types.map { MachOIndexedValue(machO: machO, value: $0) } + subIndexers.flatMap { $0.allTypes } | ||
| allStorageCache.allTypes = result | ||
| return result | ||
| } |
There was a problem hiding this comment.
The memoization logic in SwiftInterfaceIndexer is not thread-safe for atomic check-and-set operations. Since allStorageCache is a struct protected by @Mutex, accessing allStorageCache.allTypes and then setting it back involves multiple non-atomic lock/unlock cycles. If two threads compute different properties (e.g., one computes allTypes and another allProtocols) simultaneously, one update will overwrite the other because the whole struct is replaced. Additionally, copying the entire AllStorageCache struct on every access is inefficient.
Consider using withLockUnchecked (or the appropriate synchronization method provided by your @Mutex implementation) to perform the check and update atomically within a single lock acquisition. This applies to all memoized properties in this extension.
public var allTypes: [MachOIndexedValue<MachO, TypeContextWrapper>] {
_allStorageCache.withLockUnchecked { cache in
if let cached = cache.allTypes { return cached }
let result = currentStorage.types.map { MachOIndexedValue(machO: machO, value: $0) } + subIndexers.flatMap { $0.allTypes }
cache.allTypes = result
return result
}
}| let requirements = try collectRequirements( | ||
| for: paramName, | ||
| from: genericContext.allRequirements.flatMap { $0 }, | ||
| parameterIndex: index, | ||
| depth: depth | ||
| from: mergedRequirements | ||
| ) |
There was a problem hiding this comment.
collectRequirements is called for every generic parameter, and it iterates over all requirements, demangling each one every time. This results in
It is recommended to pre-process mergedRequirements once, demangling each requirement and grouping them by their target parameter name to ensure each requirement is demangled only once.
| for step in pathInfo.steps { | ||
| currentMetadata = try resolveAssociatedTypeStep( | ||
| currentMetadata: currentMetadata, | ||
| step: step, | ||
| allProtocolDefinitions: allProtocolDefinitions | ||
| ) |
There was a problem hiding this comment.
In resolveAssociatedTypeWitnesses, the associated type path is resolved for every requirement. If multiple requirements target the same associated type (e.g., A.Element: P and A.Element: Q), the same path is traversed and resolved multiple times. This involves redundant indexer lookups and expensive runtime calls (getAssociatedTypeWitness).
Implementing a local cache within this method to store and reuse resolved metadata for associated type paths would significantly improve efficiency.
There was a problem hiding this comment.
Pull request overview
Introduces a new GenericSpecializer workflow in SwiftInterface, improves concurrency behavior in MachOCaches by deduplicating in-flight shared-cache builds, and reorganizes tests (including moving environment-dependent tests into Tests/IntegrationTests/ and updating fixtures/snapshots/baselines).
Changes:
- Add promise-based in-flight deduplication to
MachOCaches.SharedCacheand add a newMachOCachesTeststarget validating concurrency behavior. - Memoize expensive “all*” aggregate properties in
SwiftInterfaceIndexerand invalidate them on preparation/sub-indexer mutations. - Expand nested-generic fixtures and update snapshots/baselines; move non-asserting/manual tests into
IntegrationTests.
Reviewed changes
Copilot reviewed 90 out of 113 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| Tests/SwiftInterfaceTests/Snapshots/Snapshots/SymbolTestsCoreInterfaceSnapshotTests/interfaceSnapshot.1.txt | Snapshot update to include new nested generic fixture declarations. |
| Tests/SwiftDumpTests/Snapshots/Snapshots/SymbolTestsCoreDumpSnapshotTests/nestedGenericsSnapshot.1.txt | Snapshot update reflecting corrected nested-generic dump output. |
| Tests/Projects/SymbolTests/SymbolTestsCore/NestedGenerics.swift | Adds a 3-level constrained nested generic fixture used for regression coverage. |
| Tests/MachOSymbolsTests/XcodeMachOFilesSymbolDemanglingTests.swift | Removes environment-dependent demangling test. |
| Tests/MachOSymbolsTests/DyldCacheSymbolDemanglingTests.swift | Removes environment-dependent demangling test. |
| Tests/MachOSymbolsTests/DemanglingTests.swift | Removes shared demangling test harness previously used by deleted tests. |
| Tests/MachOSwiftSectionTests/LayoutTests.swift | Adds new layout-offset assertions for descriptor layouts. |
| Tests/MachOSwiftSectionTests/Fixtures/Generic/GenericContextTests.swift | Adds hardcoded 3-level nested-generic regression guard tests. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/VTableDescriptorHeaderBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ValueTypeDescriptorWrapperBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/TypeReferenceBaseline.swift | Regenerated fixture baseline offsets/relatives. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/TypeMetadataRecordBaseline.swift | Regenerated fixture baseline offsets/relatives. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/TypeGenericContextDescriptorHeaderBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/TypeContextWrapperBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/TypeContextDescriptorWrapperBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/TypeContextDescriptorBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/StructDescriptorBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/StructBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/SingletonMetadataInitializationBaseline.swift | Regenerated fixture baseline offsets/relatives. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ResilientWitnessesHeaderBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ResilientWitnessBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ResilientSuperclassBaseline.swift | Regenerated fixture baseline offsets/relatives. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ProtocolWitnessTableBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ProtocolRequirementBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ProtocolRecordBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ProtocolDescriptorRefBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ProtocolDescriptorBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ProtocolConformanceDescriptorBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ProtocolConformanceBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ProtocolBaseRequirementBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ProtocolBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/OverrideTableHeaderBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ObjCResilientClassStubInfoBaseline.swift | Regenerated fixture baseline offsets/relatives. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ObjCProtocolPrefixBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/MultiPayloadEnumDescriptorBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ModuleContextDescriptorBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ModuleContextBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/MethodOverrideDescriptorBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/MethodDescriptorBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/GlobalActorReferenceBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/GenericValueHeaderBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/GenericValueDescriptorBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/GenericRequirementDescriptorBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/GenericRequirementBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/GenericParamDescriptorBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/GenericPackShapeHeaderBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/GenericPackShapeDescriptorBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/GenericContextDescriptorHeaderBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/GenericContextBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/FieldRecordBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/FieldDescriptorBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ExtensionContextDescriptorBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ExtensionContextBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/EnumDescriptorBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/EnumBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ContextWrapperBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ContextDescriptorWrapperBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ContextDescriptorBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ClassDescriptorBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/ClassBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/BuiltinTypeDescriptorBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/BuiltinTypeBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/AssociatedTypeRecordBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/AssociatedTypeDescriptorBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/AssociatedTypeBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/AnonymousContextDescriptorBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOSwiftSectionTests/Fixtures/Baseline/AnonymousContextBaseline.swift | Regenerated fixture baseline offsets. |
| Tests/MachOCachesTests/SharedCacheTests.swift | Adds concurrency-focused tests for SharedCache.resolve. |
| Tests/IntegrationTests/TypeIndexing/SwiftInterfaceParserTests.swift | Adds macOS-only integration test for parsing a SwiftUI swiftinterface file. |
| Tests/IntegrationTests/TypeIndexing/SourceKitManagerTests.swift | Adds macOS-only integration test for SourceKit-based interface retrieval. |
| Tests/IntegrationTests/TypeIndexing/SDKIndexerTests.swift | Adds macOS-only integration test for SDK indexing. |
| Tests/IntegrationTests/TypeIndexing/APINodesManagerTests.swift | Adds macOS-only integration test for API Notes indexing. |
| Tests/IntegrationTests/SymbolIndexStoreTests.swift | Removes prior integration test location/content. |
| Tests/IntegrationTests/SwiftInterface/SwiftInterfaceIndexerTests.swift | Adds integration test driving SwiftInterfaceIndexer on a dyld cache image. |
| Tests/IntegrationTests/SwiftInterface/SwiftInterfaceBuilderTests.swift | Adds integration tests to run the interface builder across MachO inputs. |
| Tests/IntegrationTests/SwiftInspection/MultiPayloadEnumTests.swift | Adds integration test for multi-payload enum analysis. |
| Tests/IntegrationTests/SwiftInspection/ClassHierarchyDumpTests.swift | Adds integration test dumping class hierarchies from system frameworks. |
| Tests/IntegrationTests/SwiftDump/XcodeMachOFileDumpTests.swift | Adds integration tests dumping types/protocols/etc from an Xcode Mach-O file. |
| Tests/IntegrationTests/SwiftDump/MachOImageDumpTests.swift | Adds integration tests dumping from a loaded Mach-O image. |
| Tests/IntegrationTests/SwiftDump/MachOFileDumpTests.swift | Adds integration tests dumping from a Mach-O file fixture. |
| Tests/IntegrationTests/SwiftDump/DyldCacheDumpTests.swift | Adds integration tests dumping from dyld shared cache contents. |
| Tests/IntegrationTests/MachOSymbols/SymbolIndexStoreTests.swift | Adds integration test for symbol index preparation + memory reporting. |
| Tests/IntegrationTests/MachOSymbols/MachOFileSymbolTests.swift | Adds integration tests measuring symbol indexing performance on fixtures. |
| Tests/IntegrationTests/MachOSymbols/ExternalSymbolTests.swift | Adds integration test enumerating external Swift symbols. |
| Tests/IntegrationTests/MachOSymbols/DyldCacheSymbolTests.swift | Adds shared integration helpers for collecting Swift symbols from dyld cache. |
| Tests/IntegrationTests/MachOSymbols/DyldCacheSymbolSimpleTests.swift | Adds macOS-only integration test for writing demangled symbols to disk. |
| Tests/IntegrationTests/MachOSwiftSection/ProtocolRequirementSignatureTests.swift | Adds integration test dumping protocol requirement kinds. |
| Tests/IntegrationTests/MachOSwiftSection/ProtocolGenericContextTests.swift | Adds integration test dumping protocol generic signatures. |
| Tests/IntegrationTests/MachOSwiftSection/PrimitiveTypeMappingTests.swift | Adds integration test dumping primitive type mapping. |
| Tests/IntegrationTests/MachOSwiftSection/OpaqueTypeTests.swift | Adds integration tests enumerating opaque type descriptors/requirements. |
| Tests/IntegrationTests/MachOSwiftSection/MetadataAccessorTests.swift | Removes a local enum fixture from an integration test file. |
| Tests/IntegrationTests/MachOSwiftSection/DyldCacheAssociatedTypeTests.swift | Adds integration test enumerating associated types from dyld cache. |
| Sources/SwiftInterface/SwiftInterfaceIndexer.swift | Adds memoization for expensive “all*” aggregations and cache invalidation points. |
| Sources/SwiftInterface/GenericSpecializer/REVIEW_FIXUPS.md | Adds review-tracking notes (excluded from SwiftPM target sources). |
| Sources/SwiftInterface/GenericSpecializer/Models/SpecializationValidation.swift | Refines diagnostics types (errors/warnings) and removes unused cases. |
| Sources/SwiftInterface/GenericSpecializer/Models/SpecializationSelection.swift | Tightens selection API by removing redundant convenience initializers. |
| Sources/SwiftInterface/GenericSpecializer/Models/SpecializationResult.swift | Clarifies in-process-only semantics and removes file-context accessors. |
| Sources/SwiftInterface/GenericSpecializer/Models/SpecializationRequest.swift | Adds candidate metadata (generic-ness) and candidate-generation options. |
| Sources/SwiftInterface/GenericSpecializer/IMPLEMENTATION_PLAN.md | Removes outdated implementation-plan document. |
| Sources/SwiftInterface/GenericSpecializer/ConformanceProvider.swift | Documents preparation contract for indexer-backed conformance provider; removes stdlib provider. |
| Sources/SwiftDump/Extensions/GenericContext+Dump.swift | Fixes nested-generic parameter dumping to avoid duplicates at depth ≥ 2. |
| Sources/MachOTestingSupport/ProcessMemory.swift | Moves a previously embedded test out of the library source file. |
| Sources/MachOSwiftSection/Models/Generic/GenericContext.swift | Fixes currentRequirements slicing for cumulative parent storage at depth ≥ 2. |
| Sources/MachOFixtureSupport/Baseline/BaselineFixturePicker.swift | Adds fixture picker for the new 3-level constrained nested generic struct. |
| Sources/MachOCaches/SharedCacheBuildPromise.swift | Adds an internal promise primitive used to deduplicate in-flight cache builds. |
| Sources/MachOCaches/SharedCache.swift | Reworks cache entries to support in-flight deduplication and parallel builds across keys. |
| Package.swift | Bumps swift-dyld-private, excludes review markdown from target, adds MachOCachesTests, and comments out some test targets. |
| docs/superpowers/specs/2026-05-02-generic-specializer-cleanup-design.md | Adds a design spec for GenericSpecializer cleanup/polish work. |
| docs/superpowers/reviews/2026-05-06-generic-specializer-bug-review.md | Adds a detailed review + bug-fix summary document. |
| docs/superpowers/plans/2026-05-02-generic-specializer-cleanup.md | Adds an execution plan document for the cleanup spec. |
| CLAUDE.md | Updates guidance to run tests with --skip IntegrationTests. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @Test func concurrentCallsForSameKeyShareOneBuild() { | ||
| let cache = TestCache() | ||
| let buildCount = OSAllocatedUnfairLock(initialState: 0) | ||
| let buildEnter = DispatchSemaphore(value: 0) |
| /// Concurrent calls for **different** keys must build in parallel, not | ||
| /// serialize behind one another. Verified by wall-clock: with the lock | ||
| /// held over the build, N keys × T per build = N*T; with the promise | ||
| /// fix, all N builds overlap, so wall-clock is ~T. | ||
| @Test func concurrentCallsForDifferentKeysRunInParallel() { | ||
| let cache = TestCache() | ||
| let keyCount = 8 | ||
| let perBuildSeconds: Double = 0.20 | ||
|
|
||
| let allDone = DispatchSemaphore(value: 0) | ||
| let start = ContinuousClock.now | ||
| for index in 0 ..< keyCount { | ||
| DispatchQueue.global().async { | ||
| _ = cache.resolve(key: AnyHashable(index)) { | ||
| Thread.sleep(forTimeInterval: perBuildSeconds) | ||
| return index | ||
| } | ||
| allDone.signal() | ||
| } | ||
| } | ||
| for _ in 0 ..< keyCount { allDone.wait() } | ||
| let elapsed = (ContinuousClock.now - start) | ||
| let elapsedSeconds = Double(elapsed.components.seconds) | ||
| + Double(elapsed.components.attoseconds) / 1e18 | ||
|
|
||
| // Allow generous slack for CI variance — the contract is "wall-clock | ||
| // is closer to one build than to N builds", not a precise multiplier. | ||
| // Serial would be ~keyCount * perBuildSeconds; we cap at half of | ||
| // that, which is still >2× the parallel ideal. | ||
| let serialCeiling = Double(keyCount) * perBuildSeconds | ||
| let parallelBudget = serialCeiling * 0.5 | ||
| #expect(elapsedSeconds < parallelBudget, | ||
| "elapsed=\(elapsedSeconds)s should be well below serial=\(serialCeiling)s") | ||
| } |
| // MARK: - EmptyConformanceProvider | ||
|
|
||
| /// Empty provider for testing or when no conformance info is available | ||
| public struct EmptyConformanceProvider: ConformanceProvider { | ||
| public init() {} | ||
|
|
||
| public func types(conformingTo protocolName: ProtocolName) -> [TypeName] { [] } | ||
| public func doesType(_ typeName: TypeName, conformTo protocolName: ProtocolName) -> Bool { false } | ||
| public func conformances(of typeName: TypeName) -> [ProtocolName] { [] } | ||
| public var allTypeNames: [TypeName] { [] } | ||
| public func typeDefinition(for typeName: TypeName) -> TypeDefinition? { nil } | ||
| public func imagePath(for typeName: TypeName) -> String? { nil } | ||
| } |
| @@ -10,16 +10,6 @@ public struct SpecializationSelection: Sendable { | |||
| self.arguments = arguments | |||
| } | |||
|
|
|||
| extension SpecializationValidation { | ||
| /// Validation error | ||
| public enum Error: Swift.Error, Sendable, CustomStringConvertible { | ||
| /// A required parameter is missing from the selection | ||
| case missingArgument(parameterName: String) | ||
|
|
||
| /// Selected type does not satisfy a protocol requirement | ||
| case protocolRequirementNotSatisfied( | ||
| parameterName: String, | ||
| protocolName: String, | ||
| actualType: String | ||
| ) | ||
|
|
||
| /// Selected type does not satisfy a same-type requirement | ||
| case sameTypeRequirementNotSatisfied( | ||
| parameterName: String, | ||
| expectedType: String, | ||
| actualType: String | ||
| ) | ||
|
|
||
| /// Selected type does not satisfy a base class requirement | ||
| case baseClassRequirementNotSatisfied( | ||
| parameterName: String, | ||
| expectedBaseClass: String, | ||
| actualType: String | ||
| ) | ||
|
|
||
| /// Selected type does not satisfy a layout requirement | ||
| case layoutRequirementNotSatisfied( | ||
| parameterName: String, | ||
| expectedLayout: SpecializationRequest.LayoutKind, | ||
| actualType: String | ||
| ) | ||
|
|
||
| /// Could not resolve candidate type to metadata | ||
| case candidateResolutionFailed( | ||
| /// Could not resolve metadata for the parameter — preflight | ||
| /// could not run conformance/layout checks. `specialize` runs | ||
| /// the same metadata resolution path, so the failure is | ||
| /// guaranteed to surface there as well; reporting it here lets | ||
| /// the caller see the diagnostic before the accessor call. | ||
| case metadataResolutionFailed(parameterName: String, reason: String) | ||
|
|
||
| /// Could not construct the protocol descriptor that a | ||
| /// requirement references — preflight could not run the | ||
| /// conformance check. Distinct from `protocolNotInIndexer` | ||
| /// (which is a warning): here the indexer *did* find the entry, | ||
| /// but materializing it as a `MachOSwiftSection.Protocol` failed. | ||
| case protocolDescriptorResolutionFailed( | ||
| parameterName: String, | ||
| candidateTypeName: String, | ||
| reason: String | ||
| ) | ||
|
|
||
| /// Associated type could not be resolved | ||
| case associatedTypeResolutionFailed( | ||
| parameterName: String, | ||
| associatedTypePath: [String], | ||
| protocolName: String, | ||
| reason: String | ||
| ) |
| // Plugins | ||
| .RegenerateBaselinesPlugin, | ||
|
|
||
| // Testing | ||
| .MachOSymbolsTests, | ||
| // .MachOSymbolsTests, | ||
| .MachOSwiftSectionTests, | ||
| .MachOCachesTests, | ||
| .SwiftInspectionTests, | ||
| .SwiftDumpTests, | ||
| .TypeIndexingTests, | ||
| // .TypeIndexingTests, | ||
| .SwiftInterfaceTests, | ||
| .MachOTestingSupportTests, | ||
| .IntegrationTests, |
Summary
SwiftInterfacefor specializing generic Swift types at runtime. Two-step flow (makeRequest()→specialize()) with typed diagnostics, fail-fast candidate validation, conditional invertible-protocol handling, multi-level associated types, and caller-suppliedMetadataRequest.SharedCacheBuildPromise), preventing redundant work when multiple consumers race on the same cache.Tests/IntegrationTests/<module>/soswift test --skip IntegrationTestscleanly runs the assertion-bearing suites; reorganizeGenericSpecializertests into nested suites and broaden coverage to enum/class,~Escapable, three-level nested generics, and remainingArgumentcases.NestedGenericsfixture and snapshot).swift-dyld-privateto 1.2.0; tightenGenericSpecializerAPI surface; addMachOTestingSupport.ProcessMemoryhelper; regenMachOSwiftSectionbaselines.Test plan
swift package updateswift buildswift test --skip IntegrationTestsswift test --filter SwiftInterfaceTests.GenericSpecializationTestsswift test --filter MachOCachesTestsswift test --filter MachOSwiftSectionCoverageInvariantTestsswift run swift-section interfaceagainst a representative binary