Skip to main content

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 the inclusion of another FunC source file 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 will be issued if the verbosity level is 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 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 compiler version to be equal to the a part and the minor to be no lower than the b part;
    • ^a.b—Requires the major compiler version to be *equal to a part and minor be no lower than b part;
    • ^a—Requires the major compiler version to be no lower than a part.

For comparison operators (=, >, >=, <, <=) , omitted parts default to zero. For example:

  • >a.b is equivalent to >a.b.0 and does not match version a.b.0.;
  • <=a is equivalent to <=a.0.0 and does not match version a.0.1 version;
  • ^a.b.0 is not the same as ^a.b

Examples:

  • ^a.1.2 matches a.1.3 but not a.2.3 or a.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 of ds.
  • 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:

  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 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:

  1. load_dict()
  2. load_uint(8)
  3. load_uint(256)
  4. 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.

Was this article useful?