develooper Front page | perl.perl5.porters | Postings from July 2013

RE: [perl #118691] Perl crash on usage of index [-1] for array in subroutine call

Thread Next
From:
=?UTF-8?Q?Wolf-Dietrich_Moeller_=28M=C3=BCnchen=29?=
Date:
July 2, 2013 19:39
Subject:
RE: [perl #118691] Perl crash on usage of index [-1] for array in subroutine call
Message ID:
000101ce74e3$1dce7170$596b5450$@de
Hi Jim,
many thanks for your immediate response.

I can follow your explanation up to a certain point. Before I sent the bug report, I did some additional checks on the second call of my example. Also that element ($array[0]) does not exist, but the call works fine. So according to your explanation, this element must be auto-vivified. To check for auto-vivification of that element, I checked scalar(@array) after accessing $array[0], but still @array == 0. So no sign of auto-vivification.

Or is such auto-vivified element $array[0] automatically deleted again, immediately after giving back the value "undefined"? And the fourth call fails, as this "transient auto-vivification" fails?

If that is the case, then the behaviour of Perl is very much dependent on the internal implementation, which was not obvious to me reading the documentation. When I searched perldoc, I only found text about negative indices in perldoc perldata, chapter "subscripts".
It reads there " The array indices start with 0. A negative subscript retrieves its value from the end. In our example, $myarray[-1] would have been ...".
I understood that text in such a way that $array[0] is always the first element (or undef if not existing), similar to shift, and $array[-1] is always the last element (or undef if not existing), similar to pop which does not fail on an empty array. This text did not give a hint to me, that (as an intermediate stage) Perl tries to auto-vivify an array element with negative index, and thus will fail. Maybe the difference in handling should be mentioned in that chapter, to avoid that others stumble over the same obstacle.

Maybe also the documentation about splice in perldoc perlfunc should be amended, as there the same problem arises.
The text there reads:
"The following equivalences hold (assuming $#a >= $i )
    .... (something on push)
    pop(@a)             splice(@a,-1)"

Here pop @a works fine (giving undef if the array is empty), but splice dies with the same message when applied to an empty array. The assumption given in the first line cited does not apply to pop. So there should be a second assumption written, that the equivalence for pop only holds for non-empty arrays.  

Also thanks for your hints about simplification of the test program. My example was a simplification from a program started at times of Perl 5.6, so only a few new features (like "//") were used there.

Best regards and again many thanks
Wolf

-----Original Message-----
From: James E Keenan via RT [mailto:perlbug-followup@perl.org] 
Sent: Samstag, 29. Juni 2013 17:06
To: wolf-dietrich_moeller@t-online.de
Subject: [perl #118691] Perl crash on usage of index [-1] for array in subroutine call

On Sat Jun 29 06:54:53 2013, wolf-dietrich_moeller@t-online.de wrote:
> Hi,
> the following behaviour of Perl looks like a bug to me:
> 
> In the test program below, the third and fourth call to the subroutine
> should behave equal, i.e. the fourth call should not result in a crash of
> Perl with the following message:
> "Modification of non-creatable array value attempted, subscript -1 at
<PATH
> TO PERL PROGRAM> line 17."
> 
> Reason for my assumption is that the  " // undef" in the first call
and the
> third call to "func" should be superfluous, as the non-existing array
> element should return "undef" anyhow. Thus the third call and the fourth
> call should behave identical.
> 
> Test program:
> ########################################
> # Test program to show crash of Perl in the fourth call to 'sub func'
> # Such crash seems to happen only when the index is negative and the array
> element does not exist.
> # For positive indices no crash, even if the array element does not exist.
> use strict;
> sub func ($) { my $val = shift; print $val // 'undef'," in sub func\n" }
> my @array;
> my $x = $array[0];
> my $y = $array[-1];
> print '@array[0,-1] : ',join(' ',map $_ // 'undef',$x,$y)," in main\n";
> print '($array[0] // undef) :',"\n";
> func ($array[0] // undef);
> print '($array[0]) :',"\n";
> func ($array[0]);
> print '($array[-1] // undef) :',"\n";
> func ($array[-1] // undef);
> print '($array[-1]) :',"\n";
> func ($array[-1]);
> ###########################################
> 
> The output of the test program is:
> @array[0,-1] : undef undef in main
> ($array[0] // undef) :
> undef in sub func
> ($array[0]) :
> undef in sub func
> ($array[-1] // undef) :
> undef in sub func
> ($array[-1]) :
> Modification of non-creatable array value attempted, subscript -1 at <PATH
> TO PERL PROGRAM> line 17.
> 

I don't think this is a bug in Perl.  In fact, it's a feature
specifically documented in 'perldoc perldiag'"

#####
Modification of non-creatable array value attempted, %s

(F) You tried to make an array value spring into existence, and the
subscript was probably negative, even counting from end of the array
backwards.
#####

To explain this, I would like to strip your demonstration code of
superfluous aspects.  Among other things, there is absolutely no reason
for a prototype on your subroutine.  Since you're working in Perl
5.16.3, you have the 'say' function available to you, and I'll use that
to reduce the number of keystrokes in the code.

#####
use strict;
use warnings;
use feature 'say';

my @array;
func ($array[0] // undef);
func ($array[0]);
func ($array[-1] // undef);
func ($array[-1]);

sub func { my $val = shift; say $val // 'undef'; }
#####

Perl's '//' operator tests the defined-ness of the left-hand side of the
operator, returns that value if it is defined, but returns the
right-hand side if the left-hand is not defined.

In your third call to 'func()', the '//' operator imposes a context on
the values to its left and to its right.  In this context, the left-hand
value, $array[-1], is found to be not defined, so whatever is on the
right-hand side of '//' is returned.  It so happens that what is on the
right-hand side is the undefined value.  So that is what is passed to
func(), and func() handles it appropriately by printing the string
'undef' to STDOUT.

Your fourth call is not equivalent to your third call.  There's no '//'
operator imposing a context on $array[-1].  Perl looks at that variable
directly and notes that it runs afoul of the error condition described
above.  You're asking for $array[-1] to spring to life.  I'm sure that
there are others who can explain better than I can why we treat that as
a fatal error -- but fatal it is nonetheless.

> PS.
> Sorry I did not use the recommended tools for version reporting, as
nowadays
> I only have access to Windows environment with ActiveState Perl windows
> binary.

For reporting the version of Perl in which you are experiencing a
problem, all you need to do is to include the output of 'perl -V' in
your email to 'perlbug@perl.org'.

Thank you very much.
Jim Keenan


Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About