Skip to content

Releases: matteopolak/jacket

Jacket 0.21.0

Choose a tag to compare

@github-actions github-actions released this 19 Oct 20:57
v0.21.0
6e64bf2

New

  • Two new lints: constant_condition (a condition that is always true or false) and string_literal_write (writing into a string literal).
  • Inlay hints for it and it_index in for loops.
  • Code after a call that never returns is faded as unreachable.
  • jaifmt --version and jaifmt --is-ignored.
  • -timings shows memory allocated per phase.

Improved

  • Rename in the editor is more complete (overloads, polymorphic parameters, loop variables and more) and refuses names it can't find.
  • Hover and go-to-definition work in more places, including code not checked yet and module aliases.

Fixed

  • Release archives were missing native libraries, most visibly on Windows (#13).
  • Writing into a string literal at compile time is now an error instead of silently changing the literal everywhere (#14).
  • Multisampled OpenGL windows on Windows (#15).
  • String.tprint and other Basic procedures through String (#16).
  • jacket run runs the executable the build actually produced.
  • Several error messages point at the right place, and a few invalid programs the reference compiler rejects are now rejected too.
  • An editor hang on structs that refer to #this.

Jacket 0.20.0

Choose a tag to compare

@github-actions github-actions released this 19 Oct 04:49
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.

Jacket 0.19.1

Choose a tag to compare

@github-actions github-actions released this 18 Oct 15:29
v0.19.1
05c326d

Restores stdlib compatibility and makes jaic faster. Several stdlib changes in 0.19.0 made observable behaviour differ from the official standard library; they are reverted, and UTF-8 decoding of malformed input now matches it too.

Performance

  • jaic uses mimalloc instead of the system allocator, which makes checks and builds about 7–13% faster (a 60k-line check takes 137 ms instead of 154 ms).

Fixes

  • split_from_right(s, byte) returns (false, "", s) again when the byte is absent.
  • scan checks the % that %% matches but leaves it unconsumed again.
  • Sort.quick_sort is no longer deprecated, and Sort no longer exports intro_sort or heap_sort. quick_sort keeps the faster introsort from 0.19.0, and its key form still computes each key once. IntroSort.intro_sort is unchanged.
  • Strict character_utf8_to_utf32 again reports only the bytes before a bad continuation byte as consumed, which is 0 when the first continuation is bad.
  • character_utf32_to_utf8 encodes surrogates (U+D800 to U+DFFF) as themselves again.
  • find_index_from_left(s, substring, start_index) asserts on a negative start_index instead of treating it as 0.
  • Window_Creation builds for Android again instead of failing with an #assert.
  • character_utf8_to_utf32 decodes malformed input to the payload bits of its bytes: a continuation byte without its 10 marker contributes its low six bits, and a stray continuation byte decodes as 0 in one byte, also in strict mode.

Jacket 0.19.0

Choose a tag to compare

@github-actions github-actions released this 18 Oct 14:59
v0.19.0
8f3e604

Faster default builds and compile-time string code, a much faster jailsp on large workspaces, stdlib fixes found by fuzzing and review, and a new excessive_nesting lint. This release includes the changes planned for 0.18.1, which was not published separately. The breaking changes are small stdlib behaviour fixes, listed below.

Highlights

  • jailsp keeps up with typing. In this repository a keystroke is re-analysed in about 60 ms (it was about 700 ms when editing a stdlib file), and the first hover or completion answers in under 10 ms instead of about half a second. The server keeps its #load graph and per-module indexes across edits instead of rescanning the stdlib, and workspace symbols no longer resolve every file path. On very large single programs the remaining cost is the full type check.
  • Faster default builds. Library files skip the #if OS/CPU branches the target rules out while they are lexed and parsed, which removes most of an empty program's front-end time. Default builds are about 10% faster, and small leaf procedures no longer get a stack-trace node, so debug executables run up to 13% faster.
  • Faster compile-time string code. String_Builder appends, integer printing and float formatting take far fewer steps, so string-heavy #run code runs about twice as fast.
  • Stdlib and #loaded files analyse correctly. A file inside a standard library tree is checked against that library, so its internal/... imports are allowed. A file that another module #loads is checked as part of that module.
  • New lint: excessive_nesting. jailint reports blocks nested deeper than max_nesting (also --max-nesting).
  • Coverage builds. jaic -coverage builds a program that records which code ran; tools/coverage/report.py turns that into line and function coverage. See coverage.
  • Fuzzing builds. -sanitize fuzzer (with address) instruments a program for libFuzzer and links its engine. See sanitizers.

Breaking changes

  • split_from_right(s, byte) returns (false, s, "") when the byte is absent, as the string overload and split_from_left already did. It returned (false, "", s).
  • scan consumes the % that %% matches, as scan2 does. scan("%%", "%") now succeeds; it used to fail on the unconsumed byte.
  • Sort.quick_sort is deprecated. The comparator form was a heap sort and the key form an insertion sort. Use intro_sort (a real introsort, also in Sort; the key form computes each key once and keeps equal keys in order) or heap_sort. quick_sort forwards to intro_sort for one release.
  • Strict character_utf8_to_utf32 reports at least the lead byte as consumed when a continuation byte is invalid (it could report 0). The consumed count for such input is now 1 or more.
  • Window_Creation fails with a clear #assert on Android instead of leaving its procedures undefined.

Fixes

  • A decode loop over invalid UTF-8 no longer stalls on a bad first continuation byte.
  • character_utf32_to_utf8 encodes surrogates (U+D800 to U+DFFF) as U+FFFD.
  • find_index_from_left(s, substring, start_index) treats a negative start_index as 0 instead of reading before the string.
  • table_find_multiple probes from the key's slot instead of scanning every entry, and a REFILL_REMOVED = false table no longer doubles under remove/add churn.
  • Substring search skips to the first byte of the substring, about ten times faster on large text.
  • A declaration whose initializer has an error still declares its name, so one mistake no longer causes a cascade of "unknown identifier" errors.
  • Completions rank named arguments first, then locals, the file's own names, the rest, internal names and auto-imports.
  • In auto-import, a workspace's own stdlib is offered only through its public modules.
  • jailint and jaic render diagnostics in linear time, so a file with thousands of findings no longer takes seconds to report. jailint also caches neighbouring files between runs.
  • The interpreter runs compile-time code faster: dead local setup is removed, jumps are straightened, and copies, fills and tests use dedicated operations.
  • On Windows, a compile-time call no longer loses the program's last error while the interpreter sets up a frame, and a failed check is reported at its own line.
  • A compile-time call into a foreign library that is missing names the library.
  • Path completion works inside a closed #import "..." and with ,file and ,dir; a completion after . with no expected type is about ten times faster.
  • Machine_X64 can be imported on every target, with feature checks that report no features and no-op hints off x64.
  • A missing #import,file prints only its path in the diagnostic.
  • #run with no result as a constant's value now says that it has no result.
  • Input keeps its per-frame arrays on the default allocator when a program sets the context allocator to temp.
  • Simp resolves its GL procedures once per context instead of on every draw.

For tool authors

  • jaic exposes more of what the editor tools need, and jailsp and jailint now use it instead of their own copies: jaic::project (settings, directive scan, load closure, root resolution, loader graph), jaic::paths (normalisation, canonical caches, stdlib discovery), jaic::source::LineIndex, jaic::lexer::lex_with_trivia (comments), #asm block lookup, and ide_facts::cursor_context.
  • The message callback receives a ReportSeverity (ReportSink).

Jacket 0.18.0

Choose a tag to compare

@github-actions github-actions released this 18 Oct 01:49
v0.18.0
c6fd62b

The project has a new name: Jacket ("Jai kit"). This release also lets editors open code you don't trust safely, and makes jailint faster and easier to extend.

Note

The standard library has been rewritten, this release may be a bit unstable.

Highlights

  • New name, same tools. The repository is now matteopolak/jacket. jaic, jailsp, jaifmt and jailint keep their names, and so do the crates.
  • Workspace trust. In an untrusted folder, jailsp checks your code without running any of its compile-time code (#run, #insert, build metaprograms). VS Code follows its own workspace trust setting; in Neovim, pass trusted = false to setup(). See workspace trust.
  • jailint is several times faster on large trees. It lints each file once and compiles programs in parallel (-j). --allow-incomplete lets you lint a tree where some modules only build on another OS.
  • Better switch completion. The "add missing cases" action now works on every kind of if x == { case ...; }, and its error covers the switch's header instead of the whole block.
  • Native console colours on Windows. Print_Color sets console colours through the Windows API, so they also work in consoles that don't understand ANSI escapes.

Breaking changes

What Was Now
VS Code extension matteopolak.jai-toolchain (and matteopolak.jai before that) matteopolak.jacket. It offers to uninstall the old one, and your jai.* settings carry over.
Neovim plugin matteopolak/jai.nvim matteopolak/jacket.nvim. The jai Lua module and the :Jai command are unchanged.
Homebrew matteopolak/tap/jai matteopolak/tap/jacket. brew upgrade moves you over.
Release archives jai-<platform>.tar.gz jacket-<platform>.tar.gz (.zip on Windows), unpacking to jacket-<platform>/.
How jailint recognises procedures by name (trim, min, free, ...) by a note on the declaration: @UseResult, @Minimum, @Maximum, @RemovesElements, @Releases. The standard library already has them; add them to your own procedures if you want the rules to apply. See declaration notes.
ImGui and Curl printf-style procedures (Text, TreeNode, curl_msnprintf, ...) a Jai string format, forwarded through a libffi bridge that releases never shipped the C functions themselves: the format is a C string and the arguments are C varargs. Pass text arguments as *u8 (cast(*u8) "ok", name.data), and TextColored takes *ImVec4. The _CFormat names are gone.

Earlier releases have been removed. Install 0.18.0 or later.

Fixes

  • A *void converts to a procedure type without a cast, as it should.
  • #no_reset is accepted alongside other declaration modifiers.
  • A variable is never treated as a constant, even when its initializer is one.
  • The interpreter runs memcpy, memset and memcmp without extra system calls, so memory-heavy programs under -interp run faster, especially on Linux.
  • On Linux, a shared library marked link_always is no longer dropped by --as-needed.
  • On X11, an empty clipboard reads as empty text instead of failing, and fullscreen works without a window manager.