In principle, foreach iterates over a list that is constructed before the looping begins,
so you should have no concerns about making extra work for each iteration by having a complex expression for the list being iterated over.
In practice, iterations over a range (foreach ($x .. $y)) or an array (even one built on the fly: foreach (@{[ sort @x ]})) are optimized to not need to load the full list ahead of time. Since this optimization shouldn't introduce differences in behaviour, what you are seeing with a push in the loop is a bug.