This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Make pad_fixup_inner_anons cope with closed-over subs
authorFather Chrysostomos <sprout@cpan.org>
Sun, 8 Jul 2012 21:18:43 +0000 (14:18 -0700)
committerFather Chrysostomos <sprout@cpan.org>
Sun, 16 Sep 2012 05:45:00 +0000 (22:45 -0700)
When a sub starts being parsed, a new CV is created.  When it fin-
ishes, it is stored in its final location.  If there is a stub there
already, the pad is copied to the stub and the body attached thereto.

Since there may be closures inside the sub whose CvOUTSIDE
pointers point to the temporary CV used during compilation,
pad_fixup_inner_anons is called, to reassign all those
CvOUTSIDE pointers.

This happens in cases like this:

    sub f;
    sub f { sub { } }

When a sub closes over a lexical item in an outer sub, the inner sub
gets its own pad entry with the same value as the outer pad entry.

This means that, now that we have lexical subs (currently just state
subs), we can end up with a pad entry (&s) holding a sub whose
CvOUTSIDE does not point to the sub (f) that owns the pad:

    state sub s { }
    sub f { s() }

If the f sub has to reuse a stub, then pad_fixup_inner_anons gets to
see that, and complains bitterly:

$ ./perl -Ilib -E 'state sub s; sub f; sub f { s() }'
Assertion failed: (CvOUTSIDE(innercv) == old_cv), function Perl_pad_fixup_inner_anons, file pad.c, line 2095.
Abort trap

pad.c
t/cmd/lexsub.t

diff --git a/pad.c b/pad.c
index 68058be..c80a6e7 100644 (file)
--- a/pad.c
+++ b/pad.c
@@ -2122,7 +2122,7 @@ Perl_pad_fixup_inner_anons(pTHX_ PADLIST *padlist, CV *old_cv, CV *new_cv)
 
     for (ix = AvFILLp(comppad_name); ix > 0; ix--) {
         const SV * const namesv = namepad[ix];
-       if (namesv && namesv != &PL_sv_undef
+       if (namesv && namesv != &PL_sv_undef && !SvPAD_STATE(namesv)
            && *SvPVX_const(namesv) == '&')
        {
          if (SvTYPE(curpad[ix]) == SVt_PVCV) {
index de5db96..404f7dd 100644 (file)
@@ -138,17 +138,13 @@ is do foo(), 43, 'state sub falling out of scope (called via amper)';
   sub sb2 {
     if (shift) {
       package bar;
-     eval "
       is sb2, 44, 'state sub visible inside itself after decl';
       is &sb2, 44, 'state sub visible inside itself after decl (amper)';
       is do sb2(), 44, 'state sub visible inside itself after decl (do)';
-     ";
     }
     44
   }
-SKIP: { ::skip "Assertion failure", 3;
   sb2(1);
-}
   state sub sb3;
   {
     state sub sb3 { # new pad entry