Repository navigation
Receipt same-module call-target support as a Method Query Source capability - #9938
Conversation
…bility Move typed same-module callee resolution (#8700) into one owner, SameModuleMethodResolver, with two candidate sources: Library Body Analysis's declared-method map and a lazy, receipted Method Query Source lookup over metadata. The new CallTargets layer lets an Assembly Analysis producer resolve a call operand to a current-module MethodDef, an external declaring-type origin, a runtime-provided array member, or a typed unresolved reason without the transitional module lookup. Token-bound calls decode no signature; by-signature binding indexes the module's TypeDefs once and decodes only same-named candidates of the matched type. Parity with LibraryCallGraphAnalysisResult.ResolveTarget is gated on every physical call site of the callee-resolution fixture, the Analysis test assembly, System.Collections.Immutable, System.Linq, and the synthetic malformed-metadata cases. Refs #8700 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 1 reconciliation (head
|
A token-bound query reads the definition's MethodDef row for its declaring type even when the call site fails its checks first, so count that probe where it is read rather than in the generic-declaration check. An unreadable generic-declaration header is an invalid declaration, as Library Body Analysis judges it. The design text now lists the remaining hand-authored-metadata differences and the remembered caller scope. Refs #8700 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 2 reconciliation (head
|
| Item | Disposition |
|---|---|
| F1 | Resolved. The definition probe is counted where it is read, and the new gate asserts an independent count. |
| Observations 1, 3 and 4 | Resolved. |
Non-blocking observations, carried forward. No push for optional feedback.
-
Aggregate work bound. Each query decodes its MemberRef and TypeSpec parent again, with no cache and no assembly budget. Library Body Analysis decodes each MemberRef once under a 4M-char structural budget. Each decode is individually bounded, but crafted input can make total work grow with call sites × signature width. No production consumer declares the layer yet. The first consumer, the census producer, will either memoize per (MemberRef, caller scope) under a stated aggregate bound or state the bound explicitly. That is a design obligation of the census slice.
-
More hand-authored-metadata differences:
- the Library Body Analysis decode-budget failure;
- MethodSpec target kind or unsupported target;
- a TypeDef name that cannot be read, which fails every by-signature query in the module;
- unreceipted TypeSpecs nested inside a signature.
All outcomes stay contained. They are carried to the same slice, which will extend the list or close the gaps.
-
CallerScopecache-field ordering. IfGetTypeFromDefinitionthrows, the cache fields are left in a mixed state. Sequential visits make this practically unreachable. The fix, assigning the fields after computing them, will land with the consumer slice.
🤖 Generated with Claude Code
Summary
Refs #8700. This is prerequisite 2 of 6 for the unsafe member census on Assembly Analysis Operation (#5254, Unsafe member findings).
#8701 and #8704 implemented #8700 on
LibraryCallGraphAnalysisResult, which belongs to the Library Body Analysis infrastructure being deleted. This PR moves the judgment into one owner,SameModuleMethodResolver, so an Assembly Analysis producer can use it without a second resolver.SameModuleMethodResolvergets candidates from two sources:MethodDefinitionMap, the existing eager declared-method map, now a subclass of the resolver.ResolveTargetdelegates to it unchanged.CallTargetslayer (SameModuleCallTargetLookup). It is declared, execution-scoped, built lazily, shared across request-set lanes, and receipted. It does not implyModuleLookup.Normative owner:
docs/design/method-query-source.md#same-module-call-target-support. The target semantics stay as Direct-call target resolution defines them.How the lookup stays bounded
calli: returns unresolved (indirect) and reads no metadata.TypeRefidentity the map uses. It decodes only the same-named methods of the matched type, once per type and name.The receipt (
SameModuleCallTargetCoverage) reports:It attributes work to the physical group and to each request the same way #9831's support does.
Evidence
Parity:
MethodQuerySource_CallTargetsMatchLibraryBodyAnalysischecks every physical call site againstLibraryCallGraphAnalysisResult.ResolveTarget, keyed by body, IL offset, and operand token, in both directions. It runs over:A one-off run also matched on System.Private.CoreLib and System.Text.Json; those two are left out of the suite to keep it fast.
Synthetic cases: each
SameModuleTarget_*case now also asserts source parity (unmatched, ambiguous, unsupported, malformed, single-definition).Receipt and boundary gates:
SameModuleTarget_UnsupportedTokenBoundCalleeReceiptsItsProbes: an independent hand count of the caller row, the definition row, and the guard bytes when a call fails its site checks early.CallTargetsAbsentWhenUndeclaredCallTargetsRequireDeclarationCallTargetsReceiptExactMethodWork: exact work equals a fresh oracle lookup, and the TypeDef index is read once.CallTargetsBindDefinitionTokensWithoutTypeIndex: no index, and the receipt reports only guard bytes.CallTargetsSharedOnceAcrossLanesSuites (Release):
Performance
NativeAOT linux-x64 on
perf-2underperf-guard(CPU 99.80% idle), 2026-10-10T23:33:19Z–23:43:01Z, 25 interleaved samples per side after 3 warm-ups. Base is2768de627(main) and head is6240dfb22. Binary size: 147,593,856 → 147,581,248 bytes (−12,608).Both sections route through the declared-method map whose resolution logic moved into
SameModuleMethodResolver.Every difference is within noise. No production producer declares
CallTargetsyet, so these numbers cover the Library Body Analysis path through the shared resolver. The lookup's own cost will be measured when the census adopts it.Review
6240dfb22.The round-2 observations carry forward to the census slice, the first consumer:
CallerScopecache-field ordering.Non-claims
CallTargetsyet. The census adopts it in its own slice, and theCallslayer (Implement Method Query Source operand and signature resolution (Calls layer) #9868) carries it per call site.🤖 Generated with Claude Code