Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

ref to read-only alias ... why?

by dk (Chaplain)
on Jan 05, 2012 at 22:31 UTC ( #946488=perlquestion: print w/ replies, xml ) Need Help??
dk has asked for the wisdom of the Perl Monks concerning the following question:

Does anybody know why the following code does NOT die with "Modification of a read-only value attempted"? It puzzles me, I can't find the relevant info in the perldoc .

for (1) { my $ref = \ $_; $$ref ++; }

( it dies when $ref = \ 1, as expected )

Update: seems to be a perl bug. I'll check out the blead and if it is still there, will report. Thanks everyone!

Update2: Sent patch to https://rt.perl.org/rt3/Public/Bug/Display.html?id=21979. Thanks to ikegami for the place where it should be hacked into.

Comment on ref to read-only alias ... why?
Select or Download Code
Re: ref to read-only alias ... why?
by tobyink (Abbot) on Jan 05, 2012 at 22:47 UTC

    From perlsyn's section on foreach loops:

    If any element of LIST is an lvalue, you can modify it by modifying VAR inside the loop. Conversely, if any element of LIST is NOT an lvalue, any attempt to modify that element will fail.

    What that's saying is that given:

    use 5.010; use Data::Dumper; my $foo = "world"; my $bar = "Hello planet"; my $iter = 0; foreach my $x ((1, $foo, substr($bar,6))) { local $@ = undef; printf "Iteration %d:\n", ++$iter; eval { $x = "World" } or say $@; } print Dumper [$foo, $bar];

    The first iteration will fail as it's essentially trying to assign 1 = "Hello"; the other iterations will succeed because $foo and substr($bar,6) are both lvalues, so can be assigned to.

    In your case, $_ is an alias for the constant 1, not an lvalue, so can't be assigned to.

      In your case, $_ is an alias for the constant 1, not an lvalue, so can't be assigned to.

      This doesn't really answer the OP's question why it does not fail when you try to modify the constant via a reference to the alias.

Re: ref to read-only alias ... why?
by LanX (Canon) on Jan 05, 2012 at 23:27 UTC
    Interesting ...

    It appears like read-only values are not modified when aliased.

    DB<123> sub tst { my $r = \ $_[0]; print ++$$r; } DB<124> $x=3; for (1,2,$x) { tst($_); print ": ",$_,"\n" } 2: 1 3: 2 4: 4

    UPDATE:

    Seems to me like "aliasing" read-only values is falling back to "copying".

    Or in other words: Only lvalues can be "really" aliased.

    Which makes sense in a way, because the original value can't be changed anyway.

    Cheers Rolf

Re: ref to read-only alias ... why?
by morgon (Deacon) on Jan 05, 2012 at 23:40 UTC
    On my Debian-system I have a 5.14.1 and a 5.14.2 version of perl.

    5.14.1 does die with the error you expect.

    5.14.2 however does not die.

    So I guess it is a bug that you should report.

        5.14.2 behaves as in your posting.

        5.14.1 dies with "Modification of a read-only value attempted".

      Thanks, I guess I will. It seemed to me like a bug, but knowing that there's too many arcane special cases in Perl, I decided to ask first.
Re: ref to read-only alias ... why?
by ikegami (Pope) on Jan 06, 2012 at 01:31 UTC

    The condition in which "\" copies a scalar is: SvPADTMP(sv) && !IS_PADGV(sv). Not sure that that means. The condition is still there in the latest codebase.

    It turns out that whether you get a read-only error or not depends on whether you are using a threaded Perl or not.

    Using the lastest codebase:

    $ git clean -dfx \ && ./Configure -des -Dusedevel -Dcc='ccache cc' \ && make miniperl ... $ ./miniperl -le'for (1) { my $r = \$_; $$r++; print; print $$r; }' Modification of a read-only value attempted at -e line 1. $ git clean -dfx \ && ./Configure -des -Dusedevel -Dcc='ccache cc' -Dusethreads \ && make miniperl ... $ ./miniperl -le'for (1) { my $r = \$_; $$r++; print; print $$r; }' 1 2
Re: ref to read-only alias ... why? (notabug)
by tye (Cardinal) on Jan 06, 2012 at 05:13 UTC

    Whether you get an alias or a copy of a read-only value is a bit of an optimization concern and it has changed previously. I'm not terribly surprised that it has changed again (and seems to change based on features of Perl you include and may have changed several times in just recent versions). I would consider Perl code that depends on this behavior to be broken much more so than I consider either behavior to be a bug in Perl.

    You really want it to be a Perl bug unless for(1..3) makes $_ read-write while for(1,2,3) makes $_ read-only?

    - tye        

      I want it documented, and consistent. I agree that the for() case can be seen as stretched ( I like my for(1) to make an alias to $_ so I can do all magic with it inside the for block, but that's another story).

      But anyway, what would you say about this then? Is this a bug or not?

      sub x { my $ref = \ $_[0]; $$ref ++; } x(1);
      I'd say that even if this behavior will be decided on as not a bug, then it has to be documented, at the very least.

        I'd document it pretty much as I already described it. Passing a read-only value or a literal constant to something that makes aliases (for, a sub) may decide to make an alias to the read-only value or may decide to make a copy of it. The decision might even be different in those two case (a literal constant vs. some other read-only scalar). The choice is a matter of optimization and subtle edge cases and Perl code should not depend on either specific behavior. Both behaviors have existed in many different versions of Perl.

        No, I don't consider it a bug that some versions of Perl don't die in the face of:

        sub add1 { return ++$_[0]; } my $two= add1(1);

        Despite the use of "++" over "1+" there being questionable. It does have the interesting and perhaps useful side effect of allowing: add1($count). Yes, it is a contrived example. As is yours.

        I don't find it hard to imagine cases where either result would be preferred. I do find it hard to imagine cases where either result is a serious problem that I wouldn't just address with a better interface for the subroutine.

        - tye        

        Your right it's not consistent.

        But IMHO consistency will most likely break legacy code.

        I think that there should at least be a warning.

        Since it's not always clear if a variable is an alias, this can cause very hard to detect errors in subs which are meant to modify call-by-reference parameters. (not to be confused with Perl references)

        Did you check if there are already older bug-reports regarding this?

        Cheers Rolf

        UPDATE:

        DB<108> sub inc_a { $_[0]++; return } DB<109> $x=1; inc_a($x); print $x 2 DB<110> inc_a(1) Modification of a read-only value attempted at (eval 13)[/usr/share/pe +rl/5.10/perl5db.pl:638] line 2.

        but

        DB<111> sub inc_b { my $r=\ $_[0]; $$r++; return } DB<112> $x=1; inc_b($x); print $x 2 DB<113> inc_b(1) DB<114>

        When passing an aliasing variable instead of 1 it's the same problem. With inc_b non-aliasing vars will increment, but aliases will silently fail to increment.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://946488]
Approved by herveus
Front-paged by Eliya
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others about the Monastery: (9)
As of 2014-09-16 09:26 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite cookbook is:










    Results (158 votes), past polls