Stream: beginners

Topic: Compiler panic with module params


view this post on Zulip Ian McLerran (Sep 20 2024 at 18:24):

I have discovered what appears to be a bug in compiling modules which require params. Not sure exactly what is causing it but:

I have a module which depends on two parameters. The code in this module is verified to be compilable/runnable by copy-pasting it directly into a roc application. The only modifications are to rename the functions imported through module params to their basic-cli names.

However, when running roc check on the module, I get a compiler panic.

thread '<unnamed>' panicked at crates/compiler/can/src/pattern.rs:917:18:
internal error: entered unreachable code: Any other pattern should have given a parse error
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

There was an unrecoverable error in the Roc compiler. The `roc check` command can sometimes give a more helpful error report than other commands.

view this post on Zulip Ian McLerran (Sep 20 2024 at 18:26):

The module code is as follows (note that the only changes between the exported code and the code that works inside a roc app is that sendHttpReq and getEnvVar were replaced with Http.send and Env.var respectively.)

module {
    sendHttpReq,
    getEnvVar,
} -> [serperTool, serper]

import InternalTools

serperTool =
    queryParam = {
        name: "q",
        type: "string",
        description: "The search query to send to the serper.dev API",
        required: Bool.true,
    }
    InternalTools.buildTool "serper" "Access to the serper.dev google search API" [queryParam]

serper : Str -> Task Str _
serper = \args ->
    apiKey = getEnvVar! "SERPER_API_KEY"
    request = {
        method: Post,
        headers: [{ key: "X-API-KEY", value: apiKey }],
        url: "https://google.serper.dev/search",
        mimeType: "application/json",
        body: args |> Str.toUtf8,
        timeout: NoTimeout,
    }
    when sendHttpReq request |> Task.result! is
        Ok response ->
            response.body
            |> Str.fromUtf8
            |> Result.withDefault "Failed to decode API response"
            |> Task.ok
        Err _ ->
            "Failed to get response from serper.dev"
            |> Task.ok

view this post on Zulip Agus Zubiaga (Sep 20 2024 at 18:34):

Interesting. I’ll take a look in a bit.

view this post on Zulip Ian McLerran (Sep 20 2024 at 18:40):

@Agus Zubiaga Looks like I jumped the gun here on assuming it has to do with module params. Seems that for some reason it doesn't like using my InternalTools import, even though I'm importing and using it in other modules.

view this post on Zulip Ian McLerran (Sep 20 2024 at 18:43):

Okay, definitely a weird bug here though - if I do import InternalTools exposing [buildTool] and call buildTool unqualified, it checks just fine.

view this post on Zulip Agus Zubiaga (Sep 20 2024 at 18:44):

I copied the header into a module and replaced the body of the functions with crash, and I still run into it

view this post on Zulip Agus Zubiaga (Sep 20 2024 at 18:44):

Forgot to say, I removed the import InternalTools line and it still failed

view this post on Zulip Agus Zubiaga (Sep 20 2024 at 18:45):

This seems to be enough to cause it:

module {
    sendHttpReq,
    getEnvVar,
} -> [serper]

serper = \args ->
    crash "todo"

view this post on Zulip Ian McLerran (Sep 20 2024 at 18:45):

Wow... thats interesting. Thats pretty minimal.

view this post on Zulip Agus Zubiaga (Sep 20 2024 at 18:46):

I think we are not handling SpaceBefore nodes in that specific function in can

view this post on Zulip Agus Zubiaga (Sep 20 2024 at 18:46):

I'm surprised about this because I just reused an existing one

view this post on Zulip Agus Zubiaga (Sep 20 2024 at 18:46):

it might expect those to be removed by an earlier pass, though

view this post on Zulip Ian McLerran (Sep 20 2024 at 18:46):

After getting roc check to pass on the module (with unqualified buildTool), I imported the module to my app and got the following:

internal error: entered unreachable code:
        No borrow signature for LambdaName { name: `54.IdentId(0)`, niche: Niche(Captures([])) } layout.

        Tip 1: This can happen when you call a function with less arguments than it expects.
        Like `Arg.list!` instead of `Arg.list! {}`.

                            Tip 2: `roc check yourfile.roc` can sometimes give you a helpful error.

view this post on Zulip Agus Zubiaga (Sep 20 2024 at 18:48):

Hm, that seems like a separate issue

view this post on Zulip Agus Zubiaga (Sep 20 2024 at 18:49):

Can you try removing the newlines in the params pattern?

module { sendHttpReq, getEnvVar } -> [serperTool, serper]

view this post on Zulip Agus Zubiaga (Sep 20 2024 at 18:49):

I think the first issue was only caused by that

view this post on Zulip Agus Zubiaga (Sep 20 2024 at 18:51):

I have tests for multiline params, but only for the parser which don't go through canonicalization

view this post on Zulip Agus Zubiaga (Sep 20 2024 at 18:56):

Ok yeah, I can confirm multiline module param patterns break in canonicalization because they don't go through desugaring like the other patterns do. I'm on it!

view this post on Zulip Ian McLerran (Sep 20 2024 at 19:06):

Agus Zubiaga said:

Can you try removing the newlines in the params pattern?

Yes, that is passing roc check :+1:

view this post on Zulip Agus Zubiaga (Sep 20 2024 at 19:06):

but you still get that mysterious "borrow signature" error when you run, right?

view this post on Zulip Ian McLerran (Sep 20 2024 at 19:07):

Correct. I have a second module with different params that produces the identical error as well.

view this post on Zulip Agus Zubiaga (Sep 20 2024 at 19:11):

Ok, that one seems to be something going wrong in lowering, which won't be as easy of a fix. I'm probably going to need the whole project (as much as needed to reproduce) to find where it's going wrong.

view this post on Zulip Agus Zubiaga (Sep 20 2024 at 19:12):

If you can share that a branch, or a gist where this is reproducible, that'd be great!

view this post on Zulip Ian McLerran (Sep 20 2024 at 19:13):

Full code base is here: https://github.com/imclerran/roc-openrouter/tree/prefab-tools (prefab-tools branch)

view this post on Zulip Agus Zubiaga (Sep 20 2024 at 19:13):

awesome, thanks!

view this post on Zulip Ian McLerran (Sep 20 2024 at 19:14):

hangon, I'm gonna create a new static branch so I don't mess with anything you're looking at.

view this post on Zulip Ian McLerran (Sep 20 2024 at 19:15):

https://github.com/imclerran/roc-openrouter/tree/lowering-bug

view this post on Zulip Ian McLerran (Sep 20 2024 at 19:21):

Oh and this may be apparent, but its the examples/tools.roc file which can reproduce the bug.

view this post on Zulip Agus Zubiaga (Sep 21 2024 at 20:31):

I looked deeply into this issue today.

At first I thought it had to do with putting ai.PrefabTools.Serper.serper (a function that uses params) inside a Dict, but that seems to be handled properly by the lower params step. It correctly wraps the reference in a closure that captures the params so that it can be passed around. I tested doing something similar in a smaller example, and it worked just fine.

After that, I tried removing all params in the example and pass sendHttpReq directly as an argument to Tools.handleToolCall, and I found I still run into the panic! So this appears to be an unrelated compiler bug. I thought it was caused by passing sendHttpReq as an argument/param, but even if we don't do this (and replace its call with a crash), we run into a similar panic.

My guess is that there's a type error somewhere in the program that we aren't catching by the time we reach borrow inference. Unfortunately, there's a lot of code involved (including packages), and it's pretty hard to find the root cause. I think the next step should be to try to find the smallest example that can reproduce the issue.

view this post on Zulip Ian McLerran (Sep 24 2024 at 15:31):

Thanks for looking at this Agus. I'm working on breaking this down to a minimum reproduction. I've stripped it down a bit, but have a long way to go to be any where near a minimum reproduction.

One interesting finding so far is that moving the call to Tools.handleToolCalls out of the loop eliminates the error.

view this post on Zulip Ian McLerran (Sep 24 2024 at 17:09):

Alright, I've got a min repro:

main.roc

app [main] {
    cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br",
}

import Module { task: (Task.ok {}) }

main = Task.loop! {} loop

loop = \{} -> Task.ok (Step  Module.effect!)

Module.roc

module { task } -> [effect]

effect = task

view this post on Zulip Ian McLerran (Sep 24 2024 at 17:43):

Issue filed at #7116

view this post on Zulip Ian McLerran (Sep 24 2024 at 17:48):

One additional finding:

if I inline the loop as an anonymous function, the issue does not occur.

IE the follwing does not cause the panic:

main = Task.loop! {} \{} -> Task.ok (Step Module.effect!)

view this post on Zulip Agus Zubiaga (Sep 24 2024 at 17:54):

Thank you so much for breaking this down, this is way more workable!

view this post on Zulip Agus Zubiaga (Sep 24 2024 at 17:55):

Interestingly, I was able to reproduce the panic on the original code without using params. However, at least in your example, it looks like the issue goes away if the task is passed as an argument, so there does seem to be an issue with params after all.

view this post on Zulip Ian McLerran (Sep 24 2024 at 17:57):

That is interesting... sounds like there may be two potential issues at play here? :thinking:

view this post on Zulip Agus Zubiaga (Sep 24 2024 at 17:59):

Yeah, maybe. I'll try to fix this and we'll see if that solves the issue in roc-openrouter :smile:

view this post on Zulip Ian McLerran (Sep 24 2024 at 18:09):

I had forgotten you mentioned hitting the panic with sendHttpReq sent directly as an function argument... I guess if the panic was the same, its probably the same bug. Funny that passing the task as an argument to the function doesn't work in the min repro though...


Last updated: Jul 05 2025 at 12:14 UTC