Attribute

Attribute ::= AbstractAttribute | AsmAttribute | ConstructorAttribute | DeprecatedAttribute | DestructorAttribute | FinalAttribute | ForeignAttribute | InlineAttribute | LLVMAttribute | NoInitAttribute | NoOptAttribute | OperatorAttribute | OverloadAttribute | OverrideAttribute | ProtectionAttribute | ReturnAttribute | TLSAttribute | UnittestAttribute | VirtualAttribute | UserAttribute ;

Semantics§

Although Declarations rule for the attributes is Attribute* (zero or more), it should be a syntax error to specify more than once the same attribute.

This rule cannot be reasonably expressed formally however it is a requirement for a styx parser to at least emit a warning in case duplicated attributes.

AbstractAttribute§

Grammar§

AbstractAttribute ::= "@" "abstract"

Semantics§

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

It is illegal to call an abstract function.

This is currently verified by a runtime check that has for effect to crash the program.

AsmAttribute§

Grammar§

AsmAttribute ::= "@" "asm" "(" Expression ")"

Semantics§

The @asm attribute indicates which asm syntax flavor is used in the AsmExpressions located in the function it is attached to.

The expression must be an IdentExpression that gives either intel or att.

@asm(intel) function f(): bool
{
    return asm("mov al, 1", "={al}"):bool;
}

@asm(att) function g(): s32
{
    return asm("movb $1, $0", "=r,i", 1):bool;
}

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* s1 = (new S).create(123);
var S  s1 = S.create(123);

When the this argument matches to the this parameter, i.e a pointer, the constructor is called and the compiler adjust the expression so that the this argument appears as a return.

var auto a = (new C).create();

// is rewritten

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

When no this argument is found, the compiler generates a stack allocated variable, copy the default aggregate initializer and finally calls the constructor with a pointer to this variable.

var auto a = C.create();

// is rewritten

var C __tmp;
C.create(&__tmp);
var auto a = __tmp;

static constructor§

Static constructors can be used to initialize static members and global variables. They are run automatically before the program main function.

static constructors

unit u;

var s32 a;

@constructor static u_init_1()
{
    a = 8;
}

function main(): s32
{
    assert(a == 8);
    return 0;
}

When several static constructor are declared, the order of execution is undefined.

DeprecatedAttribute§

Grammar§

DeprecatedAttribute ::= "@" "deprecated" ( "(" Expression")")?

Semantics§

The @deprecated attribute is used to mark a declaration as obsolete. The compiler generates warnings when an IdentExpression resolved to a deprecated declaration.

The optional argument must be simplifiable at compile-time to a StringExpression. That string is then displayed instead of the default message.

DestructorAttribute§

Grammar§

DestructorAttribute ::= "@" "destructor"

Semantics§

Member destructor§

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

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

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

// same as `delete s`
s.destroy();
free(s:u8*);

Destructors functions dont have to be called explicitly

Static destructor§

Static destructor can be used to finalize static members and 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.

When several static desstructor are declared, the order of execution is undefined.

FinalAttribute§

Grammar§

FinalAttribute ::= "@" "final"

Semantics§

Final classes§

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

unit u;

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

A class can also be set @final using an alias to a generic application

unit u;

class C[T]  {}
@final alias D = apply(C, :u8*);
class E : D {} // error, D is final

Final functions§

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 the use of the virtual table, saving several indirections.

The optimization is implementation defined but may at least occur if

unit u;

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

function start()
{
    var D* d = (new 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();
}

Final enums§

When attached to an EnumDeclaration it indicates that the enum cannot be extended.

@final enum A
{
    a0, a1
}

enum AB : A // error, A is final
{
    a2
}

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 and 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.

The attribute can be applied to local declarations. That implies that the declarations is implictly static.

function sayHelloTo(s8[] name)
{
    @foreign function printf(s8 specifier; ...): s32;
    printf("hello %s\n", (name ~ "\0").ptr);
}

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 qualified or overloaded intrinsic.

If not specified then the name of the function must match exactly to the targeted intrinsic name. If specified then it must match exactly to the intrinsic name.

// 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;
}

NoInitAttribute§

Grammar§

NoInitAttribute ::= "@" "noinit"

Semantics§

Can only be attached to local VariableDeclarations.

Instruct the backend not to store the default initializer for the variable type, meaning that the variable value is undefined until first assignment.

NoOptAttribute§

Grammar§

NoOptAttribute ::= "@" "noopt"

Semantics§

If attached to a FunctionDeclaration then the optimizer is instructed not to optimize the body.

OperatorAttribute§

Grammar§

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

Semantics§

General presentation§

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 AddExpression 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

The DotExpression can be indirectly overloaded by overloading the IdentExpression:

struct JSON
{
    var JSON*[+] members;
    var s8[+] name;
    var JSONValue value;
    @operator(id) function getMember(s8[] identifier): auto
    {
        foreach var auto m in members[] do
            if m.name == identifier do
                return m;
        return null;
    }
}

var JSON js;
var JSON* m = js.request; // js.getMember("request");

Primary expressions§

It is generally not possible to overload PrimaryExpressions but there are two exceptions. The first is the BoolExpression which 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);

The second is the DollarExpression. Note that just like for the default expression, @operator($) is only tried when used within the square brackets of the IndexExpressions and the SliceExpressions.

Compound expressions§

Complex sequences of expressions are not overloadable to the exception of

Static operators§

@operator functions may be static. For example to implement an alternative value constructor:

struct Point
{
    var s32 x;
    var s32 y;

    @constructor create(s32 x; s32 y)
    {
        this.x = x;
        this.y = y;
    }

    @operator(a(b)) static function initPoint(s32 x; s32 y): Point
    {
        return Point.create(x,y);
    }
}

function test()
{
    var Point p1 = Point.create(44,13);
    var Point p2 = Point(44,13);        // rewritten `Point.initPoint(44,13)`
    assert(p1 == p2);
}

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 dont 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.

ProtectionAttribute§

Grammar§

ProtectionAttribute ::= "@" "private" | "@" "protected" | "@" "public" | "@" "strict" ;

Semantics§

Overwrite the current ProtectionDeclaration for the declaration that’s attached. Past the declaration, the previous ProtectionDeclaration value is restored.

struct S
{
protection(strict)
    var s32 a;          // strict
    @public var s32 b;  // public
    var s32 c;          // strict
}

ReturnAttribute§

Grammar§

ReturnAttribute ::= "@" "return"

Semantics§

The @return attribute allows to skip or handle the automatic management of local variables that happens on function returns.

It is possible to annotate either variables or member functions.

Variables§

If a local variable is annotated @return, the automatic management is simply skipped.

For TypeRcArray that means that the array reference count is not touched at all, skipping the default protection system.

function lessEfficientButWorks(): s8[+]
{
    var s8[+] r = get();
    // 1. hidden protection: `r.incref`
    // 2. hidden auto-management: `r.decref`
    return r;
}
function moreEfficient(): s8[+]
{
    @return var s8[+] r = get();
    // no hidden refcount operations at all
    return r;
}

For classes, structs and unions that means that the destructor function (see DestructorAttribute) is not called.

unit demo_return_var;

struct S
{
    var s32 member = 8;
    @destructor function destroy()
    {
        member = 0;
    }
}

function test(): S
{
    @return var S s;
    assert(s.member == 8);
    return s;
}

function main(): s32
{
    var S s = test();
    assert(s.member == 8);
    return 0;
}

Return constructor functions§

If a non-static, parameter-less, member function is annotated with @return then it is called on return to adjust the aggregate instance and copy it right before the destructor call.

unit demo_return_func;

struct S
{
    var s32 member = 8;
    @destructor function destroy()
    {
        member = 0;
    }

    @return function returnCtor(): S  // copy by return
    {
        var S* s = this;
        // still on what will be destroyed, mutate, adjust refcount, etc.
        return *s;
    }
}

function test(): S
{
    var S s;
    assert(s.member == 8);
    return s;
}

function main(): s32
{
    var S s = test();
    assert(s.member == 8);
    return 0;
}

TLSAttribute§

Grammar§

TLSAttribute ::= "@" "tls"

Semantics§

By default a static VariableDeclaration is shared by all the threads.

The @tls attribute indicates that it is available as a distinct entity for each thread and that consequently it does not require synchronization.

UnittestAttribute§

Grammar§

UnittestAttribute ::= "@" "unittest"

Semantics§

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

unit u;

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

struct S
{
    @unittest static 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 of the EchoExpression.

@unittest functions are only 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§