Turf 2.1.0 changes nothing you have already written. Every program that compiled with 2.0 still compiles and behaves the same. Some programs that 2.0 rejected now compile, and some warnings that 2.0 printed by mistake are gone.
Language
Fixed-size arrays are assigned from their declaration
mut a: int[1000]; declares an array whose elements all read as the zero value of the element type until something writes them: 0, 0.0, false, "", null for &?T, or a zero-filled struct. Reading one before any write is no longer E008.
import io;
fn main(): int {
mut squares: int[10];
for i in 0..10 {
squares[i] = i * i;
}
io::printline(squares[3]);
return 0;
}
9
In 2.0 this program was rejected with E008, because the loop might not run. Version 2.0 tracked an array as a whole: one element write such as a[0] = 1; counted as assigning every element, so a[3] then read a zero that nothing wrote, while an array filled completely in a loop was rejected. Elements cannot be tracked one by one, so 2.1.0 says out loud what the storage already did: the array is zero-filled from its declaration.
An array whose element type holds a non-null reference, (&T)[N] or a struct with a &T field, still needs an initializer (E128). So does an unsized T[].
An array literal of nulls takes a (&?T)[N] type
a = [null, null]; for a: (&?N)[2] compiles, and so does the same literal as an argument, a return value or a field value. So does [&n, &m] where a (&?N)[2] is expected. Version 2.0 accepted these only in a declaration.
Size parameters
A generic function can take an array’s length as a parameter, written N: size. N is used as an array dimension and reads as an int in the body. A call gets it from an array argument, or else from the type the result is stored into.
fn<T, N: size> fill(value: T): T[N] {
mut r: T[N];
for i in 0..N {
r[i] = value;
}
return r;
}
fn main(): int {
mut zeros: int[8] = fill(0);
return zeros[7];
}
Here N is 8, because that is the length of the array the result is stored into. When nothing gives N, the call is E129. A size parameter on a struct, method or operator, or one used as a type or assigned to, is the new E150.
recover and assignment
recover binds more loosely than = and +=. So v = f() recover run { ... }; means (v = f()) recover ..., and that is E066. Write v = (f() recover run { ... }); instead. The parser is unchanged; the reference now says so.
Diagnostics
Fixes in diagnostics, in every build
When the compiler knows the exact edit that removes an error, the diagnostic now ends with a Fix: block showing the corrected line. It needs no flag and no model, and the plain build and the --no-smart build both print it. This is TDP-2-001.
── error at line 6, col 27 [E040 · undeclared variable]
I can't find anything named 'nmae'.
Hint: It may be from the current file: 'name'
6 │ printline("Hello, " + nmae + "!");
│ ^~~~ Here!
Fix: replace 'nmae' with 'name'
6 │ printline("Hello, " + name + "!");
│ ~~~~
Run `turf --explain E040` for more details.
A fix is shown only after it has been checked. The compiler applies the edit to a copy of the program in memory, analyses the copy, and prints the fix only if the original error is gone and no new error appeared. Your files are never changed. A fix is offered only for an error, never a warning, never inside the standard library, and only when there is exactly one candidate. A typo with two equally close names keeps its hint listing both and has no Fix:. At most ten fixes are checked in one compile, and a clean compile does none.
These errors can carry a fix: E014, E026, E029, E030, E031, E039, E040, E041, E042, E046, E048, E052, E054, E060, E068, E087, E102 and E138. Error codes are unchanged.
--smart: the compiler’s own fix comes first, and when an error has one the model is never called. The model is asked only when there is no compiler fix. Its suggestion is now labelled[Model Suggestion], where it used to say[AI Suggestion]. The[Deterministic Fix]status line is gone, because compiler fixes are no longer part of--smart.--smart-write-fix=<path>writes your program with everyFix:the compiler showed applied, in any build and without--smart. With--smartand no compiler fix, it writes the model’s validated suggestion. The flag and theturfcinterface version are unchanged.- Test authors: a new marker,
// EXPECT-FIX: replace 'nmae' with 'name', pins a printedFix:line. In a test that has one,turf testrequires each marker to match a printedFix:exactly, then applies the fixes and checks that the fixed program has fewer errors and none the original lacked. Tests without the marker behave as before.TURF_FIX_DEBUG=1prints a[fix]line for each error the fix path looks at, and why its fix was not shown. - Tools that match diagnostic text will see the new
Fix:lines. Diagnostic wording is outside the compatibility promise. The error codes are the stable part.
Analysis
- No false “dead store” or “never used” inside a
recover run { }block. A loop in the block, andreturn,breakandcontinuein it, are part of the function’s control flow, so a value carried round such a loop, or set before acontinue, is read. - Writing one element no longer makes the array’s earlier value dead.
mut x: int[3]; x[0] = 1; x[1] = 2; print(x[0]);no longer warns thatx[0] = 1is never read, andmut y = [1, 2, 3]; y[2] = 9;no longer warns abouty’s initializer. - A dead initializer says so.
mut x: int = 0;, whose value every path overwrites before reading, now reports “The initial value of ‘x’ is never read: every path assigns ‘x’ again before reading it.”, with a hint to declaremut x: int;. Version 2.0 printed “Variable ‘x’ is assigned but the value is never read (dead store)”, which read as ifxitself were unused. - No false “infinite loop” warning for a
while truewhose onlybreakorreturnis inside arecoverblock, acomparearm or anunsafeblock.
Standard library
arrays(import arrays;):fill,generate,reversedandmapbuild whole fixed-size arrays. Each returns a newT[N]and changes nothing it is given:mut zeros: int[1000] = arrays::fill(0);. The length comes from the type the result is stored into, or from an array argument. It is not in the prelude, and it is not the prelude’sarraymodule, whose helpers read an existing array.
Compiler crashes fixed
- An
if/elsewhose branches differ only in anINT_MINliteral (x = -9223372036854775808;againstx = -7;) crashed the compiler. It was found by turf-fuzz and is now a regression test.
Editor support
turf-lsp shows the same warnings as turf, because it runs the compiler’s own analysis. The fixes above reach your editor once turf-lsp is rebuilt against 2.1.0. No turf-lsp source change is needed.