Can I get away with memoizing a :foldable method?

Starting to dabble in moving long calls from runtime into compile-time, and I’m finding that repeated calls in compile-time are also annoying. Naively, I tried compile-time memoization, also avoiding cache access overhead or type instability at runtime. A base Julia-only toy example:

const _minusoneto = IdDict{Any, Any}() # typical type instability in memoization packages
Base.@assume_effects :foldable function minusoneto(n::Int)
  if n < 0
    throw(DomainError(n, "minusoneto only supports nonnegative integer exponents"))
  elseif haskey(_minusoneto, n)
    return _minusoneto[n] # ::Any
  else
    x = 1
    for _ in 1:n  x *= -1  end
    _minusoneto[n] = x
    return x
  end
end
foo(::Val{N}) where N = minusoneto(N)
bar(::Val{N}) where N = 10*minusoneto(N)

Memoization improves compile times for different callers that repeat a call. For anyone who tries this example, note that @time @eval is needed to capture JIT compile times for :foldable callees. This example’s callers optimize to constant return values, so I just time precompile:

julia> @time precompile(foo, (Val{123456789012},))
  4.154013 seconds (36.21 k allocations: 1.743 MiB, 99.99% compilation time)
true

julia> @time precompile(bar, (Val{123456789012},))
  0.000127 seconds (232 allocations: 11.578 KiB, 83.42% compilation time)
true

julia> @time precompile(bar, (Val{123456789013},))
  4.129439 seconds (232 allocations: 11.656 KiB, 100.00% compilation time)
true

julia> empty!(_minusoneto); # often need this when minusoneto is redefined

julia> @time precompile(foo, (Val{123456789013},))
  4.139190 seconds (170 allocations: 8.641 KiB, 100.00% compilation time)
true

:foldable is a sum of these effects:

  • :consistent
  • :effect_free
  • :terminates_globally
  • :noub
  • :nortcall

I think minusoneto simply satisfies the last 3, and I think it is :consistent as long as the cache strictly follows the method’s results, not the other way around on any level. It doesn’t satisfy the documented requirements for :effect_free because it’s mutating an externally heap-allocated object, but I can’t imagine how this can be a problem if said object only caches a consistent method (or potentially several within a world age). Did I stumble into an undocumented exception, or am I missing a danger here?