symbol
May 5, 2026 ยท View on GitHub
Scoped symbol table for variables, types, functions, and labels.
Overview
The symbol package provides SymMap, a flat map from scoped names to
Symbol entries. It is shared between the parser and compiler: the parser
populates it during parsing; the compiler reads it during code generation
to resolve addresses and types.
Key types and functions
SymMap(typemap[string]*Symbol) -- symbol table. Keys are scoped names likemain/foo/xor0/int(scope0for builtins).Symbol-- a table entry:Kind-- one ofValue,Type,Label,Const,Var,LocalVar,Func,Pkg,Builtin,Generic.Varis for global data;LocalVaris for frame-relative locals;Genericmarks a generic function or type template.Index-- address in the VM data segment or frame.Type--*vm.Typefor the symbol's runtime type.Captured/FreeVars-- closure capture metadata.RecvName-- for method symbols: raw receiver variable name, cached from Phase 1 soparseFunccan re-use it in Phase 2.InNames/OutNames-- raw input/output parameter names for func symbols, cached during Phase 1 signature parsing so Phase 2 does not re-parse the signature.Data any-- opaque payload. Used byGenericsymbols to store a*genericTemplate(type params, raw token stream, func-or-type flag). Nil for all other kinds.
Kind(int enum) -- symbol classification.Get(name, scope string) (*Symbol, string, bool)-- lookup by walking from the innermost scope outward.MethodByName(sym, name) (*Symbol, []int)-- find a method on a type, including promoted methods from embedded fields.Package-- package descriptor withPath,Bin(binary flag), andValues map[string]vm.Valuefor exported symbols.BinPkg(m map[string]reflect.Value, name string) *Package-- creates a binary package from a map of reflect values (used for stdlib wrappers).Init()-- populates builtin types (int,string,bool, ...), values (nil,true,false,iota), and builtin functions (print,println,len,cap,append,copy,delete,new,make,panic,recover,trap) withKind: Builtin. The compiler emits dedicated opcodes for each builtin.
Internal design
Symbol lookup walks the scope hierarchy: given scope main/foo/for0 and
name x, it tries main/foo/for0/x, then main/foo/x, then main/x,
then 0/x (builtins). First match wins.
Method lookup traverses embedded field chains to find promoted methods, returning the field index path needed to reach the receiver.
MethodByName ambiguity heuristic
For Var/LocalVar/Value receivers whose Type.Name is empty (the
common case for mvm-created struct types and pointer-to-struct
results from field access), MethodByName searches the symbol table
for a Type entry whose Rtype matches and uses that key as the
type name to construct the method lookup. The same Rtype can appear
under multiple keys:
- the unqualified short name
T(from the user's type decl), - a package-qualified alias
pkgpath.Twritten byimportSrc, - an anonymous-struct stringification like
struct { F int }(fromzeroInitLocalsregistering names for var init).
Methods are registered under the short receiver name (e.g. *T.M), so
lookups through the qualified or stringified key would miss. To handle
this, MethodByName iterates candidate keys and prefers the one that
also has a registered method (k.M or *k.M is in the map). If
none has a registered method, it falls back to the first match -- this
preserves prior behavior for receivers that genuinely have no methods.
This is a heuristic. The planned canonical-pkg-qualified-symbol-keys
refactor will replace it: every type and method gets stored under
pkgpath.T / pkgpath.*T.M with short names becoming a
scope-visibility concern, eliminating the ambiguity at the root.
Dependencies
vm/--Type,Valuestructures.