roc
roc copied to clipboard
Error in alias analysis
This only happens with the basic-cli args example, not with the similar args example in the roc repo.
RUST_BACKTRACE=1 ./target/release/roc build --linker=legacy /home/username/gitrepos/basic-cli/examples/args.roc
🔨 Rebuilding platform...
thread 'main' panicked at 'Error in alias analysis: error in module ModName("UserApp"), function definition FuncName("\x1b\x00\x00\x00\x12\x00\x00\x00\xbe\xd12$b\x077\x15"), definition of value binding ValueId(59): could not find func in module ModName("UserApp") with name FuncName("\x8c\x00\x00\x00\x12\x00\x00\x00\x1d\xca$\xf15\xe8\xd8\x98")', crates/compiler/gen_llvm/src/llvm/build.rs:5094:19
stack backtrace:
0: rust_begin_unwind
at /rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/std/src/panicking.rs:575:5
1: core::panicking::panic_fmt
at /rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/panicking.rs:64:14
2: roc_gen_llvm::llvm::build::build_procedures_help
3: roc_gen_llvm::llvm::build::build_procedures
4: roc_build::program::gen_from_mono_module
5: roc_build::program::build_loaded_file
6: roc_build::program::build_file
7: roc_cli::build
8: roc::main
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
This error was introduced by PR #5576
@ayazhafiz would it help if I try to minimize the args example or do you already know what's going wrong?
Yes please, minimizing would help a lot.
Here is my attempt at a minimal repro, I may have found something related see comment in this example.
app "args"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.4.0/DI4lqn7LIZs8ZrCDUgLK-tHHpQmxGF1ZrlevRKq5LXk.tar.br" }
imports [
pf.Arg,
pf.Task,
]
provides [main] to pf
main =
args <- Arg.list |> Task.await
when Arg.parseFormatted parser args is
Ok _ -> crash ""
Err _ -> crash ""
UserCommand : [Div F64 F64, Log F64 F64]
parser : Arg.NamedParser UserCommand
parser =
Arg.program
logSubCmd
{
name: "args-example",
help: "A calculator example of the CLI platform argument parser"
}
logSubCmd : Arg.Parser UserCommand
logSubCmd =
# WORKS WITH THIS
# Arg.succeed (Log 1.0 1.0)
# DOES NOT WORK WITH THIS
Arg.succeed (\base -> \num -> Log (Num.toF64 base) (Num.toF64 num))
|> Arg.withParser
(
Arg.i64Option {
long: "base",
short: "b",
help: "base of the logarithm",
}
)
|> Arg.withParser
(
Arg.i64Option {
long: "num",
help: "the number to take the logarithm of",
}
)
Thanks @lukewilliamboswell :)
Should we try to minimize the basic-cli platform as well @ayazhafiz?
friendly ping @ayazhafiz
Should we try to minimize the basic-cli platform as well @ayazhafiz?
Yes please!
On Tue, Aug 1, 2023 at 9:57 AM Anton-4 @.***> wrote:
friendly ping @ayazhafiz https://github.com/ayazhafiz
Should we try to minimize the basic-cli platform as well @ayazhafiz https://github.com/ayazhafiz?
— Reply to this email directly, view it on GitHub https://github.com/roc-lang/roc/issues/5701#issuecomment-1660498356, or unsubscribe https://github.com/notifications/unsubscribe-auth/AE6GL6THGJRWWK2FJDCBVPTXTEKOPANCNFSM6AAAAAA23JS634 . You are receiving this because you were mentioned.Message ID: @.***>
I have maximally minimized the platform and the example: args_minimal_platform.tar.gz
Code to run:
basic-cli on main [✘!?] via ❄ impure (nix-shell-env)
❯ ./roc_nightly/roc build examples/argsGOOD.roc
🔨 Rebuilding platform...
0 errors and 0 warnings found in 6294 ms while successfully building:
examples/args
basic-cli on main [✘!?] via ❄ impure (nix-shell-env) took 6s
❯ ./roc_nightly/roc build examples/argsBROKEN.roc
🔨 Rebuilding platform...
thread 'main' panicked at 'Error in alias analysis: error in module ModName("UserApp"), function definition FuncName("\x1b\x00\x00\x00\x12\x00\x00\x00f\xf9\x128M\x96\x07G"), definition of value binding ValueId(59): could not find func in module ModName("UserApp") with name FuncName("\x8c\x00\x00\x00\x12\x00\x00\x00\t\xdaC\x18\xe3\xeb\x0e\xab")', crates/compiler/gen_llvm/src/llvm/build.rs:5123:19
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Do you have some time available to look into this issue in the coming weeks @ayazhafiz? In case it may help, Brendan also recently did some digging into a very similar issue.
Given that this issue breaks command line arguments for the basic-cli platform, it would be really valuable to have this fixed.
@Anton-4 I think the first step is a further minimization. I know you, Luke, and Brendan have looked into simplifying the reproduction and that has been super valuable, but it's unlikely we will make progress on diagnosing the issue unless we can have a minimal reproducer that is only a few functions long (or at most one module).
That is, stand-alone functions with no dependency on any platform.
Alright , that's good to know. I can look at further optimization once I'm done with higher priority issues, but it if anyone else can take it that would be great too :)
Another large example with a similar error. Once we've fixed the minimal case, we should check if this code is fixed too.
roc script
# this source is partially minimized, see original at https://github.com/salarii/peek/blob/e6e0b29fd5b715cb05d0bdfef2a16194f474d01d/regex.roc
app "reg"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.5.0/Cufzl36_SnJ4QbOoEmiJ5dIpUxBvdB3NEySvuH82Wio.tar.br" }
imports [pf.Stdout]
provides [main] to pf
firstStagePatterns = [
{ tag : Dot, str : "."},
]
priorities =
Dict.empty {}
|> Dict.insert Empty -1
|> Dict.insert Character 0
|> Dict.insert Dot 1
emptyNode = { locked : Bool.false, children : [], value : [] }
treeBase =
{cnt : 0, content : Dict.empty {} }
|> addElement 0 emptyNode
createParsingRecord = \ regex, meta ->
{ regex : regex, current : regex, matched : [], result : Bool.false, missed : [], left : [] , captured : treeBase, meta : meta, strict : No }
checkMatchingValidity = \ matchingResult ->
when matchingResult.strict is
No -> Bool.true
Front -> List.isEmpty matchingResult.missed
Back -> List.isEmpty matchingResult.left
Both -> (List.isEmpty matchingResult.missed) && (List.isEmpty matchingResult.left)
modifyLastInList = \ lst, elem ->
List.dropLast lst 1
|> List.append elem
getPrioToken = \ patterns ->
if List.isEmpty patterns then
Err NoTokens
else
List.walk patterns (Err Empty) (\ state, pattern ->
when state is
Err Empty ->
when Dict.get priorities pattern.tag is
Ok _ -> Ok pattern
Err _ -> Err PriorityListErr
Ok prevPat ->
when Dict.get priorities prevPat.tag is
Ok val1 ->
when Dict.get priorities pattern.tag is
Ok val2 ->
if val2 > val1 then
Ok pattern
else
state
Err _ -> Err PriorityListErr
Err _ -> Err PriorityListErr
Err message -> Err message
)
createToken = \ token, serie, capture ->
{ token :token, serie : serie, capture : capture }
regexSeedPattern = [
{ tag : Character, tokens : [ createToken Dot Once Bool.false ] } ]
# test this, I could not figure out how to do this properly
splitChainOnSeparators = \ chain, inputLst ->
when List.first chain is
Ok elem ->
when elem.token is
Separator ->
List.walk ( splitChainOnSeparators (List.dropFirst chain 1) []) [] ( \ state, lst ->
List.append state lst
)
|> List.append []
Sequence inChain ->
partialSeqResult =
List.walk ( splitChainOnSeparators inChain []) [] ( \ outState, frontLst ->
List.walk ( splitChainOnSeparators (List.dropFirst chain 1) []) outState ( \ state, lst ->
List.append state ( List.concat [ {elem & token : Sequence frontLst } ] lst)
)
)
when List.last partialSeqResult is
Ok activElem ->
List.dropLast partialSeqResult 1
|> List.walk [] ( \ state, lst ->
List.append state (List.concat inputLst lst))
|> List.append activElem
Err _ ->
[]
_ ->
partialResult =
List.walk ( splitChainOnSeparators (List.dropFirst chain 1) (List.append inputLst elem )) [] ( \ state, lst ->
List.append state lst
)
when List.last partialResult is
Ok lst ->
modifyLastInList partialResult (List.concat [elem] lst)
Err _ ->
[]
Err _ ->
[[]]
evalRegex = \ utfLst, patterns, regex ->
if List.isEmpty utfLst then
Ok regex
else
List.walk patterns [] ( \ state, pattern ->
out = checkMatching utfLst pattern.tokens
if out.result == Bool.true && List.isEmpty out.missed then
List.append state { tag : pattern.tag, parsedResult : out }
else
state
)
|> getPrioToken
|> ( \ prioToken ->
when prioToken is
Ok token -> evalRegex token.parsedResult.left patterns ( List.append regex token )
Err message -> Err message )
checkMatching = \ utfLst, reg ->
matchStr = \ utf, pattern ->
when pattern is
Character val ->
if val == utf then
Consume
else
NoMatch
Dot ->
Consume
_ -> NoMatch
matchUtf = ( \ utf, tokenMeta ->
result = matchStr utf tokenMeta.token
when result is
Consume -> Consume tokenMeta
NoMatch -> NoMatch tokenMeta
)
updateRegex = (\regex ->
List.walk regex [] ( \ state, regItem ->
when List.first regItem.current is
Ok pat ->
when pat.token is
Sequence chain ->
when pat.serie is
AtLeastOne ->
changeFront =
(List.dropFirst regItem.current 1)
|> List.prepend { pat & serie : ZeroOrMore }
List.concat chain changeFront
|> (\ updatedCurrent -> List.append state {regItem & current : updatedCurrent} )
ZeroOrMore ->
List.append state {regItem & current : (List.dropFirst regItem.current 1)}
|> List.append
(List.concat chain (List.dropFirst regItem.current 1)
|> (\ updatedCurrent -> {regItem & current : updatedCurrent, meta : Active} ))
NTimes cnt ->
concatIter = (\ n , lst, stored ->
if n == 0 then
stored
else
concatIter (n-1) lst (List.concat lst stored ))
List.concat (concatIter cnt chain [] ) (List.dropFirst regItem.current 1)
|> (\ updatedCurrent -> List.append state {regItem & current : updatedCurrent} )
Once ->
List.concat chain (List.dropFirst regItem.current 1)
|> (\ updatedCurrent -> List.append state {regItem & current : updatedCurrent} )
_ -> List.append state regItem
Err _ -> List.append state regItem )
)
getFirstPat = (\ state ->
if state.meta == Inactive then
Inactive state
else
when List.first state.current is
Ok pat ->
Active { pattern : pat , state : state }
Err _ ->
if state.meta == Origin then
# BUG in this line
#getFirstPat { state & current : state.regex }
getFirstPat { regex : state.regex, current : state.regex, matched : state.matched, result : state.result, missed : state.missed, left : state.left, captured : state.captured, meta : state.meta, strict : state.strict }
else
Inactive {state & meta : Inactive }
)
complexSearch =
List.walk utfLst [createParsingRecord reg Origin] ( \ outState, utf ->
updatedStates = updateRegex outState
List.walk updatedStates [] ( \ state, processedReg ->
if processedReg.result == Bool.true then
List.append state { processedReg & left : List.append processedReg.left utf}
else
manageIteration = ( \ inProcessedReg,curState ->
when getFirstPat inProcessedReg is
Inactive _patternSet ->
List.append curState inProcessedReg
Active matchThis ->
toUpdateState = matchThis.state
if matchThis.pattern.token == CaptureOpen then
tmpState = {toUpdateState & current : List.dropFirst toUpdateState.current 1}
manageIteration { tmpState & captured : (composeMatchedStructure tmpState.captured 0 CaptureOpen) } curState
else if matchThis.pattern.token == CaptureClose then
tmpState = {toUpdateState & current : List.dropFirst toUpdateState.current 1}
manageIteration { tmpState & captured : composeMatchedStructure tmpState.captured 0 CaptureClose } curState
else
updatedState = matchThis.state
current = List.dropFirst updatedState.current 1
# BUG this crashes
ppp = List.isEmpty current == Bool.true
updatedCapture =
if matchThis.pattern.capture == Bool.true then
when changeValue updatedState.captured 0 utf is
Ok updatedTree -> updatedTree
Err _ -> updatedState.captured
else
updatedState.captured
when matchUtf utf matchThis.pattern is
Consume _updatedToken ->
if List.len current == 0 then
List.append curState { updatedState & matched : List.append updatedState.matched utf, current : current, result : Bool.true, left : [], captured : updatedCapture}
else
List.append curState { updatedState & matched : List.append updatedState.matched utf, current : current, left : [], captured : updatedCapture}
NoMatch _ ->
# BUG second line
# updateMissed = List.concat updatedState.missed updatedState.matched
#List.append curState { updatedState & matched : [], current : updatedState.regex, missed : List.append updateMissed utf, left : [], captured : treeBase}
List.append curState { regex : updatedState.regex, current : updatedState.regex, matched : [], result : updatedState.result, missed : updatedState.missed, left : updatedState.left, captured : treeBase, meta : updatedState.meta, strict : updatedState.strict }
_ -> curState
)
manageIteration processedReg state )
)
List.walk complexSearch (createParsingRecord reg Inactive) ( \ state, parsResult ->
if state.result == Bool.true then
if (List.len parsResult.matched > List.len state.matched ) && (checkMatchingValidity state) then
parsResult
else
state
else
parsResult )
getRegexTokens = \ result ->
when result.tag is
Character->
when List.first result.parsedResult.matched is
Ok matched -> Ok [(createToken ( Character matched ) Once Bool.false )]
Err _ -> Err "character tag problem"
_ -> Err "wrong tag"
regexCreationStage = \ inPatterns, ignitionPatterns ->
regPatterns =
List.walk inPatterns (Ok []) ( \ state, pat->
when state is
Ok patterns ->
when evalRegex (Str.toUtf8 pat.str ) ignitionPatterns [] is
Ok results ->
List.walk results (Ok []) ( \ inState, result ->
when inState is
Ok patLst ->
when getRegexTokens result is
Ok tokens ->
workaround =
List.walk tokens [] ( \ workState, token ->
List.append workState (createToken token.token token.serie token.capture) )
# !!!!!!!!!!! crashes here Ok [{ tag : pat.tag, tokens : tokens }]
Ok (List.concat patLst workaround )
Err message -> Err message
Err message -> Err message
)
|> (\ inTokensResult ->
when inTokensResult is
Ok inTokens ->
Ok ( List.append patterns { tag : pat.tag, tokens : inTokens } )
Err message -> Err message )
Err Empty -> Err "Empty"
Err NoTokens -> Err "NoTokens"
Err PriorityListErr -> Err "PriorityListErr"
Err message -> Err message )
when regPatterns is
Ok patterns ->
Ok ( List.concat ignitionPatterns patterns )
Err message -> Err message
stagesCreationRegex = \ _param ->
regexCreationStage firstStagePatterns regexSeedPattern
#stage1 = regexCreationStage firstStagePatterns regexSeedPattern
#when stage1 is
# Ok stage1Pat ->
# regexCreationStage secondStagePatterns stage1Pat
# Err message ->
# Err message
regexCreationStage2 = \ str, patterns, currReg ->
( evalRegex (Str.toUtf8 str) patterns currReg )
|> ( \ resultSet ->
when resultSet is
Ok results ->
List.walk results (Ok { lst : [] , capture : Bool.false }) ( \ outState, result ->
when outState is
Err message -> Err message
Ok state ->
modifLastInChain = (\ chainLst, token ->
when List.last chainLst is
Ok elem ->
when elem.token is
Sequence chain ->
when List.last chain is
Ok _lastOnChain ->
when token.token is
CaptureClose ->
List.append chainLst token
_ ->
modifyLastInList chainLst (createToken (Sequence ( modifLastInChain chain token)) Once Bool.false)
Err _ ->
modifyLastInList chainLst (createToken (Sequence ( modifLastInChain chain token)) Once Bool.false)
_ ->
List.append chainLst token
Err _ ->
List.append chainLst token
)
when result.tag is
Character ->
when List.first result.parsedResult.matched is
Ok matched ->
Ok { state & lst : modifLastInChain state.lst (createToken (Character matched) Once state.capture) }
Err _ -> Err "parser match problem"
Dot ->
Ok { state & lst : modifLastInChain state.lst (createToken Dot Once state.capture ) }
CaptureOpen ->
openLst =
modifLastInChain state.lst (createToken CaptureOpen Once Bool.false)
|> modifLastInChain (createToken (Sequence []) Once Bool.false)
Ok { lst : openLst, capture : Bool.true}
Digit ->
Ok { state & lst : modifLastInChain state.lst (createToken Digit Once Bool.false ) }
NoDigit ->
Ok { state & lst : modifLastInChain state.lst (createToken NoDigit Once Bool.false ) }
Alphanumeric ->
limitRangToken = LimitRanges [{ left : 'A', right : 'Z' },{ left : 'a', right : 'z' },{ left : '0', right : '9' }]
Ok { state & lst : modifLastInChain state.lst (createToken limitRangToken Once Bool.false ) }
Empty -> Err "Empty"
)
|> (\ tokenLst ->
when tokenLst is
Ok lstRec ->
Ok lstRec.lst
Err message -> Err message )
Err Empty -> Err "Empty"
Err NoTokens -> Err "NoTokens"
Err PriorityListErr -> Err "PriorityListErr"
)
# checkMatching str reg ->
# create patterns and than parse string with it
availableRegex = stagesCreationRegex []
# maybe at some point add some additional error handling ??
# to be replaced
addElement = \ tree, parentId ,node ->
when Dict.get tree.content parentId is
Ok element ->
updatedContent =
Dict.remove tree.content parentId
|> Dict.insert parentId {element & children : (List.append element.children tree.cnt )}
{ cnt : tree.cnt + 1, content : (Dict.insert updatedContent tree.cnt node ) }
Err _ -> { cnt : tree.cnt + 1, content : Dict.insert tree.content tree.cnt node }
changeElement = \ tree, id ,node ->
when Dict.get tree.content id is
Ok _element ->
updatedContent =
Dict.remove tree.content id
|> Dict.insert id node
{ tree & content : updatedContent }
Err _ -> { cnt : tree.cnt + 1, content : Dict.insert tree.content tree.cnt node }
modifyActive = \ tree, headId, op ->
when Dict.get tree.content headId is
Ok head ->
if List.isEmpty head.children == Bool.true then
op headId tree
else
when List.last head.children is
Ok nodeId ->
when Dict.get tree.content nodeId is
Ok child ->
if child.locked == Bool.true then
op headId tree
else
modifyActive tree nodeId op
Err _ -> Err "internal logic error"
Err _ -> Err "internal logic error"
Err _ -> Err "wrong tree node id"
changeValue = \ tree, id, value->
modify =
\ idValue, treeValue ->
when Dict.get treeValue.content idValue is
Ok node ->
Ok (changeElement treeValue idValue {node & value : List.append node.value value } )
Err _ -> Err "internal logic error"
modifyActive tree id modify
composeMatchedStructure = \ tree, id,tag ->
addNewNode = \ idNew, treeNew -> Ok (addElement treeNew idNew emptyNode)
lockNode =
\ idLock, treeLocked ->
when Dict.get treeLocked.content idLock is
Ok node ->
Ok (changeElement treeLocked idLock {node & locked : Bool.true } )
Err _ -> Err "internal logic error"
result =
when tag is
CaptureOpen ->
if Dict.isEmpty tree.content == Bool.false then
modifyActive tree id addNewNode
else
Err "internal logic error"
CaptureClose ->
modifyActive tree id lockNode
when result is
Ok newTree -> newTree
Err _ -> tree
parseStr = \ str, pattern ->
when availableRegex is
Ok stage1Pat ->
tokensFromUserInputResult = regexCreationStage2 pattern stage1Pat []
when tokensFromUserInputResult is
Ok tokensFromUserInput ->
independentChainlst = splitChainOnSeparators tokensFromUserInput []
# for now get longest maybe??
Ok (List.walk independentChainlst (createParsingRecord [] Inactive) ( \ state, regexParser ->
parsResult = checkMatching (Str.toUtf8 str ) regexParser
if state.result then
if List.len parsResult.matched > List.len state.matched then
parsResult
else
state
else
parsResult ))
Err message ->
Err (Str.concat "You screwed up something, or not supported construction, or internal bug \n" message )
Err message ->
Err (Str.concat "This is internal regex error not your fault\n" message )
main =
pp = parseStr "sss" "a"
Stdout.line "outStr"
Very compact example, still depends on the basic-cli platform though...
mainLoop = \x ->
_ <- Stdout.line "hello \(Num.toStr x)" |> Task.await
if x == 3 then
Task.ok {}
else
mainLoop (x + 1)
main =
mainLoop 0
I encountered a similar recursive task problem when I played with roc-w4 platform:
Point : {
x : I32,
y : I32,
}
randPoint : Point -> Task Point []
randPoint = \maxPoint ->
x <- W4.randBetween { start: 0, before: maxPoint.x } |> Task.await
y <- W4.randBetween { start: 0, before: maxPoint.y } |> Task.await
Task.ok {
x,
y,
}
anotherRandPoint : Point, Point -> Task Point []
anotherRandPoint = \maxPoint, point ->
nextPoint <- randPoint maxPoint |> Task.await
if nextPoint != point then
Task.ok nextPoint
else
anotherRandPoint maxPoint point
and then used it like this:
start <- randPoint { x: 20, y: 20 } |> Task.await
end <- anotherRandPoint { x: 20, y: 20 } start |> Task.await
I get this error during compilation with roc build ...
thread 'main' panicked at 'Error in alias analysis: error in module ModName("UserApp"), function definition FuncName("\x12\x00\x00\x00\x02\x00\x00\x00_\xcbk6\xad\xefW\x99"), definition of value binding ValueId(3): could not find func in module ModName("UserApp") with name FuncName("\x19\x00\x00\x00\x04\x00\x00\x00_\xcbk6\xad\xefW\x99")', crates/compiler/gen_llvm/src/llvm/build.rs:5743:19
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Anyway, I planned to implement a simple prng to avoid touching the platform. Just reporting another example of the problem
I ran into this error before doing some parsing. Given it was in a package without a platform involved, I tried minimalizing the error. This is the best I got, split over two modules:
Main.roc
interface Main
exposes []
imports [Helper]
expect
_ = Helper.f (loop {})
1 == 1
loop = \{} ->
Helper.g \_ ->
if Bool.true then
loop {}
else
\_ -> {}
Helper.roc
interface Helper
exposes [f, g]
imports []
f : ({} -> {}) -> {}
f = \_ -> {}
g : ({} -> ({} -> {})) -> ({} -> {})
g = \fn -> \_ -> (fn {}) {}
I get the error when running roc test Main.roc.
thread 'main' panicked at 'Error in alias analysis: error in module ModName("UserApp"), function definition FuncName("\x11\x00\x00\x00\x00\x00\x00\x00z\xfd\x94\xa6\xfbT\x849"), definition of value binding ValueId(4): could not find func in module ModName("UserApp") with name FuncName("\x12\x00\x00\x00\x01\x00\x00\x00W\xb5\xe6M\xdd\xd3\xb7\xe4")', crates/compiler/gen_llvm/src/llvm/build.rs:5779:19
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Interestingly, if I move either f or g from Helper.roc to Main.roc, then the error disappears. My guess is that's maybe why we haven't managed reproductions without a platform before: it's not the platform that was relevant, but crossing a module boundary. @Anton-4's mainLoop example also has two calls to another module: Task.await and Task.ok.
Sorry for the messy types of f and g. If I try simplify them further the error disappears.
Nice @jwoudenberg! Thanks for this compact reproduction :)
Here's a single file reproduction based on an error I encountered while developing Weaver:
app [main] {
pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br",
}
import pf.Stdout
import pf.Task exposing [Task]
First : {}
Second : First {}
main =
testFunc {}
testFunc : Second -> Task {} [StdoutErr Stdout.Err]
testFunc = \{} ->
Stdout.line "abc"
$ roc appname-module-error.roc
thread 'main' panicked at crates/compiler/gen_llvm/src/llvm/build.rs:5764:19:
Error in alias analysis: error in module ModName("UserApp"), function definition FuncName("\'\x00\x00\x00,\x00\x00\x00x\xdd\x1f\x9d\x83PkI"), definition of value binding ValueId(7): could not find func in module ModName("UserApp") with name FuncName("\'\x00\x00\x00/\x00\x00\x00\xa1\xca\xcb\x7fz\xd0&\xd1")
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
The theory that a single layout is used for two different but identical types makes sense here.