ADR-0060: Complete Interface Signatures (Self + Receiver Modes)
Status
Implemented
Summary
Extend interface declarations to support what their grammar already promises: Self as a parameter / return type, and the three receiver modes (self, inout self, borrow self) on method signatures. ADR-0056 deferred both as future work; without them, structural interfaces can express only by-value receivers and concrete return types, which is too narrow for the next wave of interfaces (Copy, Clone, Eq, etc.).
Context
ADR-0056 landed structural interfaces with a deliberate Phase-1 simplification: every method has a by-value self receiver and concrete (non-Self) parameter and return types. The grammar (§6.5) reserved inout self / borrow self and declared Self a usable type, but validate_interface_decls resolves return types via the general resolve_type, which doesn't know Self, and InterfaceMethodSig carries no receiver mode.
Current concrete blockers, both surfaced while implementing ADR-0059:
- No
Selfin interface signatures.interface Copy { fn copy(self) -> Self; }fails at sema witherror: unknown type 'Self'. The same hits any method that returns or accepts the implementor's own type —Clone,Eq(fn eq(self, other: Self) -> bool),Default(fn default() -> Self), arithmetic-style interfaces, etc. - No receiver modes on interface methods.
interface Copy { fn copy(borrow self) -> Self; }can't be expressed; only by-valueselfparses through to sema. Withoutborrow self/inout self,Copywould consume its receiver on every implicit copy — wrong semantics for the central use case.
These two gaps are independent in the surface but tightly coupled in the implementation: both extend the data carried by InterfaceMethodReq and both flow through check_conforms. They land best as one ADR.
Decision
Self at interface scope
Self becomes a recognized type symbol only inside an interface body, where it stands for "the type that conforms to this interface." It is not a real Type value; it's a marker carried by interface signatures.
Implementation: replace the Type-typed fields on InterfaceMethodReq with a small wrapper:
validate_interface_decls resolves each type symbol with a small wrapper over resolve_type that recognizes the symbol Self and yields IfaceTy::SelfType; everything else flows through resolve_type as today.
Self at conformance check
check_conforms(candidate, interface_id) already iterates the interface's required methods and compares signatures slot-by-slot. Substitution happens at compare time:
The candidate's method must match the substituted signature exactly. No subtyping; no inference.
Receiver modes
InterfaceMethodReq grows a receiver: ReceiverMode field with three variants matching the existing RirParamMode:
InterfaceMethodSig (RIR) grows a corresponding field; the parser already accepts self / inout self / borrow self (per the §6.5 grammar) and routes the latter two through RirParamMode, so the work is mostly threading.
check_conforms compares the candidate method's actual receiver mode against the interface's required mode. Mismatch is rejected with the existing InterfaceMethodSignatureMismatch diagnostic, citing the offending signature.
Display and diagnostics
format_interface_method_sig and format_concrete_method_sig learn to print Self (when the slot is IfaceTy::SelfType) and to print the receiver mode (inout self / borrow self / self). No new diagnostic types — the existing two (InterfaceMethodMissing, InterfaceMethodSignatureMismatch) suffice.
What's intentionally not in scope
Selfoutside interfaces (e.g. as a type alias in free functions). Already works inside method bodies via the existing self-substitution machinery; this ADR doesn't touch that path.- Associated types / functions on interfaces. Future work; not needed for
Copy/Drop/Eq. - Bounded
Self(Self: SomeTrait). Future work. - Field requirements. ADR-0056 explicitly deferred these; still deferred.
Implementation Phases
Phase 1:
Selfin interface signatures.- Introduce
IfaceTy(or equivalent) and updateInterfaceMethodReq. validate_interface_declsrecognizes the symbolSelfand storesIfaceTy::SelfType.check_conformssubstitutesSelfwith the candidate before comparing.- Diagnostic formatters print
Selfcorrectly. - Testable:
interface Clone { fn clone(self) -> Self; }parses and resolves; a struct withfn clone(self) -> StructNameconforms; a struct withfn clone(self) -> i32is rejected with a sig-mismatch diagnostic citingSelf.
- Introduce
Phase 2: Receiver modes in interface methods.
- Add
receiver: ReceiverModetoInterfaceMethodSig(RIR) andInterfaceMethodReq(AIR). validate_interface_declsreads the receiver mode from the parsed method signature.check_conformscompares the candidate method's receiver mode against the requirement.- Diagnostic formatters print the receiver mode.
- Testable:
interface Copy { fn copy(borrow self) -> Self; }parses and resolves; a struct withfn copy(borrow self) -> StructNameconforms; a struct withfn copy(self) -> StructName(wrong receiver) is rejected.
- Add
Phase 3: Spec + traceability.
- Update §6.5 to document
Selfin interface signatures and the three receiver modes; add normative paragraphs and examples. - Drop the "future work" notes from
InterfaceMethodReqandInterfaceMethodSigdoc comments. - Run traceability check; backfill any uncovered paragraphs.
- Update §6.5 to document
Consequences
Positive
- Unblocks
Copy,Clone,Eq,Default, and any future interface whose signature mentions the implementor's own type. borrow self/inout selfclose the gap between the §6.5 grammar and what sema actually accepts — no more "the syntax parses but errors later."check_conforms's diagnostics get more precise (it can now point at a wrong receiver mode, not just wrong types).
Negative
- One more compiler-internal type (
IfaceTy) shaped like aType-with-a-marker. Limits the blast radius — only interface code carries it — but is one more thing to remember.
Neutral
- No user-visible language change beyond "the things the grammar already advertises now work."
Open Questions
Where to put
IfaceTy?gruel-air/src/types.rsnext toInterfaceDef, or a new module? Probably co-located — small enum, used only by interfaces.Should
Selfresolve to anything when written inside a method body of an interface (e.g., for default methods, future work)? Today there are no method bodies in interfaces; this is moot.
References
- ADR-0056 — Structural Interfaces (the
Phase 1whose deferred work this completes) - ADR-0057 — Anonymous Interfaces
- ADR-0059 — Drop and Copy as Interfaces (the immediate consumer; blocked on this)