develooper Front page | perl.perl5.porters | Postings from October 2003

Re: [perl #24296] scalars and hashes sometimes not available in quoted evals in END blocks

Thread Previous
From:
Dave Mitchell
Date:
October 28, 2003 12:10
Subject:
Re: [perl #24296] scalars and hashes sometimes not available in quoted evals in END blocks
Message ID:
20031028201003.GA16229@fdgroup.com
On Tue, Oct 28, 2003 at 10:15:23AM -0800, David Dyck wrote:
> On Tue, 28 Oct 2003 at 10:31 -0000, Dave Mitchell <perlbug-followup@perl.or...:
> 
> > This is the correct behaviour. The showvars sub is *not* a closure (it
> > doesn't have a compile-time mention of the outer lexical), while the END
> > sub *is a closure. Thus at compile time the END sub captures the first
> > (and only) instance of $my_scalar. By the time END is called, the pad for
> > the main code block has already been freed, so the var isn't available.
> > But END has its own private reference to it, so the END sub can still
> > access it.
> >
> > You can see a similar affect in this piece of code (not involving END):
> >
> >     {
> > 	my $x = 55;
> > 	sub f1 { print "f1: x=$x\n" }
> > 	sub f2 { print "f2: x=", eval '$x', "\n" }
> >     }
> >
> >     f1;
> >     f2;
> >
> >     __END__
> >
> >     f1: x=55
> >     Variable "$x" is not available at (eval 1) line 2.
> >     Use of uninitialized value in print at /tmp/p line 7.
> >     f2: x=
> >
> > Dave.
> 
> Thanks for responding!  Could we please start with
>  This is the implemented behaviour. :-)
> and work towards
>  This is not the documented behaviour.
> or at least
>  This is not the do-what-I-mean behaviour.

The behaviour of closures is severely under-documented at the moment.
One of the things on my copious list of things to do is to write it up
properly sometime. Under that hypothetical documentation, the behaviour of
your code correct ;-)

Part of the problem is that you are using a combination of two edge
features, both of which can do funny things with closures. First, eval
can't always do the right thing, because Perl doesn't necessarily know at
the time that the statement containing the 'eval' was compiled, that the
eval may later refer to some outer lexicals that therefore need preserving
beyond their normal lifespan.  Second, END is called vary late, when some
things no longer exist that that you may want to rely upon.

> I'm not sure I understand, or at least I have more to learn
> in this area.  The my variable that I declared was at file
> scope, and if we modify your case to move $x to file scope,
> then both of the functions can see $x.

In both cases, the lexical in question has ceased to be before the
code in question is called. Yes, in your example the lexical has file
scope, but END is called so late that it has still ceased to be (from the
perspective of the main file code itself).

as another example, consider the following:

Foo.pm:
    package Foo;
    sub DESTROY { print "destroying $_[0][0]\n" }

    my $x1 = bless ["x1"];
    my $x2 = bless ["x2"];
    sub f1 { print "x1=$x1->[0]\n" }
    sub f2 { print "x2=", eval '$x2->[0]', "\n" }


script:
    use Foo;
    Foo::f1;
    Foo::f2;

oputput:

    destroying x2
    x1=x1
    Variable "$x2" is not available at (eval 1) line 1.
    Use of uninitialized value in print at /tmp/Foo.pm line 7.
    x2=
    destroying x1

Both $x1 and $x2 are "file scope", but $x2 is destroyed as soon as the
'use Foo' is complete; $x1 hangs on because it has been captured by the
closure f1.





> I think you are describing what perl is doing, and
> we both observe that something is happening (something
> that I don't expect), but from my reading of the
> documentation I didn't pick up on the limitations of END subs
> that you are stating.
> 
> I do see the statement in perlsub.pod that states:
>  The "my" operator declares the listed variables to be
>  lexically confined to the enclosing block,
>  ...  or "do/require/use"'d file.
> 
> and
> 
>  An "eval()", however, can see lexical variables of the
>  scope it is being evaluated in, so long as the names
>  aren't hidden by declarations within the "eval()" itself.
>  See perlref.

Like  I said, its severely under-documented at the moment :-(

> Examples like the following that do what I mean
> lead me to think that the my initial bug report
> shows that perl isn't dwim'ing.
> 
> 
>     my $x = 55;
>     {
> 	sub f1 { print "f1: x=$x\n" }
> 	sub f2 { print "f2: x=", eval '$x', "\n" }
>     }
> 
>     f1;
>     f2;
> 
>     __END__
> 
>     f1: x=55
>     f2: x=55

Here, f2 is called while the 'file' is still executing, so $x hasn't been
freed up yet.

> And even here
> 
>     my $x = 55;
>     END {
> 	sub f1 { print "f1: x=$x\n" }
> 	sub f2 { print "f2: x=", eval '$x', "\n" }
> 	f1;
> 	f2;
>     }
> 
>     __END__
> 
>     f1: x=55
>     f2: x=55

That's slighyl more subtle, but here the END block is a closure (there
are inner compile-time references to  $x in it), so it captures $x.
Later the eval in f2 searches outwards and finds a copy of $x that has
been captured by END.

> The reason I even started looking into this is
> that I have some real code (vs. the example code
> that I posted) that whas having elements of
> a hash table disappearing during the END processing.
> 
> 
> Ok, here's another example that may be closer so showing
> the problem.
> 
> The very same eval in one case can see $x, but
> when other code is removed then perl issues warnings
> The evals in the first and third END subs
> have $x available, but not the second.
> 
> Please help me find the perl documentation
> that describes this as the correct behaviour.
> 
>     use warnings;
>     use strict;
>     $| = 1;
> 
>     # 2 lines from perlsub
>     my $x = 10;
>     sub bumpx { $x++ }
> 
>     eval 'print "x=$x\n"';
>     bumpx;
>     eval 'print "x=$x\n"';
> 
>     END {
> 	print "in first END\n";
> 	eval 'print "in eval x=$x\n"';
> 	bumpx;
> 	eval 'print "in eval x=$x\n"';
> 	$x;	# commenting out this line causes the
> 	    # above eval's to loose their access to $x
>     }

The naked $x above turns END() into a closure, so causing it to capture
$x at compile time, making it available for later use by the evals.

> 
>     END {
> 	print "in second END\n";
> 	print "END\n";
> 	eval 'print "in eval x=$x\n"';
> 	bumpx;
> 	eval 'print "in eval x=$x\n"';
>     ### $x;	# commenting out this line causes the
> 	    # above eval's to loose their access to $x
>     }
> 
>     END {
> 	print "in third END\n";
> 	eval 'print "in eval x=$x\n"';
> 	bumpx;
> 	eval 'print "in eval x=$x\n"';
> 	$x;	# commenting out this line causes the
> 	    # above eval's to loose their access to $x
>     }
> 
>     __END__
> 
>     x=10
>     x=11
>     in third END
>     in eval x=11
>     in eval x=12
>     in second END
>     END
>     Variable "$x" is not available at (eval 5) line 1.
>     Use of uninitialized value in concatenation (.) or string at (eval 5) line 1.
>     in eval x=
>     Variable "$x" is not available at (eval 6) line 1.
>     Use of uninitialized value in concatenation (.) or string at (eval 6) line 1.
>     in eval x=
>     in first END
>     in eval x=13
>     in eval x=14

HTH,

Dave.


-- 
Nothing ventured, nothing lost.

Thread Previous


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About