Guide
Builtin Registry And linkme
How `linkme` distributed slices collect builtin descriptors, why miniR uses that pattern, and why it blocks WASM today
miniR does not keep a giant hand-maintained list of builtins in one central file.
Instead, builtin definitions register themselves into one global registry at link time. The main mechanism behind that is linkme.
If you are new to the project, this is one of the least obvious but most important pieces of infrastructure on the site.
The Registry In One Sentence
src/interpreter/builtins.rs declares a distributed slice:
#[distributed_slice]
pub static BUILTIN_REGISTRY: [BuiltinDescriptor];
Every builtin registration then emits a static BuiltinDescriptor into that slice.
At interpreter startup, miniR iterates the final slice and binds those descriptors into the base environment.
Where The Registrations Come From
The registrations are generated by minir-macros.
The relevant attribute macros are:
#[builtin]#[interpreter_builtin]#[pre_eval_builtin]
Each one validates the Rust function signature and then emits a registration static with:
- the R-visible name
- aliases
- implementation kind
- arity metadata
- doc string
- formal parameter metadata
The generated static is annotated with:
#[linkme::distributed_slice(crate::interpreter::builtins::BUILTIN_REGISTRY)]
That is what makes the builtin "show up" without anyone editing a central list by hand.
What Happens At Startup
register_builtins() in src/interpreter/builtins.rs walks BUILTIN_REGISTRY and:
- creates builtin function values
- binds them into the base environment under the primary name
- binds aliases to the same descriptor
The same registry also feeds:
- builtin lookup helpers such as
find_builtin() - synthesized help pages through
synthesize_builtin_help() - generated
.Rdoutput for documented builtins
So the registry is not only a dispatch list. It is also documentation infrastructure.
Why miniR Uses This Pattern
The upside is straightforward:
- builtin definitions stay close to the module that implements them
- new builtins do not require editing a giant central registration file
- aliases, docs, and formals travel with the implementation
- the project can have hundreds of builtins without one file becoming the bottleneck
That is especially useful in a codebase where builtins are spread across strings, stats, graphics, packages, native helpers, and many optional features.
How This Interacts With FromArgs
miniR is slowly growing more ergonomic argument-decoding paths, including FromArgs.
That does not replace the registry. It only changes how a builtin implementation decodes and validates its arguments before execution.
Both styles still end up in the same BUILTIN_REGISTRY:
- classic attribute-macro builtins
FromArgs-based wrappers generated by macros
This is why the registry is a stable architectural piece even while builtin authoring style evolves.
Tradeoffs
The pattern is convenient, but it is not free:
- registration is more magical than a plain Rust array
- understanding startup requires understanding macro expansion
- the linker is part of the registration mechanism
- target support depends on platform-specific linker behavior
That last point is the reason the registry matters for target support, not just for code organization.
Why linkme Blocks WASM Today
linkme distributed slices rely on native linker-section behavior.
That works on the usual host targets, but wasm32-unknown-unknown does not provide the same mechanism. As a result:
- the feature graph already supports a small WASM-oriented build shape
- the builtin registry still depends on native-style distributed-slice assembly
- a real
wasm32build needs either alinkmefork or a different registration strategy
This is the main architectural blocker called out in plans/linkme-wasm.md and on the WASM Support page.
How To Read Bugs Through This Page
Start from the registry when a bug looks like:
- a builtin exists in code but is not callable in R
- aliases do not resolve correctly
- builtin help pages are missing or wrong
- a feature-gated builtin disappears unexpectedly
- a target-specific build fails around builtin auto-registration
Those are registry or macro-expansion problems, not evaluator problems.