C2 incorporates standardized attributes. There can also be compiler-specific attributes to do all sorts of funky things the compilers do.

The currently supported attributes are:

  • export (type, func, var)
  • packed (type)
  • unused (type, func, var)
  • unused_params (func)
  • section (func, var), requires argument
  • noreturn (func)
  • inline (func)
  • aligned (type, func, var), requires argument
  • weak (func, var)
  • opaque (public struct/union types)

The standard syntax for all attributes is @( ) (get it?!, @, at, attributes... ;) )

Please, take a look at the following example showing how to add them to various declarations:

// variables
i32 counter @(unused);
i32[1024] bigdata @(section="data") = {};

// types
type Point struct {
    i32 x;
    i32 y;
} @(packed, aligned=16)

type Weird enum u32 {
} @(unused)

// functions
public func void init() @(export) {
    // ..

NOTE: compiler-specific attributes will be required to start with an underscore, like _c3_my_attribute_, so other compilers can recognize and ignore them

Opaque pointers

The opaque attribute deserves some special attention. It is used to implement the opaque pointer pattern in C2. See the Wikipedia article Opaque Pointer for more background info.

In short, opaque pointers are used to hide the implementation while giving the users a typed handle to pass to your library, maintaining type safety. The opaque attribute can only be used on public struct/union types and tells the compiler that other modules can only use that type by pointer and are not allowed to dereference it.

public type Handle struct {
    ..   // members are not visible outside module
} @(opaque)

when c2c generates an interface file (eg. module.c2i), it will only generate

type Handle struct {} @(opaque)

Note that it is allowed to put other non-public types as full members inside a public opaque struct, since the members are not visible outside the module.