Compiler: release-fast-0a4869dd (panic surfaced with a matching debug- build)
Command: roc check repro.roc
Severity: compiler crash; the offending reference produces neither a binding nor a clear "not found" diagnostic.
Problem
A module contains a nominal block App := [].{ … } whose method body qualifies a name from an import (Host.now!), and the import pf.Host line is placed after the block. The qualified reference does not bind to that import — but instead of reporting an unresolved name, the checker records an erroneous type and the compiler later aborts in monomorphization with Common.invariant("erroneous checked type reached Monotype instantiation"). The crash only surfaces once the block's method is used from another module (repro.roc calls App.stamp!). Moving import pf.Host above the nominal block makes the whole program check cleanly (App_ok.roc is that one-line-different version). Imports are otherwise order-insensitive elsewhere, so the after-a-nominal-block position appears to be a resolution gap specific to references inside the block's associated methods.
Repro
repro.roc + App.roc (import after the block) — crashes.
- Swap
App.roc for App_ok.roc (import above the block) — checks clean.
$ roc check repro.roc
thread N panic: postcheck invariant violated: erroneous checked type reached Monotype instantiation
src/postcheck/monotype/lower.zig:8611:37 in instNodeContent
src/postcheck/monotype/lower.zig:8428:47 in constrainTypeToMono
src/postcheck/monotype/lower.zig:9285:45 in restoredHoistedConstAtType
src/postcheck/monotype/lower.zig:9306:51 in restoredHoistedExprAtType
src/postcheck/monotype/lower.zig:18662:47 in lowerExprAtType
…
This reaches the same invariant as #10220 but via a different path (hoisted-const restoration rather than instNominalNode), and from a different root cause: an unresolved qualified reference producing an error type rather than a diagnostic. The desirable outcome is either that the import resolves regardless of its position relative to the block, or that an out-of-scope reference is reported as such instead of crashing.
Files
platform/main.roc:
platform ""
requires {} {
main : {
things : List(Box({ ctx : U64 } => U64)),
},
}
exposes [Host]
packages {}
provides { "roc_main": main_for_host! }
hosted { "roc_now": Host.now! }
targets: {
inputs_dir: "targets/",
arm64mac: { inputs: [app], output: Archive },
x64mac: { inputs: [app], output: Archive },
}
import Host
main_for_host! : {} => {}
main_for_host! = |_| {}
platform/Host.roc:
Host := {}.{
now! : () => U64
}
App.roc (import placed AFTER the block — triggers the crash):
App := [].{
# `Host.now!` is referenced here, but `import pf.Host` is placed AFTER this
# nominal block (below). Moving the import ABOVE the block makes it check
# cleanly.
stamp! : () => U64
stamp! = || Host.now!()
}
import pf.Host
App_ok.roc (the fix — import ABOVE the block; swap in for App.roc and it checks clean):
import pf.Host
App := [].{
stamp! : () => U64
stamp! = || Host.now!()
}
repro.roc (CRASHES — roc check repro.roc):
app [main] { pf: platform "platform/main.roc" }
import App
main = { things: [Box.box(handler!)] }
handler! = |_req| App.stamp!()
Compiler:
release-fast-0a4869dd(panic surfaced with a matchingdebug-build)Command:
roc check repro.rocSeverity: compiler crash; the offending reference produces neither a binding nor a clear "not found" diagnostic.
Problem
A module contains a nominal block
App := [].{ … }whose method body qualifies a name from an import (Host.now!), and theimport pf.Hostline is placed after the block. The qualified reference does not bind to that import — but instead of reporting an unresolved name, the checker records an erroneous type and the compiler later aborts in monomorphization withCommon.invariant("erroneous checked type reached Monotype instantiation"). The crash only surfaces once the block's method is used from another module (repro.roccallsApp.stamp!). Movingimport pf.Hostabove the nominal block makes the whole program check cleanly (App_ok.rocis that one-line-different version). Imports are otherwise order-insensitive elsewhere, so the after-a-nominal-block position appears to be a resolution gap specific to references inside the block's associated methods.Repro
repro.roc+App.roc(import after the block) — crashes.App.rocforApp_ok.roc(import above the block) — checks clean.This reaches the same invariant as #10220 but via a different path (hoisted-const restoration rather than
instNominalNode), and from a different root cause: an unresolved qualified reference producing an error type rather than a diagnostic. The desirable outcome is either that the import resolves regardless of its position relative to the block, or that an out-of-scope reference is reported as such instead of crashing.Files
platform/main.roc:
platform/Host.roc:
App.roc (import placed AFTER the block — triggers the crash):
App_ok.roc (the fix — import ABOVE the block; swap in for App.roc and it checks clean):
repro.roc (CRASHES —
roc check repro.roc):