This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Fix up outside pointers for my subs
authorFather Chrysostomos <sprout@cpan.org>
Mon, 13 Aug 2012 00:57:35 +0000 (17:57 -0700)
committerFather Chrysostomos <sprout@cpan.org>
Sun, 16 Sep 2012 05:45:06 +0000 (22:45 -0700)
I had not yet fixed Perl_pad_fixup_inner_anons to account for the
fact that my sub prototype CVs are stored in magic attached to
the SV slot in the pad, rather than directly in the pad.  It also
did not like & entries that close over subs defined in outer
or inner subs (‘my sub foo; sub bar; sub bar { &foo } }’ and
‘sub bar; sub bar { my sub foo; sub { sub foo { } } }’ respectively).

This was resulting in assertion failures, unsurprisingly.

Some of the tests I added, which were causing assertion failures, are
now failing for other reasons, and are marked as to-do.

pad.c
t/cmd/lexsub.t

diff --git a/pad.c b/pad.c
index afd6389..960d725 100644 (file)
--- a/pad.c
+++ b/pad.c
@@ -2213,10 +2213,15 @@ Perl_pad_fixup_inner_anons(pTHX_ PADLIST *padlist, CV *old_cv, CV *new_cv)
            && *SvPVX_const(namesv) == '&')
        {
          if (SvTYPE(curpad[ix]) == SVt_PVCV) {
-           CV * const innercv = MUTABLE_CV(curpad[ix]);
-           assert(CvWEAKOUTSIDE(innercv));
-           assert(CvOUTSIDE(innercv) == old_cv);
-           CvOUTSIDE(innercv) = new_cv;
+           MAGIC * const mg =
+               SvMAGICAL(curpad[ix])
+                   ? mg_find(curpad[ix], PERL_MAGIC_proto)
+                   : NULL;
+           CV * const innercv = MUTABLE_CV(mg ? mg->mg_obj : curpad[ix]);
+           if (CvOUTSIDE(innercv) == old_cv) {
+               assert(CvWEAKOUTSIDE(innercv));
+               CvOUTSIDE(innercv) = new_cv;
+           }
          }
          else { /* format reference */
            SV * const rv = curpad[ix];
index 348facf..f17eee0 100644 (file)
@@ -8,7 +8,7 @@ BEGIN {
     *bar::like = *like;
 }
 no warnings 'deprecated';
-plan 107;
+plan 111;
 
 # -------------------- our -------------------- #
 
@@ -453,6 +453,19 @@ sub make_anon_with_my_sub{
     is eval{s2},eval{\&s1}, 'my sub in anon closure closing over sibling my sub';
   }
 }
+
+# Test my subs inside predeclared my subs
+{
+  my sub s2;
+  sub s2 {
+    my $x = 3;
+    my sub s3 { eval '$x' }
+    s3;
+  }
+  local $::TODO = 'closure problem?';
+  is s2, 3, 'my sub inside predeclared my sub';
+}
+
 {
   my $s = make_anon_with_my_sub;
   &$s;
@@ -488,3 +501,40 @@ is sub {
     s1
   }->()(), 3, 'state sub inside my sub closing over my sub uncle';
 
+{
+  my sub s2 { 3 };
+  sub not_lexical { state sub foo { \&s2 } foo }
+  is not_lexical->(), 3, 'state subs that reference my sub from outside';
+}
+
+# Test my subs inside predeclared package subs
+# This test also checks that CvOUTSIDE pointers are not mangled when the
+# inner sub’s CvOUTSIDE points to another sub.
+sub not_lexical2;
+sub not_lexical2 {
+  my $x = 23;
+  my sub bar;
+  sub not_lexical3 {
+    not_lexical2();
+    sub bar { $x }
+  };
+  bar
+}
+$::TODO = 'closing over wrong sub';
+is not_lexical3, 23, 'my subs inside predeclared package subs';
+
+# Test my subs inside predeclared package sub, where the lexical sub is
+# declared outside the package sub.
+# This checks that CvOUTSIDE pointers are fixed up even when the sub is
+# not declared inside the sub that its CvOUTSIDE points to.
+{
+  my sub foo;
+  sub not_lexical4;
+  sub not_lexical4 {
+    my $x = 234;
+    sub foo { $x }
+    foo
+  }
+  is not_lexical4, 234,
+    'my sub defined in predeclared pkg sub but declared outside';
+}