Atomic types
-
intis a 257-bit signed integer type. Overflow checks are enabled by default and trigger an exception if exceeded. -
cellis a TVM cell type used to store persistent data in the TON Blockchain. Data is organized in trees of cells, with each cell containing up to 1023 bits of arbitrary data and up to four references to other cells. Cells function as memory units in stack-based TVMs. -
sliceis a read-only view of a cell that allows sequential access to its data and references. A cell can be converted into a slice, extracting stored bits and references without modifying the original cell. -
builderis a mutable structure used to construct cells by adding data and references before finalizing them into a new cell. -
tupleis an ordered collection of up to 255 elements, each capable of holding a value of any type. -
contis a TVM continuation used to manage execution flow in TVM programs. Although a low-level construct, it provides flexible execution control.
No boolean type
FunC does not have a dedicated boolean type. Instead, booleans are represented as integers:falseis0,trueis-1(a 257-bit integer with all bits set to 1).- Logical operations are performed using bitwise operations.
- In conditional checks, any nonzero integer is treated as
true.
Null values
In FunC, thenull value of the TVM type Null represents the absence of a value for a given atomic type.
Any atomic type variable can have null to indicate absence of a value.
Since any atomic type allows null, it is important to be aware of the following regarding functions:
- Functions that return an atomic type may return
nullin some instances. - Functions that expect an atomic type as input could also accept
null. - For library functions, the cases in which the function accepts
nullas valid input or output are explicitly described in the function specification.
cell_depth(cell c) receives a cell as input, and its specification states that if the input cell is null, the function returns 0.
Hole type
FunC supports type inference. The hole types_ and var serve as placeholders that are resolved during type checking.
For example, in the declaration:
x is of type int since 2 is an int,
and both sides of the assignment must have matching types.
As another example, in the following function declaration (see Function declarations for more details):
_ must have type int, since the type of the returned expression a + 1 is int.
Composite types
Types can be combined to form more complex structures.Functional type
A functional type is written in the formA -> B, where:
Ais the input type, which is called domain.Bis the output type, which is called codomain.
int -> cell represents a function that:
- Takes an integer as input.
- Returns a TVM cell as output.
(int -> int) -> int is a function with domain int -> int and codomain int.
Similarly, int -> (int -> int) is a function with domain int and codomain int -> int.
Tensor types
Tensor types represent ordered collections of values and are written in the form(A, B, ...).
These types occupy multiple TVM stack entries, unlike atomic types, which use a single entry.
Example:
If a function foo has the type int -> (int, int),
it takes one integer as input and returns a pair of integers as output.
A call to this function may look like: (int a, int b) = foo(42);.
Internally, the function consumes one stack entry and produces two.
Type representation: Although the values (2, (3, 9)) of type (int, (int, int)) and (2, 3, 9) of type (int, int, int) are stored identically as three stack entries (2, 3, and 9), FunC treats them as distinct types.
For instance, the following code will not compile:
A type of the form
(A) is considered by the type checker as the same type as A.()
The unit type () is used to indicate that:
- A function does not return a value or
- A function takes no arguments
() has a single value, also written as (), occupying zero stack entries.
Examples
print_inthas the typeint -> (), meaning it takes an integer but returns nothing.randomhas the type() -> int, meaning it takes no arguments but returns an integer.
Tuple types
Tuple types in FunC are written in the form[A, B, ...] and represent TVM tuples with a fixed length and known component types at compile time.
For example, [int, cell] defines a tuple with exactly two elements:
- The first element is an integer.
- The second element is a cell.
[] represents an empty tuple with a unique value—the empty tuple itself.
Note: Unlike the unit type (), an empty tuple [] occupies one stack entry.
Polymorphism with type variables
FunC features a custom type system with support for polymorphic functions. For example, consider the following function:X is a type variable that allows the function to operate on values of any type. Type variables are declared after forall and before of ->.
The function receives a value of type X, and duplicates this value to return a value of type (X, X).
For example,
- Calling
duplicate(6)produces(6, 6). - Calling
duplicate([])produces two copies of an empty tuple:([], []).
At the moment, type variables in polymorphic functions cannot be instantiated with tensor types.
There is only one exception: the tensor type
(a), where a is not a tensor type itself, since the compiler treats (a) as if it was a.User-defined types
Currently, FunC does not support defining custom types beyond the type constructions described above.Type width
Every value in FunC occupies a certain number of stack entries. If this number is consistent for all values of a given type, it is called the type width. For example, all atomic types have a type width of 1, because all their values occupy a single stack entry. The tensor type(int, int) has type width 2, because all its values occupy 2 stack entries.