Implement interfaceId via new type(C).publicMethods builtin#462
Draft
axic wants to merge 8 commits into
Draft
Conversation
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`.
`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).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.