diff options
author | Jan Dubois <jand@activestate.com> | 2011-03-18 15:37:20 -0700 |
---|---|---|
committer | Jan Dubois <jand@activestate.com> | 2011-03-20 00:46:58 -0700 |
commit | 658a9f31e43dcb2b52fd873b87444968c94594fa (patch) | |
tree | ef56b0f86d8ba09c7e7fb228c43a60cc578ce51b | |
parent | 8d5d17fad2e8a7a4ca7bd0e424933fd94274f607 (diff) | |
download | perl-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.c | 5 | ||||
-rw-r--r-- | pod/perldelta.pod | 13 | ||||
-rw-r--r-- | pod/perlsub.pod | 19 | ||||
-rw-r--r-- | t/op/local.t | 19 |
4 files changed, 35 insertions, 21 deletions
@@ -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/ } } }; |