Skip to content

ref to variable not kept by nested closure #8400

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
p5pRT opened this issue Apr 11, 2006 · 9 comments
Closed

ref to variable not kept by nested closure #8400

p5pRT opened this issue Apr 11, 2006 · 9 comments

Comments

@p5pRT
Copy link

p5pRT commented Apr 11, 2006

Migrated from rt.perl.org#38895 (status was 'resolved')

Searchable as RT38895$

@p5pRT
Copy link
Author

p5pRT commented Apr 11, 2006

From [email protected]

Created by [email protected]

Run the following test case.
It prints "foo" but not "bar".
It seems the reference to $test2 is not kept by the closure.

sub handler(&) {
  shift->();
}

my $call;

{

my $test1 = "foo";
my $test2 = "bar";
$call = sub {
  handler { print "$test1\n" };
  handler { print "$test2\n" };
};
}

$call->();

Perl Info

Flags:
    category=core
    severity=high

Site configuration information for perl v5.8.7:

Configured by Gentoo at Tue Jan 17 14:39:02 CET 2006.

Summary of my perl5 (revision 5 version 8 subversion 7) configuration:
  Platform:
    osname=linux, osvers=2.6.14-gentoo-r5, archname=i686-linux
    uname='linux acer 2.6.14-gentoo-r5 #2 preempt wed jan 11 00:48:45 cet 2006 i686 intel(r) pentium(r) m processor 1600mhz genuineintel gnulinux '
    config_args='-des -Darchname=i686-linux -Dcccdlflags=-fPIC -Dccdlflags=-rdynamic -Dcc=i686-pc-linux-gnu-gcc -Dprefix=/usr -Dvendorprefix=/usr -Dsiteprefix=/usr -Dlocincpth=  -Doptimize=-O2 -march=pentium-m -pipe -fomit-frame-pointer -Duselargefiles -Dd_semctl_semun -Dscriptdir=/usr/bin -Dman1dir=/usr/share/man/man1 -Dman3dir=/usr/share/man/man3 -Dinstallman1dir=/usr/share/man/man1 -Dinstallman3dir=/usr/share/man/man3 -Dman1ext=1 -Dman3ext=3pm -Dinc_version_list=5.8.0 5.8.0/i686-linux 5.8.2 5.8.2/i686-linux 5.8.4 5.8.4/i686-linux 5.8.5 5.8.5/i686-linux 5.8.6 5.8.6/i686-linux  -Dcf_by=Gentoo -Ud_csh -Di_ndbm -Di_gdbm -Di_db'
    hint=recommended, useposix=true, d_sigaction=define
    usethreads=undef use5005threads=undef useithreads=undef usemultiplicity=undef
    useperlio=define d_sfio=undef uselargefiles=define usesocks=undef
    use64bitint=undef use64bitall=undef uselongdouble=undef
    usemymalloc=n, bincompat5005=undef
  Compiler:
    cc='i686-pc-linux-gnu-gcc', ccflags ='-fno-strict-aliasing -pipe -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/gdbm',
    optimize='-O2 -march=pentium-m -pipe -fomit-frame-pointer',
    cppflags='-fno-strict-aliasing -pipe -I/usr/include/gdbm'
    ccversion='', gccversion='3.4.4 (Gentoo 3.4.4-r1, ssp-3.4.4-1.0, pie-8.7.8)', gccosandvers=''
    intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12
    ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
    alignbytes=4, prototype=define
  Linker and Libraries:
    ld='i686-pc-linux-gnu-gcc', ldflags =' -L/usr/local/lib'
    libpth=/usr/local/lib /lib /usr/lib
    libs=-lpthread -lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lc
    perllibs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
    libc=/lib/libc-2.3.5.so, so=so, useshrplib=false, libperl=libperl.a
    gnulibc_version='2.3.5'
  Dynamic Linking:
    dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-rdynamic'
    cccdlflags='-fPIC', lddlflags='-shared -L/usr/local/lib'

Locally applied patches:
    


@INC for perl v5.8.7:
    /etc/perl
    /usr/lib/perl5/site_perl/5.8.7/i686-linux
    /usr/lib/perl5/site_perl/5.8.7
    /usr/lib/perl5/site_perl/5.8.6
    /usr/lib/perl5/site_perl/5.8.6/i686-linux
    /usr/lib/perl5/site_perl
    /usr/lib/perl5/vendor_perl/5.8.7/i686-linux
    /usr/lib/perl5/vendor_perl/5.8.7
    /usr/lib/perl5/vendor_perl/5.8.6
    /usr/lib/perl5/vendor_perl/5.8.6/i686-linux
    /usr/lib/perl5/vendor_perl
    /usr/lib/perl5/5.8.7/i686-linux
    /usr/lib/perl5/5.8.7
    /usr/local/lib/site_perl
    .


Environment for perl v5.8.7:
    HOME=/home/hg
    LANG=fr
    LANGUAGE (unset)
    LC_ALL=fr_FR@euro
    LD_LIBRARY_PATH (unset)
    LOGDIR (unset)
    PATH=/opt/xi3/bin:/home/hg/bin:/opt/dcmtk/bin/:/bin:/usr/bin:/opt/bin:/usr/i686-pc-linux-gnu/gcc-bin/3.4.5:/opt/ati/bin:/opt/blackdown-jdk-1.4.2.03/bin:/opt/blackdown-jdk-1.4.2.03/jre/bin:/usr/qt/3/bin:/usr/games/bin:/usr/lib/openoffice/program
    PERL_BADLANG (unset)
    SHELL=/bin/zsh

@p5pRT
Copy link
Author

p5pRT commented Apr 12, 2006

From @ysth

On Tue, Apr 11, 2006 at 05​:32​:01AM -0700, Hervé Guillemet wrote​:

Run the following test case.
It prints "foo" but not "bar".
It seems the reference to $test2 is not kept by the closure.

sub handler(&) {
shift->();
}

my $call;

{

my $test1 = "foo";
my $test2 = "bar";
$call = sub {
handler { print "$test1\n" };
handler { print "$test2\n" };
};
}

$call->();

This is fixed in bleadperl, but probably won't be in 5.8.x.
As a workaround, just make sure there's a dummy mention of the
lexicals at each sub {} level​:

sub handler(&) {
  shift->();
}

my $call;

{

my $test1 = "foo";
my $test2 = "bar";
$call = sub {
  ($test1, $test2) if 0;
  handler { print "$test1\n" };
  handler { print "$test2\n" };
};
}

$call->();

@p5pRT
Copy link
Author

p5pRT commented Apr 12, 2006

The RT System itself - Status changed from 'new' to 'open'

@p5pRT
Copy link
Author

p5pRT commented Apr 12, 2006

@rgs - Status changed from 'open' to 'resolved'

@p5pRT p5pRT closed this as completed Apr 12, 2006
@p5pRT
Copy link
Author

p5pRT commented Dec 8, 2006

From [email protected]

This is a bug report for perl from adb@​enki.net,
generated with the help of perlbug 1.35 running under perl v5.8.8.


This is perhaps best illustrated with a short test case​:

$ perl -Wle '{ my $a = 1; sub b { sub { print $a } }} &{b()}'
Use of uninitialized value in print at -e line 1.

$ perl -Wle '{ my $a = 1; sub b { my $c = $a; sub { print $a } }} &{b()}'
1

That is, if I define one closure inside another inside an outer block,
the inner closure has access to lexical symbols from the outer block
if and only if those symbols have been referenced elsewhere in the
outer closure.

It seems like Perl is trying to optimize by throwing away lexical
scopes it thinks it doesn't need, without looking hard enough to see
if it needs them.

This comes from a real-world problem​: I've got a function that scans
for links in a file using HTML​::Parser, then stores them in a database
with DBI. I want to prepare() the database queries once and then
execute them repeatedly as you normally do, so I do it in an anonymous
block around my function, which then sets up an HTML​::Parser with an
anonymous sub as the "start" event handler.

I have a workaround, gratuitously referencing the query variables in
the outer closure. But this behavior does not follow the principle of
least surprise​:

$ ruby -We 'proc { a = 1; proc { proc { puts a } } }.call.call.call;'
1
$ guile -c '(((let ((a 1)) (lambda () (lambda () (write a) (newline))))))'
1
$ python -c 'b = lambda a : lambda : lambda : a; print b(1)()()'
1

I don't believe Perl should take such shortcuts with closures. I'd be
happy to take a crack at fixing it, but since Perl's been around so
long I suspect this behavior has been declared a Feature. Can anyone
enlighten me?



Flags​:
  category=core
  severity=medium


Site configuration information for perl v5.8.8​:

Configured by Debian Project at Sun Aug 6 17​:22​:11 UTC 2006.

Summary of my perl5 (revision 5 version 8 subversion 8) configuration​:
  Platform​:
  osname=linux, osvers=2.6.16-1-vserver-amd64-k8, archname=x86_64-linux-gnu-thread-multi
  uname='linux excelsior 2.6.16-1-vserver-amd64-k8 #2 smp tue apr 4 03​:40​:49 utc 2006 x86_64 gnulinux '
  config_args='-Dusethreads -Duselargefiles -Dccflags=-DDEBIAN -Dcccdlflags=-fPIC -Darchname=x86_64-linux-gnu -Dprefix=/usr -Dprivlib=/usr/share/perl/5.8 -Darchlib=/usr/lib/perl/5.8 -Dvendorprefix=/usr -Dvendorlib=/usr/share/perl5 -Dvendorarch=/usr/lib/perl5 -Dsiteprefix=/usr/local -Dsitelib=/usr/local/share/perl/5.8.8 -Dsitearch=/usr/local/lib/perl/5.8.8 -Dman1dir=/usr/share/man/man1 -Dman3dir=/usr/share/man/man3 -Dsiteman1dir=/usr/local/man/man1 -Dsiteman3dir=/usr/local/man/man3 -Dman1ext=1 -Dman3ext=3perl -Dpager=/usr/bin/sensible-pager -Uafs -Ud_csh -Uusesfio -Uusenm -Duseshrplib -Dlibperl=libperl.so.5.8.8 -Dd_dosuid -des'
  hint=recommended, useposix=true, d_sigaction=define
  usethreads=define use5005threads=undef useithreads=define usemultiplicity=define
  useperlio=define d_sfio=undef uselargefiles=define usesocks=undef
  use64bitint=define use64bitall=define uselongdouble=undef
  usemymalloc=n, bincompat5005=undef
  Compiler​:
  cc='cc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -DTHREADS_HAVE_PIDS -DDEBIAN -fno-strict-aliasing -pipe -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64',
  optimize='-O2',
  cppflags='-D_REENTRANT -D_GNU_SOURCE -DTHREADS_HAVE_PIDS -DDEBIAN -fno-strict-aliasing -pipe -I/usr/local/include'
  ccversion='', gccversion='4.1.2 20060729 (prerelease) (Debian 4.1.1-10)', gccosandvers=''
  intsize=4, longsize=8, ptrsize=8, doublesize=8, byteorder=12345678
  d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16
  ivtype='long', ivsize=8, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
  alignbytes=8, prototype=define
  Linker and Libraries​:
  ld='cc', ldflags =' -L/usr/local/lib'
  libpth=/usr/local/lib /lib /usr/lib
  libs=-lgdbm -lgdbm_compat -ldb -ldl -lm -lpthread -lc -lcrypt
  perllibs=-ldl -lm -lpthread -lc -lcrypt
  libc=/lib/libc-2.3.6.so, so=so, useshrplib=true, libperl=libperl.so.5.8.8
  gnulibc_version='2.3.6'
  Dynamic Linking​:
  dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E'
  cccdlflags='-fPIC', lddlflags='-shared -L/usr/local/lib'

Locally applied patches​:
 


@​INC for perl v5.8.8​:
  /etc/perl
  /usr/local/lib/perl/5.8.8
  /usr/local/share/perl/5.8.8
  /usr/lib/perl5
  /usr/share/perl5
  /usr/lib/perl/5.8
  /usr/share/perl/5.8
  /usr/local/lib/site_perl
  .


Environment for perl v5.8.8​:
  HOME=/home/adb
  LANG (unset)
  LANGUAGE (unset)
  LD_LIBRARY_PATH (unset)
  LOGDIR (unset)
  PATH=/usr/local/bin​:/usr/bin​:/bin​:/usr/bin/X11​:/usr/games
  PERL_BADLANG (unset)
  SHELL=/bin/bash

@p5pRT
Copy link
Author

p5pRT commented Dec 8, 2006

From @mhx

On 2006-12-08, at 08​:34​:08 -0800, Aaron D.Ball (via RT) wrote​:

This is perhaps best illustrated with a short test case​:

$ perl -Wle '{ my $a = 1; sub b { sub { print $a } }} &{b()}'
Use of uninitialized value in print at -e line 1.

$ perl -Wle '{ my $a = 1; sub b { my $c = $a; sub { print $a } }} &{b()}'
1

[...]

I don't believe Perl should take such shortcuts with closures. I'd be
happy to take a crack at fixing it, but since Perl's been around so
long I suspect this behavior has been declared a Feature. Can anyone
enlighten me?

I don't think it's a "Feature", and it has apparently been
fixed in the development version of Perl​:

mhx@​r2d2 ~ $ bleadperl -Wle '{ my $a = 1; sub b { sub { print $a } }} &{b()}'
1

mhx@​r2d2 ~ $ bleadperl -v

This is perl, v5.9.5 DEVEL29485 built for i686-linux-64int-ld

Marcus

--
Wesley Crusher tries to upgrade the warp drive and they work better
than ever.
  -- Things that never happen in "Star Trek" #33

@p5pRT
Copy link
Author

p5pRT commented Dec 8, 2006

From @iabyn

On Fri, Dec 08, 2006 at 07​:21​:09PM +0100, Marcus Holland-Moritz wrote​:

I don't think it's a "Feature", and it has apparently been
fixed in the development version of Perl​:

Yep​:

http​://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/2003-02/msg01109.html

  * named subs now close on behalf of inner subs;

--
You live and learn (although usually you just live).

@p5pRT
Copy link
Author

p5pRT commented Dec 8, 2006

From [email protected]

Marcus Holland-Moritz via RT wrote​:

I don't think it's a "Feature", and it has apparently been
fixed in the development version of Perl​:

mhx@​r2d2 ~ $ bleadperl -Wle '{ my $a = 1; sub b { sub { print $a } }} &{b()}'
1

mhx@​r2d2 ~ $ bleadperl -v

This is perl, v5.9.5 DEVEL29485 built for i686-linux-64int-ld

Yeah, I saw that when doing followup, so I merged my bug into the older
related one shortly after submitting it.

I just rsynced perl-current and am building a local Debian package to
try it out with. Is that all "bleadperl" means?

@p5pRT
Copy link
Author

p5pRT commented Dec 8, 2006

From [email protected]

Aaron D. Ball did write​:

Marcus Holland-Moritz via RT wrote​:

[...]

This is perl, v5.9.5 DEVEL29485 built for i686-linux-64int-ld

Yeah, I saw that when doing followup, so I merged my bug into the older
related one shortly after submitting it.

I just rsynced perl-current and am building a local Debian package to
try it out with. Is that all "bleadperl" means?

Highly likely. blead is a pun on leading edge/bleeding edge. Running
perl -v will confirm it for you​: if it's 5.9.x it's blead (what will
become 5.10, the development track). If it's 5.8.x, it's maint, the
stable track.

--
"It's overkill of course, but you can never have too much overkill."

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant