Skip to content

chore: resolve builtin/foreign attribute names to a shared enum - #13521

Open
asterite wants to merge 1 commit into
ab/clone-elision-sibling-arg-mutationfrom
ab/builtin-enum
Open

chore: resolve builtin/foreign attribute names to a shared enum#13521
asterite wants to merge 1 commit into
ab/clone-elision-sibling-arg-mutationfrom
ab/builtin-enum

Conversation

@asterite

@asterite asterite commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #13520 — the first three commits are that PR; review only the last commit here.

Problem

The name in a #[builtin(name)]/#[foreign(name)] attribute was carried as a String through the whole pipeline and re-matched textually at every consumer: the comptime interpreter's ~150-arm dispatch, the monomorphizer's HandledOpcode, Definition::Builtin/LowLevel, Intrinsic::lookup in SSA generation, and the ownership pass's clone-elision list (added in #13520). Nothing tied these tables together except one test, and an unknown name silently survived to SSA generation where it panicked.

Change

Builtin/foreign names now resolve once to noirc_frontend::shared::Builtin — a single enum (the two attribute kinds share one namespace at consumption) covering all 148 stdlib #[builtin] names and the 12 #[foreign] names, grouped by consumer: SSA intrinsics, monomorphizer-evaluated, comptime-only. The ACIR black box functions are not repeated as variants: they nest as Builtin::BlackBox(BlackBoxFunc), so acvm's enum stays the single source of those names and a new black box function is a Builtin by construction. strum derives provide lookup/name/iter for the unit variants (the payload variant is #[strum(disabled)] and handled explicitly), with a round-trip test.

Attribute tokens still hold free-form strings, so malformed or unknown names parse normally; resolution happens at the semantic boundaries:

  • the comptime interpreter's call_special (then call_builtin/call_foreign match enum variants),
  • the monomorphizer when constructing Definition::Builtin(Builtin)/LowLevel(Builtin),
  • SSA generation maps BuiltinIntrinsic via the new Intrinsic::from_builtin (Intrinsic::lookup(&str) remains only for the SSA text parser).

Oracle (#[oracle]) names stay strings deliberately: they are an open, user-extensible namespace.

Behavior changes

  • Calling a builtin whose name the compiler doesn't implement is now a proper MonomorphizationError::UnknownBuiltin diagnostic (with test); previously it was an SSA-gen panic!.
  • The five unresolved_type_* comptime dispatch arms and their helpers are deleted: stdlib no longer declares those builtins, and only stdlib may declare builtins, so they were unreachable.
  • BlackBoxFunc::AND/XOR/RANGE are filtered out of Builtin::lookup: they are not callable functions (the compiler emits their opcodes from binary ops, casts and range checks), so names like #[foreign(and)] — never declared anywhere — now error like any unknown name.

Follow-on wins

Validation

noirc_frontend + noirc_evaluator (4192 tests), the full nargo_cli --test execute suite (9350 tests), stdlib tests (431 × 9 configs), AST fuzzer smoke, clippy, and the enum round-trip/black-box coverage tests all pass.

🤖 Generated with Claude Code

The name in a #[builtin]/#[foreign] attribute was carried as a String
all the way through the pipeline and re-matched textually at every
consumer: the comptime interpreter's dispatch, the monomorphizer's
HandledOpcode, Definition::Builtin/LowLevel, Intrinsic::lookup in SSA
generation, and the ownership pass's clone-elision list.

The names now resolve once to noirc_frontend::shared::Builtin, a single
flat enum covering all 148 stdlib builtins, the 12 foreign functions,
and the compiler-emitted black box names. Attribute tokens still hold
free-form strings (so malformed or unknown names parse and error with a
diagnostic); resolution happens at the semantic boundaries: the
comptime interpreter, and the monomorphizer when constructing
Definition::Builtin/LowLevel. Every downstream consumer now matches on
enum variants.

Behavior changes:
- Calling a builtin with an unknown name is now a proper
  MonomorphizationError::UnknownBuiltin diagnostic; previously it
  survived to SSA generation and panicked there.
- The five unresolved_type_* comptime dispatch arms (and their helper
  functions) are deleted: stdlib no longer declares those builtins and
  only stdlib may declare builtins, so they were unreachable.

The clone-elision sync test now iterates Builtin::iter() exhaustively
instead of a hand-maintained name list.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant