roc-lang / roc

A fast, friendly, functional language.
https://roc-lang.org
Universal Permissive License v1.0
4.2k stars 304 forks source link

`roc dev` crashes with 'encountered allocation error: Layout { size: 11258999068426240, align: 8 (1 << 3) }' #4760

Open lukewilliamboswell opened 1 year ago

lukewilliamboswell commented 1 year ago

Note changing the size of the inspectionCount in Monkey type does crazy things when using dbg on the state

192-168-1-108:aoc-2022 luke$ roc dev day11.roc 
thread 'main' panicked at 'encountered allocation error: Layout { size: 11258999068426240, align: 8 (1 << 3) }', /Users/luke/.cargo/registry/src/github.com-1ecc6299db9ec823/bumpalo-3.11.0/src/alloc.rs:30:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
app "aoc-2022"
    packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.1.2/3bKbbmgtIfOyC6FviJ9o8F8xqKutmXgjCJx3bMfVTSo.tar.br" }
    imports [
        pf.Stdout,
        pf.Task.{ Task },
    ]
    provides [ main, stateToStr ] to pf

Monkey : {
    items : List U64, 
    op : (U64 -> U64), 
    test : (U64 -> Nat), 
    inspectionCount : U16, 
    # Changing this ^^ inspectionCount type does crazy things when using dbg on the state
    # U8 - panicked at 'encountered allocation error: Layout { size: 2882303761517117440, align: 8 (1 << 3) }'
    # U16 - panicked at 'encountered allocation error: Layout { size: 11258999068426240, align: 8 (1 << 3) }'
    # I32/U32 - segfaults
    # I64/U64/Nat - runs but does crazy things with Dict
}

main : Task {} []
main =
    dbg (runRound { current : 0, monkeys : sampleMonkeys, round : 0 })
    Stdout.line "Complete"

State : {
    round : Nat,
    current : Nat,
    monkeys : Dict Nat Monkey,
}

runRound : State -> State
runRound = \state ->
    if state.round >= 20 then 
        state
    else     
        when Dict.get state.monkeys state.current is
            Err _ -> crash "couldn't find monkey"
            Ok currentMonkey ->  
                when List.first currentMonkey.items is
                    Err ListWasEmpty ->
                        # monkey has no more items start with the next monkey
                        # if we are on the last monkey then increment round
                        if state.current == Dict.len state.monkeys - 1 then
                            state
                            |> incrementCurrentMonkey
                            |> incrementRound
                            |> runRound
                        else
                            state
                            |> incrementCurrentMonkey
                            |> runRound

                    Ok item -> 
                        # got an item, inspect it and throw to next monkey
                        itemToThrow = calcItemToThrow currentMonkey.op item                        
                        idToThrowItem = calcIdToThrow itemToThrow currentMonkey.test

                        state
                        |> updateMonkey state.current incrementInspection
                        |> updateMonkey state.current dropItem
                        |> updateMonkey idToThrowItem (receiveItem itemToThrow)
                        |> runRound

updateMonkey : State, Nat, ([Present Monkey, Missing] -> [Present Monkey, Missing]) -> State
updateMonkey = \state, id, updateFn ->
    {state & monkeys : Dict.update state.monkeys id updateFn}

incrementInspection : [Present Monkey, Missing] -> [Present Monkey, Missing]
incrementInspection = \m -> 
    when m is 
        Missing -> crash "expected monkey to bump inspection"
        Present monkey -> 
            Present {monkey & inspectionCount : monkey.inspectionCount + 1}

receiveItem : U64 -> ([Present Monkey, Missing] -> [Present Monkey, Missing])
receiveItem = \item -> \m -> 
    when m is 
        Missing -> crash "expected monkey to receive item"
        Present monkey -> 
            Present {monkey & items : List.append monkey.items item}

dropItem : [Present Monkey, Missing] -> [Present Monkey, Missing]
dropItem = \m ->
    when m is 
        Missing -> crash "expected monkey to bump inspection"
        Present monkey -> 
            Present {monkey & items : List.dropFirst monkey.items}

incrementRound : State -> State
incrementRound = \state -> {state & round : state.round + 1}

incrementCurrentMonkey : State -> State
incrementCurrentMonkey = \state -> 
    monkeyCount = Dict.len state.monkeys
    {state & current : (state.current + 1) % monkeyCount}

calcItemToThrow : (U64 -> U64), U64 -> U64
calcItemToThrow = \op, item -> item |> op |> \n -> n // 3

calcIdToThrow : U64, (U64 -> Nat) -> Nat
calcIdToThrow = \item, test -> test item 

expect calcItemToThrow (\n -> n * 19) 79 == 500
expect calcIdToThrow 500 (\n -> if n % 23 == 0 then 2 else 3) == 3
expect calcItemToThrow (\n -> n * 19) 98 == 620
expect calcIdToThrow 620 (\n -> if n % 23 == 0 then 2 else 3) == 3
expect  
    got = 
        { current : 0, monkeys : sampleMonkeys, round : 0 }
        |> updateMonkey 0 incrementInspection
        |> updateMonkey 0 (receiveItem 999)
        |> updateMonkey 0 dropItem
        |> updateMonkey 0 dropItem

    first = when Dict.get got.monkeys 0 is 
        Ok m -> m
        Err _ -> crash "expected a monkey"

    first.inspectionCount == 1 && List.first first.items == Ok 999

# Hardcoding this... wasted waaayyy too much time on parsers :D
sampleMonkeys : Dict Nat Monkey
sampleMonkeys = 
    Dict.empty 
    |> Dict.insert 0 { items: [79,98], op : \n -> n * 19, test : \n -> if n % 23 == 0 then 2 else 3, inspectionCount : 0 }
    |> Dict.insert 1 { items: [54,65,75,74], op : \n -> n + 6, test : \n -> if n % 19 == 0 then 2 else 0, inspectionCount : 0 }
    |> Dict.insert 2 { items: [79,60,97], op : \n -> n * n, test : \n -> if n % 13 == 0 then 1 else 3, inspectionCount : 0 }
    |> Dict.insert 3 { items: [74], op : \n -> n + 3, test : \n -> if n % 17 == 0 then 0 else 1, inspectionCount : 0 }

# For debugging
stateToStr : State -> Str
stateToStr = \state ->
    round = Num.toStr state.round
    monkeys = 
        state.monkeys
        |> Dict.toList 
        |> List.map \T id monkey -> 
            idStr = Num.toStr id
            items = List.map monkey.items Num.toStr |> Str.joinWith ","
            inspectionCount = Num.toStr monkey.inspectionCount
            "        id:\(idStr), inspections:\(inspectionCount), items:[\(items)]"
        |> Str.joinWith "\n"

    "{\n    round:\(round),\n    monkeys:\n\(monkeys)\n}"
ayazhafiz commented 1 year ago

Is this with a debug or release build of roc? (The compiler itself, not the --optimize flag)

lukewilliamboswell commented 1 year ago

This was using cargo build --release --locked on main.

ayazhafiz commented 1 year ago

@lukewilliamboswell would it be possible to minimize this issue as well?

lukewilliamboswell commented 1 year ago

@ayazhafiz I don't think I can minimise it much further. The best I've been able to manage so far is just removing the expects and the stateToStr debugging fn. Everything I've tried and the issue goes away.