This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
#84774: local $_ calls STORE when $_ is aliased to a tied hash element
authorJan Dubois <jand@activestate.com>
Fri, 18 Mar 2011 22:37:20 +0000 (15:37 -0700)
committerJan Dubois <jand@activestate.com>
Sun, 20 Mar 2011 07:46:58 +0000 (00:46 -0700)
local($_) will now strip all magic from $_, so that it is always safe
to localize $_, regardless what kind of special (or tied) variable it
may have been aliased to.

mg.c
pod/perldelta.pod
pod/perlsub.pod
t/op/local.t

diff --git a/mg.c b/mg.c
index 331e34b..1ac7e31 100644 (file)
--- a/mg.c
+++ b/mg.c
@@ -286,7 +286,7 @@ Perl_mg_set(pTHX_ SV *sv)
            mg->mg_flags &= ~MGf_GSKIP; /* setting requires another read */
            (SSPTR(mgs_ix, MGS*))->mgs_magical = 0;
        }
-       if (PL_localizing == 2 && !S_is_container_magic(mg))
+       if (PL_localizing == 2 && (!S_is_container_magic(mg) || sv == DEFSV))
            continue;
        if (vtbl && vtbl->svt_set)
            vtbl->svt_set(aTHX_ sv, mg);
@@ -511,6 +511,9 @@ Perl_mg_localize(pTHX_ SV *sv, SV *nsv, bool setmagic)
 
     PERL_ARGS_ASSERT_MG_LOCALIZE;
 
+    if (nsv == DEFSV)
+       return;
+
     for (mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic) {
        const MGVTBL* const vtbl = mg->mg_virtual;
        if (!S_is_container_magic(mg))
index 2ac1a3e..942c1cc 100644 (file)
@@ -59,6 +59,19 @@ XXX For a release on a stable branch, this section aspires to be:
 
 [ List each incompatible change as a =head2 entry ]
 
+=head2 local($_) will strip all magic from $_
+
+local() on scalar variables will give them a new value, but keep all
+their magic intact.  This has proven to be problematic for the default
+scalar variable $_, where L<perlsub> recommends that any subroutine
+that assigns to $_ should localize it first.  This would throw an
+exception if $_ is aliased to a read-only variable, and could have
+various unintentional side-effects in general.
+
+Therefore, as an exception to the general rule, local($_) will not
+only assign a new value to $_, but also remove all existing magic from
+it as well.
+
 =head2 Passing references to warn()
 
 An earlier Perl 5.13.x release changed C<warn($ref)> to leave the reference
index ece4f15..ea5fa20 100644 (file)
@@ -608,16 +608,9 @@ magical and read-only :
 
     local $1 = 2;
 
-Similarly, but in a way more difficult to spot, the following snippet will
-die in perl 5.9.0 :
-
-    sub f { local $_ = "foo"; print }
-    for ($1) {
-       # now $_ is aliased to $1, thus is magic and readonly
-       f();
-    }
-
-See next section for an alternative to this situation.
+One exception is the default scalar variable: starting with perl 5.14
+C<local($_)> will always strip all magic from $_, to make it possible
+to safely reuse $_ in a subroutine.
 
 B<WARNING>: Localization of tied arrays and hashes does not currently
 work as described.
@@ -644,12 +637,6 @@ those variables is locally lost.  In other words, saying C<local */>
 will not have any effect on the internal value of the input record
 separator.
 
-Notably, if you want to work with a brand new value of the default scalar
-$_, and avoid the potential problem listed above about $_ previously
-carrying a magic value, you should use C<local *_> instead of C<local $_>.
-As of perl 5.9.1, you can also use the lexical form of C<$_> (declaring it
-with C<my $_>), which avoids completely this problem.
-
 =head3 Localization of elements of composite types
 X<local, composite type element> X<local, array element> X<local, hash element>
 
index f664df4..1f36a73 100644 (file)
@@ -644,12 +644,23 @@ while (/(o.+?),/gc) {
 eval { local $1 = 1 };
 like($@, qr/Modification of a read-only value attempted/);
 
+# local($_) always strips all magic
 eval { for ($1) { local $_ = 1 } };
-like($@, qr/Modification of a read-only value attempted/);
+is($@, "");
 
-# make sure $1 is still read-only
-eval { for ($1) { local $_ = 1 } };
-like($@, qr/Modification of a read-only value attempted/);
+{
+    my $STORE = 0;
+    package TieHash;
+    sub TIEHASH { bless $_[1], $_[0] }
+    sub FETCH   { 42 }
+    sub STORE   { ++$STORE }
+
+    package main;
+    tie my %hash, "TieHash", {};
+
+    eval { for ($hash{key}) {local $_ = 2} };
+    is($STORE, 0);
+}
 
 # The s/// adds 'g' magic to $_, but it should remain non-readonly
 eval { for("a") { for $x (1,2) { local $_="b"; s/(.*)/+$1/ } } };