Attribute

Attribute ::= AbstractAttribute | ConstructorAttribute | DeprecatedAttribute | DestructorAttribute | FinalAttribute | ForeignAttribute | InlineAttribute | LLVMAttribute | OperatorAttribute | OverloadAttribute | OverrideAttribute | UnittestAttribute | VirtualAttribute | UserAttribute ;

AbstractAttribute§

Grammar§

AbstractAttribute ::= "@" "abstract"

Semantics§

The @abstract attribute marks a class member function as virtual so that its address is written in the virtual table, however the body can be omitted.

It is illegal to call an abstract function that’s not been overridden. This is currently verified by a runtime check that has for effect to crash the program if such a call ever happens.

ConstructorAttribute§

Grammar§

ConstructorAttribute ::= "@" "constructor"

Semantics§

Member constructor§

The @constructor marks a member function as a constructor. Constructor functions

struct S
{
    var u64 v;
    // rewritten @constructor function create(S* this; u64 v)
    @constructor function create(u64 v)
    {
        this.v = v;
        return;
    }
}

var S* s = S.create(123);

Before the call, the compiler generates a heap allocation on a temporary. The call uses the temporary as this. After the call, as constructors are void functions, an AssignExpression returns the temporary.

var auto a = C.create();

// is rewritten

var C* __tmp  = malloc(C.sizeof):C*;
C.create(__tmp);
var auto a = __tmp;

After the mallocation the compiler copies the default aggregate initializer. the allocation and the initializer copy require both the static type to be correct, that’s why constructors are not allowed to be virtual. Virtual calls are only possible after copy of the initializer, which setup the virtual table.

static constructor§

static constructor can be used to initialize static members or globals. They are run automatically before the program main function.

static constructors

DeprecatedAttribute§

Grammar§

DeprecatedAttribute ::= "@" "deprecated"

Semantics§

The @deprecated attribute is used to mark a declaration as obsolete.

DestructorAttribute§

Grammar§

DestructorAttribute ::= "@" "destructor"

Semantics§

Member destructor§

The @destructor attribute marks a member function as a destructor.

struct S
{
    @constructor function create() { return; }
    // rewritten @destructor function destroy(S* this)
    @destructor function destroy() { return; }
}

var auto s = S.create();
s.destroy();
assert(s == null);

After the call the compiler deallocates the heap chunck that is pointed by the argument and assigns it to null.

Static destructor§

static destructor can be used to finalize static members or globals. They have the same requirements as member destructors but dont have any hidden this parameter. They are run automatically after the program main function.

FinalAttribute§

Grammar§

FinalAttribute ::= "@" "final"

Semantics§

The @final attribute, when attached to a ClassDeclaration, indicates that the class cannot be derived anymore.

unit u;

@final class C  {}
class D : C     {} // error, C is final

When attached to a FunctionDeclaration it indicates that the function cannot be overridden anymore.

unit u;

class C     {        @virtual function  f(){}   }
class D : C { @final @override function f(){}   }
class E : D {        @override function f(){}   } // error, D.f is final

The attribute can be used as an optimization. Under certain circumstances it allows to perform a call without using the virtual table, saving several indirections.

Even in the scope of a function body the devirtualization is not based on flow analysis, and only the two most obvious cases are handled

unit u;

class C
{
    @virtual function  f(){}
}
@final class D : C
{
    @constructor function create(){}
    @final @override function f(){}
}

function start()
{
    var D* d = D.create();
    test1(d);
    test2(d:C*);
}

function test1(D* d)
{
    // devirtualized:
    // the `this` arg `d`, has the type `D*` and `D` is final
    d.f();
}

function test2(C* c)
{
    // virtual:
    // 1. C.f() is not final.
    // 2. the `this` arg `c`, has the type `C*` and `C` is not final
    c.f();
}

ForeignAttribute§

Grammar§

ForeignAttribute ::= "@" "foreign"

Semantics§

The @foreign attribute indicates that

This is notably used to bind declarations defined in C libraries

@foreign function puts(s8* str): s32;

function main(): s32
{
    puts("call libc puts");
    return 0;
}

Only, [StructDeclaration]s, [UnionDeclaration]s, FunctionDeclarations and VariableDeclarations can be @foreign.

@foreign alias int = s32; // error

Foreign struct, union or function must not have bodies whereas foreign var cant have initializers.

@foreign function puts(s8* str): s32 {return 0;} // error

There is currently no predefined attribute to alter the mangling of styx symbols, they are simply exported using their fully qualified names.

InlineAttribute§

Grammar§

InlineAttribute ::= "@" "inline" ( "(" Expression")")?

Semantics§

The @inline attribute takes an optional argument that must be evaluable to a BoolExpression.

@inline(true) function f(){}

function g()
{
    f(); // will be inlined
}

Note that inlining only occurs if optimization are enabled (-O1, -O2, -O3).

LLVMAttribute§

Grammar§

LLVMAttribute ::= "@" "LLVM" ( "(" Expression ")" )?

Semantics§

The @LLVM attribute takes one or no argument.

It is used to mark FunctionDeclarations as matching to an LLVM intrinsic. The optional argument must be a StringExpression that allows to identify an overloaded intrinsic. When specified the declaration must matches exactly to the official declaration.

// https://llvm.org/docs/LangRef.html#llvm-ctlz-intrinsic
@LLVM("ctlz.i8")  function ctlz8(u8 value; bool is_zero_undef): u8;
@LLVM("ctlz.i16") function ctlz16(u16 value; bool is_zero_undef): u16;
@LLVM("ctlz.i32") function ctlz32(u32 value; bool is_zero_undef): u32;
@LLVM("ctlz.i64") function ctlz64(u64 value; bool is_zero_undef): u64;

function main(): s32
{
    { var u8  a = 1; assert(ctlz8(a, false)  ==  7); }
    { var u16 a = 2; assert(ctlz16(a, false) == 14); }
    { var u32 a = 4; assert(ctlz32(a, false) == 29); }
    { var u64 a = 8; assert(ctlz64(a, false) == 60); }
    return 0;
}

OperatorAttribute§

Grammar§

OperatorAttribute ::= "@" "operator" "(" Expression (',' Expression )* ")"

Semantics§

The @operator attribute takes one or more arguments.

It is used to mark FunctionDeclarations and OverloadDeclarations as alternative to try when built-in operators are used on variable of custom types.

@operator declarations must be member of [StructDeclaration]s or ClassDeclarations. Operator overloads are not supported at the UnitDeclaration scope.

Each parameter of the attribute indicates, using a dummy expression, the operator implemented by the member. For example

and so on. @operator arguments are templates of the effect. Note that they have not to be semantically correct.

Even if usually a single parameter is used, two operator overloads are allowed to share the same implementation.

struct Int32
{
    var s32 value;

    function addBuiltInt32(s32 value): s32
    {
        return this.value + value;
    }

    function addInt32(Int32 other): s32
    {
        return this.value + other.value;
    }

    // allows to add Int32 or s32 to a Int32 using "+"
    @operator(a + b) overload binaryAdd
    {
        addBuiltIn32,
        addInt32,
    }
}

function test()
{
    var Int32 a;
    auto b = a + 42; // rewritten in a first time to `auto b = a.binaryAdd(42);`
                     // then after full semantics to `auto b = a.addBuiltIn32(42);`
}

When a custom type implements several operators and given the fact the overloads are explicit, it’s prefereable to use @operator on OverloadDeclarations rather that on FunctionDeclarations.

All the unary and binary operators are overloadable, to the exception of those used by

For binary the expressions the the LHS is the aggregate that implements the overload whereas the RHS is the overload parameter.

For the [AddEpxression] and the MulExpression the overload is also tried in the other way.

struct A
{
    s32 value;
    @operator (a+b) function add(s32 value): s32 { return this.value + value; }
    @operator (a/b) function div(s32 value): s32 { return this.value / value; }
}
var A a;
a + 3; // a.add(3)
3 + a; // a.add(3)
3 / a; // NG, division operands cant be exchanged

Primary expressions are not overloadable to the exception of the BoolExpression. It can be used to evaluate the aggregate as a condition

struct R
{
    @operator(true) // `true` or `false` does not matter here
    function toCondition()
    {
        return true;
    }
}
var R r;
assert(r);

Compound expressions are not overloadable to the exception of

@operator functions may be static. This can be used for example to construct value types. A CallExpression on a type is translated to another CallExpression on a static function implemented for the type.

struct Point
{
    var s32 x;
    var s32 y;

    @operator(a(b)) static function initPoint(s32 x; s32 y): Point
    {
        var Point pt;
        result.x = x;
        result.y = y;

        return pt;
    }
}

function test()
{
    var Point p = Point(44,13);
}

OverloadAttribute§

Grammar§

OverloadAttribute ::= "@" "overload" "(" Expression ")"

Semantics§

The @overload attribute indicates that a function is part of an OverloadDeclaration.

The mandatory argument must be an IdentExpression. It is used to implicitly create a set or to retrieve an existing one.

The semantics of implicit sets does not differ from explicit OverloadDeclarations.

OverrideAttribute§

Grammar§

OverrideAttribute ::= "@" "override"

Semantics§

The @override attribute indicates that the entry of virtual table, initially copied from the parent class, is overwritten with the address of the override.

UnittestAttribute§

Grammar§

UnittestAttribute ::= "@" "unittest"

Semantics§

The @unittest attribute indicates that a function is a unit test. Annotated functions must

unit u;

@unittest function test1() {}                   // OK
@unittest function test2() : s32 {return 0;}    // NG, has a return type

struct S
{
    @unittest function test3() {}               // NG, not in the global scope
}

the unit tests declared in a unit can be obtained as an array of static function()* using the getUnittests command in an EchoExpression.

@unittest functions may only be considered available if --unittest is a compiler argument.

VirtualAttribute§

Grammar§

VirtualAttribute ::= "@" "virtual"

Semantics§

The @virtual attribute marks a class member function as virtual so that its address is written in the virtual table.

UserAttribute§

Grammar§

UserAttribute ::= "@" Identifier ("(" Expression (',' Expression )* ")")?

Semantics§

Users attributes use an identifier that must not collide with the reserved attributes. They are then followed by no or several expressions.