Skip to content

Implement interfaceId via new type(C).publicMethods builtin#462

Draft
axic wants to merge 8 commits into
argotorg:mainfrom
axic:interface-id
Draft

Implement interfaceId via new type(C).publicMethods builtin#462
axic wants to merge 8 commits into
argotorg:mainfrom
axic:interface-id

Conversation

@axic

@axic axic commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

This is for https://eips.ethereum.org/EIPS/eip-165 support.

In Solidity this is the type(C).interfaceId (https://docs.soliditylang.org/en/v0.8.35/units-and-global-variables.html#type-information), i.e. all of it is calculated in the compiler.

If we introduce proper in-memory array support, the builtin part can reduce further. Rewrote it with recursive code.

claude and others added 6 commits June 14, 2026 16:07
Replicates Solidity's `type(I).interfaceId` by exposing a contract's public
methods to user code and computing the ERC-165 style interface id in the
standard library, reusing the dispatcher's existing selector machinery.

- New `type(C).publicMethods` expression primitive: parses to `ExpTypeInfo`,
  resolves to a call to a generated per-contract helper, and desugars (new
  `Solcore.Desugarer.PublicMethods` pass) into a runtime
  `memory(DynArray(bytes4))` holding each public method's selector.

- Selectors are produced via the dispatcher's `Selector.compute` (which reuses
  `sigStr`/`SigString`), so no hashing is reimplemented in the compiler. The
  pass runs before dispatch generation so it sees only user-declared methods
  and refers to the `DispatchNameTy_*` name types dispatch then creates.

- `std/dispatch.solc` gains `calculateInterfaceId` (the XOR fold over the
  selector array), `calculateSelector`, and an `xorWord` helper.

- Example/test under test/examples/dispatch/interfaceid.{solc,json}.
A bare `return 0` in a uint256 function defaults to Int.fromInteger, which
has no uint256 instance. Use the uint256 constructor, matching the idiom in
the other dispatch examples (e.g. basic.solc `return uint256(1)`).
The generic `memory(DynArray(t))` + `IndexAccess`/`memory(t):Typedef` path is
unused elsewhere and does not specialise (`specCall: no resolution found for
Typedef.rep : DynArray(bytes4) -> word` — higher-kinded instance resolution on
the memory wrapper).

Replace it with a dedicated concrete `SelectorArray` type (a single-word
newtype with its own `Typedef(word)` instance and a flat, length-prefixed
memory layout), plus `newSelectorArray`/`setSelector`/`selectorAt`/
`selectorCount` helpers. This keeps the whole interface-id path monomorphic,
using only the same primitives the dispatcher already relies on (e.g.
`Typedef.rep` on a concrete type, as in `selector_matches`).

The generated `type(C).publicMethods` helper now builds a `SelectorArray` with
a plain word index (no `uint256`/`Typedef.abs`).
Merged origin/main, which reworked FunDef to carry `funIsPublic` and made
contract methods opt into dispatch via an explicit `public` modifier.

- publicMethodTypes now filters `FunDef True` (public methods only),
  mirroring the dispatch table in genMainFn, so `type(C).publicMethods`
  matches exactly the dispatched public methods.
- The generated publicMethods helper is built as `FunDef False ...`.
- Example contract methods marked `public`.
claude added 2 commits June 14, 2026 14:42
`type(C).publicMethods` previously desugared to a per-contract helper that
built a runtime `SelectorArray` (a length-prefixed memory array of precomputed
`bytes4` selectors), which `calculateInterfaceId` then iterated.

Instead, expose the public methods as a type-level token and move all
iteration into the standard library:

- The generated helper now returns `Proxy((Method(...), (Method(...), ... ())))`
  — a right-nested tuple, terminated by `()`, whose elements carry the same
  `Method(name,payability,args,rets,fn)` typing that `Selector.compute`
  consumes. No selector array is built in the compiler.
- New `PublicMethods` class in std/dispatch.solc walks that tuple structurally
  (a `()` base case and an `(n, m)` recursive case, mirroring `RunDispatch`),
  exposing `length` (method count) and `interfaceId` (the XOR fold of the
  per-method selectors). A runtime `word` index is not possible here because
  the tuple is heterogeneous, so iteration is structural.
- `calculateInterfaceId(type(C).publicMethods)` now takes `Proxy(ty)` with a
  `ty:PublicMethods` constraint and reads like Solidity's `type(I).interfaceId`.
- Drop the now-unused `SelectorArray` type and its
  new/set/at/count helpers from std/dispatch.solc and the export list.

Observable behaviour is unchanged: the interfaceid example still returns the
XOR of its public-method selectors (0xed9d1481).
The recursive instance now matches a head Method(name,payability,args,rets,fn)
explicitly and carries the same name:SigString, args:SigString constraints as
the Method(...):Selector instance, so Selector.compute resolves directly on the
head method (mirroring compute_selector). The unused length method is removed;
PublicMethods now only exposes interfaceId (the XOR fold).
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.

2 participants