Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is @$ref->{@keys} trying to access?

In a comment on an answer to a question about hash slices, someone wanted to know how to use arrow syntax to access a hash slice through a hash reference, thinking perhaps that

@$ref->{@keys}

would do so.

Yes, the correct syntax is either @$ref{@keys} or @{$ref}{@keys}, but that's beside the point of this question.

I tried to work out the data structure that the expression with an arrow requires:

#! /usr/bin/env perl

use strict;
use warnings;

my $ref = {"a" => 1, "b" => 2, "c" => 3};

my @keys = qw/ a b c /;

#$ref = [ { a => 9, b => 8, c => 7 } ];
#$ref = [ { a => {}, b => {}, c => {} } ];
print @$ref->{@keys}, "\n";

As written, the code fails with

Not an ARRAY reference at ./prog line 12.

That makes sense: @$ref wants a reference to an array, so I tried wrapping hash references inside a reference to an anonymous array. Those attempts failed with

Can't use an undefined value as a HASH reference at ./prog line 12.

The trace output is

$ debugperl -Dt prog
[...]
(prog:12)   pushmark
(prog:12)   padsv($ref)
(prog:12)   rv2av
(prog:12)   rv2hv
Can't use an undefined value as a HASH reference at prog line 12.

The syntax dump for the print line is

$ debugperl -Dx prog
[...]
    {
484     TYPE = print  ===> 2
        FLAGS = (VOID,KIDS)
        {
485         TYPE = pushmark  ===> 486
            FLAGS = (SCALAR)
        }
        {
372         TYPE = helem  ===> 371
            FLAGS = (SCALAR,KIDS)
            {
487             TYPE = rv2hv  ===> 361
                TARG = 5
                FLAGS = (SCALAR,KIDS,REF)
                PRIVATE = (STRICT_REFS)
                {
373                 TYPE = rv2av  ===> 487
                    TARG = 4
                    FLAGS = (SCALAR,KIDS,REF)
                    PRIVATE = (STRICT_REFS)
                    {
486                     TYPE = padsv  ===> 373
                        TARG = 1
                        FLAGS = (SCALAR,MOD)
                    }
                }
            }
            {
361             TYPE = padav  ===> 372
                TARG = 2
                FLAGS = (SCALAR)
            }
        }
        {
371         TYPE = const  ===> 484
            TARG = 19
            FLAGS = (SCALAR)
        }
    }
[...]

Where is the undefined value coming from? For what values of $ref does the program terminate normally?

like image 226
Greg Bacon Avatar asked Nov 16 '11 10:11

Greg Bacon


2 Answers

It's not valid (or at least not meaningful) Perl syntax — I'm a bit surprised it's not flagged as a syntax error.

I first thought it was trying to evaluate the array @$foo in scalar context and use the result as a hash reference, but that doesn't quite seem to be what's going on. Rather, from the debug output you posted, it looks more like it's trying to directly use the internal array variable (AV) structure as a hash reference (RV), which is a type of scalar (SV; see perlguts for details).

I haven't looked at the source, but it looks as if the rv2hv routine either notices that it's been given the wrong kind of structure and returns null, or just tries to use the AV as an RV and achieves the same effect that way. (Sorry if that may sound a bit muddled, it's been some years since I last looked at the internals of perl.)

You might want to consider submitting a bug report.

BTW, a simpler test case that demonstrates the effect is just @foo->{bar}.

like image 197
Ilmari Karonen Avatar answered Oct 05 '22 08:10

Ilmari Karonen


@$ref->{@keys}

means

scalar(@$ref)->{@keys}

so it should be equivalent to

my $ref2 = @$ref;
$ref2->{@keys}

It's not, so it's a bug. It's still present in the near-current state of what will become Perl 5.16.0. (v5.15.4, to be specific)

Please report using the perlbug command line tool. (Just enter perlbug and answer a few simple questions.)

like image 43
ikegami Avatar answered Oct 05 '22 09:10

ikegami