Fix bug 45607 - for the corner case *{"BONK"} = \&{"BONK"} the order
Nicholas Clark [Sat, 22 Sep 2007 15:46:44 +0000 (15:46 +0000)]
of op evaluation means that what had been a reference to a constant
can turn into a typeglob before the sassign gets to run.

p4raw-id: //depot/perl@31940

pp_hot.c
t/op/gv.t

index 62eddad..6fb53d4 100644 (file)
--- a/pp_hot.c
+++ b/pp_hot.c
@@ -168,18 +168,44 @@ PP(pp_sassign)
        if (!got_coderef) {
            /* We've been returned a constant rather than a full subroutine,
               but they expect a subroutine reference to apply.  */
-           ENTER;
-           SvREFCNT_inc_void(SvRV(cv));
-           /* newCONSTSUB takes a reference count on the passed in SV
-              from us.  We set the name to NULL, otherwise we get into
-              all sorts of fun as the reference to our new sub is
-              donated to the GV that we're about to assign to.
-           */
-           SvRV_set(left, (SV *)newCONSTSUB(GvSTASH(right), NULL,
+           if (SvROK(cv)) {
+               ENTER;
+               SvREFCNT_inc_void(SvRV(cv));
+               /* newCONSTSUB takes a reference count on the passed in SV
+                  from us.  We set the name to NULL, otherwise we get into
+                  all sorts of fun as the reference to our new sub is
+                  donated to the GV that we're about to assign to.
+               */
+               SvRV_set(left, (SV *)newCONSTSUB(GvSTASH(right), NULL,
                                                 SvRV(cv)));
-           SvREFCNT_dec(cv);
-           LEAVE;
+               SvREFCNT_dec(cv);
+               LEAVE;
+           } else {
+               /* What can happen for the corner case *{"BONK"} = \&{"BONK"};
+                  is that
+                  First:   ops for \&{"BONK"}; return us the constant in the
+                           symbol table
+                  Second:  ops for *{"BONK"} cause that symbol table entry
+                           (and our reference to it) to be upgraded from RV
+                           to typeblob)
+                  Thirdly: We get here. cv is actually PVGV now, and its
+                           GvCV() is actually the subroutine we're looking for
+
+                  So change the reference so that it points to the subroutine
+                  of that typeglob, as that's what they were after all along.
+               */
+               GV *const upgraded = (GV *) cv;
+               CV *const source = GvCV(upgraded);
+
+               assert(source);
+               assert(CvFLAGS(source) & CVf_CONST);
+
+               SvREFCNT_inc_void(source);
+               SvREFCNT_dec(upgraded);
+               SvRV_set(left, (SV *)source);
+           }
        }
+
     }
     SvSetMagicSV(right, left);
     SETs(right);
index bca84e7..5b04f87 100755 (executable)
--- a/t/op/gv.t
+++ b/t/op/gv.t
@@ -12,7 +12,7 @@ BEGIN {
 use warnings;
 
 require './test.pl';
-plan( tests => 160 );
+plan( tests => 161 );
 
 # type coersion on assignment
 $foo = 'foo';
@@ -485,6 +485,15 @@ foreach my $value ([1,2,3], {1=>2}, *STDOUT{IO}, \&ok, *STDOUT{FORMAT}) {
     eval "`` if 0";
     is($@, '', "Can't trip up readpipe overloading");
 }
+
+{
+    die if exists $::{BONK};
+    $::{BONK} = \"powie";
+    *{"BONK"} = \&{"BONK"};
+    eval 'is(BONK(), "powie",
+             "Assigment works when glob created midway (bug 45607)"); 1'
+       or die $@;
+}
 __END__
 Perl
 Rules