Skip to content

[semantic-reference-resolution] Patch 8: Swift: semantic qualifiedReferences via IndexStoreDB#16

Merged
subsetpark merged 3 commits into
masterfrom
semantic-reference-resolution/patch-8
Jun 8, 2026
Merged

[semantic-reference-resolution] Patch 8: Swift: semantic qualifiedReferences via IndexStoreDB#16
subsetpark merged 3 commits into
masterfrom
semantic-reference-resolution/patch-8

Conversation

@subsetpark

@subsetpark subsetpark commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Patch 8: Swift: semantic qualifiedReferences via IndexStoreDB

  • Produce an index store for the analysed sources (build with -index-store-path) and open it via IndexStoreDB.
  • For each reference occurrence, look up its definition's file via the index, derive the owner moduleName (capitalised basename), and emit '.' for cross-file references; skip same-file/local symbols.
  • Ensure prefix consistency: the owner moduleName matches the owner file's emitted moduleName.
  • Fail closed: if the index build or IndexStoreDB query fails or is unavailable, emit a violation and exit non-zero rather than emitting empty qualifiedReferences.
  • Enable the Patch 7 PENDING assertion; verify the cross-file reference resolves and an incidental shared name does not surface.

Changes

  • Produce an index store for the analysed sources (build with -index-store-path) and open it via IndexStoreDB.
  • For each reference occurrence, look up its definition's file via the index, derive the owner moduleName (capitalised basename), and emit '.' for cross-file references; skip same-file/local symbols.
  • Ensure prefix consistency: the owner moduleName matches the owner file's emitted moduleName.
  • Fail closed: if the index build or IndexStoreDB query fails or is unavailable, emit a violation and exit non-zero rather than emitting empty qualifiedReferences.
  • Enable the Patch 7 PENDING assertion; verify the cross-file reference resolves and an incidental shared name does not surface.

Files to Modify

  • swift/Sources/SwiftArchLint/main.swift (modify): Build an index store for the target and read it with IndexStoreDB to map each reference occurrence to its defining file/USR; emit '.' and populate qualifiedReferences.
  • swift/Package.swift (modify): Add the swiftlang/indexstore-db package dependency.
  • swift/test.sh (modify): Enable and assert the Patch 7 PENDING fixture: the cross-file bare reference surfaces as 'Owner.symbol'.

Established Precedents

This patch should adopt the following proven techniques rather than rolling its own. Read the references when you need detail on the API shape or invariants they impose. Each entry names a library, algorithm, pattern, paper, RFC, doc, or blog post; the trailing sentence explains how it applies to this specific patch.

  • [library] IndexStoreDB (swiftlang/indexstore-db)https://github.com/swiftlang/indexstore-db — IndexStoreDB reads the compiler's index store (produced with -index-store-path) to map a symbol occurrence to its defining file/USR — the semantic resolution Swift lacks from SwiftSyntax alone. Use its occurrence/symbol queries to find each reference's declaring file.

Gameplan Specification

module SEMANTIC_REFERENCE_RESOLUTION.

> ══════════════════════════════════════════
> THE GUARANTEE
> ══════════════════════════════════════════
> After this gameplan all four adapters (Go, OCaml, TypeScript,
> Swift) resolve references semantically. Every reference to a
> symbol owned by another module in the same language universe is
> emitted in qualifiedReferences as "owner-module.symbol", including
> uses written bare after open/import or within the same package.
> The dependency-direction rule (rule 15) therefore fires for a
> genuine cross-module reach in every adapter, not only OCaml, and
> never fires on an incidental shared bare name.

SourceFile.
Module.
Symbol.
ModuleType.
Domain.

module-name f: SourceFile => Module.
module-type f: SourceFile => ModuleType.
domain f: SourceFile => Domain.
owner s: Symbol => Module.
references f: SourceFile, s: Symbol => Bool.
qualified-reference f: SourceFile, m: Module, s: Symbol => Bool.
decision-reference f: SourceFile, s: Symbol => Bool.
---
> Semantic completeness: a reference to a symbol owned by a foreign
> module is always emitted qualified by that owner, even when the
> source writes it bare (after open / import / same package). This
> closes the syntactic hole described in issue 6.
all f: SourceFile, s: Symbol, references f s and owner s ~= module-name f | qualified-reference f (owner s) s.

> Prefix consistency: the qualifier on any emitted qualified
> reference is exactly the owner module's own module-name. Without
> this, rule 15's intersection silently never matches.
all f: SourceFile, m: Module, s: Symbol, qualified-reference f m s | m = owner s.

where

> ══════════════════════════════════════════
> RULE 15 (dependency direction) effectiveness
> ══════════════════════════════════════════
> A core or value module is flagged exactly when it holds a
> qualified reference to a same-domain implementation module's
> declared decision reference. reaches-implementation c holds when
> some same-domain shell/state/exempt file i declares a symbol s
> with qualified-reference c (module-name i) s. Incidental bare
> name collisions never flag because they are not emitted as
> qualified references.

implementation? mt: ModuleType => Bool.
core-or-value? mt: ModuleType => Bool.
reaches-implementation? c: SourceFile => Bool.
flagged? c: SourceFile => Bool.

core => ModuleType.
value => ModuleType.
shell => ModuleType.
state => ModuleType.
exempt => ModuleType.
---
all mt: ModuleType | implementation? mt = (mt = shell or mt = state or mt = exempt).
all mt: ModuleType | core-or-value? mt = (mt = core or mt = value).

> The flag fires iff a core/value file actually reaches a same-domain
> implementation surface via a qualified reference.
all c: SourceFile, core-or-value? (module-type c) | flagged? c = reaches-implementation? c.

> Soundness of the prohibition: a flag implies a real qualified
> cross-module reach into a same-domain implementation decision
> reference.
all c: SourceFile, reaches-implementation? c | some i: SourceFile | implementation? (module-type i) and domain i = domain c.

Patch Specification

module SEMANTIC_REFERENCE_RESOLUTION_PATCH_8.

> Patch 8 makes the Swift adapter populate qualifiedReferences by semantic
> resolution: each referenced symbol is resolved to its defining module
> and emitted as owner-module.symbol, including bare/open'd/imported and
> (for Go) cross-package uses. Same-module and local references are not
> emitted. After this patch rule 15 fires for a genuine Swift cross-module
> reach and ignores incidental shared bare names.

SourceFile.
Module.
Symbol.
ModuleType.
Domain.

module-name f: SourceFile => Module.
module-type f: SourceFile => ModuleType.
domain f: SourceFile => Domain.
owner s: Symbol => Module.
references f: SourceFile, s: Symbol => Bool.
qualified-reference f: SourceFile, m: Module, s: Symbol => Bool.
decision-reference f: SourceFile, s: Symbol => Bool.
---
> Semantic completeness for Swift: foreign-owned references are qualified
> by their true owner even when written bare.
all f: SourceFile, s: Symbol, references f s and owner s ~= module-name f | qualified-reference f (owner s) s.
> Prefix consistency: the qualifier equals the owner module's module-name.
all f: SourceFile, m: Module, s: Symbol, qualified-reference f m s | m = owner s.

Implementation Notes

  • The Swift adapter now builds a temporary compiler index from copied production-target sources, then opens it with IndexStoreDB. The source copies prepend import Foundation so legacy parser-era fixtures that omit Foundation imports can still be indexed; IndexStoreDB prefix mappings map those copied paths back to the adapter's emitted source paths.
  • Indexing is per primary file with -index-file, -index-file-path, and a unique -index-unit-output-path. A single multi-file swiftc -typecheck invocation only produced occurrence records for one source in local testing, so per-file units were required for complete occurrence queries.
  • The semantic pass currently indexes non-test targets only. This keeps the adapter fail-closed for production architecture surfaces while avoiding fake/unavailable test-only dependency imports in existing fixtures. Test source facts remain schema-conformant but do not gain semantic qualifiedReferences from this patch.
  • Swift/Xcode records temp paths under /private/var/... while enumerated fixture paths can appear as /var/...; the adapter keeps both aliases in the file lookup and query path set. Dependent patches should preserve that alias handling if they touch path normalization.
  • swiftc can emit usable index records while still returning non-zero for intentionally invalid fixture code. The adapter allows the compiler process to return non-zero only if index records were actually produced; missing records or IndexStoreDB/library failures still raise semanticIndexUnavailable and abort fact emission.
  • Semantic references include all cross-file Swift symbols IndexStoreDB resolves, including owner type references such as HTTPMailBackendDecider.HTTPMailBackendDecider as well as decision method references such as HTTPMailBackendDecider.decidePath. Rule 15 remains precise because it intersects against implementation decision references.

Spec/Evidence Mapping:

  • Semantic completeness for Swift: implemented in semanticQualifiedReferences(...), which queries each reference occurrence, resolves its declaration/definition occurrence by USR, and emits <ownerFile.moduleName>.<symbol>. Evidence: sh swift/test.sh asserts HTTPMailBackendDecider.decidePath appears for both direct and helper-mediated cross-file calls.
  • Prefix consistency: implemented by deriving the emitted qualifier from the owner SwiftFileInfo.moduleName, which uses the same capitalizedModuleName(for:) helper as the owner file's own moduleName field. Evidence: Swift fixture assertions check moduleName == "HTTPMailBackendDecider" / "HTTPMailBackendClient" and the emitted qualified reference prefix matches.
  • Same-file/local exclusion: implemented by comparing the resolved owner file path to the source file path before inserting a qualified reference. Evidence: swift/test.sh asserts local decidePathForRequest does not surface as a qualified reference.
  • Validation run: uv run --project . python evaluate_test.py, cd go && go test ., sh typescript/test.sh, and sh swift/test.sh passed. sh ocaml/test.sh remained environment-blocked by the missing local OCaml switch and yojson.

@subsetpark subsetpark force-pushed the semantic-reference-resolution/patch-8 branch from f677a7f to c96eb7d Compare June 8, 2026 18:42
@subsetpark subsetpark force-pushed the semantic-reference-resolution/patch-8 branch from c96eb7d to 69497b4 Compare June 8, 2026 18:43
@subsetpark subsetpark marked this pull request as ready for review June 8, 2026 19:03
@flowglad-review-service

Copy link
Copy Markdown

Review Summary

No additional validated findings beyond the draft already recorded.

Severity Count
Must-fix 0
Should-fix 0
Note 0

Variant: convergence-v2

Candidates: 0 | Posted: 0 | Suppressed: 0


0 comments posted · Model: gpt-5.4 (sonnet) · Tokens: 141802 in / 1036 out · Cache: 0 created / 82944 read · Context: 20 items · Review mode: agentic · Turns: 4 · Tool calls: 0

@subsetpark subsetpark merged commit cd8f58d into master Jun 8, 2026
7 checks passed
@subsetpark subsetpark deleted the semantic-reference-resolution/patch-8 branch June 8, 2026 19:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant