Function Types and Pointers¶
Accepted
Complete for V1 source semantics; overloading, named arguments, anonymous functions, and capturing closures are deferred to focused CEPs.
Function types compare parameter types, return type, error type, and calling convention. Parameter names and destructuring patterns are binding syntax and diagnostics/documentation metadata; they do not create a separate function type dimension.
Signatures¶
A destructured parameter has no single canonical public parameter name unless the pattern is a simple identifier. Tooling and reflection should preserve the binding pattern separately, and documentation may render the full pattern as the parameter display.
Destructuring does not change call shape. Future named arguments, if added, should target parameters, not destructured internal bindings:
fn parse({ alloc, diag }: *const Context, source: Source) Ast {
}
parse(&ctx, source)
Destructuring does not imply future named-argument labels:
parse(alloc: a, diag: d, source: s)
External labels for destructured parameters are deferred until named-argument design:
fn parse(ctx { alloc, diag }: *const Context, source: Source) Ast
This matters anywhere functions are named by type, including structural constraints, semantic contracts, function pointers, and future comptime reflection over declarations:
fn write(self: *Self, io: *Io, bytes: []const u8) usize!WriteError
An implementation must match the required parameter and return types. Destructuring a parameter in the implementation is allowed when it does not change the parameter type.
Function Type Expressions¶
Function type expressions use bodyless fn(...) ReturnType syntax:
const CompareU8 = fn(a: *const u8, b: *const u8) Ordering
Parameter names are allowed in function type expressions as documentation metadata and are ignored semantically:
const CompareU8 = fn(*const u8, *const u8) Ordering
Function type expressions do not allow destructuring patterns because they do not introduce bindings:
const ParseFn = fn(ctx: *const CompilerContext, source: Source) Ast!ParseError
Function type expressions have no body, so they require a complete return type and error type:
const Reader = fn(path: Path) []u8!ReadError
const AnyReader = fn(path: Path) []u8!Error
Function type expressions also require concrete runtime parameter types. Anonymous static constraint pointer parameters such as *const impl Sequence(f32) are function-declaration parameter shorthand, not function pointer ABI types:
const Sum = fn(xs: *const impl Sequence(f32)) f32
This does not forbid function pointers to concrete static-dispatch instantiations. A function pointer type must name the concrete ABI after the implementer type has been chosen:
fn sum(xs: *const impl Sequence(f32)) f32
const SumBuffer = fn(xs: *const Buffer) f32
V1 does not define a direct source spelling for naming a generic function specialization as a function item. Use a concrete wrapper when a function pointer value needs a selected concrete ABI:
fn sum_buffer(xs: *const Buffer) f32 {
return sum(xs)
}
const f: *const SumBuffer = sum_buffer
Use *dyn Sequence(f32) when one runtime function pointer must accept many concrete sequence implementers through a single erased ABI.
Invalid bodyless inference
Bare T! is invalid in bodyless function type expressions because there is no body to infer from.
Future anonymous functions
Future anonymous functions are deferred to CEP-0017: Function Literals and Callable Values. Return inference belongs to functions and function literals with bodies, not bodyless function type expressions.
Function Items¶
fn(...) ReturnType is a function item type. Values of this type are compile-time-known function definitions:
fn scale(x: i32) i32 {
return x * 2
}
const scale_fn: fn(i32) i32 = scale
Function item values are not runtime-storable values in V1. They are valid for const bindings and comptime parameters:
fn apply(comptime f: fn(i32) i32, x: i32) i32 {
return f(x)
}
A comptime fn declaration also produces a compile-time-known function item, but that item is comptime-only. Calling it implicitly forces comptime evaluation and all arguments it depends on must be comptime-known:
comptime fn fibonacci(n: u32) u32 {
if n < 2 {
return n
}
return fibonacci(n - 1) + fibonacci(n - 2)
}
const fib10 = fibonacci(10)
Runtime-known arguments are rejected:
fn runtime(n: u32) u32 {
return fibonacci(n) // error: n is runtime-known
}
Runtime storage
V1 does not allow function item values in runtime var storage, struct fields, arrays, or slices. Use a function pointer for runtime storage.
Function Pointers¶
Runtime function pointer values are pointers to function types:
fn sort_u8(items: []u8, compare: *const CompareU8) void {
}
fn sort_u8_inline(items: []u8, compare: *const fn(a: *const u8, b: *const u8) Ordering) void {
}
Function pointers are non-null unless wrapped in ?:
callback: ?*const Callback
Function items coerce to compatible function pointer types in expected-type contexts:
const scale_ptr: *const fn(x: i32) i32 = scale
comptime fn items do not coerce to runtime function pointer types:
const fib_ptr: *const fn(n: u32) u32 = fibonacci
Invalid address-of form
V1 does not use address-of syntax for function items:
const scale_ptr: *const fn(x: i32) i32 = &scale
Method function pointers use the receiver-explicit signature. Bound method values are deferred with closures and captures:
const WriteFn = fn(self: *File, bytes: []const u8) usize!WriteError
var write_fn: *const WriteFn = File.write
var n = write_fn(file, bytes)
Default parameter values are declaration metadata, not part of function type identity, ABI shape, or any future overload identity. Calling through a function pointer requires the full parameter list.
Calling Convention¶
Calling convention is part of function type identity and function pointer compatibility. The default calling convention is CallConv.catalyst.
Non-default calling conventions use @callconv(...) before the fn type form:
const CScale = @callconv(.c) fn(x: f32) f32
const c_scale_ptr: *const CScale = scale_c
const fabs: *const @callconv(.c) fn(x: f32) f32 = c_fabs
@callconv(.c) changes call ABI only. It does not export a Catalyst function, import an external symbol, or set a link name. Catalyst-default and C ABI function pointers are not interchangeable without an explicit adapter.
@callconv is valid on function declarations and function type expressions. It is not valid on V1 semantic contract operations. On inherent methods, it is checked against the receiver-explicit signature and normal ABI-safety rules.