Perhaps this is done only once at compile time?
Yes. In the past, I have confirmed this by generating long lists and taking memory measurements. It can also be seen in the opcode tree
$ perl -MO=Concise -e'@a = (1..3)'
...
- <1> ex-list lK ->6
3 <0> pushmark s ->4
5 <1> rv2av lKP/1 ->6
4 <$> const[AV ] s ->5
...
compared with
$ perl -MO=Concise -e'@a = ($x..$y)'
...
- <1> ex-list lK ->7
3 <0> pushmark s ->4
- <1> null lKP/1 ->-
6 <1> flop lK ->7
d <1> flip[t6] lK ->7
4 <|> range(other->5)[t5] lK/1 ->c
- <1> ex-rv2sv sK/1 ->d
c <#> gvsv[*x] s ->d
- <1> ex-rv2sv sK/1 ->6
5 <#> gvsv[*y] s ->6
...
so the range operator does NOT generate a list
In fact, there is no range operator.
$ perl -MO=Concise -e'for ($x..$y) {}'
...
7 <{> enteriter(next->9 last->c redo->8) lKS ->a
- <0> ex-pushmark s ->3
- <1> ex-list lK ->6
3 <0> pushmark s ->4
- <1> ex-rv2sv sK/1 ->5
4 <#> gvsv[*x] s ->5
- <1> ex-rv2sv sK/1 ->-
5 <#> gvsv[*y] s ->6
6 <#> gv[*_] s ->7
...
compared with
$ perl -MO=Concise -e'for ((),$x..$y) {}'
...
8 <{> enteriter(next->a last->d redo->9) lK ->b
- <0> ex-pushmark s ->3
- <1> ex-list lKM ->7
3 <0> pushmark sM ->4
- <0> stub lPM ->4
- <1> null lKM/1 ->-
6 <1> flop lKM ->7
g <1> flip[t4] lK ->7
4 <|> range(other->5)[t3] lK/1 ->f
- <1> ex-rv2sv sK/1 ->g
f <#> gvsv[*x] s ->g
- <1> ex-rv2sv sK/1 ->6
5 <#> gvsv[*y] s ->6
7 <#> gv[*_] s ->8
...
|