a Expr::TrySuffix expression was not completely removed in desugar_value_def_suffixed
So after stripping down all my effectful if then else
blocks to dummy blocks to eliminate the stack overflow issue (seems to have been in the fileTreeHelper!
if block), I have encountered a new bug, as listed at the top of this post.
Wondering if it could have to do with using !
at the end of a record field name? (I have records which contain one effectful function as a field.)
Can confirm. When I eliminate the records which contain the field name ending in !
, this error is resolved.
The records look like this:
readFileContents : { name : Str, handler! : Str => Result Str *, tool : Tool }
readFileContents = {
name: readFileContentsTool.function.name,
handler!: readFileContentsHandler!,
tool: readFileContentsTool,
}
However, if I simply rename the handler!
field to handler
, the same error is encountered, presumably due to assigning an effectful function to the field.
So this record still encounters the bug:
readFileContents : { name : Str, handler : Str => Result Str *, tool : Tool }
readFileContents = {
name: readFileContentsTool.function.name,
handler: readFileContentsHandler!,
tool: readFileContentsTool,
}
https://github.com/imclerran/roc-ai/tree/desugar-suffixed-bug
Okay, while trying to make a min-repro, I have semi disproven my hypothesis for the cause of the bug...
Since the following works:
main! = \_args ->
try effects.echo! "Hello, world!"
Ok {}
effects : { echo!: Str => Result {} _ }
effects = {
echo!: Stdout.line!,
}
We parse differently if the module is purity inference or Task based
Both of these examples should be PI based... unless there's something I'm missing in the original module that is triggering it to treat it as non-pi...
The code worked fine in my original non-pi version.
So if your module exposed an ident with a ! at the end, it parses as PI
If not, Task
So what does your module expose?
Ah.... it exposes a record which DOES NOT have a !
at the end, but the record contains an ident ending in !
. That must be the issue.
My module exposes 4 records with the same type as:
readFileContents : { name : Str, handler! : Str => Result Str *, tool : Tool }
They all contain handler
, but none of the record names themselves were named with a !
since the records have both the effectful field, and idents which are not effectful.
Let me just expose the handlers themselves as well. Sounds like that should trigger PI parsing.
Yep, if I expose the readFileContentsHandler!
in addition to my records, the module parses without issue. I wonder if lack of PI parsing was the reason I hit the stack overflow in the last bug...? :thinking:
I'm 99% sure the answer is no
Well I was using PI syntax, and we know now it wasn't parsing as PI, so that was bound to cause some kind of issues...
Interesting. I created what I thought would be a min repro of this issue, but it parses correctly:
main.roc
app [main!] {
cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br",
}
import cli.Stdout
import Module { echo!: Stdout.line! } exposing [effects]
main! = \_args ->
try effects.echo! "Hello, world!"
Ok {}
Module.roc
module { stdout! } -> [ effects ]
effects : { echo!: Str => Result {} _ }
effects = {
echo!: stdout!,
}
More investigation required. But I can say that in my large code file, simply exposing a single ident ending in !
resolves the issue.
Ah, but - if I simply roc check Module.roc
I do hit a broken compiler expectation. However it is a different error than my original bug here.
Even though running main.roc in the min repro compiles and runs.
The code that caused the overflow is known to be fragile. If we weren't planning on removing it, I'd be trying to investigate it more
We really want to avoid ever crashing
Luckily, I don't think it's a problem outside of the case you're in
Well it turns out the only reason the stack overflow is hit at all is because the module isn't being parsed as PI. If the parser recognizes the file as PI, the code is now 100% correct.
Yes
Raises the question of why this parses correctly:
module { stdout! } -> [ effects ]
effects : { echo!: Str => Result {} _ }
effects = {
echo!: stdout!,
}
but why I have to expose listDirectoryHandler!
in FileSystem.roc for the code to parse as PI...
module { pathFromStr, pathToStr, listDir!, isDir!, readFile!, writeUtf8! } -> [
listDirectory,
#listDirectoryHandler!,
listFileTree,
readFileContents,
writeFileContents,
]
listDirectory : { name : Str, handler! : Str => Result Str *, tool : Tool }
#...
It's not a parsing thing, it's a desugaring thing: https://github.com/roc-lang/roc/blob/a089cf2487d2cb5f1d977a2e01be5ab7b4de3275/crates/compiler/can/src/desugar.rs#L540
They parse the exact same
Yeah, but it shouldn't be desugaring the !
. It should be parsing as PI, and not desugaring at all.
If you just uncomment that listDirectoryHandler!
in the module exports, doesn't try to desugar, because it parses the file as a PI module.
Maybe I'm getting my terminology mixed a little here, forgive me if I'm confusing things here between parsing and desugaring
You're totally fine
Let me try to say this:
The code is shitty and we are okay with that
Because it won't be shitty as soon as we permanently move to FxMode::PurityInference
And rip out the bad part
Is there something you think we should do on top of that?
But seems, from my limited understanding of the compiler, that it is trying to desugar !
to Task.await
, because it thinks the code is not purity inference, because there is no !
ident exported.
You are correct
PI is not determined from the code you write, but only from the exports
I'm with ya. Since we won't have split modes of parsing forever, no need to perfectly determine PI, since eventually it will always be used, and that will solve the issue.
Ian McLerran has marked this topic as resolved.
Also, no offense to you Agus if you're reading this, the use of FxMode
to allow for a smooth transition from Task to PI has been a godsend
Yeah, none taken. Things can be shitty and useful at the same time :smiley:
Last updated: Jul 06 2025 at 12:14 UTC