v0.4.0
Frontend and performance focused release.
Comptime engine
A featured comptime engine landed in this release. The new zeen-comptime crate evaluates code during compilation and substitutes the result into the program.
Simple constants and splicing:
comptime N = 16;comptime SquaredN = N * N;
fn main() { @println("{} -> {}", N, SquaredN); @println("$(N) -> $(SquaredN)");}Compile time functions, recursion and results in type expressions:
comptime fn fact(n: comptime_int) comptime_int { if (n <= 1) { return 1; }; return n * fact(n - 1);}
comptime fn double(x: comptime_int) comptime_int { x * 2}
fn main() { let arr: [double(2)]i32 = [1, 2, 3, 4]; @println("{} {}", fact(5), arr[3]);}Compile time functions can return declarations. Here pair_of builds a struct and @emit places it into the declaration scope:
comptime fn pair_of(struct_name: comptime_str, a: zeen_type, b: zeen_type) zeen_decl { @zeen_decl( struct $(struct_name) { pub a: $(a), pub b: $(b), } )}
comptime { @emit(pair_of("NumsPair", i32, f32));}
fn main() { let pair = NumsPair { .a = 1, .b = 1.0 }; @println("{} {}", pair.a, pair.b);}Comptime structs, blocks and reflection work as well:
comptime struct Point { x: comptime_int, y: comptime_int,}
comptime P = Point { .x = 123, .y = 321 };
fn main() { @println("x = $(P.x)"); @println("y = $(P.y)");
comptime { for (field : @fields(Point)) { @println("[{}] {}: {}", field.index, field.name, field.ty); } }}Runtime macros ported to the comptime area:
| Macro | Destination |
|---|---|
@print(...) / @println(...) |
Print a formatted compile time string with the prefix [comptime FUNCTION][FILE:LINE] MESSAGE |
@format(...) comptime_str |
Format and return a compile time string |
@dbg(...) |
Debug print a compile time expression |
@panic(...) |
Compile time formatted panic error as a miette error with source display |
@sizeof(T) comptime_int |
Size of the provided type |
@alignof(T) comptime_int |
Alignment of the provided type |
New comptime-only macros:
| Macro | Destination |
|---|---|
@zeen_type(T) |
Capture the inner type as a zeen_type value |
@zeen_expr(expr) |
Capture an expression as a zeen_expr value |
@zeen_decl(decl) |
Capture a declaration as a zeen_decl value |
@emit(zeen_decl) |
Emit the captured declaration into the program scope |
@fresh_name(prefix: comptime_str) |
Generate a unique name with the prefix prefix_N |
@fields(T) |
Comptime array of comptime struct Field from core.reflection |
@variants(T) |
Comptime array of comptime struct Variant from core.reflection |
@typeInfo(T) |
String of named type info: struct / enum |
@hasField(T, name) |
true if the struct has a field with the provided name |
@fieldType(T, name) |
Field type as a @zeen_type |
Performance
A set of frontend optimizations landed in #62. Measured on a synthetic benchmark project (15 files, 1.5 millions LOC, 26.9 MB):
| Metric | Before | After | Speedup |
|---|---|---|---|
--check (best baseline run) |
58.9 s | 9.5 s | 6.2x |
--check (typical baseline run) |
158.0 s | 9.5 s | 16.6x |
| full binary build | 137.8 s | 11.3 s | 12.2x |
--check cycles |
254.6e9 | 40.3e9 | 6.3x |
| build cycles | 597.0e9 | 48.0e9 | 12.4x |
| peak RSS | 1948 MiB | 1959 MiB | +0.5% |
Changelog from Github Release
Compiler/Library
- New
comptimefrontend engine (cratezeen-comptime) with supported: compile time constants, functions, blocks, structs and types. See examples and more detailed about macros and features in #64 - Improvement of warnings system. New warnings for:
unused_variable,unused_fn,unused_const,unreachable_code,unused_expression_result. New features for warnings:
--no-warnsdisables all warnings// @no-warningsmarker to silence a single module- Core packages are always quiet about warnings
- Compiler frontend performance: about 16.6x speedup on typical baseline
--checkrun and 12.2x on full binary build. See more in #62 - Closures and generics:
- Closures can capture generic params, zero capture closures bind the enclosing generics
- Anonymous fn literals and
Fn/FnOncevalues work in global inits through the synthesizedzeen_init_globals - Bound methods resolve through pointers to generic params
- New macros:
@variantNames(T) [N][]const char,@variantCount(T) comptime_int - C ABI fixes: extern calls pass the
byvalattribute, struct arguments are passed per the C ABI, small struct returns are lowered per the C ABI and struct ABI now follows the target instead of the host - Correctness fixes:
- Statements after
return/break/continue/@panicno longer execute and no longer count as the block value. - Integer literals that do not fit the target type are rejected instead of silently truncating.
- Bare
_over aDroppayload is a hard error, use&_to borrow or discard. - Multiplicative operators are left associative now (
a / b * cis(a / b) * c). - Negated literals fold correctly in MIR,
addr_oflowers with the operand type and casts the pointer on coercion. - Function body spans stay inside the closing brace,
@familyreportsunixfor Android targets.
- Statements after
- Native linking flags
--link <LIB>and--link-dir <PATH>(repeatable) for both the CLI and Zora builds.
Library
- Replaced basic constants with comptime constants in:
std.math,std.collections,std.json,std.string,std.sync std.collections.listgot new method:swapand theIndexinterface implementation for non-Copy elements- Windows std fixes reported by the platform test suite
LSP
- New semantic tokens for:
comptime_int,comptime_float,comptime_bool,comptime_char,comptime_str,zeen_typeandzeen_decl - Hover for comptime names, comptime macros and variant macros
- Diagnostics are reported for every module of a project (not just entry file)
- Locals filtered by scope, interface method completion,
// @no-warningssupport - Parse errors of included modules surface directly
Package Manager
[build]acceptslinkandlink-dir, directives from the package and its dependencies are collected inZora.lockand passed to the compiler- New platform sections:
[build.target.<triple>],[build.arch.<value>],[build.env.<value>],[build.os.<value>],[build.family.<value>]. First match wins and overrides global[build] - Lockfile format bumped to
version = 3, older lockfiles should be deleted and re-created withzora check - New flags for run/build:
--release/--debug(alias for-m Release/-m Debug) - Added forwards program arguments after
-- [package]section gained an optionaldescriptionstring field
Breaking Changes
- Integer literals that exceed the target type are a compile error now. Code that relied on truncation has to annotate the binding or use
@as. - Bare
_over aDroppayload is rejected (zeen::mir::discard_drop_payload). Use&_. - Warnings are reported by default, including unused functions, unused constants and unreachable code. Use
--no-warnsor// @no-warningsto silence them. - Statements after
return/break/continue/@panicno longer execute. Previously the followup statement replaced the flow cut and actually ran. - Multiplicative operators are left associative, so
a / b * cparses as(a / b) * c. - Std constants are
comptimevalues now (PI,TAU,E, collection capacities,jsonlimits). A binding that needs a runtime type has to annotate it:let cap: usize = InitialMapCapacity;. @familyon Android targets resolves tounixinstead of an empty string.
Pull Requests
- Compiler frontend performance by @mealet in https://github.com/mealet/zeen/pull/62
- Warnings system improvement by @mealet in https://github.com/mealet/zeen/pull/63
- Featured comptime engine by @mealet in https://github.com/mealet/zeen/pull/64
Full Changelog: https://github.com/mealet/zeen/compare/v0.3.0...v0.4.0
