elm/core

List.repeat fails (in an ugly way) when given Infinity

bengolds opened this issue · 2 comments

The following code crashes, in a really difficult-to-debug way:

List.repeat (1/0 |> floor) "a"

The problem here is that List.repeat simply crashes with an out of memory error when Infinity is given as the repeat value. None of the major browsers throw error messages that point to the code that caused the OOM error, nor does the Elm REPL (see stack trace below). You also can't debug this error through the performance profilers nor through hitting Pause in any of the script debuggers.

I'm not sure if the added overhead of an isInfinite check in List.repeat is worth it, but this is currently a case that is uncovered by #377?

Elm REPL stack trace

> List.repeat inf "hello"

<--- Last few GCs --->
li[5135:0x150008000]     9172 ms: Mark-sweep (reduce) 3731.2 (3827.7) -> 3731.2 (3796.7) MB, 2240.2 / 0.0 ms  (+ 17.4 ms in 5 steps since start of marking, biggest step 17.3 ms, walltime since start of marking 2259 ms) (average mu = 0.165, current mu = 0.11[5135:0x150008000]    11338 ms: Mark-sweep (reduce) 3913.8 (3982.2) -> 3913.8 (3982.2) MB, 1910.2 / 0.0 ms  (+ 0.0 ms in 3 steps since start of marking, biggest step 0.0 ms, walltime since start of marking 2005 ms) (average mu = 0.143, current mu = 0.118)

<--- JS stacktrace --->

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
 1: 0x104584e0c node::Abort() [/opt/homebrew/bin/node]
 2: 0x104584f8c std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::basic_string<std::nullptr_t>(char const*) [/opt/homebrew/bin/node]
 3: 0x1046b3d28 v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [/opt/homebrew/bin/node]
 4: 0x1046b3cbc v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/opt/homebrew/bin/node]
 5: 0x1047f9554 v8::internal::Heap::CollectionBarrier::ShutdownRequested() [/opt/homebrew/bin/node]
 6: 0x1047fa698 v8::internal::Heap::MarkCompactPrologue() [/opt/homebrew/bin/node]
 7: 0x1047f8058 v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [/opt/homebrew/bin/node]
 8: 0x1047f6640 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/opt/homebrew/bin/node]
 9: 0x1047ff1e0 v8::internal::Heap::AllocateRawWithLightRetrySlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/opt/homebrew/bin/node]
10: 0x1047ff248 v8::internal::Heap::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/opt/homebrew/bin/node]
11: 0x1047dcc30 v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationType, v8::internal::AllocationOrigin) [/opt/homebrew/bin/node]
12: 0x104a55d68 v8::internal::Runtime_AllocateInOldGeneration(int, unsigned long*, v8::internal::Isolate*) [/opt/homebrew/bin/node]
13: 0x104cd678c Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit [/opt/homebrew/bin/node]
14: 0x25ee7e20a4b0 
15: 0x104c6f494 Builtins_InterpreterEntryTrampoline [/opt/homebrew/bin/node]
16: 0x104c6f494 Builtins_InterpreterEntryTrampoline [/opt/homebrew/bin/node]
17: 0x104c6f494 Builtins_InterpreterEntryTrampoline [/opt/homebrew/bin/node]
18: 0x104c6f494 Builtins_InterpreterEntryTrampoline [/opt/homebrew/bin/node]
19: 0x104c6cd44 Builtins_JSEntryTrampoline [/opt/homebrew/bin/node]
20: 0x104c6c9e8 Builtins_JSEntry [/opt/homebrew/bin/node]
21: 0x150008000 

Thanks for reporting this! To set expectations:

  • Issues are reviewed in batches, so it can take some time to get a response.
  • Ask questions a community forum. You will get an answer quicker that way!
  • If you experience something similar, open a new issue. We like duplicates.

Finally, please be patient with the core team. They are trying their best with limited resources.

I’m working on a PR now.

If we really want to optimize it, we could create a new Kernel.Basics function isPositiveInfinity, because isInfinite checks for both positive and negative infinities (negative infinity doesn’t break List.repeat).

Note: This issue does not apply to String.repeat.