#
, 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 the inclusion of another FunC source file parsed in place of the directive.
Syntax:
<path_to_filename>
is the path to the FunC source file to include.
Files are automatically checked for multiple inclusions.
By default, the compiler will ignore redundant inclusions if the same file is included more than once.
This also applies to inclusions along a path of nested inclusions.
A warning will be issued if the verbosity level is 2 or higher.
For example, suppose that main.fc
contains the main
function. Suppose also that main.fc
includes a file A.fc
, which in turn includes a file B.fc
,
which in turn includes main.fc
. When main.fc
is compiled, the inclusion of main.fc
in B.fc
will be ignored.
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.
Syntax:
<op>
is an optional version operator that allows to specify a constraint, and <sem_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
2.3.4
.
The second uses the greater than operator >
, and it means that the compiler must have a version greater
than 2.3.4
(see precedence and operators below for details).
Equality and precedence
Two versions are equal if their respective major, minor, and patch numbers are equal. Two versions are not equal if at least one of those numbers differ. Example:- 1.2.3 is equal to 1.2.3
- 3.4.5 is not equal to 3.1.5
- If a is smaller than d, then a.b.c precedes d.e.f
- If a is equal to d, and b is smaller than e, then a.b.c precedes d.e.f
- If a is equal to d, and b is equal to e, and c is smaller than f, then a.b.c precedes d.e.f
- 1.0.0 precedes 2.0.0. Equivalently: 1.0.0 is smaller than 2.0.0 or 2.0.0 is greater than 1.0.0.
- 2.0.0 precedes 2.1.0. Equivalently: 2.0.0 is smaller than 2.1.0 or 2.1.0 is greater than 2.0.0.
- 2.1.0 precedes 2.1.1. Equivalently: 2.1.0 is smaller than 2.1.1 or 2.1.1 is greater than 2.1.0.
Operators
Developers can specify version constraints using the following operators:- a.b.c or =a.b.c - Requires exactly version a.b.c of the compiler
- >a.b.c - Requires the compiler version to be greater than a.b.c.
- >=a.b.c - Requires the compiler version to be greater than or equal to a.b.c
- <a.b.c - Requires the compiler version to be less than a.b.c
- <=a.b.c - Requires the compiler version to be less than or equal to a.b.c
- ^a.b.c - Requires the major part of the compiler version to be equal to the a part, the minor to be equal to the b part, and the patch to be no lower than the c part
- ^a.b - Requires the major compiler version to be equal to the a part and the minor to be no lower than the b part
- ^a - Requires the major compiler version to be no lower than the a part
- >a.b is equivalent to >a.b.0
- <=a is equivalent to <=a.0.0
- ^a.b is not equivalent to ^a.b.0
- ^a is not equivalent to ^a.0.0
- ^5.1.2 matches compiler version 5.1.3 because patch
3
is no lower than patch2
. - ^5.1.2 does not match compiler version 5.2.3 because minor
2
does not equal minor1
. - ^5.1.2 does not match compiler version 5.1.1 because patch
1
is lower than patch2
. - ^5.1 matches compiler version 5.1.3 because minor
1
is no lower than minor1
. - ^5.1 matches compiler version 5.2.3 because minor
2
is no lower than minor1
. - ^5.1 matches compiler version 5.1.0 because minor
1
is no lower than minor1
. - ^5.1 does not match compiler version 5.0.2 because minor
0
is lower than minor1
. - ^5 matches compiler version 5.1.0 because major
5
is no lower than major5
. - ^5 does not match compiler version 4.1.0 because major
4
is lower than major5
. - >5.1.2 matches compiler version 5.1.3 because patch
3
is bigger than patch2
. - >5.1.2 matches compiler version 5.2.0 because minor
2
is bigger than minor1
. - >5.1.2 matches compiler version 6.0.0 because major
6
is bigger than major5
. - =5.1.2 does not match compiler version 5.2.2 because minor
2
is not equal to minor1
.
The
#pragma
version directive can be used multiple times, and the compiler must satisfy all specified constraints.#pragma not-version
The #pragma not-version
is similar to #pragma version
, but it fails if the specified condition is met.
Syntax:
<op>
is an optional version operator that allows to specify a constraint, and <sem_version>
is identical as
in #pragma version
.
This directive is useful for blocking specific compiler versions known to have issues.
Here are some examples:
not-version >2.1.3
matches any compiler version that is not bigger than 2.1.3, like 2.1.2, 2.0.5 and even 2.1.3 itself.
In the second example, not-version ^3.4
matches any compiler version that does not match ^3.4, like 3.3.1, 4.4.0, and 3.3.9
In the third example, not-version 1.2.3
matches any compiler version different from 1.2.3.
#pragma allow-post-modification
Introduced in FunC v0.4.1
In Func, using a variable before it is modified within the same expression is prohibited by default.
For example, the following code will not compile, because ds
is used before it is modified in ds~load_uint(8)
.
See modifying notation for more details on using symbol ~
.
ds
is used after it is modified:
#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
, while y
the 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:
load_ref()
load_uint(256)
load_dict()
load_uint(8)
asm
declaration:
asm(value index dict key_len)
notation dictates a rearrangement of arguments.
To ensure strict left-to-right computation order of the arguments, use #pragma compute-asm-ltr
.
With this directive enabled, the same function call:
load_dict()
load_uint(8)
load_uint(256)
load_ref()
asm
rearrangement will occur.
#pragma compute-asm-ltr
works only for code after the pragma.