summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Dubois <jand@activestate.com>2011-03-18 15:37:20 -0700
committerJan Dubois <jand@activestate.com>2011-03-20 00:46:58 -0700
commit658a9f31e43dcb2b52fd873b87444968c94594fa (patch)
treeef56b0f86d8ba09c7e7fb228c43a60cc578ce51b
parent8d5d17fad2e8a7a4ca7bd0e424933fd94274f607 (diff)
downloadperl-658a9f31e43dcb2b52fd873b87444968c94594fa.tar.gz
#84774: local $_ calls STORE when $_ is aliased to a tied hash element
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.
-rw-r--r--mg.c5
-rw-r--r--pod/perldelta.pod13
-rw-r--r--pod/perlsub.pod19
-rw-r--r--t/op/local.t19
4 files changed, 35 insertions, 21 deletions
diff --git a/mg.c b/mg.c
index 331e34b53d..1ac7e31aba 100644
--- 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))
diff --git a/pod/perldelta.pod b/pod/perldelta.pod
index 2ac1a3ed36..942c1cce37 100644
--- a/pod/perldelta.pod
+++ b/pod/perldelta.pod
@@ -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
diff --git a/pod/perlsub.pod b/pod/perlsub.pod
index ece4f159ca..ea5fa207cc 100644
--- a/pod/perlsub.pod
+++ b/pod/perlsub.pod
@@ -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>
diff --git a/t/op/local.t b/t/op/local.t
index f664df4706..1f36a7354b 100644
--- a/t/op/local.t
+++ b/t/op/local.t
@@ -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/ } } };