This is the result of an undocumented optimization on which you shouldn't rely.
Normally LHS = RHS is evaluated as follows:
- RHS is evaluated.
- LHS is evaluated.
- Assignment is evaluated.
As you can see, the right-hand side of the assignment is evaluated first[1]. This allows the following to work:
my $x = 123;
{
my $x = $x * 2;
say $x; # 456
}
say $x; # 123
Obviously, something different —and undocumented— is happening in your case. That's because LHS = <$fh> is special. Rather than reading from the file then assigning the result to the left-hand side, readline (<>) writes directly to the result of the left-hand side of the assignment.[2]
- LHS is evaluated. (This backs up
$/ and sets it to undef in your case.)
$fh is evaluated.
readline is evaluated, writing directly to the result of the left-hand side of the assignment.
No assignment is performed.
This optimization is undocumented, and you shouldn't rely on it.
local $/ = uc(<$fh>) wouldn't work, for example.
The compiled code has the right-hand side evaluated first:
$ perl -MO=Concise,-exec -e'$L = $R'
1 <0> enter
2 <;> nextstate(main 1 -e:1) v:{
3 <#> gvsv[*R] s <- $R
4 <#> gvsv[*L] s <- $L
5 <2> sassign vKS/2 <- =
6 <@> leave[1 ref] vKP/REFC
-e syntax OK
The following shows the right-hand side evaluated first:
$ perl -e'sub f :lvalue { CORE::say $_[0]; $x } f("L") = f("R")'
R
L
$x = uc(<>) evaluates uc(<>) before $x, then performs an assignment:
$ perl -MO=Concise,-exec -e'$x = uc(<>)'
1 <0> enter
2 <;> nextstate(main 1 -e:1) v:{
3 <#> gv[*ARGV] s \
4 <1> readline[t3] sK/1 > RHS
5 <1> uc[t4] sK/1 /
6 <#> gvsv[*x] s -> LHS
7 <2> sassign vKS/2
8 <@> leave[1 ref] vKP/REFC
-e syntax OK
$x = uc(<>) evaluates $x before <>, and it doesn't perform an assignment:
$ perl -MO=Concise,-exec -e'$x = <>'
1 <0> enter
2 <;> nextstate(main 1 -e:1) v:{
3 <#> gvsv[*x] s -> LHS
4 <#> gv[*ARGV] s \ RHS
5 <1> readline[t3] sKS/1 /
6 <@> leave[1 ref] vKP/REFC
-e syntax OK
Note the (uppercase) S next to readline that wasn't there before. This "special" flag is what tells readline to write to $x.
Adding local doesn't change anything.
$ perl -MO=Concise,-exec -e'local $x = <>'
1 <0> enter
2 <;> nextstate(main 1 -e:1) v:{
3 <#> gvsv[*x] s/LVINTRO
4 <#> gv[*ARGV] s
5 <1> readline[t3] sKS/1
6 <@> leave[1 ref] vKP/REFC
-e syntax OK