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)
  • cname (type, func, var), interface
  • no_typedef (interface struct/union types)

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

Take a look at the following example showing their usage in 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.

Cname / No_typedef

Some legacy C types/functions don't map really well to the C2 style. An example of this is stat.h:

struct stat {
    // ...

int stat(const char *pathname, struct stat *statbuf);

So both the struct and the function are called stat.

To solve this situation and offer a nice way to embed these calls into a C2 application, C2 offers the attributes cname and no_typedef. In the C2 version of sys_stat.h:

type Stat struct {
    // ...
} @(cname="stat", no_typedef)

func c_int stat(const c_char* pathname, Stat* buf);

This means C2 code can use 'Stat' instead of 'struct stat', so the spelling conventions stay intact (types start with capital case). Also for the C-backend, we cannot generate:

typedef struct stat_ stat;

struct stat_ {
    // ...

...since that would clash with the function stat. So the attribute no_typedef tells c2c not to generate the typedef, instead simply:

struct stat {
    // ...