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:

  1. creates builtin function values
  2. binds them into the base environment under the primary name
  3. 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 .Rd output 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 wasm32 build needs either a linkme fork 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.