Ruta graveolens  ·  notes from a language experiment  ·  cultivated since 2025

Call Expressions

A call expression invokes a function with a list of arguments.

The callee may be either a function name or a callable function alias defined by a constant item (6.5:12). Calling an alias is equivalent to calling the function it aliases.

call_expr = expression "(" [ call_arg { "," call_arg } ] ")" ;
call_arg  = [ "inout" | "borrow" ] expression ;

An inout argument must denote a place; that requirement is a post-parse legality rule of the parameter-mode system (6.1:17), not a grammar restriction (see the grammar notes in appendix A). A borrow argument that denotes no place is elaborated into one instead of being rejected (6.1:39).

The number of arguments MUST match the number of parameters in the function signature. Each explicit argument's source-level passing mode MUST exactly match the corresponding parameter: an inout parameter requires an inout argument, a borrow parameter requires a borrow argument, and every other parameter, including a comptime parameter, requires an unmarked argument. Arguments to built-in call forms and enum tuple-variant payloads are likewise unmarked unless a source-level parameter explicitly declares a mode.

A method receiver is not an explicit argument for this rule. Its passing mode is selected automatically from the receiver declaration as specified by the receiver-autoref rule (6.4:25).

Each argument's type MUST be the corresponding parameter's type after any argument-position coercion explicitly defined by that type's rules. Those coercions include the never type (3.4:3), a first-class str or string buffer viewed through a borrow str parameter, a caller-owned string buffer viewed through an inout str parameter (3.7:55, 3.7:58, 3.7:60), and the analogous fixed-array-to-slice coercion while the slices preview is enabled. View materialization may change the physical calling convention — for example, a borrowed two-word view is passed by value — but it does not admit an unrelated source type or change the exact source-level argument-mode rule (4.10:3). No other type difference is accepted (core calculus docs/formal/01-core-calculus.md §5.8, rule (Call)).

The type of a call expression is the function's declared return type (core calculus docs/formal/01-core-calculus.md §5.8, rule (Call)).

A call expression evaluates to the value the invocation returns: the callee's body is evaluated with each parameter bound to its corresponding argument — a by-value argument's evaluated value in fresh storage, or, for an inout/borrow argument, the argument place itself (6.1:18) — and the value the body produces (see 4.5 and 4.9) is the call expression's value (core calculus docs/formal/01-core-calculus.md §6.9, rules (D-Call)/(D-Return-Value)). When the return type is (), the call evaluates to ().

Passing an argument by value is a use of it — a move for a non-Copy type, a copy for a Copy type (3.8:11). An inout/borrow argument is not used; it takes a scoped loan for the call's duration (6.1; core calculus docs/formal/01-core-calculus.md §5.4). Those rules are specified in sections 3.8 and 6.1, not here.

fn add(x: i32, y: i32) -> i32 {
    x + y
}

fn main() -> i32 {
    add(40, 2)  // 42
}

Arguments are evaluated left-to-right before the function is called, as specified in section 4.0 (core calculus docs/formal/01-core-calculus.md §6.2).

Call expressions can be nested:

fn add(x: i32, y: i32) -> i32 { x + y }

fn main() -> i32 {
    add(add(10, 20), add(5, 7))  // 42
}