Looks like it’s just the inlining heuristic (the empty loop is making the function “too complex” to automatically inline) — if you add @inline to your iterate definition then the allocations go away.
(I think that in order for the compiler to eliminate the allocations from the inherently non-typestable iterate function it needs to be inlined?)