Compiler directives
Compiler directives are keywords that begin with #
, instructing the compiler to perform specific actions, enforce checks, or modify parameters.
These directives can only be used at the outermost level of a source file and cannot be placed inside function definitions.
#include
The #include
directive enables another FunC source file to be parsed in place of the directive.
Syntax:
#include "filename.fc";
Files are automatically checked for multiple inclusions. By default, the compiler will ignore redundant inclusions if the same file is included more than once. A warning is issued when the verbosity level is set to 2 or higher.
If an error occurs while parsing an included file, the compiler displays an inclusion stack, showing the locations of each file in the inclusion chain.
#pragma
The #pragma
directive provides additional information to the compiler beyond what the language conveys.
#pragma version
The #pragma
version directive enforces using a specific FunC compiler version when compiling the file.
The version is specified in semantic versioning (semver) format: a.b.c
, where:
a
is the major version.b
is the minor version.c
is the patch version.
Supported comparison operators
Developers can specify version constraints using the following operators:
a.b.c
or=a.b.c
—Requires exactly versiona.b.c
of the compiler.>a.b.c
—Requires the compiler version to be greater thana.b.c
.>=a.b.c
—Requires the compiler version to be greater than or equal toa.b.c
.
<a.b.c
—Requires the compiler version to be less thana.b.c
.<=a.b.c
—Requires the compiler version to be less than or equal toa.b.c
.
^a.b.c
—Requires the major compiler version to be equal to thea
part and the minor to be no lower than theb
part.^a.b
—Requires the major compiler version to be equal to thea
part and the minor to be no lower than theb
part.^a
—Requires the major compiler version to equala
(any minor/patch).
For comparison operators (=
, >
, >=
, <
, <=
), omitted parts default to zero.
For example:
>a.b
is equivalent to>a.b.0
and does not match versiona.b.0
.<=a
is equivalent to<=a.0.0
and does not match versiona.0.1
.^a.b.0
is not the same as^a.b
.
Examples:
^a.1.2
matchesa.1.3
but nota.2.3
ora.1.0
.^a.1
matches all of them.
The #pragma
version directive can be used multiple times, and the compiler must satisfy all specified conditions.
#pragma not-version
The syntax of #pragma not-version
is identical to #pragma version
, but it fails if the specified condition is met.
This directive is applicable for blocking specific compiler versions known to have issues.
#pragma allow-post-modification
Introduced in FunC v0.4.1
Using a variable before it is modified within the same expression is prohibited by default.
For example, the following code will not compile:
(x, y) = (ds, ds~load_uint(8))
However, this version is valid:
(x, y) = (ds~load_uint(8), ds)
To override this restriction, use #pragma allow-post-modification
. This allows variables to be modified after usage in mass assignments and function calls while sub-expressions are still computed left to right.
In the following example, x
will contain the initial value of ds
:
#pragma allow-post-modification
(x, y) = (ds, ds~load_bits(8));
Here, in f(ds, ds~load_bits(8));
:
- The first argument of
f
will contain the initial value ofds
. - The second argument will contain the 8-bit-modified value of
ds
.
#pragma allow-post-modification
works only for code after the pragma.
#pragma compute-asm-ltr
Introduced in FunC v0.4.1
asm
declarations can override the order of argument evaluation. For example, in the following expression:
idict_set_ref(ds~load_dict(), ds~load_uint(8), ds~load_uint(256), ds~load_ref())
The execution order is:
load_ref()
load_uint(256)
load_dict()
load_uint(8)
This happens due to the corresponding asm
declaration:
cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
Here, the asm(value index dict key_len)
notation dictates a reordering of arguments.
To ensure strict left-to-right computation order, use #pragma compute-asm-ltr
. With this directive enabled, the same function call:
#pragma compute-asm-ltr
...
idict_set_ref(ds~load_dict(), ds~load_uint(8), ds~load_uint(256), ds~load_ref());
will be evaluated in the following order:
load_dict()
load_uint(8)
load_uint(256)
load_ref()
All asm
reordering will occur only after computation.
#pragma compute-asm-ltr
works only for code after the pragma.
Note: #pragma compute-asm-ltr
applies only to the code after the directive in the file.