Make libclang fallback symbol keys collision-safe across translation units #55
Labels
No labels
area/cli
area/core
area/database
area/indexing
area/query
type/epic
type/feature
type/foundation
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
JayKobe6k/ClariTree#55
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem and impact
Full-corpus indexing now completes semantic analysis inside the issue #53 memory bound, but production source-fact mapping rejects the pinned 1,103-TU corpus with
ConflictingCanonicalSymbol("fallback:FieldDecl:src/melee/ft/types.h:48867"). Staging, checkpoint, read-only reopen, and publication therefore never run. This independently blocks the complete workflow and issue #53 closure.Do not weaken or suppress canonical-symbol conflict validation: two observations assigned one key but carrying incompatible canonical metadata would otherwise be silently merged.
Environment and reproduction
51a57a890d2e1c06ebb3964b71227800a40fa2c4melee-event-match6b6f8a8a987c2e871b8b96baa863aaa99e406f33compile_commands.jsonSHA-256:0c025066b2f5ed7940a668d4fc97b912d8eb5d467fb7bb33ba0e72ab7d50ea16MemoryMax=32G,MemorySwapMax=0,RuntimeMaxSec=7200CLARITREE_PROFILE_PERSISTENCE=1, 4,096 MiB LadybugDB buffer poolBuild and run the ignored
profile_lineagerelease test through Nix withCLARITREE_PROFILE_PROJECTpointing to the pinned corpus andCLARITREE_PROFILE_LIMIT=1103. The current memory-remediation candidate is not required to explain the collision: it changes identity storage and analyzer indexes, not fallback symbol construction or persistence canonicalization. Earlier baseline runs died before mapping and therefore could not expose this later failure.Baseline evidence
The final bounded run completed semantic analysis with 4,224,058 definitions, 4,973,123 uses, and 9,551,602 relationships, then emitted:
The process exited 101 from the typed mapping error and was not OOM-killed. The sanitized local artifact is
target/issue53-shared-scopes-full.log; its measured facts and hashes are preserved inannals/2026-09-12-borrowed-dense-analysis-indexes.md.The referenced byte offset is at the anonymous union beginning near
src/melee/ft/types.h:1586, containingthrow_flagsand an anonymous bit-field struct.Source-backed root-cause boundary
At
symbol_key, a cursor without a usable USR receives a fallback key containing only entity kind, portable path, and spelling start byte offset. Atmap_symbols, observations with the same key are canonicalized project-wide.merge_canonical_symbolcorrectly rejects equal keys whose ID, key, name, kind, or linkage differs.The verified failure therefore proves that the fallback key is not unique enough for these observations, or that metadata expected to be canonical varies across compilation contexts. The exact disagreeing fields and translation units have not yet been captured, so the precise parser remedy remains to be measured rather than guessed.
Proposed implementation
Alternatives rejected: ignoring the conflict, selecting the first observation, or weakening metadata comparison can silently conflate distinct symbols; including compilation fingerprint unconditionally would prevent legitimate cross-TU canonicalization.
Expected benefit, risks, and validation
Expected benefit is restoring full-corpus publication after issue #53 creates sufficient memory headroom. Confidence is high in the collision boundary and medium in the final discriminator until both observations are captured. Risks are fragmenting one real symbol across TUs, making keys traversal-dependent, or changing search/lineage handles.
Acceptance criteria:
Rollback: revert the fallback-key change if canonical handles become unstable or same-declaration observations stop merging; retain the diagnostic and strict conflict test for the next design.
Investigation update (validated, uncommitted):
The conflicting observations are the same implicit anonymous-record
FieldDecl, not two declarations that need different fallback keys. libclang generated eitherunion Fighter::(anonymous at src/melee/ft/types.h:1586:19)or the equivalent absolute-path spelling while both observations retainedfallback:FieldDecl:src/melee/ft/types.h:48867. The unstable path is presentation metadata. Strictmerge_canonical_symbolvalidation remains unchanged.The candidate now recognizes only a
FieldDeclwhose generated name contains(anonymous atand whose type declaration is an anonymous record, then emitsname=None, display<anonymous>, and qualified component<anonymous>. A focused libclang regression proves the implicit field becomesFighter::<anonymous>while an explicitly named field of anonymous-record type remainsFighter::named.Validation evidence on
melee-event-match6b6f8a8a987c2e871b8b96baa863aaa99e406f33:fcbb1a390e51d5e6dc87ae13633e2a7c3c929e8c4c5bc1c1f24516fba16d39c7; peak 1.9 GiB, zero swap.A full 1,103-TU source-only mapping attempt parsed all units but was then OOM-killed at the 32-GiB zero-swap bound during
MetadataGraphconstruction. Thus this candidate removes the issue #55 correctness failure, but it does not satisfy the issue #53 full-publication acceptance criterion. Evidence and limitations are recorded inannals/2026-09-12-anonymous-record-field-symbol-normalization.md. Issue #55 remains open pending delivery/acceptance review.