in reply to Re: File::Temp survival and scope created by "do"
in thread File::Temp survival and scope created by "do"

Interesting...

So somehow do blocks aren't blocks either?

I'm pretty sure a do BLOCK has its own lexical scope:

$ perl -wMstrict -le 'my $x="A"; do { print $x; my $x="B"; print $x }; print $x' A B A $ perl -wMstrict -le 'do { my $x="A" }; print $x' Global symbol "$x" requires explicit package name (did you forget to d +eclare "my $x"?) at -e line 1. Execution of -e aborted due to compilation errors.

But it seems to me like this might have something to do with a do block being able to return a value:

$ perl -wMstrict -le 'sub Foo::DESTROY {print "BLAM"}; print "A"; do { my $x = bless {}, "Foo" }; print "B"' A B BLAM $ perl -wMstrict -le 'sub Foo::DESTROY {print "BLAM"}; print "A"; do { my $x = bless {}, "Foo"; 1 }; print "B"' A BLAM B

Although I don't know why do { my $x = bless {}, "Foo" } vs. do { bless {}, "Foo" } makes a difference either. Seems strange to me.

Replies are listed 'Best First'.
Re^3: File::Temp survival and scope created by "do"
by Eily (Monsignor) on Dec 10, 2018 at 16:54 UTC

    Well it looks like the variable $x (ie, the name) is only visible inside the do BLOCK, but the associated scalar's life is bound to the containing scope.

    I'm not sure what you mean about being able to return a value, but having the inverting the order of the statements inside the do BLOCK yields the same result:

    perl -le "sub Foo::DESTROY {print 'BLAM'}; print 'A'; do { 1; my $x = +bless {}, 'Foo' }; print 'B' "; A BLAM B
    (yup, windows over here). This looks like an optimization issue, when do contains only a single statement, I guess it is optimized as a single statement executed in the containing scope, while the visibility of the variable is still limited to the do block.

    The fact that it works properly when the do contains more than one statement means there isn't a real problem though, because if there's only one statement, the lexical declaration is technically not needed. (Edit: Or maybe the issue is that perl lets you declare a lexical that you never use, but that's not specific to do)

      Interesting, thanks for looking into it further!

      I'm not sure what you mean about being able to return a value

      I mean that in e.g. my $x = do { my $y = "foo" }, $x is assigned the return value of the assignment ("foo") - you can't do things like for example my $x = do { if (/x/) { "X" } else { "_" } } with a bare { BLOCK }.

      This looks like an optimization issue, when do contains only a single statement, I guess it is optimized as a single statement executed in the containing scope

      You may be onto something, there are some differences in the following two, although I don't know enough about the internals to say if the differences are relevant to the issue. I do think do { EXPR } shouldn't be optimized to just EXPR...

      $ perl -MO=Concise -e 'sub DESTROY{print "Foo"} do{1;my $x=bless{}}; p +rint "Bar"' f <@> leave[1 ref] vKP/REFC ->(end) 1 <0> enter ->2 2 <;> nextstate(main 3 -e:1) v:{ ->3 - <1> null vK*/1 ->b a <@> leave vKP ->b 3 <0> enter v ->4 - <;> ex-nextstate(main 4 -e:1) v ->- - <0> ex-const v ->4 4 <;> nextstate(main 4 -e:1) v ->5 9 <2> sassign vKS/2 ->a 7 <@> bless sK/1 ->8 - <0> ex-pushmark s ->5 6 <@> anonhash sK* ->7 5 <0> pushmark s ->6 8 <0> padsv[$x:4,5] sRM*/LVINTRO ->9 b <;> nextstate(main 6 -e:1) v:{ ->c e <@> print vK ->f c <0> pushmark s ->d d <$> const(PV "Bar") s ->e $ perl -MO=Concise -e 'sub DESTROY{print "Foo"} do{ my $x=bless{}}; p +rint "Bar"' c <@> leave[1 ref] vKP/REFC ->(end) 1 <0> enter ->2 2 <;> nextstate(main 3 -e:1) v:{ ->3 - <1> null vK*/1 ->8 - <@> scope vK ->- - <;> ex-nextstate(main 4 -e:1) v ->- 7 <2> sassign vKS/2 ->8 5 <@> bless sK/1 ->6 - <0> ex-pushmark s ->3 4 <@> anonhash sK* ->5 3 <0> pushmark s ->4 6 <0> padsv[$x:4,5] sRM*/LVINTRO ->7 8 <;> nextstate(main 6 -e:1) v:{ ->9 b <@> print vK ->c 9 <0> pushmark s ->a a <$> const(PV "Bar") s ->b
      The fact that it works properly when the do contains more than one statement means there isn't a real problem though

      It's still pretty inconsistent behavior IMO. Bug #133720 reported.

      Update: Minor edits to wording.

        If you use -MO=Concise,-exec and put the two programs side by side, one can see the scope (enter+leave) is literally missing.

        do{1;my $x=bless{}}; print "Bar" do{my $x=bless{}}; print "Bar" ================================= ================================= 1 <0> enter 1 <0> enter 2 <;> nextstate(main 3 -e:1) v:{ 2 <;> nextstate(main 3 -e:1) v:{ 3 <0> enter v 4 <;> nextstate(main 4 -e:1) v 5 <0> pushmark s 3 <0> pushmark s 6 <@> anonhash sK* 4 <@> anonhash sK* 7 <@> bless sK/1 5 <@> bless sK/1 8 <0> padsv[$x:4,5] sRM*/LVINTRO 6 <0> padsv[$x:4,5] sRM*/LVINTRO 9 <2> sassign vKS/2 7 <2> sassign vKS/2 a <@> leave vKP b <;> nextstate(main 6 -e:1) v:{ 8 <;> nextstate(main 6 -e:1) v:{ c <0> pushmark s 9 <0> pushmark s d <$> const[PV "Bar"] s a <$> const[PV "Bar"] s e <@> print vK b <@> print vK f <@> leave[1 ref] vKP/REFC c <@> leave[1 ref] vKP/REFC

        The thing is, do always has a return value, if you put ; 1 after the assignment that value is just always 1. Also, since the defect also disappears when you put 1; in front of the assignment, that's why I concluded that it was not about what do returns but rather how many statements are included.

        I also looked at the output of B::Concise, but immediatly concluded that I didn't understand much about it, so I didn't want to draw conclusions on something I had so little grasp on :P

        As for the bug, I guess you're right, I said that the bug shouldn't happen, because there's no need for a lexical in a single statement block, but you don't have to look very far to find one occurence of the issue ... since we're discussing it :P