Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
c650292
feat(identity): callable identity plumbing, no language enabled (#2061)
DeusData Sep 25, 2026
7abaa91
chore(lint): clang-format the callable_sig.h include comment in store.c
DeusData Sep 25, 2026
d8a845e
fix(swift): preserve overload identity and label-aware call edges
DavidHLP Sep 26, 2026
7ae8300
style(swift): format overload patch
DavidHLP Sep 26, 2026
20b55ff
test(swift): cover parallel overload candidate edges
DavidHLP Sep 26, 2026
d5cb9b5
test(swift): accept signature-qualified caller names in call matrix
DavidHLP Sep 26, 2026
19c8d07
fix(swift): unify incompatible-overload fallback across resolver paths
DavidHLP Sep 29, 2026
bbd0957
fix(pipeline): use tracked allocator for suffix lookup
DavidHLP Sep 29, 2026
2100204
fix: guard zero-length signature buffer copy
DavidHLP Sep 30, 2026
b0958c6
refactor: narrow Swift candidate variable scope
DavidHLP Sep 30, 2026
b7c8669
fix(swift): merge current callable identity plumbing
DavidHLP Oct 1, 2026
f583ad9
fix(swift): match multiple trailing closure labels
DavidHLP Oct 3, 2026
73f4475
docs(swift): record focused identity validation evidence
DavidHLP Oct 3, 2026
6fc5af9
test(swift): add standalone issue 2061 graph reproduction
DavidHLP Oct 3, 2026
ceb1914
docs(swift): record original issue 2061 graph red-green evidence
DavidHLP Oct 3, 2026
617c030
test(swift): add MCP presentation repro driver
DavidHLP Oct 3, 2026
f6c5ef6
docs(swift): record scoped MCP validation failure
DavidHLP Oct 3, 2026
eea3143
docs(swift): record index format rebuild validation
DavidHLP Oct 3, 2026
251c06a
fix(swift): integrate callable identity with current main
DavidHLP Oct 5, 2026
b4e3daf
test(swift): reproduce default-before-required argument matching
DavidHLP Oct 5, 2026
233f569
fix(swift): propagate route mounts through overload emitters
DavidHLP Oct 5, 2026
9aad2f9
fix(swift): retain default argument matching alternatives
DavidHLP Oct 5, 2026
98c3a40
style(swift): format variadic matching transition
DavidHLP Oct 5, 2026
44294f8
test(swift): preserve MCP observations through sanitizer teardown
DavidHLP Oct 5, 2026
997cb1e
refactor(swift): bound matcher transition complexity
DavidHLP Oct 5, 2026
a03131c
fix(swift): account for service signature scratch allocation
DavidHLP Oct 5, 2026
0f294df
refactor(swift): reuse signature depth tracking for closure types
DavidHLP Oct 5, 2026
39d5db4
docs(swift): publish current main integration and validation evidence
DavidHLP Oct 5, 2026
7188414
test(swift): expose generic and async identity collisions
DavidHLP Oct 5, 2026
e1eb929
fix(swift): retain generic requirements and async callable identity
DavidHLP Oct 5, 2026
5d214b3
docs(swift): archive history and provide portable graph and MCP repro
DavidHLP Oct 5, 2026
5f77b45
docs(swift): clarify fresh reproduction database requirements
DavidHLP Oct 5, 2026
6222b52
test(swift): share incompatible-call fixture across resolver paths
DavidHLP Oct 5, 2026
5d21def
test(swift): require persisted incremental repair path
DavidHLP Oct 5, 2026
6a8905e
test(swift): verify incompatible-edge assertion detects injected faults
DavidHLP Oct 5, 2026
9ea6058
style(swift): format identity regression additions
DavidHLP Oct 5, 2026
9a18323
refactor(swift): keep signature parsing within lint limits
DavidHLP Oct 5, 2026
ee61f3b
refactor(swift): remove new static analysis violations
DavidHLP Oct 5, 2026
d4f10e9
style(swift): preserve signature constant formatting
DavidHLP Oct 5, 2026
3bde72e
refactor(swift): preserve service matching order without assignment c…
DavidHLP Oct 5, 2026
f2da1f4
style(swift): format service matching table
DavidHLP Oct 5, 2026
500707c
docs(swift): publish final repair evidence and main controls
DavidHLP Oct 5, 2026
f234417
test(swift): reproduce qualified and return overload collisions
DavidHLP Oct 5, 2026
598d068
test(swift): cover return candidates and format-three migration
DavidHLP Oct 5, 2026
3113c6a
test(swift): guard returned operator and capped identities
DavidHLP Oct 5, 2026
e3cd15a
test(swift): preserve nested generic requirement boundaries
DavidHLP Oct 5, 2026
ab0dce1
fix(swift): retain qualified parameter and return identities
DavidHLP Oct 5, 2026
90bfd2e
style(swift): format overload identity regressions
DavidHLP Oct 5, 2026
7867379
refactor(swift): simplify balanced return validation
DavidHLP Oct 5, 2026
10f4769
style(swift): wrap generic boundary guard
DavidHLP Oct 5, 2026
35533b2
docs(swift): record qualified and return identity validation
DavidHLP Oct 5, 2026
6ca78d0
chore(swift): merge main and resolve indexing conflicts
DavidHLP Oct 7, 2026
f6d8c70
chore(swift): merge main PHP route resolution without conflicts
DavidHLP Oct 8, 2026
542e9e0
chore(swift): merge main and retain definition metadata
DavidHLP Oct 9, 2026
4810738
merge: resolve Swift identity conflicts with current main
DavidHLP Oct 10, 2026
a146763
fix(lint): clarify PDF duplicate node name comparison
DavidHLP Oct 10, 2026
67c46cb
fix(swift): integrate upstream candidate ranking API
DavidHLP Oct 11, 2026
2a4d50d
merge: resolve PR #2436 conflicts with main
DavidHLP Oct 13, 2026
caa4d0c
fix: pass language to parallel service edge
DavidHLP Oct 13, 2026
ba26041
fix: address CI route and sanitizer findings
DavidHLP Oct 13, 2026
49d6798
merge: sync main and resolve PR #2436 conflicts
DavidHLP Oct 14, 2026
09fcf9b
test(cypher): cap Windows UNION stress branches
DavidHLP Oct 14, 2026
fd05880
Merge branch 'main' of github.com:DeusData/codebase-memory-mcp into f…
DavidHLP Oct 14, 2026
1010bde
fix(swift): preserve distinct identities and uncapped selectors
DavidHLP Oct 15, 2026
5142a75
Merge upstream main into feat/issue-2061-swift-identity
DavidHLP Oct 15, 2026
a9e56d9
style(swift): format candidate scratch allocation
DavidHLP Oct 15, 2026
4ab447f
chore: merge main and reconcile index migrations
DavidHLP Oct 17, 2026
4885230
Merge branch 'main' into feat/issue-2061-swift-identity
DavidHLP Oct 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
270 changes: 257 additions & 13 deletions internal/cbm/callable_sig.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* would misread: such a suffix degrades to its hashed form.
*/
#include "callable_sig.h"
#include "cbm.h"
#include "helpers.h"
#include <ctype.h>
#include <stdbool.h>
Expand All @@ -22,20 +23,29 @@ enum {
SIG_HASH_HEX = 16, /* FNV-1a 64 rendered as hex */
SIG_OPERATOR_LEN = 8, /* strlen("operator") */
SIG_CONST_LEN = 5, /* strlen("const") */
SIG_ASYNC_LEN = 5, /* strlen("async") */
SIG_ARROW_LEN = 2, /* strlen("=>") */
SIG_VOLATILE_LEN = 8, /* strlen("volatile") */
SIG_ARITY_DIGITS = 16, /* "(%d)" scratch */
SIG_CAPPED_TAIL = 1 + SIG_HASH_HEX + 1, /* "#" hex ")" */
SIG_CHAR_LEN = 1,
SIG_SWIFT_DEFAULT_BITS = 64,
};

static const uint64_t SIG_FNV_OFFSET = 0xcbf29ce484222325ULL; /* FNV-1a 64 basis */
static const uint64_t SIG_FNV_PRIME = 0x100000001b3ULL;
static const char SIG_OPERATOR_CHARS[] = "+-*/%^&|~!=<>?[]";
static const char SIG_TYPE_OPEN[] = "([<";
static const char SIG_TYPE_CLOSE[] = ")]>";

CBMCallableIdentity cbm_callable_identity(CBMLanguage lang) {
/* Every language keeps its historical QN until its enable change lands
* (with the index-format bump that change carries). The planned modes:
* Java/Kotlin/C#/C++/CUDA/Scala TYPED, Swift LABELED_TYPED, ObjC LABELED,
* dynamic tier-2 languages ARITY. */
/* Each language enables identity with its own index-format bump. */
if (lang == CBM_LANG_SWIFT) {
return CBM_CALLABLE_ID_LABELED_TYPED;
}
/* Other languages keep their historical QN until their own enable changes
* land with an index-format bump. Planned modes: Java/Kotlin/C#/C++/CUDA/
* Scala TYPED, ObjC LABELED, dynamic tier-2 languages ARITY. */
(void)lang;
return CBM_CALLABLE_ID_NONE;
}
Expand Down Expand Up @@ -79,7 +89,9 @@ static void sig_raw(sig_ctx_t *c, const char *s, size_t n) {
c->overflow = true;
return;
}
memcpy(grown, c->buf, c->len);
if (c->len != 0) {
memcpy(grown, c->buf, c->len);
}
c->buf = grown;
c->cap = cap;
}
Expand Down Expand Up @@ -737,6 +749,10 @@ static void sig_swift_param(sig_ctx_t *c, TSNode p) {
if (ts_node_eq(ch, internal)) {
continue;
}
/* A default expression selects call arity, not type identity. */
if ((field && strcmp(field, "default_value") == 0) || sig_node_text_is(c, ch, "=")) {
break;
}
const char *k = ts_node_type(ch);
if (strcmp(k, "parameter_modifiers") == 0) {
uint32_t mc = ts_node_named_child_count(ch);
Expand Down Expand Up @@ -770,6 +786,106 @@ static void sig_swift_params(sig_ctx_t *c, TSNode node) {
}
}

/* Keep generic declarations and requirements in identity, without interpreting
* applicability at call sites. '/' preserves associated-type paths without
* introducing a QN separator; comments and whitespace use the token rules. */
static TSNode sig_swift_extension_requirements(sig_ctx_t *c, TSNode node) {
for (TSNode parent = ts_node_parent(node); !ts_node_is_null(parent);
parent = ts_node_parent(parent)) {
if (strcmp(ts_node_type(parent), "class_declaration") == 0) {
return sig_node_text_is(c, sig_field(parent, "declaration_kind"), "extension")
? cbm_find_child_by_kind(parent, "type_constraints")
: (TSNode){0};
}
}
return (TSNode){0};
}

static void sig_swift_tparams(sig_ctx_t *c, TSNode node) {
TSNode params = cbm_find_child_by_kind(node, "type_parameters");
TSNode requirements = cbm_find_child_by_kind(node, "type_constraints");
TSNode extension = sig_swift_extension_requirements(c, node);
if (ts_node_is_null(params) && ts_node_is_null(requirements) && ts_node_is_null(extension)) {
return;
}
size_t start = c->len;
if (!ts_node_is_null(params)) {
sig_type_tokens(c, params);
if (c->len > start && c->buf[c->len - SIG_CHAR_LEN] == '>') {
c->buf[--c->len] = '\0';
}
} else {
sig_raw_str(c, "<");
}
if (!ts_node_is_null(requirements)) {
sig_raw_str(c, ";");
sig_type_tokens(c, requirements);
}
if (!ts_node_is_null(extension)) {
sig_raw_str(c, ";");
sig_type_tokens(c, extension);
}
sig_raw_str(c, ">");
for (size_t i = start; i < c->len; i++) {
if (c->buf[i] == '.') {
c->buf[i] = '/';
}
}
}

static const char *sig_swift_dispatch(sig_ctx_t *c, TSNode node) {
TSNode modifiers = cbm_find_child_by_kind(node, "modifiers");
uint32_t count = ts_node_is_null(modifiers) ? 0 : ts_node_child_count(modifiers);
for (uint32_t i = 0; i < count; i++) {
TSNode modifier = ts_node_child(modifiers, i);
if (sig_node_text_is(c, modifier, "static")) {
return "static";
}
if (sig_node_text_is(c, modifier, "class")) {
return "class";
}
}
return ts_node_is_null(cbm_find_child_by_kind(node, "class")) ? NULL : "class";
}

static bool sig_swift_parameter_has_default(TSNode parameter) {
uint32_t children = ts_node_child_count(parameter);
for (uint32_t i = 0; i < children; i++) {
const char *kind = ts_node_type(ts_node_child(parameter, i));
if (strcmp(kind, "=") == 0 || strstr(kind, "default_value") != NULL) {
return true;
}
}
return false;
}

uint64_t cbm_swift_default_mask(TSNode node, const char *source, uint8_t *count) {
(void)source;
uint64_t defaults = 0;
unsigned parameters = 0;
int last = -SIG_CHAR_LEN;
uint32_t children = ts_node_child_count(node);
for (uint32_t i = 0; i < children; i++) {
TSNode ch = ts_node_child(node, i);
const char *kind = ts_node_type(ch);
if (strcmp(kind, "parameter") == 0) {
last = (int)parameters++;
if (last < SIG_SWIFT_DEFAULT_BITS && sig_swift_parameter_has_default(ch)) {
defaults |= UINT64_C(1) << last;
}
} else if (last >= 0 && last < SIG_SWIFT_DEFAULT_BITS && strcmp(kind, "=") == 0) {
defaults |= UINT64_C(1) << last;
} else if (last >= 0 && (strcmp(kind, ")") == 0 || strcmp(kind, "function_body") == 0)) {
break;
}
}
if (count) {
/* ponytail: >64 parameters have no bitmask; use a dynamic mask if Swift
* code with that many parameters needs overload call resolution. */
*count = parameters > SIG_SWIFT_DEFAULT_BITS ? UINT8_MAX : (uint8_t)parameters;
}
return defaults;
}
/* ── Scala ─────────────────────────────────────────────────────── */

static void sig_scala_params(sig_ctx_t *c, TSNode node) {
Expand Down Expand Up @@ -852,10 +968,11 @@ static void sig_generic_params(sig_ctx_t *c, TSNode node) {

/* ── Spelling pass ─────────────────────────────────────────────── */

/* Qualified type paths keep their last segment; "->" becomes "=>"; any other
/* Swift qualified paths use '/'; other type paths keep their last segment.
* "->" becomes "=>"; any other
* '.' or "::" (a receiver function type, a leading global scope) is dropped,
* so the suffix contract holds whatever the grammar produced. */
static size_t sig_spell(char *s, size_t n) {
static size_t sig_spell(char *s, size_t n, CBMLanguage lang) {
size_t out = 0;
size_t i = 0;
while (i < n) {
Expand All @@ -866,6 +983,11 @@ static size_t sig_spell(char *s, size_t n) {
sep = 2;
}
if (sep) {
if (lang == CBM_LANG_SWIFT) {
s[out++] = '/';
i += sep;
continue;
}
bool ident_before = out > 0 && sig_ident_char((unsigned char)s[out - 1]);
bool ident_after = i + sep < n && sig_ident_char((unsigned char)s[i + sep]);
if (ident_before && ident_after) {
Expand Down Expand Up @@ -958,6 +1080,8 @@ static void sig_render(sig_ctx_t *c, TSNode node) {
}
if (c->lang == CBM_LANG_CSHARP) {
sig_cs_tparams(c, node);
} else if (c->lang == CBM_LANG_SWIFT) {
sig_swift_tparams(c, node);
}
c->open_off = c->len;
sig_raw(c, "(", 1);
Expand Down Expand Up @@ -986,6 +1110,29 @@ static void sig_render(sig_ctx_t *c, TSNode node) {
}
sig_raw(c, ")", 1);
c->close_off = c->len;
if (c->lang == CBM_LANG_SWIFT) {
const char *dispatch = sig_swift_dispatch(c, node);
if (dispatch) {
sig_raw_str(c, dispatch);
}
/* Only direct declaration children: a parameter's async closure type
* is already in params and must not mark the enclosing function. */
uint32_t count = ts_node_child_count(node);
for (uint32_t i = 0; i < count; i++) {
if (sig_node_text_is(c, ts_node_child(node, i), "async")) {
if (dispatch) {
sig_raw_str(c, " ");
}
sig_raw_str(c, "async");
break;
}
}
TSNode result = sig_field(node, "return_type");
if (!ts_node_is_null(result) && sig_writes_types(c)) {
sig_raw_str(c, "=>");
sig_type_tokens(c, result);
}
}
}

static bool sig_roundtrips(const char *suffix, size_t n) {
Expand All @@ -1005,6 +1152,22 @@ static const char *sig_hashed(CBMArena *a, const char *full, size_t n) {
return cbm_arena_sprintf(a, "(#%016llx)", (unsigned long long)sig_fnv64(full, n));
}

const char *cbm_swift_selector(CBMArena *a, TSNode node, const char *source) {
if (!a || !source || ts_node_is_null(node)) {
return NULL;
}
sig_ctx_t c = {
.arena = a, .src = source, .lang = CBM_LANG_SWIFT, .mode = CBM_CALLABLE_ID_LABELED_TYPED};
sig_raw_str(&c, "(");
sig_swift_params(&c, node);
sig_raw_str(&c, ")");
if (c.overflow) {
return NULL;
}
(void)sig_spell(c.buf, c.len, CBM_LANG_SWIFT);
return c.buf;
}

const char *cbm_callable_sig_mode(CBMArena *a, TSNode func_node, const char *source,
CBMLanguage lang, CBMCallableIdentity mode) {
if (!a || !source || ts_node_is_null(func_node) || mode == CBM_CALLABLE_ID_NONE) {
Expand Down Expand Up @@ -1035,7 +1198,7 @@ const char *cbm_callable_sig_mode(CBMArena *a, TSNode func_node, const char *sou
if (!spelled) {
return NULL;
}
size_t n = sig_spell(spelled, full);
size_t n = sig_spell(spelled, full, lang);
if (n <= CBM_CALLABLE_SIG_MAX) {
return sig_roundtrips(spelled, n) ? spelled : sig_hashed(a, spelled, n);
}
Expand All @@ -1046,12 +1209,12 @@ const char *cbm_callable_sig_mode(CBMArena *a, TSNode func_node, const char *sou
int limit = c.too_many ? SIG_MAX_ENTRIES : c.entries;
for (int i = 0; i < limit; i++) {
char *prefix = cbm_arena_strndup(a, c.buf, c.entry_end[i]);
c.entry_end[i] = prefix ? sig_spell(prefix, c.entry_end[i]) : 0;
c.entry_end[i] = prefix ? sig_spell(prefix, c.entry_end[i], lang) : 0;
}
char *open_prefix = cbm_arena_strndup(a, c.buf, c.open_off);
char *close_prefix = cbm_arena_strndup(a, c.buf, c.close_off);
c.open_off = open_prefix ? sig_spell(open_prefix, c.open_off) : 0;
c.close_off = close_prefix ? sig_spell(close_prefix, c.close_off) : 0;
c.open_off = open_prefix ? sig_spell(open_prefix, c.open_off, lang) : 0;
c.close_off = close_prefix ? sig_spell(close_prefix, c.close_off, lang) : 0;
c.buf = spelled;
c.len = n;
const char *capped = sig_capped(a, &c, spelled, n);
Expand Down Expand Up @@ -1119,13 +1282,94 @@ size_t cbm_qn_callable_base_len_named(const char *qn, const char *name) {
return anchored ? base : len;
}

/* Locate only a declaration's return marker, never a nested closure arrow or
* an arrow in generic requirements. Shared by inverse and Swift registry. */
static bool sig_return_type_valid(const char *type) {
char stack[SIG_DEPTH_LIMIT];
unsigned depth = 0;
if (!type[0] || strpbrk(type, ".#;{}") || strstr(type, "::") || strstr(type, "->")) {
return false;
}
for (size_t i = 0; type[i]; i++) {
char ch = type[i];
if (ch == '>' && i > 0 && type[i - SIG_CHAR_LEN] == '=') {
continue;
}
const char *closing = strchr(SIG_TYPE_CLOSE, ch);
if (strchr(SIG_TYPE_OPEN, ch)) {
if (depth == SIG_DEPTH_LIMIT) {
return false;
}
stack[depth++] = ch;
} else if (closing) {
if (!depth) {
return false;
}
depth--;
if (stack[depth] != SIG_TYPE_OPEN[closing - SIG_TYPE_CLOSE]) {
return false;
}
}
}
return depth == 0;
}

static size_t sig_swift_qualifiers_start(const char *text, size_t end) {
static const char *const qualifiers[] = {"async", "static", "class"};
for (size_t i = 0; i < sizeof(qualifiers) / sizeof(qualifiers[0]); i++) {
size_t len = strlen(qualifiers[i]);
if (end > len && memcmp(text + end - len, qualifiers[i], len) == 0 &&
(text[end - len - 1] == ')' || text[end - len - 1] == ' ')) {
end -= len;
while (end && text[end - 1] == ' ') {
end--;
}
}
}
return end;
}

size_t cbm_callable_return_offset(const char *suffix) {
size_t len = strlen(suffix);
if (!strstr(suffix, "=>")) {
return len;
}
int parens = 0;
int brackets = 0;
int angles = 0;
for (size_t i = 0; i + SIG_CHAR_LEN < len; i++) {
char ch = suffix[i];
bool arrow = ch == '=' && suffix[i + SIG_CHAR_LEN] == '>';
size_t params_end = sig_swift_qualifiers_start(suffix, i);
bool after_params = params_end && suffix[params_end - SIG_CHAR_LEN] == ')';
if (arrow && after_params && parens == 0 && brackets == 0 && angles == 0) {
return sig_return_type_valid(suffix + i + SIG_ARROW_LEN) ? i : 0;
}
if (ch == '(') {
parens++;
} else if (ch == ')') {
parens--;
} else if (ch == '[') {
brackets++;
} else if (ch == ']') {
brackets--;
} else if (ch == '<' &&
(angles > 0 || sig_ident_char((unsigned char)suffix[i + SIG_CHAR_LEN]))) {
angles++;
} else if (ch == '>' && angles > 0 && (i == 0 || suffix[i - SIG_CHAR_LEN] != '=')) {
angles--;
}
}
return len;
}

size_t cbm_qn_callable_base_len(const char *qn) {
if (!qn) {
return 0;
}
size_t len = strlen(qn);
/* Trailing cvref (C++): const / volatile / & / && after the ')'. */
size_t t = len;
size_t t = sig_swift_qualifiers_start(qn, cbm_callable_return_offset(qn));
for (;;) {
if (t > 0 && qn[t - 1] == '&') {
t--;
Expand Down Expand Up @@ -1171,7 +1415,7 @@ size_t cbm_qn_callable_base_len(const char *qn) {
size_t q = p;
while (q > 0) {
q--;
if (qn[q] == '>') {
if (qn[q] == '>' && (q == 0 || qn[q - SIG_CHAR_LEN] != '=')) {
adepth++;
} else if (qn[q] == '<') {
adepth--;
Expand Down
Loading
Loading