Skip to content

Jacket 0.20.0

Choose a tag to compare

@github-actions github-actions released this 19 Oct 04:49
· 388 commits to main since this release
v0.20.0
15cd85e

Adds jacket, one front door and one config file for the toolchain, along with editor completion and dead-code features and a faster jaic. Checking a 60k-line program is about 1.5x faster than in 0.19.1, and building it about 1.4x faster.

Highlights

  • jacket, one command for the toolchain. jacket build, check, run, lint, fmt and lsp are drop-ins for jaic, jailint, jaifmt and jailsp: every argument, single-dash Jai flags included, reaches the tool exactly as typed. What jacket adds is the project: with no .jai file named it builds the configured or inferred entry file. jacket new, init, config, clean and version are its own. It ships in the release archives, the installers and the Homebrew formula. See docs/tools/jacket.md.
  • One config file, jacket.toml. Its [project], [toolchain], [lint], [format] and [experimental] tables replace jai.toml, jailint.toml and jaifmt.toml, and jaic, jacket, jailsp, jailint and jaifmt read the same file. The old files still work; the first jacket command next to them writes jacket.toml (never under CI, never deleting anything), and jacket config migrate [--dry-run] does it on demand.
  • Entry files are inferred the same way everywhere: the configured entry, then build.jai / first.jai, then main.jai / src/main.jai, then module.jai.

Added

  • jailsp completes a procedure as a call when the client takes snippets: name($0) leaves the cursor inside the parentheses and opens parameter hints, name()$0 for a procedure without parameters. Parentheses are left out where the procedure is a value: before an existing (, after * or #procedure_of_call, inside type_of(...), in a :: or global := initialiser, and in type positions. Setting jai.completion.callSnippets (completion.callSnippets in the server's options): addParentheses (default), fillArguments (a placeholder per required parameter) or none. Also in the Neovim plugin as completion.call_snippets.
  • jailsp offers postfix completions after a value: flag.if, .while, .for, .return (at the start of a statement), .not and .ref, which rewrite the whole expression (if flag { }, !flag, *flag).
  • jailsp shows // name as an inlay hint after the closing brace of procedures, structs and enums of 25 lines or more. Setting jai.inlayHints.closingBraceMinLines (0 turns it off), also inlay_hints.closing_brace_min_lines in the Neovim plugin.
  • jailsp completes if, for and while at the start of a statement as a skeleton with the cursor in the body, and case inside the braces of if x == {.
  • jailsp fades code that can never run: statements after an exit, branches and loops a condition rules out (also by integer ranges and narrowing), cases a value cannot select, and #if/ifx arms that were not taken. They arrive as unreachable hints tagged Unnecessary, with no squiggle.
  • The browser formatter reads jacket.toml: the playground driver finds the nearest jacket.toml or jaifmt.toml, and jaifmt.wasm --config-name jacket.toml takes jacket.toml text.
  • A misplaced remove says why it cannot apply: the loop is over a fixed-size array (with the [..] and [] types that would work), a range of numbers or is a while loop, or there is no enclosing loop. It used to say only that remove is valid inside a for loop over an array.

Performance

  • Source files are lexed and parsed on worker threads ahead of type checking. Workers crawl the #load and #import graph from the main file, and type checking takes each finished file from a shared cache. Output is identical to a serial parse. -serial_parse turns the workers off, and -parse_threads N sets their number (default: cores - 1, at most 4).
  • Name lookup no longer copies using and import lists on every lookup, skips exported-using probing for modules that have none, and remembers scopes whose imports hold nothing under a name.
  • On macOS, LLVM's own C++ allocations also go through mimalloc.
  • Finding procedures that need no stack-trace instrumentation uses one parallel scan and a worklist instead of repeated passes.

Fixes

  • In a polymorphic procedure that nothing calls, a declaration initialised by a call to another polymorphic procedure (found, index := array_find(array.*, item)) got the type hint : ? and a ? hover, because the editor refused the call. The call now takes the callee's signature with the caller's own $T, so the hints read : bool, : s64, : T, : [] T; a hint is never ?.
  • jailint --allow-incomplete prints how many programs did not compile completely on the target instead of one line for each; -v lists them.
  • A baked $xs: [$N] T parameter takes $N from the element count of a constant array view (xs :: #run make()), where it used to say "array kind mismatch".
  • Assigning to an indexed expression (= and the compound assignments) on a type with operator *[] calls it and writes through the returned pointer, where it used to say "cannot assign to a value of type"; reads keep using operator [].
  • A [$N] T parameter takes $N from the element count of an untyped .[...] argument or default (f(.["a", "b"]) makes N 2), where it used to say it could not infer $N. A default .[...] whose length differs from the inferred count is an error at the default; [$N] $T still cannot get $T from an untyped literal.
  • Vectors, quaternions and square matrices of the Math module compare with == and !=, where it used to say no operator== matched.
  • a op= b is a = a op b when no operator op= accepts the operands (v /= 2.0 with only operator /= (*Vector2, Vector2) and a Vector2 / float), where it used to be a no-overload error.
  • A for_expansion taking *T iterates a T value through its address when other for_expansion overloads in scope have polymorphic first parameters that do not match, where it used to say none matched.
  • A [$N] T module parameter with a .[...] default has the default's count when the import gives no value, and can be forwarded to another module's parameter.
  • An overload that is not polymorphic is preferred to $T when the argument needs only a mild conversion (a wider integer, a pointer to the struct), as in the reference compiler; an integer is still not made a float for it.
  • An untyped .{...} argument no longer matches a string or other built-in aggregate parameter whose members its values cannot fill (an array literal for data).
  • value[xx x] on a struct with operator [] casts to the parameter of the overload picked for the struct, where it used to cast to an integer.
  • In jailsp, a statement missing only its ; at the end of a line keeps working: goto-definition, hover and highlighting work on it, and the error is shown on its last token instead of covering the statement's first name.

Breaking changes

Before Now
jai.toml, jailint.toml, jaifmt.toml jacket.toml (the old files are still read, with a hint to migrate; a jacket.toml in the same directory wins)
jai.toml build_files [project] entry
JAIC_PARSE_THREADS=N, JAIC_SERIAL_PARSE=1 -parse_threads N, -serial_parse; [experimental] for projects
JAIC_CODEGEN_UNITS=N -codegen_units N (-no_split for 1); [experimental] codegen_units
JAIC_PROFILE=1, JAIC_PROFILE=ops -profile, -profile_ops
JAIC_COVERAGE=FILE -coverage_file FILE
JAIC_VERIFY_IR, JAIC_SPLIT_UNITS, JAIC_NO_SINK, JAIC_NO_FLOW, JAIC_DUMP_OPS, JAIC_BODY_PROBE, JAIC_BODY_ORDER, JAIC_O0_PASSES -dev_verify_ir, -dev_split_units, -dev_no_sink, -dev_no_flow, -dev_dump_ops, -dev_body_probe, -dev_body_fifo, -dev_o0_passes (for working on the compiler; may change)
JAIC_CODEGEN_UNITS=4 for CI's native tests JACKET_TEST_CODEGEN_UNITS=4 (the test helpers pass -codegen_units 4)

Kept: JAIC_STDLIB, JAIC_NATIVE_LIBS, JAIC_LINKER, JAIC_AR, JAIC_CACHE_DIR, JAIC_MEMORY_LIMIT, JAIC_DIAGNOSTICS, JAIC_DSYM, JAIC_COVERAGE_DIR/JAIC_COVERAGE_FILE (read by the instrumented program), JAIC_SANITIZER_CC, JAIC_WASM_LD and the installer variables. They locate things on the machine or are read where a flag cannot reach, rather than tuning one project.