All Features
Here is a summary of all the features of C3 and changes from C
Symbols and literals
Changes relating to literals, identifiers etc.
Added
- 0o prefix for octal.
- 0b prefix for binary.
- Optional ”_” as digit separator.
- Hexadecimal byte data, e.g
x"abcd"
. - Base64 byte data, e.g.
b64"QzM="
. - Type name restrictions (PascalCase).
- Variable and function name restrictions (must start with lower case letter).
- Constant name restrictions (no lower case).
- Character literals may be 2, 4, 8, 16 bytes long. (2cc, 4cc etc).
- Raw string literals between ”`”.
\e
escape character.- Source code must be UTF-8.
- Assumes
\n
for new row\r
is stripped from source. - Bit-width integer and float suffixes:
u8
/i8
/u16
/i16
/…f32
/f64
/… - The
null
literal is a pointer value of 0. - The
true
andfalse
are boolean constants true and false.
Removed
- Trigraphs / digraphs.
- 0123-style octal.
z
,LL
andULL
suffixes.
Built-in types
Added
- Type declaration is left to right:
int[4]*[2] a;
instead ofint (*a[2])[4];
- Simd vector types using
[<>]
syntax, e.g.float[<4>]
, use[<*>]
for inferred length. - Slice type built in, using
[]
suffix, e.g.int[]
- Distinct types, similar to a typedef but forms a new type. (Example: the
String
type is a distinctchar[]
) - Built-in 128-bit integer on all platforms.
char
is an unsigned 8-bit integer.ichar
is its signed counterpart.- Well-defined bitwidth for integer types: ichar/char (8 bits), short/ushort (16 bits), int/uint (32 bits), long/ulong (64 bits), int128/uint128 (128 bits)
- Pointer-sized
iptr
anduptr
integers. isz
andusz
integers corresponding to thesize_t
bitwidth.- Optional types are formed using the
!
suffix. bool
is the boolean type.typeid
is a unique type identifier for a type, it can be used at runtime and compile time.any
contains atypeid
andvoid*
allowing it to act as a reference to any type of value.anyfault
holds anyfault
value (see below).
Changed
- Inferred array type uses
[*]
(e.g.int[*] x = { 1, 2 };
). - Flexible array member uses
[*]
.
Removed
- The spiral rule type declaration (see above).
- Complex types
- size_t, ptrdiff_t (see above).
- Array types do not decay.
Types
Added
bitstruct
a struct with a container type allowing precise control over bit-layout, replacing bitfields and enum masks.fault
an enum type with unique values which are used together with optional.- Vector types.
- Optional types.
enum
allows a set of unique constants to be associated with each enum value.- Compile time reflection and limited runtime reflection on types (see “Reflection”)
- All types have a
typeid
property uniquely referring to that particular type. - Distinct types, which are similar to aliases, but represent distinctly different types.
- Types may have methods. Methods can be added to any type, including built-in types.
- Subtyping: using
inline
on a struct member allows a struct to be implicitly converted to this member type and use corresponding methods. - Using
inline
on a distinct type allows it to be implicitly converted to its base type (but not vice versa). - Types may add operator overloading to support
foreach
and subscript operations. - Generic types through generic modules, using
(< ... >)
for the generic parameter list (e.g.List(<int>) list;
). - Interface types,
any
types which allows dynamic invocation of methods.
Changed
typedef
is replaced bydef
and has somewhat different syntax (e.g.def MyTypeAlias = int;
).- Function pointer syntax is prefix
fn
followed by a regular function declaration without the function name.
Removed
- Enums, structs and unions no longer have distinct namespaces.
- Enum, struct and union declarations should not have a trailing ’;’
- Inline
typedef
is not allowed.def
can only be used at the top level. - Anonymous structs are not allowed.
- Type qualifiers are all removed, including
const
,restrict
,volatile
- Function pointers types cannot be used “raw”, but must always be used through a type alias.
Introspection
Compile time type methods: alignof
, associated
, elements
, extnameof
, inf
, inner
, kindof
, len
,
max
, membersof
, min
, nan
, names
, params
, returns
, sizeof
, typeid
, values
,
qnameof
, is_eq
, is_ordered
.
Runtime type methods: inner
, kind
, len
, names
, sizeof
.
Expressions
Added
- Expression block using
{| ... |}
. Somewhat similar to GCC statement expressions. - Array initializers may use ranges. (e.g.
int[256] x = { [0..128] = 1 }
) ?:
operator, returning the first value if it can be converted to a boolean true, otherwise the second value is returned.- Orelse
??
returning the first value if it is a result, the second if the first value was an optional value. - Rethrow
!
suffix operator with an implicitreturn
the value if it was an optional value. - Dynamic calls, allowing calls to be made on the
any
and interfaces dispatched using a dynamic mechanism. - Create a slice using a range subscript (e.g.
a[4..8]
to form a slice from element 4 to element 8). - Two range subscript methods:
[start..inclusive_end]
and[start:length]
. Start, end and length may be omitted for default values. - Indexing from end: slices, arrays and vectors may be indexed from the end using
^
.^1
represents the last element. This works for ranges as well. - Range assignment, assign a single value to an entire range e.g.
a[4..8] = 1;
. - Slice assignment, copy one range to the other range e.g.
a[4..8] = b[8..12];
. - Array, vector and slice comparison:
==
can be used to make an element-wise comparison of two containers. ?
suffix operator turns a fault into an optional value.!!
suffix panics if the value is an optional value.$defined(...)
returns true if the last expression is defined (sub-expressions must be valid).$and(...)
$or(...)
perform compile time logic, and may also be written as&&&
and|||
respectively.
- It does not check any elements after the first false value found for
$and()
also written as&&&
. To check both conditions are false use:!false_condition &&& !false_condition
. - It does not check any values after the first true found for
$or()
also written as|||
.
- Lambdas (anonymous functions) may be defined, they work just like functions and do not capture any state.
- Simple bitstructs (only containing booleans) may be manipulated using bit operations
& ^ | ~
and assignment. - Structs may implicitly convert to their
inline
member if they have one. - Pointers to arrays may implicitly convert to slices.
- Any pointer may implicitly convert to an
any
with type being the pointee. - Optional values will implicitly invoke “flatmap” on an expression it is a subexpression of.
- Swizzling for arrays and vectors.
Changed
- Compound literals use
Type { ... }
rather than(Type) { ... }
- Operator precedence of bit operations is higher than
+
and-
. - Well defined-evaluation order: left-to-right, assignment after expression evaluation.
sizeof
is$sizeof
and only works on expressions. UseType.sizeof
on types.alignof
is$alignof
for expressions. Types useType.alignof
.- Narrowing conversions are only allowed if all sub-expressions is as small or smaller than the type.
- Widening conversions are only allowed on simple expressions (i.e. most binary expressions and some unary may not be widened)
Removed
- The comma operator is removed.
Cast changes
Functions
Added
- Functions may be invoked using named arguments, the name is the dot-prefixed parameter name, e.g.
foo(name: a, len: 2)
. - Typed varargs are declared
Type... argument
, and will take 0 or more arguments of the given type. - It is possible to “splat” an array or slice into the location of a typed vararg using
...
:foo(a, b, ...list)
any
varargs are declaredargument...
, it can take 0 or more arguments of any type which are implicitly converted to theany
type.- The function declaration may have
@inline
or@noinline
as a default. - Using
@inline
or@noinline
on a function call expression will override the function default. - Type methods are functions defined in the form
fn void Foo.my_method(Foo* foo) { ... }
, they can be invoked using dot syntax. - Type methods may be attached to any type, even arrays and vectors.
- Error handling using optional return types.
Changed
- Function declarations use the
fn
prefix.
Removed
- Functions with C-style varargs may be called, and declared as external functions, but not used for C3 functions.
Attributes
C3 adds a long range of attributes in the form @name(...)
. It is possible to create custom
attribute groups using def
(e.g. def MyAttribute(usz align) = { @aligned(align) @weak };
) which
groups certain attributes. Empty attribute groups are permitted.
The complete list: @align
, @benchmark
, @bigendian
, @builtin
,
@callconv
, @deprecated
, @dynamic
, @export
,
@extern
, @if
, @inline
, @interface
,
@littleendian
, @local
, @maydiscard
, @naked
,
@nodiscard
, @noinit
, @noreturn
, @nostrip
,
@obfuscate
, @operator
, @overlap
, @priority
,
@private
, @public
, @pure
, @reflect
,
@section
, @test
, @used
, @unused
.
Declarations
Added
var
declaration for type inferred variables in macros. E.g.var a = some_value;
var
declaration for new type variables in macros. E.g.var $Type = int;
var
declaration for compile time mutable variables in function and macros. E.g.var $foo = 1;
const
declarations may be untyped. Such constants are not stored in the resulting binary.
Changed
tlocal
declares a variable to be thread local.static
top level declarations are replaced with@local
. (static
in functions is unchanged)
Removed
restrict
removed.atomic
should be replaced by atomic load/store operations.volatile
should be replaced by volatile load/store operations.
Statements
Added
- Match-style variant of the
switch
statement, allows eachcase
to hold an expression to test. - Switching over type with
typeid
. - Unpack
any
to the underlying type with anany
-switch. nextcase
to fallthrough to the next case.nextcase <expr>
to jump to the case with the expression value (this may be an expression evaluated at runtime).nextcase default
to jump to thedefault
clause.- Labelled
while
/do
/for
/foreach
to use withbreak
nextcase
andcontinue
. foreach
to iterate over arrays, vectors, slices and user-defined containers using operator overloading.foreach_r
to iterate in reverse.foreach
/foreach_r
may take the element by value or reference. The index may optionally be provided.$if
,$switch
,$for
,$foreach
statements executing at compile time.$echo
printing a message at compile time.$assert
compile time assert.defer
statement to execute statements at scope exit.defer catch
anddefer try
similar todefer
but executes only on optional exit or regular exit of scope respectively.do
statements may omitwhile
, behaving same aswhile (0)
if
may have a label. Labelledif
may be exited using labelled break.asm
blocks for inline assembly.- if-try statements allows you to run code where an expression is a result.
- if-catch statements runs code on fault. It can be used to implicitly unwrap variables.
- Exhaustive switching on enums.
Changed
- Switch cases will have implicit break, rather than implicit fallthrough.
assert
is an actual statement and may take a string or a format + arguments.static_assert
is$assert
and is a statement.
Removed
goto
removed, replaced by labelled break, continue and nextcase.
Compile time evaluation
Added
@if(cond)
to conditionally include a struct/union field, a user-defined type etc.- Compile time variables with
$
prefix e.g.$foo
. $if...$else...$endif
and$switch...$endswitch
inside of functions to conditionally include code.$for
and$foreach
to loop over compile time variables and data.$typeof
determines an expression type without evaluating it.- Type properties may be accessed at compile time.
$define
returns true if the variable, function or type exists.$error
emits an error if encountered.$embed
includes a file as binary data.$include
includes a file as text.$exec
includes the output of a program as code.$evaltype
takes a compile time string and turns it into a type.$eval
takes a string and turns it into an identifier.$extnameof
turns an identifier into its string external name.$nameof
turns an identifier into its local string name.$qnameof
turns an identifier into its local string name with the module prefixed.- Compile time constant values are always compile time folded for arithmetic operations and casts.
$$FUNCTION
returns the current function as an identifier.
Changed
#define
for constants is replaced by untyped constants, e.g.#define SOME_CONSTANT 1
becomesconst SOME_CONSTANT = 1;
.#define
for variable and function aliases is replaced bydef
, e.g.#define native_foo win32_foo
becomesdef native_foo = win32_foo;
- In-function
#if...#else..#endif
is replaced by$if
,#if...#elif...#endif
is replaced by$switch
. - For converting code into a string use
$stringify
. - Macros for date, line etc are replaced by
$$DATE
,$$FILE
,$$FILEPATH
,$$FUNC
,$$LINE
,$$MODULE
,$$TIME
.
Removed
- Top level
#if...#endif
does not have a counterpart. Use@if
instead. - No
#include
directives,$include
will include text but isn’t for the same use.
Macros
Added
macro
for defining macros.- “Function-like” macros have no prefix and has only regular parameters or type parameters.
- “At”-macros are prefixed with
@
and may also have compile time values, expression and ref parameters, and may have a trailing body. - Type parameters have the prefix
$
and conform to the type naming standard (“$TypeFoo”). - “ref” parameters are declared using with a
&
prefix operator. This is similar to C++ ref parameters. - Expression parameters are unevaluated expressions, this is similar to arguments to
#define
. - Compile time values have a
$
prefix and must contain compile time constant values. - Any macro that evaluates to a constant result can be used as if it was the resulting constant.
- Macros may be recursively evaluated.
- Macros are inlined at the location where they are invoked.
- Unless resulting in a single constant, macros implicitly create a runtime scope.
Removed
- No
#define
macros. - Macros cannot be incomplete statements.
Features provided by builtins
Some features are provided by builtins, and appears as normal functions and macros in the standard library but nonetheless provided unique functionality:
@likely(...)
/@unlikely(...)
on branches affects compilation optimization.@anycast(...)
casts anany
with an optional result.unreachable(...)
marks a path as unreachable with a panic in safe mode.unsupported(...)
similar to unreachable but for functionality not implemented.@expect(...)
expect a certain value with an optional probability for the optimizer.@prefetch(...)
prefect a pointer.swizzle(...)
swizzles a vector.@volatile_load(...)
and@volatile_store(...)
volatile load/store.@atomic_load(...)
and@atomic_store(...)
atomic load/store.compare_exchange(...)
atomic compare exchange.- Saturating add, sub, mul, shl on integers.
- Vector reduce operations: add, mul, and, or, xor, max, min.
Modules
- Modules are defined using
module <name>
. Where name is on the formfoo::bar::baz
- Modules can be split into an unlimited number of module sections, each starting with the same module name declaration.
- The
import
statement imports a given module. - Each module section has its own set of import statements.
- Importing a module gives access to the declarations that are
@public
. - Declarations are default
@public
, but a module section may set a different default (e.g.module my_module @private;
) @private
means the declaration is only visible in the module.@local
means only visible to the current module section.- Imports are recursive. For example,
import my_lib
will implicitly also importmy_lib::net
. - Multiple imports may be specified with the same
import
, e.g.import std::net, std::io;
. - Generic modules have a set of parameters after the module name
module arr(<Type, LEN>);
- Generic modules are not type checked until any of its types, functions or globals are instantiated.
Contracts
- Doc comments (starting with
/**
) are parsed. - The first part, up until the first
@
directive is ignored. - The
@param
directive for pointer arguments may define usage constraints[in]
[out]
and[inout]
. - Pointer argument constraints may add a
&
prefix to indicate that they may not benull
, e.g.[&inout]
. - Contracts may be attached to generic modules, functions and macros.
@require
directives are evaluated given the arguments provided. Failing them may be a compile time or runtime error.- The
@ensure
directive is evaluated at exit - if the return is a result and not an optional. return
can be used as a variable identifier inside of@ensure
, and holds the return value.@return!
optionally lists the errors used. This will be checked at compile time.@pure
says that no writing to globals is allowed inside and only@pure
functions may be called.
Benchmarking
- Benchmarks are indicated by
@benchmark
. - Marking a module section
@benchmark
makes all functions inside of it implicitly benchmarks. - Benchmarks are usually not compiled.
- Benchmarks are instead only run by the compiler on request.
Testing
- Tests are indicated by
@test
. - Marking a module section
@test
makes all functions inside of it implicitly tests. - Tests are usually not compiled.
- Tests are instead only run by the compiler on request.
Safe / fast
Compilation has two modes: “safe” and “fast”. Safe will insert checks for out-of-bounds access, null-pointer deref, shifting by negative numbers, division by zero, violation of contracts and asserts.
Fast will assume all of those checks can be assumed to always pass. This means that unexpected behaviour may result from violating those checks. It is recommended to develop in “safe” mode.
If debug symbols are available, C3 will produce a stack trace in safe mode where an error occurs.