Default Parameters¶
Accepted
Complete for V1 source semantics; named-argument interactions are deferred.
Default parameter values are in scope for V1:
fn reserve(buffer: *Buffer, count: usize, exact: bool = false) void {
}
A default parameter value is used when the caller omits that argument. V1 positional calls may omit only the trailing suffix of defaulted parameters:
fn parse(
{ alloc, diag }: *const CompilerContext,
source: Source,
mode: ParseMode = .module,
recover: bool = true,
) Ast!ParseError {
}
parse(&ctx, source)
parse(&ctx, source, .script)
parse(&ctx, source, .script, false)
Once a positional parameter has a default, all following positional parameters must also have defaults. This applies to comptime parameters too. Future named arguments may add non-positional omission rules.
Default Evaluation Semantics¶
Default argument semantics are:
- names in default expressions are resolved and type-checked in the function declaration scope
- default expressions are evaluated each time the argument is omitted at a call site
- default expressions use ordinary expression semantics and may have side effects
- default expressions may refer to earlier parameters
- default expressions may not refer to later parameters
- defaults for
comptimeparameters must be comptime-evaluable Scope.caller()is invalid in default parameter expressions
Default expressions may use earlier capability parameters:
fn make_buffer(alloc: *impl Allocator, capacity: usize = 4096) Buffer!AllocError {
}
Example:
const default_count = 10
fn reserve(buffer: *Buffer, count: usize = default_count, capacity: usize = count) void {
}
If a caller has a local default_count, it does not affect reserve's default. The name was resolved when reserve was declared.
Boundaries¶
Default parameter values require a function declaration with a body in V1. Bodyless import/prototype defaults are deferred with broader FFI/import design.
Inherent methods may use default parameters because they are ordinary functions with receiver syntax.
Contract operation surfaces may not use default parameters in V1. This includes required operations and default methods in contracts. Implementation-body functions that satisfy contract operations may not add defaults. Use ordinary helper functions or inherent methods for convenience defaults around contract operations.
Defaults may appear on exported Catalyst functions with bodies, but they affect only Catalyst source calls. The exported ABI has the full parameter list, and external C callers must pass every argument.
Default parameter values are declaration metadata, not part of function type identity, contract signature compatibility, ABI shape, or any future overload identity. Calling through a function pointer requires the full parameter list.
comptime parameter inference is deferred from V1. Generic calls must supply or otherwise make required comptime parameters explicit according to the accepted V1 call syntax.
Default Reflection¶
Default values are part of the source-level call contract. Adding a default is an additive source API change. Removing a public default is source-breaking for callers that omitted the argument. Changing a default is source-compatible but behavior-changing. These compatibility classifications are tooling and versioning policy, not compiler or codegen behavior.
Function type reflection ignores defaults. Function declaration reflection preserves Type.DefaultExpression metadata on parameter declaration metadata, including source location and optional normalized source text for display. Function Reflection owns the exact metadata shape. Public expression-tree and comptime-known value reflection are deferred from V1.
Function declaration reflection records defaults. Call analysis and lowering may materialize omitted arguments internally, but call-site expansion metadata belongs to AST/SIR/IR design rather than Type.FunctionDecl.