This release is not about one big feature.
It's about removing a lot of small restrictions left in the language because the compiler simply wasn't ready for them yet. Subtle, sometimes artificial, mostly unnoticeable β while the compiler underneath is getting more mature and complicated.
β Notable changes in Tolk v1.5:
1.
break/continue in loops β finally for TVM2.
@inline functions with multiple returns3. Type aliases as directional subtypes
4. Reorganized purity concept and dead-code removal
5. Colorful compilation errors with multiple locations and hints
6. Preparing the kernel for Tolk v2.0
PR on GitHub with detailed info.
β break/continue in loops
Looks and feels as expected:
while (true) {
val ch = bytes.loadUint(8);
if (ch == 0) {
break;
}
if (blacklisted.exists(ch)) {
continue;
}
bits += 8;
}
Both work in all loops, including nested ones. The compiler correctly tracks control-flow rejoins, stack layout, and other low-level details.
The most interesting part:
break is SIMPLER than continue. TVM has machinery with RETALT in loop continuations, but provides nothing to emulate continue.Instead,
continue is implemented like another feature that seems to have nothing in common at first glance:β Inline with early/multiple returns
Since Tolk v1.0, the compiler supported auto-inlining simple functions, but capitulated on early returns:
// not inlined earlier
// inlined since Tolk v1.5
fun checkPositive(x: int) {
if (x <= 0) {
return; // this prevented inlining
}
...
}
Why? Because inlining returns-in-the-middle is hard. Even C++ compilers used to struggle with it.
The algorithm I developed transforms a function into if-else chains:
fun demo() {
if (cond1) { return }
if (cond2) { xxx; return }
else { yyy }
zzz
}
gets translated to
fun demo() {
if (cond1) { }
else { if (cond2) { xxx }
else { yyy; zzz } }
}
And now the surprise: not fun+return, but loop+continue β works exactly the same way!
loop {
if (cond1) { continue }
if (cond2) { xxx; continue }
else { yyy }
zzz
}
gets translated to :)
loop {
if (cond1) { }
else { if (cond2) { xxx }
else { yyy; zzz } }
}
β Much better compilation errors
Errors can now point to multiple locations at once β the call site, declaration, type mismatch, inlining failure, and so on.
The compiler explains not only where something went wrong, but also why and what actions should be taken.
error: too many arguments in call to `calcSum`, expected 2, have 3
--> /some/file.tolk:6:30
| in function main
|
6 | return calcSum(100, 200, 300);
| ^^^
note: function declared here
--> /some/file.tolk:1:5
|
1 | fun calcSum(x: int, y: int) {
| ^^^^^^^
And they are now colorful!
β A more predictable and mature compiler
Bug bounty hunters sent over 200+ tickets, which look lots of my time to analyze β only to turn out to be non-issues.
As a result, I had to reconsider some deep questions:
- directional subtypes in the type system
- user-written code removal by the optimizer
- purity concept and
@pure keywordYou can read more in a traditionally detailed PR.
βοΈ Preparing for Tolk v2.0
This summer, I experimented with a new compiler backend (for Tolk v2.0). That backend will fully replace the old FunC kernel still living with us, and eliminate the need of Fift.
These experiments exposed a lot of places where the current compiler is still shaped around the old architecture.
π³ Tolk v1.5 has removed most of this historical baggage. It was purely internal work β not affecting how you use Tolk today. But it's the part I like the most. It opens the road to Tolk v2.0: a new backend, a new assembler, and direct bytecode generation. This is what I am starting to work on now.