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

Somehow the lexical variable in the do block gets attached to the upper scope. As shown with the test below:

use 5.014; use warnings; package Temp; use parent 'File::Temp'; sub new { my $self = shift; $self-> SUPER::new( DIR => '.' ) } sub DESTROY { my $self = shift; say "Destruction during ", ${^GLOBAL_PHASE}; $self-> SUPER::DESTROY } package main; use File::Copy; sub foo { my $h = Temp-> new } die unless -f 'x'; # this file must exist { copy 'x', my $fn1 = Temp-> new-> filename or die; copy 'x', my $fn2 = do { Temp-> new }-> filename or die; copy 'x', my $fn3 = do { my $h = Temp-> new }-> filename or die; copy 'x', my $fn4 = foo-> filename or die; say 1 if -f $fn1; say 2 if -f $fn2; say 3 if -f $fn3; say 4 if -f $fn4; say "End of scope"; } say "End of file"; __END__ Destruction during RUN Destruction during RUN Destruction during RUN 3 End of scope Destruction during RUN End of file
This works however:
copy 'x', my $fn3 = do { { my $h = Temp-> new } }-> filename or die;
because the extra block creates a lexical scope inside the do block.

The documentation for do states that it does not work as a loop, when all other blocks (even bare one, without the for or while keyword) do, (which means next and last and redo don't work on do blocks). So somehow do blocks aren't blocks either? What's weird is that there is a "local" scope bound to do, so

$_ = "Hi"; do { local $_ = "Hello" }; print
does print; "Hi" as expected...

Replies are listed 'Best First'.
Re^2: File::Temp survival and scope created by "do"
by haukex (Archbishop) on Dec 10, 2018 at 15:43 UTC

    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.

      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.