Skip to content

feat(SwiftInterface): GenericSpecializer with concurrency-safe MachOCaches#86

Merged
Mx-Iris merged 36 commits intomainfrom
feature/generic-specializer
May 8, 2026
Merged

feat(SwiftInterface): GenericSpecializer with concurrency-safe MachOCaches#86
Mx-Iris merged 36 commits intomainfrom
feature/generic-specializer

Conversation

@Mx-Iris
Copy link
Copy Markdown
Member

@Mx-Iris Mx-Iris commented May 8, 2026

Summary

  • GenericSpecializer: new interactive API in SwiftInterface for 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-supplied MetadataRequest.
  • MachOCaches: de-duplicate concurrent shared-cache builds via an in-flight promise (SharedCacheBuildPromise), preventing redundant work when multiple consumers race on the same cache.
  • Test layout: relocate non-asserting / runtime-environment-dependent tests under Tests/IntegrationTests/<module>/ so swift test --skip IntegrationTests cleanly runs the assertion-bearing suites; reorganize GenericSpecializer tests into nested suites and broaden coverage to enum/class, ~Escapable, three-level nested generics, and remaining Argument cases.
  • SwiftDump: correct cumulative-parent dump for nested generics (paired with new NestedGenerics fixture and snapshot).
  • Misc: bump swift-dyld-private to 1.2.0; tighten GenericSpecializer API surface; add MachOTestingSupport.ProcessMemory helper; regen MachOSwiftSection baselines.

Test plan

  • swift package update
  • swift build
  • swift test --skip IntegrationTests
  • swift test --filter SwiftInterfaceTests.GenericSpecializationTests
  • swift test --filter MachOCachesTests
  • swift test --filter MachOSwiftSectionCoverageInvariantTests
  • Spot-check swift run swift-section interface against a representative binary

Mx-Iris added 30 commits May 5, 2026 17:31
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.
Mx-Iris added 6 commits May 6, 2026 20:01
…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`.
Copilot AI review requested due to automatic review settings May 8, 2026 07:23
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

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.

Comment on lines 803 to 808
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
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

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
        }
    }

Comment on lines 225 to 228
let requirements = try collectRequirements(
for: paramName,
from: genericContext.allRequirements.flatMap { $0 },
parameterIndex: index,
depth: depth
from: mergedRequirements
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

collectRequirements is called for every generic parameter, and it iterates over all requirements, demangling each one every time. This results in $O(P \times R)$ demangling operations, where $P$ is the number of parameters and $R$ is the number of requirements. Since demangling is an expensive operation, this significantly impacts performance during specialization request creation.

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.

Comment on lines +1087 to +1092
for step in pathInfo.steps {
currentMetadata = try resolveAssociatedTypeStep(
currentMetadata: currentMetadata,
step: step,
allProtocolDefinitions: allProtocolDefinitions
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

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.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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.SharedCache and add a new MachOCachesTests target validating concurrency behavior.
  • Memoize expensive “all*” aggregate properties in SwiftInterfaceIndexer and 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.

Comment on lines +34 to +37
@Test func concurrentCallsForSameKeyShareOneBuild() {
let cache = TestCache()
let buildCount = OSAllocatedUnfairLock(initialState: 0)
let buildEnter = DispatchSemaphore(value: 0)
Comment on lines +88 to +121
/// 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")
}
Comment on lines 180 to 192
// 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
}

Comment on lines 36 to 72
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
)
Comment thread Package.swift
Comment on lines 733 to 745
// Plugins
.RegenerateBaselinesPlugin,

// Testing
.MachOSymbolsTests,
// .MachOSymbolsTests,
.MachOSwiftSectionTests,
.MachOCachesTests,
.SwiftInspectionTests,
.SwiftDumpTests,
.TypeIndexingTests,
// .TypeIndexingTests,
.SwiftInterfaceTests,
.MachOTestingSupportTests,
.IntegrationTests,
@Mx-Iris Mx-Iris merged commit 54b1077 into main May 8, 2026
6 checks passed
@Mx-Iris Mx-Iris deleted the feature/generic-specializer branch May 8, 2026 08:11
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.

2 participants