26.2.0 (2026-06-23)
Features
-
Contract fields are now immutable by default unless declared with
mut. Immutable fields must be initialized before every successfulinitexit and are embedded into contract code; declare fields asmutto keep using storage-backed mutable contract state. Duringinit, immutable field reads before assignment observe the zero/default value, reassignment is allowed, and the last write is the value embedded into the deployed contract. Binding a field withoutmutas amuteffect (uses (mut field)) is an error everywhere exceptinit's code-backed fields; this applies to explicit handle fields (e.g.StorPtr<T>) as well.The compiler verifies initialization with a definite-assignment analysis: a field counts as initialized when it is assigned whole-value (including compound assignment such as
+=) on every path that reaches the end ofinit, whether directly or through helper functions that receive the field via amuteffect or amut Targument. Branches merge conservatively and loop bodies are assumed to possibly run zero times (conditions that are literaltrue/falseare folded), so assignments guarded by runtime-dependent loop conditions are not credited. Member-wise initialization of an aggregate field (p.a = ...; p.b = ...) does not count; assign the whole value instead. (#1334) -
fe docoverhaul for message-based contracts:msgvariants now render
as children of their parent msg page (matching enum variants), contract
pages gain dedicated init and message-handler sections, and#[test]
functions are hidden from generated docs by default (restore them with
--include-tests). The doc viewer also exposes CSS variables for fonts,
weights, and layout so downstream sites can retheme it without CSS
specificity battles. (#1419) -
Added
Option::ok_orandOption::ok_or_elsetocore, mirroring Rust's API, plus aPanic::from(code)shorthand constructor. Combined withResult::unwrap, this lets users attach a typed error value (e.g.Panic::from(POP_EMPTY)) to an Option-unwrap so reverts carry meaningful Solidity-compatible selectors instead of always landing onPanic(0x01). (#1430) -
StorageMap<K, V>now accepts every primitive integer type (u8–u128,usize,i8–i256,isize) andboolas a key, in addition to the previously supportedu256,Address, and tuples. The newStorageKeyimpls left-pad each key to 32 bytes viaWordRepr::to_word(), so slot derivation matches Solidity'smapping(intN/uintN/bool => V)convention exactly. Useful for storage layouts that want to pack indices into a single slot (e.g.mapping(uint128 => V)queues). (#1431) -
Improved optimization options for the default Sonatina backend:
-O1/--optimize 1is now distinct from-O2and is the default optimization level. It keeps Sonatina's optimization pipeline enabled while using lower exact-search caps for stack shuffling; usually getting close to-O2gas and bytecode while substantially reducing compile time.-O0/--optimize 0now and disables the stack shuffling exact solver entirely in favor of a greedy heuristic, making unoptimized builds much faster.
(#1435)
-
Added core
String<N>utilities for fixed-width byte round-trips, effective byte length, concatenation, and equality in const and runtime code. (#1437) -
Lossless
boolto integer casts withasare now allowed. (#1445) -
Exposed selected raw EVM operations through
std::evm::ops, including byte/sign-extension, balance/code inspection, gas price, and blob fee/hash reads. (#1453) -
Added marker-only
#[must_use]support for functions, structs, and enums, and markedcore::result::Resultas must-use so ignored fallible results are diagnosed unless explicitly discarded withlet _ = .... (#1468) -
Precompiles:
std::evm::cryptoprecompile wrappers nowResult<PrecompileError, T>
instead of reverting on precompile call failure.- Added typed wrappers for the remaining EVM crypto precompiles, including
RIPEMD-160, identity, Blake2F, KZG point evaluation, BLS12-381 operations, and
P-256 verification. ecrecovernow rejects non-canonical secp256k1 signatures before returning
a recovered address.- added
ecrecover_rawwhich does not perform canonical signature checks. (#1468)
-
Added
assert!builtin, e.g.assert!(ok). Takes an optional second string
literal argument:assert!(ok, "something's not ok"). Assertion failure results
in a revert, with Solidity-compatibleError(string)payload.assert!can
also be used inconst fns at compile time; assertion failure results in a
compile-time failure. (#1471) -
Added the
Boundedtrait incore::num, exposing inclusive numeric type bounds asT::min()andT::max()const fnmethods on all primitive integer types. (#1474) -
Added the
AbsandUnsignedAbstraits incore::num, providing magnitude operations asconst fnon all signed integer types.Absbundlesabs,checked_abs,wrapping_abs,saturating_abs, andoverflowing_abs, each taking a different stance on the asymmetricT::MINedge case.UnsignedAbs::unsigned_abs()returns the magnitude in the unsigned type of the same width — handlingT::MINwithout overflow. (#1474) -
Inherent
implblocks now support associatedconstitems. Consts may reference the impl's generic parameters and other consts of the same impl, are evaluated at compile time per instantiation, and can be used in both value and type positions (e.g. array sizes):pub struct Packed<const BITS: u256> {} impl<const BITS: u256> Packed<BITS> { const LANES: u256 = 256 / BITS const LANE_MASK: u256 = (1 << BITS) - 1 pub fn lanes(self) -> u256 { Self::LANES } }This is the idiomatic place for derived constants on a generic container; previously the math had to be repeated as local bindings in every method. Inherent consts take precedence over trait consts of the same name, with the trait const still reachable via a qualified path like
<Foo as Trait>::X.Further details:
- Consts are private to the defining module by default; mark them
pub constto export them. - Consts of the enclosing impl are in scope unqualified inside the impl, like trait consts in trait default methods.
- Inherent consts can be used as const generic arguments (
Holder<Buf::SIZE>). - A const on a conditional impl (
impl<T> Wrap<T> where T: Marker) is only available when the receiver satisfies the bound. - Two inherent impls of the same type that define the same const are a definition-site conflict, exactly like conflicting inherent methods (regardless of their
whereclauses). Collisions with an enum variant name or with an inherent associated function of the same name are also rejected. - A const initializer that is not const-evaluable (e.g. a non-
const fncall) is rejected at its definition, like a top-levelconst.
(#1479)
- Consts are private to the defining module by default; mark them
-
Added
fe build --emit metadata, which writes a Solidity-standard contractmetadata.jsonper contract (sources withkeccak256, compiler version, resolved build settings, transitive dependency sources, and ABI) so verifiers like Sourcify can reproduce and check the bytecode. (#1487)
Bugfixes
-
Fixed
fe testoutput so failure details are printed immediately after the corresponding failed test status when tests run in parallel. (#1420) -
Fixed a compiler panic when code_region_len or code_region_offset was called on an unresolved generic contract runtime parameter. (#1429)
-
Fixed a compiler panic when generic associated constants were used in const expressions before their concrete types were known. (#1429)
-
Fixed Sonatina ABI encoding and decoding for negative signed integers narrower than 256 bits. (#1434)
-
Sonatina:
- Fixed EVM encoded-pointer provenance across calls and memory operations, with more precise escape summaries for i256 pointer carriers, aggregates, malloc-derived pointers, and local/argument/nonlocal storage. This also improves memory-planning performance by avoiding unnecessarily conservative escape assumptions.
- Fixed GVN value-phi materialization so cached/generated value phis are rejected unless they cover the currently reachable predecessors.
- Improved stackify spill slot reuse logic. This fixes a bug where a scratch slot could be reused for values that overlap during phi/control-flow transitions.
(#1435)
-
Fixed ABfI encoding for
evm.callmessage payloads with multiple dynamic fields, and for dynamic custom error payloads. (#1439) -
Fixed expression-context
&&and||lowering so right-hand side expressions are only evaluated when short-circuiting requires them. (#1447) -
Fixed compiler panic when lowering zero-sized effect types. (#1449)
-
Allowed blanket implementations of external traits when the implementor is a type parameter directly bounded by a local trait. (#1450)
-
Fixed calls through projected effect providers, such as
with (store.map), so nested calls preserve the projected provider's concrete target type. (#1459) -
Sonatina:
- Fix unchecked signed EVM division and remainder for narrow signed integer types.
- Fix EVM codegen for spilled phi values so control-flow joins do not let one edge's object storage clobber another edge's phi source. (#1470)
-
Fixed the parser rejecting multiple items on the same line inside
impl,trait, andexternblocks. The tree-sitter grammar already allowedimpl Foo for Bar { fn a() {} fn b() {} }, but the main parser required a newline between items. (#1475) -
Fixed compiler panics when tuple type aliases were used as event or custom error fields. (#1481)
-
Fixed JSON ABI generation for events so fields marked with
#[indexed]are emitted with"indexed": true. (#1485) -
Fixed contract field layout for nested types containing inferred const slot parameters. Contract fields whose storage slot or address space cannot be determined are now reported as errors instead of being laid out incorrectly. (#1492)
-
Trait associated
constimplementations are now validated against their declarations. A missing associated const, type mismatch (including param-typed const defaults), or otherwise ill-formed const definitions are reported as errors instead of being silently accepted or producing a panic in a later stage. (#1492) -
Fixed a compiler panic when an
if let/while letcondition's pattern was not followed by=. The parser now reports a "expected=" error and recovers instead of crashing. (#1497)
Performance improvements
-
Fe:
- Lower named aggregate constants as Sonatina const refs.
- Only emit Sonatina data regions for constants that are explicitly used as code regions.
- Avoid duplicated const data in generated bytecode for large const arrays.
- Improve runtime lowering for const-backed local borrows.
- Avoid emitting real globals/data entries for zero-sized aggregate constants.
- Lower view aggregate arguments in a representation-flexible way so the compiler can avoid unnecessary aggregate allocation and copying.
Sonatina: - Compact and specialize constref loads.
- Deduplicate const data.
- Coalesce adjacent const loads.
- Prune dead const sections and functions.
- Reduce compile-time blowups for large const arrays. (#1433)
-
Sonatina:
- Improved stack shuffling performance via cache improvements and short-circuiting trivial operand-prep cases.
- Improved memory placement performance.
(#1435)
-
Improved Solidity ABI encoding and decoding performance for dynamic payloads by avoiding redundant memory copies and adding faster paths for canonical dynamic arrays. (#1440)
-
Calldata ABI decode optimizations:
- Decode contract
recvarguments directly from calldata instead of first copying the payload into memory. - Inline hot ABI decoding helpers and storage map key paths to reduce generated wrapper overhead. (#1448)
- Decode contract
-
Sonatina:
- EVM lowering now goes via a "machine EVM" instruction set, with additional
optimizations and better stack and memory management. - Improve inlining for small specialized helpers, especially helpers with known callsite arguments or scalarizable object arguments.
- Sink pure computations into the branch or join block where their results are used, reducing unnecessary work and stack pressure. (#1470)
- EVM lowering now goes via a "machine EVM" instruction set, with additional
-
Fe:
- Avoid object-backed runtime storage for read-only owned aggregates, tuple destructuring, and read-only aggregate helper calls.
- Construct runtime aggregate values directly.
Sonatina: - Account for scalarizable aggregate inserts, extracts, and returns when deciding whether to inline small helpers.
- Copy aggregate insert-value webs directly to memory during aggregate legalization instead of materializing source aggregate slots.
- Skip zero-sized aggregate leaf memory operations during aggregate legalization. (#1483)
-
Sonatina:
- Keep explicit
inline(never)annotations on generated const-data helper variants, preserving source-level code-size controls. - Emit large EVM constants in shorter forms when a small runtime operation can replace a much larger literal.
- Remove duplicate private helper bodies after EVM lowering when they compile to identical bytecode.
- Remove repeated constant-only helper parameters when every private call passes the same value.
- Drop unnecessary overflow checks for offsets inside statically sized EVM memory allocations.
- Deduplicate terminal EVM return/revert code.
- Replace static const object initialization with direct scalar values where possible.
- Reuse fixed scratch slots for private temporary EVM buffers whose lifetimes do not overlap.
- Reuse known EVM free-pointer bounds across internal calls to avoid redundant allocator setup.
- Build private return/revert payload buffers at fixed addresses instead of routing them through the heap allocator. (#1488)
- Keep explicit
-
Avoid lowering read-only scalars as memory-backed places. (#1489)