Function declaration

Grammar§

FunctionDeclaration ::= Attribute* StorageClass* "function" Identifier? "(" FunctionParameters? ")" (":" TypeReturn)? FunctionBody TypeReturn ::= "var"? Type FunctionParameters ::= FunctionVariadicParameter | FirstFunctionParameter? (";" VariableDeclaration)* (";" FunctionVariadicParameter)? ; FirstFunctionParameter ::= ThisParameter | VariableDeclaration ; ThisParameter ::= StorageClass+ Type "this" FunctionVariadicParameter ::= "..." FunctionBody ::= ";" | BlockStatement ;

Semantics§

Attributes§

See Attribute for a list of the attributes allowed on function declarations.

Storage classes§

Functions only support the static storage class. At the global scope, static is implicit. In aggregates, static can be used to indicate that the function does not require a this argument.

When a nested function is not static then it can access to the local variables declared in the frames of the parent functions. The current implementation is based on automatically managed var parameters and arguments. Variadic functions are not allowed to access the parent frames.

Function name§

Just like other declarations the Identifier must be unique in the scope. This aspect does not prevent functions overloads, which in styx, are explicitly declared using an OverloadDeclaration.

If a function declaration is used to declare a TypeFunction then the name is optional.

The this parameter§

The this parameter is automatically created for the member functions that are not static however it can be explicitly declared when required, e.g for an AliasDeclaration.

struct S { }
// Func is the type of parameter-less S member function returning an s8
alias Func = function (S* this): s8;

The this parameter must always have for type a pointer to the parent aggregate and no storage class.

Regular parameters§

Parameters are declared just like variables to the difference that the storage classes are optional.

No storage class§

If no storage class is specified then the parameter is passed by value. IdentExpressions that resolve to those parameters are then rvalues.

Certain expressions such as the AssignExpression, the AtExpression, the IndexExpression but also the .length property, the .ptr property, etc. have for effect to turn the parameter to an lvalue, however this is made by local copy and has no impact on the variables passed as arguments, e.g

function f(ssize v): ssize
{
    v++;    // v is assigned but has no address to create the store
    v*=8;
    return v %= 100;
}

is rewritten

function f(ssize v): ssize
{
    var ssize cp = v;
    cp++;
    cp*=8;
    return cp %= 100;
}

var§

If a parameter is var then its matching argument is passed by reference. IdentExpressions that resolve to those parameters are then lvalues.

static§

If a parameter is static then its matching argument must also be static. This is only verifiable if the argument is a static VariableDeclaration.

Variadic parameter§

A single FunctionVariadicParameter is allowed. It indicates that 0 or more additional arguments are allowed, following the last regular parameter. For now it is only used to link against foreign functions, e.g libc printf().

@foreign function printf(s8* specifier; ...): s32;

Parameter default value§

If a parameter specifies an initializer Expression then it is used as argument and only when a CallExpression does not contains enough arguments. All parameters that are declared after an initialized parameter must also be initialized.

Parameter type§

TypeAuto is not allowed on parameters.

Return type§

Unspecified§

If the return type is not specified then it is set to an internal void type.

var§

If the return type is specified and prefixed with the var storage class then the return is a lvalue. For example struct members such as static arrays can be returned by reference, which avoids the creation of temporaries.

auto§

If the return type is auto then the actual return type is infered from the ReturnStatements declared in the body.

If the first return does not have an expression or if it returns a void call then the internal void type if infered and the other ReturnStatements must either return void calls or nothing.

function f(): auto
{
    if condition do
        return;     // internal void is inferred
    return 0;       // error as per implicit conversion rules
}
function v();
function f(): auto
{
    if condition do
        return v(); // internal void is inferred
    return;         // OK
}

Otherwise the type given by the expression is used to implictly convert the expression of the other returns

auto returns are never implictly var auto.

function f(): auto
{
    if condition do
        return 0;   // u64
    return v();     // OK, rewritten "return v():u64;"
}
function v(): s32;

Missing and implicit returns§

If the body of a function that returns void contains no final ReturnStatement then it is automatically inserted.

function f()
{
    if condition do
        return;
    // return; inserted implictly
}

If the body of a function that does not return void contains no final ReturnStatement then an error is issued.

function f(): s32
{
    if condition do
        return 0;
    // dont know what to return so...
    // error, missing return
}

A missing final return statement is allowed if the last statement is an AssertStatement with 0 or false as condition.

function f(): s32
{
    assert(0); // no error
}

Function body§

Unspecifed body§

If the function body consists of a semicolon then the function must either exist in another unit or in a foreign object file. If this is not the case then linking fails.

Regular body§

A body contains statements and declarations. Out of order declaration are not allowed in function bodies.