Cross-module summaries
When a borrow signature has to be honoured at a call site in another
module, the borrow checker needs to know the callee's parameter modes
and — for return-borrows — which parameter the returned loan originates
from. Inside a single compilation unit this is trivial: the checker
has the full AST. Across module boundaries the answer comes from a
.npksummary sidecar written next to each .npk source file.
When sidecars are written
After BorrowChecker::analyze() finishes for a module, the writer
emits foo.npksummary next to foo.npk only if the module
exports at least one function whose summary carries a return borrow
(XMOD-DEC-006). Modules with no cross-module return-borrow exports
produce no sidecar — keeping the source tree free of empty
[]-summary files.
Filter rules (XMOD-DEC-006/007):
- Free functions — only
pubfunctions are included. - Trait impl methods — always included regardless of source-level visibility (trait dispatch needs the summary either way).
When sidecars are loaded
During the next compilation, the loader runs in
BorrowChecker::analyze()'s pre-pass — before the AST-based
seedImportedSummaries walk (XMOD-DEC-004). Loaded summaries win on
first write; the local AST seeder then fills in anything the sidecar
did not cover. Missing sidecars silently fall back to AST seeding;
stale sidecars (source .npk mtime > sidecar mtime, XMOD-DEC-008)
trigger a stderr warning and fall back to AST seeding.
To see exactly which sidecars were consulted, set:
NPK_TRACE_XMOD_SUMMARY=1 npkc build foo.npk
Each attempted load prints one [xmod-sidecar] … line.
Sidecar schema (version 1)
{
"version": 1,
"module": "<source path>",
"summaries": [
{ "name": "<source name>",
"mangled": "<post-mangling symbol>",
"is_trait_method": <bool>,
"borrow_params": [
{ "index": <u32>, "mode": "$$i"|"$$m",
"name": "<param>" }
],
"return_borrow": { "kind": "self"|"named"|"first",
"from_param": "<name>" // when kind == named
} | null
}, ...
]
}
return_borrow.kind is one of:
self— trait method returning a loan derived fromself(e.g.Box.peek).named— the source signature usedfrom <param>;from_paramcarries the parameter's name.first— fallback used when there is exactly one borrow parameter and no explicit annotation.
The schema version is a u32. If a sidecar's version field does
not match the compiler's current value, the loader raises a hard
error:
error[ARIA-057]: summary schema mismatch: sidecar version <N> is not
supported by this compiler (expected version 1)
hint: rebuild module 'foo' to refresh its .npksummary sidecar
There is no silent fallback on a schema mismatch (XMOD-DEC-005) — mixing stale and fresh summaries is the kind of footgun this chapter exists to prevent.
Worked example
Module pair.npk:
struct:Pair = { int32:a, int32:b };
pub func:get_a = $$i int32($$i Pair:p) {
pass &p.a;
};
After npkc build pair.npk the writer produces pair.npksummary:
{
"version": 1,
"module": "pair.npk",
"summaries": [
{ "name": "get_a", "mangled": "pair__get_a",
"is_trait_method": false,
"borrow_params": [
{ "index": 0, "mode": "$$i", "name": "p" }
],
"return_borrow": { "kind": "first" }
}
]
}
Module main.npk, in another build invocation:
use pair;
func:main = int32() {
pair.Pair:pp = pair.Pair{a: 7, b: 9};
$$i int32:r = pair.get_a(pp); // sidecar lets the checker tie r → pp
exit *r; // 7
};
Without the sidecar the loader would have nothing to tie r's host
back to pp and the checker would emit ARIA-023 with the
"callee 'pair.get_a' has no borrow summary available" message
and the rebuild hint. With the sidecar in place, the borrow
parameter index resolves to 0 and r is treated as a $$i loan on
pp for the rest of main's body.
Diagnostics
ARIA-023— "Cannot bind borrow variable 'r' to call result: callee 'X' has no borrow summary available." Hint: mark the calleepuband ensure its module is imported (or its.npksummarysidecar is present next to its.npk).ARIA-057— "summary schema mismatch …" (described above).
K-semantics status
The K layer tracks the runtime value-flow lock for return-borrow
calls in tests 162 ($$m return-borrow flow), 163 (named
return-borrow second-param passthrough), and 164 (trait self
return-borrow). A first-class <func-summaries> cell is deferred
(XMOD-DEC-009) until the K grammar gains $$i T / $$m T return
types; the current value-flow lock matches the
v0.31.8.5 W-13 precedent (K tests 155–157).