Skip to main content
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 the inclusion of another FunC source file parsed in place of the directive. Syntax:
#include "<path_to_filename>";
where <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:
#pragma version <op><sem_version>;
where <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
Example:
#pragma version 2.3.4;
#pragma version >2.3.4;
The first example does not use an operator and it means that the compiler must have exactly 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
Precedence of two versions a.b.c and d.e.f is determined the following way:
  • 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
If a.b.c precedes d.e.f, then it is said that a.b.c is smaller than d.e.f, or equivalently, that d.e.f is greater than a.b.c. Example:
  • 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
For the comparison operators =, >, >=, <, <=, omitted parts default to zero. For example:
  • >a.b is equivalent to >a.b.0
  • <=a is equivalent to <=a.0.0
For the operator ^, omitted parts do not default to zero. For example:
  • ^a.b is not equivalent to ^a.b.0
  • ^a is not equivalent to ^a.0.0
Here are some examples of constraints:
  • ^5.1.2 matches compiler version 5.1.3 because patch 3 is no lower than patch 2.
  • ^5.1.2 does not match compiler version 5.2.3 because minor 2 does not equal minor 1.
  • ^5.1.2 does not match compiler version 5.1.1 because patch 1 is lower than patch 2.
  • ^5.1 matches compiler version 5.1.3 because minor 1 is no lower than minor 1.
  • ^5.1 matches compiler version 5.2.3 because minor 2 is no lower than minor 1.
  • ^5.1 matches compiler version 5.1.0 because minor 1 is no lower than minor 1.
  • ^5.1 does not match compiler version 5.0.2 because minor 0 is lower than minor 1.
  • ^5 matches compiler version 5.1.0 because major 5 is no lower than major 5.
  • ^5 does not match compiler version 4.1.0 because major 4 is lower than major 5.
  • >5.1.2 matches compiler version 5.1.3 because patch 3 is bigger than patch 2.
  • >5.1.2 matches compiler version 5.2.0 because minor 2 is bigger than minor 1.
  • >5.1.2 matches compiler version 6.0.0 because major 6 is bigger than major 5.
  • =5.1.2 does not match compiler version 5.2.2 because minor 2 is not equal to minor 1.
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:
#pragma not-version <op><sem_version>;
where <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:
#pragma not-version >2.1.3;
#pragma not-version ^3.4;
#pragma not-version 1.2.3;
In the first example, 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 ~.
(x, y) = (ds, ds~load_uint(8));
However, this version is valid, since ds is used after it is modified:
(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, while y the modified value of ds:
#pragma allow-post-modification
(x, y) = (ds, ds~load_bits(8));
#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 evaluation order of the call arguments is:
  1. load_ref()
  2. load_uint(256)
  3. load_dict()
  4. 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 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:
#pragma compute-asm-ltr
;; ...
idict_set_ref(ds~load_dict(), ds~load_uint(8), ds~load_uint(256), ds~load_ref());
will evaluate its arguments in the following order:
  1. load_dict()
  2. load_uint(8)
  3. load_uint(256)
  4. load_ref()
and only after the evaluation of all these arguments, the asm rearrangement will occur.
#pragma compute-asm-ltr works only for code after the pragma.
I