resolve

package
v0.9.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 30, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package resolve defines the cross-language binding resolvers — the piece that turns per-language symbol islands into one connected graph. Each resolver detects one FFI boundary kind and emits edges (ECgo, EAsm, ELaunch). New languages are added by implementing BindingResolver and registering it; nothing else in the pipeline changes.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type BindingResolver

type BindingResolver interface {
	Name() string        // "cgo", "plan9asm", "cuda", "gpupipe"
	Langs() []graph.Lang // languages whose file changes require a re-run
	Resolve(ctx context.Context, g graph.Graph, fs FileSet) ([]graph.Edge, error)
}

BindingResolver inspects the graph plus the source files of its languages and returns cross-language edges to be added.

type CgoResolver

type CgoResolver struct{}

CgoResolver links the Go <-> C boundary (edge kind ECgo).

Detection contract:

  • A Go file with `import "C"` is a cgo file. Every call C.<ident>(…) whose ident is not a C primitive-type conversion yields an ECgo edge from the enclosing Go function node to node "c:<ident>". The C symbol is left unresolved here (the C parser lands in M2); the indexer materializes "c:<ident>" as a stub node so the impact radius stays complete.
  • A `//export Name` directive immediately above a Go function yields the reverse ECgo edge from "c:Name" to that Go function node (C calling into Go).

#include preamble edges (Go file -> header file) arrive with file-level nodes in a later milestone; they are disconnected from the symbol radius without them, so M1 omits them to keep `get depth` results clean.

func (CgoResolver) Langs

func (CgoResolver) Langs() []graph.Lang

Langs are the languages whose changes require re-running this resolver.

func (CgoResolver) Name

func (CgoResolver) Name() string

Name identifies the resolver.

func (CgoResolver) Resolve

func (CgoResolver) Resolve(ctx context.Context, g graph.Graph, fs FileSet) ([]graph.Edge, error)

Resolve parses every Go file and emits the cgo boundary edges.

type CudaResolver

type CudaResolver struct{}

CudaResolver links host code to CUDA kernels (edge kind ELaunch).

Detection contract:

  • `__global__` function definitions in .cu/.cuh files become KKernel nodes with IDs "cu:<name>" (index.CudaParser owns that).
  • `extern "C"` host wrapper functions in .cu files are minted with "c:" IDs (not "cu:"), so the cgo resolver chains Go -> c:wrapper and this resolver chains c:wrapper -launch-> cu:kernel — completing the Go -> C -> CUDA impact path.
  • A triple-chevron launch `name<<<grid, block>>>(args)` inside the body of the *currently open* extern "C" wrapper yields an ELaunch edge from "c:<wrapper>" to "cu:<name>". A launch outside any wrapper (e.g. inside a plain __device__/__global__ helper) is skipped — this resolver only tracks the host FFI boundary, not device-side dynamic parallelism.

This is a line-oriented scanner mirroring index.CudaParser (no cgo tree-sitter dependency); it tracks the "currently active wrapper" via the same naive brace-depth walk used there.

func (CudaResolver) Langs

func (CudaResolver) Langs() []graph.Lang

func (CudaResolver) Name

func (CudaResolver) Name() string

func (CudaResolver) Resolve

func (CudaResolver) Resolve(ctx context.Context, g graph.Graph, fs FileSet) ([]graph.Edge, error)

Resolve scans every CUDA source file for extern "C" wrapper bodies and the kernel launches inside them.

type FFIResolver

type FFIResolver struct{}

FFIResolver links the C <-> C++ FFI boundary left dangling by cSpec's and cppSpec's CallRe (LSP-001): a cpp: call site invoking a name that only exists as a c: symbol (extern "C" declarations, C headers called from C++) or vice versa.

Detection contract (RSV-001: edges only, no node minting):

  • This mirrors — rather than imports — cSpec's/cppSpec's function Defs (plain function, and cppSpec's out-of-line `Foo::bar(` method) and CallRe/Stop (see internal/langspec/c.go, cpp.go): resolve is a lower layer than internal/index, which internal/langspec imports, so resolve cannot depend on langspec (same reason CudaResolver mirrors index.cudaExternCRe instead of importing internal/index). Body-span scanning itself does NOT carry this restriction: T-0054 extracted that logic into internal/cspan, a leaf package with zero non-stdlib imports that both langspec and resolve depend on directly, so this resolver's brace-span handling (K&R, prototypes, multi-line headers, Allman) is byte-for-byte the same code the primary langspec pass runs — not a second, independently-maintained copy (see docs/validation-ddnet.md's "## ffi re-validation" section for why that duplication used to block the cpp:->c: FFI crossing entirely).
  • For every c:/cpp: function def found this way, every call inside its brace-counted body that is neither Stop-listed nor the def's own name is a callee candidate exactly like the primary langspec pass. If the callee already resolves within the same language (g.Node finds lang:callee), there is nothing to bridge — the primary pass already connected it. Only when it is dangling in its own language AND the sibling language's node (siblingLang:callee) exists in g does this resolver emit a NEW edge Src -> sibling, Kind ECall, same File/Line as the call site. Only g.Node lookups are used — no g.Find/g.Neighbors graph enumeration, no node minting; the original dangling edge from the primary pass is left untouched, resolvers only add.

func (FFIResolver) Langs

func (FFIResolver) Langs() []graph.Lang

Langs are the languages whose changes require re-running this resolver.

func (FFIResolver) Name

func (FFIResolver) Name() string

Name identifies the resolver.

func (FFIResolver) Resolve

func (FFIResolver) Resolve(ctx context.Context, g graph.Graph, fs FileSet) ([]graph.Edge, error)

Resolve scans every C and C++ file for function defs and bridges any call inside a def's body that is dangling in its own language but resolves in the sibling C-family language.

type FileSet

type FileSet interface {
	ByLang(l graph.Lang) []string
	Read(path string) ([]byte, error)
}

FileSet gives resolvers read access to the indexed source files without binding them to the filesystem layout (tests use in-memory sets).

type GpuPipeResolver

type GpuPipeResolver struct{}

GpuPipeResolver links pipeline-based GPU dispatch (Metal, Vulkan) where the binding is by name at runtime, not by symbol (edge kind ELaunch, best effort).

Detection contract (Metal implemented, Vulkan future):

  • Metal: a string literal "kern_name" appearing as the argument of newFunctionWithName: (ObjC) is matched against [[kernel]] entry points of the same name in .metal files -> ELaunch edge from the enclosing host function to "msl:<name>".
  • Vulkan: host code binds a compiled SPIR-V module blob by FILE PATH via vkCreateShaderModule and names the entry point through VkPipelineShaderStageCreateInfo.pName (almost always the literal "main") — unlike Metal's newFunctionWithName:@"<kernelName>", there is no host-side kernel-name literal to string-match against a glsl entry point. A meaningful Vulkan launch edge needs module-provenance tracking (which .spv/.comp compiles into which module handle), a separate harder problem than the line-scanner name-match this resolver does for Metal. GLSL shader SOURCE is now parsed (glsl: nodes, P-0046) so the target nodes exist; only the host->module binding is unresolved.
  • These edges are heuristic string matches (RSV-002): the resolver emits edges only — it never mints or mutates nodes (RSV-001).

func (GpuPipeResolver) Langs

func (GpuPipeResolver) Langs() []graph.Lang

func (GpuPipeResolver) Name

func (GpuPipeResolver) Name() string

func (GpuPipeResolver) Resolve

func (GpuPipeResolver) Resolve(ctx context.Context, g graph.Graph, fs FileSet) ([]graph.Edge, error)

Resolve scans every Objective-C source file for host function bodies and the Metal kernel launches inside them.

type Plan9AsmResolver

type Plan9AsmResolver struct{}

Plan9AsmResolver links Go declarations to their assembly implementations (edge kind EAsm).

Detection contract:

  • A `TEXT ·Name(SB)` (or `TEXT pkg·Name(SB)`) definition in a .s file mints the same qualified name as AsmParser: "<dir-base>.Name", where dir-base is the .s file's containing directory (the Go package convention, e.g. mat_amd64.s in package mat -> "mat.mulVec").
  • If both go:<dir-base>.Name and asm:<dir-base>.Name already exist as nodes in the graph, the pair yields an EAsm edge go:<dir-base>.Name -> asm:<dir-base>.Name, sited at the TEXT line. Neither side is speculatively created here — the indexer materializes stub nodes for any edge endpoint a parser didn't produce, so a TEXT-only or Go-only side still surfaces as a stub, not a dangling edge.
  • GLOBL data symbols are not resolved to Go references in M1.5; that needs linkname/relocation analysis and is left for a later milestone.

func (Plan9AsmResolver) Langs

func (Plan9AsmResolver) Langs() []graph.Lang

Langs are the languages whose changes require re-running this resolver.

func (Plan9AsmResolver) Name

func (Plan9AsmResolver) Name() string

Name identifies the resolver.

func (Plan9AsmResolver) Resolve

func (Plan9AsmResolver) Resolve(ctx context.Context, g graph.Graph, fs FileSet) ([]graph.Edge, error)

Resolve scans every .s file for TEXT definitions and pairs each with its same-named Go declaration, if both are already present in the graph.

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry holds the active resolvers in registration order.

func Default

func Default() *Registry

Default returns the built-in resolver set.

func (*Registry) All

func (r *Registry) All() []BindingResolver

func (*Registry) Register

func (r *Registry) Register(b BindingResolver)

type VulkanResolver

type VulkanResolver struct{}

VulkanResolver links Vulkan compute/graphics dispatch (edge kind ELaunch) where the binding is neither a symbol reference (cgo/cuda) nor a host-side kernel-name string literal (Metal/gpupipe, see gpupipe.go): Vulkan host code loads a compiled SPIR-V blob by FILE PATH via vkCreateShaderModule and names the entry point through VkPipelineShaderStageCreateInfo.pName — almost always the literal "main". Recovering the binding therefore requires tracing module *provenance* across three statements in one host file:

  1. BLOB a string literal ending in ".spv" is bound to an identifier (its assignment target, if the line has one) or, absent one, remembered as the file's most recent "pending" blob. The blob's *stem* — its basename with every extension stripped ("blur.comp.spv" -> "blur") — is what ties it back to the glsl source node the primary indexing pass minted from "blur.comp".
  2. MODULE a vkCreateShaderModule(...) call binds its output module identifier (the call's last argument, leading '&' stripped) to a blob stem: the stem bound to the createInfo's pCode identifier if traceable, else the file's pending blob stem.
  3. STAGE a VkPipelineShaderStageCreateInfo struct literal whose .module field names a bound module identifier and whose .pName field carries a non-empty entry-point literal resolves to (stem, entry). There is no default pName: an omitted or empty pName means no edge (RSV-004) — this resolver never assumes "main".

(stem, entry) is then resolved to a real glsl: node BY GRAPH LOOKUP (graph.Graph.Find), never by ids.Mint: GLSL is QualFlat (internal/langspec/glsl.go), so every shader's `void main()` mints the same base ID "glsl:main" and the indexer disambiguates same-named defs across files with "~2", "~3", ... in file-path order (internal/ids). Minting "glsl:<entry>" directly would silently target the first-indexed shader by that name and wire every other same-named shader's launch to the wrong node — RSV-001 also forbids resolvers from minting nodes outright; only the parser passes do that.

Ambiguity is not an edge (RSV-004): if graph lookup finds zero or more than one glsl node matching (stem, entry), this resolver emits nothing for that dispatch rather than guessing.

func (VulkanResolver) Langs

func (VulkanResolver) Langs() []graph.Lang

func (VulkanResolver) Name

func (VulkanResolver) Name() string

func (VulkanResolver) Resolve

func (VulkanResolver) Resolve(ctx context.Context, g graph.Graph, fs FileSet) ([]graph.Edge, error)

Resolve scans every C, C++ and Go source file for the BLOB -> MODULE -> STAGE provenance chain described in the type doc and emits one ELaunch edge per resolved dispatch. Edges are sorted before returning so the result is deterministic regardless of FileSet.ByLang iteration order (map-backed FileSet implementations, including tests, do not guarantee order).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL