Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Source syntax

This compiler follows the source grammar implemented by solcore-rs on its new-syntax branch (reference revision 59f11626, including the parser's existing pragma, export, and type-alias extensions), with named-field structs carried forward from local main. Sources and imported modules use .sol. A module a.b maps to a/b.sol relative to the importing module's directory.

Older source spellings are not accepted. In particular, there are no data, class, instance, or forall declarations, postfix type locations or array types, as expressions, named function results, or destructuring let bindings. Interface and library declarations are outside this grammar.

Modules

Imports and declarations may be interleaved:

import std;
import * from std;
import * from std hiding {debug};
import * as math from @vendor.math;
import {foo, bar as baz} from lib.helpers;

Plain imports introduce module qualifiers. Wildcard and selective imports open public names and may have a hiding clause. A wildcard is written *, not {*}. Selectors and hiding lists may name operators using parentheses, such as (+). String-path imports and the former import M.{item} spelling are rejected.

The reference parser retains these export forms:

export {foo, Option(*), Result(Ok), (+)};
export helpers;
export helpers as publicHelpers;
export helpers.{foo, Option(*)};
export helpers.*;

Pragmas

The implemented switches use their hyphenated names, with an optional list of target identifiers:

pragma no-coverage-condition;
pragma no-patterson-condition Trait;
pragma no-bounded-variable-condition Trait;
pragma no-generic-instance-for MyType;

The pragma parser also preserves unknown identifier directives. It does not accept the former pragma solcore ... namespace or Solidity version expressions.

Types and bindings

Named bindings put their name before a colon and type. Type arguments use nonempty angle-bracket lists; tuple and function types may be empty.

word
Option<word>
collections.Map<address, Option<word>>
(word, bool)
mapping(address => word)
array<word>
memory<DynArray<word>>
calldata<array<word>>
storage<mapping(address => word)>
function(word, bool) returns (word)
function(word)
@word

@T is a type witness in expression position and abbreviates Proxy<T> in type position. Locations are ordinary unary constructors (memory<T>, storage<T>, calldata<T>); there are no T memory, T[], or T[N] forms.

let count: word = 1;
let inferred = compute();
let output: word;
let witness: @word = @word;
let values: memory<DynArray<uint256>> = [1, 2, 3];
let constant: comptime<word> = 42;

Local bindings introduce one identifier. An initializer uses =, never :=. Comptime parameters use comptime name: Type; comptime locals and results use comptime<Type>.

The parser also accepts comptime<Type> inside other types. The current compiler supports its evaluation mode only on direct local bindings and function results, alongside the comptime parameter modifier. Nested uses, including enum payloads, type arguments, aliases, and function-type inputs or results, produce a diagnostic at the unsupported type instead of discarding the compile-time requirement.

Transparent type aliases retain the reference parser's type syntax and parenthesized binders, including inside contracts:

type Word = word;
type PairOf(a) = (a, a);

Functions and contracts

Named function parameters require types. Omitting returns declares a unit result. Result entries are positional types; multiple entries form a tuple.

function identity<T>(value: T) returns (T) { return value; }
function pair(x: word) returns (word, word) { return (x, x); }
function nop() {}
function increment(x: word) returns (word) { x + 1 }
function constant(comptime x: word) returns (comptime<word>) { return x; }

Only a named function may end with an implicit result expression without a semicolon. Nested blocks, match arms, lambdas, constructors, and fallbacks require statement terminators.

contract Counter {
    value: word;
    constructor(initial: word) payable { value = initial; }
    function read() public returns (word) { return value; }
    function update(next: word) public payable { value = next; }
    fallback() payable { return; }
}

Contract functions accept public, then payable, before returns and where. Constructors and fallbacks are implicitly public and accept only payable. Top-level functions and trait/impl methods take neither attribute. There are no external, internal, private, pure, view, or receive declaration forms.

Lambdas retain lam, allow inferred parameters, and use an optional arrow result:

let increment = lam (x: word) -> word { return x + 1; };
let identity = lam (x) { return x; };

Enums, structs, traits, and implementations

enum Option<T> { None, Some(T) }

trait Eq<T> {
    function eq(left: T, right: T) returns (bool);
}

impl<T> Eq<Option<T>> where T: Eq {
    function eq(left: Option<T>, right: Option<T>) returns (bool) {
        match ((left, right)) {
            case (.None, .None) { return true; }
            case (.Some(x), .Some(y)) { return Eq.eq(x, y); }
            default { return false; }
        }
    }
}

The first trait parameter is the instance-head type. Further parameters are trait arguments. where T: Convert<U> constrains Convert<T, U>; multiple constraints are comma-separated and may be enclosed in parentheses. default impl is supported. An enum may have no variants, and each variant may have positional payload types. #[derive(...)] accepts a nonempty list of trait paths and is supported on top-level and contract-local enums. Explicit derives use the Generic representation and the existing trait implementations for its components.

Structs extend the reference grammar with named product fields. They accept the same angle-bracket type parameters and derive attributes as enums, at top level or inside contracts. Each field has a type and ends with a semicolon; field names must be unique within the struct. Constructors take values in field order:

#[derive(Eq)]
struct Pair<T> {
    left: T;
    right: T;
}

function first(value: Pair<word>) returns (word) { return value.left; }
function pair() returns (Pair<word>) { return Pair.Pair(1, 2); }

A member read evaluates its receiver once. Storage-backed struct fields support member updates through the storage API. A contract-local struct's type belongs to its declaring contract, so separate contracts can reuse the same type name.

Constructors use qualified names (Option.Some(1)) or expected-type shorthand (.Some(1)); the same forms appear in patterns. Tuple patterns and _ are supported in matches. Every match has at least one case/default arm, with default last. A comptime label is written case comptime expression { ... }.

Expressions and statements

Calls, member calls, indexing, tuples, array literals, and ternary expressions are supported. Unary operators are ! and ~; binary operators are *, /, %, +, -, &, ^, |, comparisons, equality, &&, and ||. Precedence matches the reference parser; bitwise operators bind more tightly than comparisons. Comparison and equality operators do not chain. There are no unary +/-, shifts, exponentiation, increment/decrement, or as expressions.

Statements include let, assignment, expressions, return, blocks, if, while, for, break, continue, match, and assembly. Compound assignments are +=, -=, *=, /=, %=, &=, |=, ^=, and unary ~=. Address expressions in compound assignments are evaluated once. for headers may contain comma-separated declarations/assignments and omit each header part. There are no unchecked blocks or bare revert statements.

Assembly blocks use the reference Yul source grammar. Compiler-internal Yul quotation templates are not source-language expressions.

Lexical rules

Identifiers start with a Unicode letter and continue with Unicode letters, numbers, or underscores. _ is a wildcard, not a binding name. Hyphenated identifiers are restricted to pragmas; put spaces around subtraction. Nested block comments and line comments are supported. Strings use double quotes and the escapes \n, \t, \", and \\. Integers are decimal or lowercase-0x hexadecimal. List trailing-comma rules follow the reference parser; ordinary function calls do not permit a trailing argument comma.