I tried to get acquainted with the new compiler, new syntax, and the language in general. I've been able to run this (yay!) with roc build (@5f964dcd):
app [main!] { pf: platform "https://github.com/lukewilliamboswell/roc-platform-template-zig/releases/download/0.5/BJBzo2SR2o5w3StmubGWvnPHq6hfePMaNWy5MwkPuZUs.tar.zst" }
import pf.Stdout
import pf.Arg exposing [Arg]
# main! : List Arg => Try {}
main! = |_args| {
Stdout.line!("Hello, World!")
dbg 5->calc()
Stdout.line!("${calc(7).to_str()}")
Ok({})
}
calc = |n| n + 1
trying to set up the LSP in helix however, I get a crash when viewing this file:
thread 27907 panic: Arrays out of sync:
type_nodes=17
region_nodes=38
/home/lukas/code/30software/roc/src/check/Check.zig:255:28: 0x4be3638 in copyVar (mod.zig)
std.debug.panic(
^
/home/lukas/code/30software/roc/src/check/Check.zig:1260:57: 0x4965ed8 in checkPlatformRequirements (mod.zig)
const copied_required_var = try self.copyVar(required_type_var, platform_env, required_type.region);
^
/home/lukas/code/30software/roc/src/compile/compile_build.zig:683:46: 0x4a76d82 in checkPlatformRequirements (mod.zig)
try checker.checkPlatformRequirements(platform_root_env, &platform_to_app_idents);
^
/home/lukas/code/30software/roc/src/compile/compile_build.zig:586:43: 0x4a7bfa5 in build (mod.zig)
try self.checkPlatformRequirements();
^
/home/lukas/code/30software/roc/src/lsp/syntax.zig:88:18: 0x4dfecec in check (mod.zig)
env.build(absolute_path) catch |err| {
^
/home/lukas/code/30software/roc/src/lsp/server.zig:236:63: 0x4e061d7 in runSyntaxCheck (mod.zig)
const publish_sets = try self.syntax_checker.check(uri, if (doc) |d| d.text else null, root_path);
^
/home/lukas/code/30software/roc/src/lsp/server.zig:225:32: 0x4e06bce in onDocumentChanged (mod.zig)
self.runSyntaxCheck(uri) catch |err| {
^
/home/lukas/code/30software/roc/src/lsp/handlers/did_open.zig:44:35: 0x4a2666d in call (mod.zig)
self.onDocumentChanged(uri);
^
/home/lukas/code/30software/roc/src/lsp/server.zig:176:24: 0x4832b25 in handleNotification (mod.zig)
handler(self, params) catch |err| {
^
/home/lukas/code/30software/roc/src/lsp/server.zig:149:44: 0x4833850 in handlePayload (mod.zig)
try self.handleNotification(method, obj.get(\params\"));\
^
/home/lukas/code/30software/roc/src/lsp/server.zig:114:31: 0x4833ec3 in processNextMessage (mod.zig)
self.handlePayload(payload) catch |err| {
^
/home/lukas/code/30software/roc/src/lsp/server.zig:100:47: 0x48341a4 in run (mod.zig)
while (try self.processNextMessage()) {}
^
/home/lukas/code/30software/roc/src/lsp/server.zig:312:19: 0x4834b40 in runWithStdIo (mod.zig)
try server.run();
^
/home/lukas/code/30software/roc/src/lsp/mod.zig:10:28: 0x4834ffd in runWithStdIo (mod.zig)
try server.runWithStdIo(allocator, debug);
^
/home/lukas/code/30software/roc/src/cli/main.zig:766:61: 0x4893ac9 in mainArgs (main.zig)
.experimental_lsp => |lsp_args| try lsp.runWithStdIo(allocs.gpa, .{
^
/home/lukas/code/30software/roc/src/cli/main.zig:643:13: 0x4895408 in main (main.zig)
mainArgs(&allocs, args) catch |err| {
^
/usr/lib/zig/std/start.zig:627:37: 0x4895a01 in main (std.zig)
const result = root.main() catch |err| {
^
/usr/lib/zig/libc/musl/src/env/__libc_start_main.c:95:7: 0x9723e9b in libc_start_main_stage2 (/usr/lib/zig/libc/musl/src/env/__libc_start_main.c)
exit(main(argc, argv, envp));
^
???:?:?: 0x9702149 in ??? (???)
Unwind error at address `exe:0x9702149` (error.MissingFDE), trace may be incomplete
This is the LSP communication to that point:
2026-01-09T19:36:48.612 helix_core::syntax [INFO] Skipping syntax config for 'roc' because the parser's shared library does not exist
2026-01-09T19:36:48.613 helix_lsp::transport [INFO] roc-lsp -> {"jsonrpc":"2.0","method":"initialize","params":{"capabilities":{"general":{"positionEncodings":["utf-8","utf-32","utf-16"]},"textDocument":{"codeAction":{"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["","quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}},"dataSupport":true,"disabledSupport":true,"isPreferredSupport":true,"resolveSupport":{"properties":["edit","command"]}},"completion":{"completionItem":{"deprecatedSupport":true,"insertReplaceSupport":true,"resolveSupport":{"properties":["documentation","detail","additionalTextEdits"]},"snippetSupport":true,"tagSupport":{"valueSet":[1]}},"completionItemKind":{}},"formatting":{"dynamicRegistration":false},"hover":{"contentFormat":["markdown"]},"inlayHint":{"dynamicRegistration":false},"publishDiagnostics":{"tagSupport":{"valueSet":[1,2]},"versionSupport":true},"rename":{"dynamicRegistration":false,"honorsChangeAnnotations":false,"prepareSupport":true},"signatureHelp":{"signatureInformation":{"activeParameterSupport":true,"documentationFormat":["markdown"],"parameterInformation":{"labelOffsetSupport":true}}}},"window":{"workDoneProgress":true},"workspace":{"applyEdit":true,"configuration":true,"didChangeConfiguration":{"dynamicRegistration":false},"didChangeWatchedFiles":{"dynamicRegistration":true,"relativePatternSupport":false},"executeCommand":{"dynamicRegistration":false},"fileOperations":{"didRename":true,"willRename":true},"inlayHint":{"refreshSupport":false},"symbol":{"dynamicRegistration":false},"workspaceEdit":{"documentChanges":true,"failureHandling":"abort","normalizesLineEndings":false,"resourceOperations":["create","rename","delete"]},"workspaceFolders":true}},"clientInfo":{"name":"helix","version":"25.07.1"},"processId":27898,"rootPath":"/home/lukas/code/30software/roc","rootUri":"file:///home/lukas/code/30software/roc","workspaceFolders":[{"name":"roc","uri":"file:///home/lukas/code/30software/roc"}]},"id":0}
2026-01-09T19:36:48.619 helix_lsp::transport [ERROR] roc-lsp err <- "roc-lsp logging to /tmp/roc-lsp-debug.log\n"
2026-01-09T19:36:48.622 helix_lsp::transport [INFO] roc-lsp <- {"jsonrpc":"2.0","id":0,"result":{"capabilities":{"positionEncoding":"utf-16","textDocumentSync":{"openClose":true,"change":2}},"serverInfo":{"name":"roc-lsp","version":"0.1"}}}
2026-01-09T19:36:48.622 helix_lsp::transport [INFO] roc-lsp -> {"jsonrpc":"2.0","method":"initialized","params":{}}
2026-01-09T19:36:48.622 helix_lsp::transport [INFO] roc-lsp -> {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"languageId":"roc","text":"app [main!] { pf: platform \"https://github.com/lukewilliamboswell/roc-platform-template-zig/releases/download/0.5/BJBzo2SR2o5w3StmubGWvnPHq6hfePMaNWy5MwkPuZUs.tar.zst\" }\n\nimport pf.Stdout\nimport pf.Arg exposing [Arg]\n\n# main! : List Arg => Try {}\nmain! = |_args| {\n\tStdout.line!(\"Hello, World!\")\n\tdbg 5->calc()\n\tStdout.line!(\"${calc(7).to_str()}\")\n\tOk({})\n}\n\ncalc = |n| n + 1\n","uri":"file:///home/lukas/code/30software/roc/test.roc","version":0}}}
2026-01-09T19:36:48.836 helix_lsp::transport [ERROR] roc-lsp err <- "thread 27907 panic: Arrays out of sync:\n"
[…rest of above stack trace]
Is this a bug in the LSP, nonsense in my attempt at writing roc, or do I need to do some special setup to make the LSP work?
If the first, how can I debug it further?
Thanks in advance :)
I noticed your using version 0.5 of the platform, does it still happen with the latest which I think is 0.6?
Either way you have definitely found a bug, if you wouldn't mind making a GH issue that'd be good :grinning:
Alrighty, will check with 0.6. thanks for the quick reaction. :)
Before reporting I'll move around a bit in gdb which I finally managed to set up
Yep, same behavior with
app [main!] { pf: platform "https://github.com/lukewilliamboswell/roc-platform-template-zig/releases/download/0.6/2BfGn4M9uWJNhDVeMghGeXNVDFijMfPsmmVeo6M4QjKX.tar.zst" }
(note to self: timestamp 2026-01-09T21:36:45.221 in helix logs)
I know that feature is super early. We haven't touched it in a while and I don't think we ever got any integration tests for it, so it definitely needs some love.
Though that bug is deep in Check which should be quite reliable so I'm guessing some simple is broken here, like how its wired up to the existing compiler pipeline or something.
No worries, I got that from the experimental. I figured it might be in a „it works but only on tuesdays and for even-length input messages“ kinda situation where I just would've had to know what knobs to turn. happy to report anything
Wanted to give debugging this myself a shot (via zig build roc && ./zig-out/bin/roc experimental-lsp < crash.lsp), but I struggle in two orthogonal ways:
gdb gets symbols (i.e., correctly shows function names in bt), but has no line info. Am I missing gdb config or a build flag?self.debugAssertArraysInSync() somewhere in Check.zig triggers a rebuild taking ~1m25s on my machine. That's a bit too long of a loop for printf debugging. A big chunk of that seems to be building interpreter shims for all sorts of platforms (arm, wasm, …) and linking in general. Is there any way I can restrict what gets build, are there any other knobs I can turn, or is my machine just too slow (i7-8650U)? I haven't found any cross-compilation related build flag in zig build --help.Perhaps I'm missing an obviously better workflow here.
To give some detail regarding the missing line info: zig itself properly reports lines in the panic:
./zig-out/bin/roc experimental-lsp --debug-build --debug-transport --debug-syntax --debug-server < crash.lsp
roc-lsp logging to /tmp/roc-lsp-debug.log
Content-Length: 177
{"jsonrpc":"2.0","id":0,"result":{"capabilities":{"positionEncoding":"utf-16","textDocumentSync":{"openClose":true,"change":2}},"serverInfo":{"name":"roc-lsp","version":"0.1"}}}thread 203073 panic: [PRE check platform reqs] Arrays out of sync:
type_nodes=0
region_nodes=38
/home/lukas/code/30software/roc/src/check/Check.zig:255:28: 0x4965751 in checkPlatformRequirements (mod.zig)
std.debug.panic(
^
[…]
but when I start this via gdb ./zig-out/bin/roc, I get:
Reading symbols from ./zig-out/bin/roc...
(gdb) run experimental-lsp < crash.lsp
Starting program: /home/lukas/code/30software/roc/zig-out/bin/roc experimental-lsp < crash.lsp
[New LWP 203790]
[LWP 203790 exited]
Content-Length: 177
{"jsonrpc":"2.0","id":0,"result":{"capabilities":{"positionEncoding":"utf-16","textDocumentSync":{"openClose":true,"change":2}},"serverInfo":{"name":"roc-lsp","version":"0.1"}}}thread 203685 panic: [PRE check platform reqs] Arrays out of sync:
type_nodes=0
region_nodes=38
[…backtrace from zig panic…]
(gdb) f 7
#7 0x0000000004965752 in Check.checkPlatformRequirements (self=0x7ffffffece60, platform_env=0x7ffff62e4f20, platform_to_app_idents=0x7ffffffee1d8)
(gdb) list
39 }
40
41 void __restore_sigs(void *set)
42 {
43 __syscall(SYS_rt_sigprocmask, SIG_SETMASK, set, 0, _NSIG/8);
44 }
which is clearly not the source. I don't know how this is supposed to work under the hood, I'm just used to it „just working“ in the gcc -g world.
Luke Boswell said:
Either way you have definitely found a bug, if you wouldn't mind making a GH issue that'd be good :grinning:
https://github.com/roc-lang/roc/issues/8995 now exists
Hi @Lukas Juhrich, looks like Richard fixed your issue :)
We rarely use gdb so I'm not sure what's going wrong there.
A big chunk of that seems to be building interpreter shims for all sorts of platforms (arm, wasm, …)
Interesting, do you happen to know if this is necessary @Luke Boswell?
Yes it's necessary to build the roc cli. Until we have the llvm backend we currently pre-built the interpreter into a shim and use that for roc build.
So instead of linking the app which has been lowered to an object file with the host, we give the app in a serialised IR to our interpreter shim, and the platform host doesn't see anything different from a real compiled app.
Luke Boswell said:
So instead of linking the app which has been lowered to an object file with the host, we give the app in a serialised IR to our interpreter shim
Oh, so because I can do cross compilation a la roc build --target=<something else>, by default we ship all the other shims, because I might build for arm even though I am on x86_64? Pretty cool that roc build supports cross compilation ootb like that.
However, but wouldn't it technically be possible to disable the cross compilation targets and just not build the n-1 other shims?
Yeah we could not build these and embed them in the cli binary -- which would disable roc build. Or only build for the current native machine and embed just that -- which would prevent cross-compilation.
If it's not a major issue -- I'd prefer to leave it as is for now, as we are close enough to having an LLVM backend and removing all these shims anyway.
I very strongly think we should consider cross compilation mandatory
Yeah I was just thinking some kind of debug build flag here to speed up the loop for debugging things
it's a major selling point of Go, and we've done a lot of work to make it possible; it would be a real shame to miss out on it at the very end by not including a few kilobytes of shims :smile:
oh sure, for debugging it's fine :+1:
Oh, of course, I was only talking about a local debug build and trying to understand things, not suggesting anything else. Thanks for all the clarification!
Last updated: Feb 20 2026 at 12:27 UTC