Implementation Declarations¶
Accepted
Accepted for the V1 implementation declaration model, including inline generic impl binders.
A type implements a contract through an explicit implementation declaration. Unlike struct { ... } and contract { ... }, an implementation is not a Type value. It adds a visible conformance to the semantic environment, so a small declaration form is clearer than forcing it into expression syntax.
The contract side of an impl must be a fully applied contract Type value. A generic contract name such as Sequence is a function, not a Type; write Sequence(f32).
Accepted syntax:
impl Buffer as Sequence(f32) {
fn len(self: *const Buffer) usize {
return self.used
}
}
Inside an implementation body, Self means the implementing concrete type:
impl Buffer as Sequence(f32) {
fn len(self: *const Self) usize {
return self.used
}
}
Generic implementations declare their comptime parameters inline at the type or contract application where the parameter is introduced:
impl Slice(comptime T: Type) as Sequence(T) {
fn len(self: *const Self) usize {
return self.len
}
}
Inline impl binders are part of the impl header, not ordinary call syntax. They are allowed only in implementation declarations. Each binder name may be introduced once and is in scope for the whole impl header and body. Unknown names are still errors; impl headers do not create implicit generic parameters from bare identifiers.
Inline impl binders follow the same self-referential comptime parameter rule as function parameters: a binder name is in scope inside its own constraint annotation as the candidate value being checked, may refer to itself and earlier binders, and may not refer to later binders.
Binders may appear on the implementing type side or the contract side:
impl Slice(comptime T: Type) as Sequence(T) {
}
impl Buffer as Sequence(comptime T: Type) {
}
The second form is valid only when the binder is meaningfully constrained by the implementation, required operation signatures, or other checked facts. An implementation that is generic only because an unconstrained binder appears in the contract side is rejected as overbroad. This keeps the inline style aligned with generic type factories without silently creating universal implementations.
Impl declarations may use the same pre-body declaration guard placement as functions and contract operations:
impl Buffer as Sequence(f32)
if Buffer.implements(Indexable(f32))
{
}
The guard controls compile-time conformance availability.
Impl declarations follow normal top-level declaration visibility. They are module-private by default; pub impl exports the conformance to importers:
impl Buffer as Sequence(f32) {
} // private to this module
pub impl Buffer as Sequence(f32) {
} // visible to importers
An exported impl must not expose private endpoints. pub impl T as C { ... } requires both the implementing type expression T and the contract expression C to be visible as public API from that module. Private impls may use private types and contracts freely.
Impl declarations are visibility facts, not ordinary bindable value/type declarations.
Lookup rules:
- impl declarations do not need
constnames. - namespace destructuring does not select impl declarations directly.
- importing a namespace brings its public impl declarations into conformance visibility.
- selecting a type-like declaration or public alias from a namespace brings public impls in that same namespace whose implementing type is exactly the resolved selected type identity.
- selecting a generic type factory brings public generic impls whose implementing type pattern is rooted in that selected factory.
- selecting a contract brings public impls in that same namespace whose contract side is rooted in the selected contract.
- adapter impls that live in other namespaces require importing those adapter namespaces explicitly.
Struct declarations may list contract requirements in struct(...). This is shorthand for same-scope bare impl declarations:
const File = struct(Disposable) {
fn dispose(self: *Self) void {
}
}
is equivalent to:
const File = struct {
fn dispose(self: *Self) void {
}
}
impl File as Disposable {
}
Entries in struct(...) are private by default. pub Contract exports the implied impl:
pub const Buffer = struct(pub Sequence(u8)) {
}
Entries must be fully applied semantic contracts. struct(...) is not a place for structural constraints or inline implementation bodies. See Structs and Methods.
Impl operation bodies do not have independent pub/priv visibility. The impl controls conformance visibility, and the contract controls the visible operation surface. A visible impl of a public contract exposes the operation through the contract; the operation body itself is not an inherent public method.
Implementation functions live in the contract implementation namespace. They are not ordinary inherent methods on the concrete type.
If a required operation is omitted from the implementation body, sema may fill it from a compatible inherent member on the implementing type:
const Buffer = struct {
fn len(self: *const Self) usize {
return self.used
}
}
impl Buffer as Sequence(f32) {
// `len` is filled from `Buffer.len` if compatible.
// Required dependency conformances such as Indexable(f32)
// must still be provided or visible.
}
This implicit fill only happens inside an explicit semantic implementation. It is not structural conformance.
An implementation-body function overrides inherent-member fill for that contract only. Ordinary concrete member lookup still prefers inherent members.
Default methods may be omitted or overridden in an impl body:
impl Buffer as Sequence(f32) {
fn is_empty(self: *const Self) bool {
return self.used == 0
}
}
Override signatures are checked with the same compatibility rules as required operation implementations. Dynamic vtables use the override when present; otherwise they use the contract default wrapper.
Inherent-member fill applies only to missing required operations. Default methods are not automatically overridden by compatible inherent members. If a compatible inherent member has the same name and signature as a default method, tooling may emit a fixable lint suggesting an explicit override if that behavior was intended.
An implementation body may define only operations that are present in the applied contract instance's surface. For guarded operations whose condition is false, the operation is absent and an implementation body function with that name is rejected. Define an inherent method instead if the concrete type needs that operation outside the contract.
Conformance reflection records whether an operation was satisfied by an implementation body, inherent-member fill, a default method, compiler-owned behavior, or generated behavior. See Conformance Reflection.
Signature Compatibility¶
Implementation operation signatures must match the contract operation after substituting Self with the concrete type, with limited authority-reducing variance:
- runtime parameter types match exactly except pointer/slice const relaxation
*Tin the contract may be implemented as*const T[]Tin the contract may be implemented as[]const T- the reverse is not allowed
- value-to-reference changes are not allowed
- return success type must match exactly
- return error set may be narrower, never wider
Implementation-body functions that satisfy contract operations may not add default parameter values in V1. Contract operation surfaces do not allow defaults, and implementation-local defaults would not be visible through contract dispatch. Use an inherent method or ordinary helper function when the concrete type needs a convenience default.
Capabilities are ordinary parameters. An implementation may not add hidden context requirements; if an operation needs an allocator, diagnostics handle, IO object, or context record, that parameter must be present in the contract operation signature. Destructuring a parameter in the implementation is allowed when it does not change the parameter type.