Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does perl report an incorrect line number for this warning regarding an uninitialized value used in an elsif?

I'm getting a strange warning against some Perl code and I'm hoping the SO-brain can help.

The code in question is:

sub add_observation {
    my $self = shift;
    my $observation = shift;

    my $result = $observation->get_datum_result($self->{datum_name});
    if(!(defined $result)) {
        croak("Datum '$self->{datum_name}' not defined for this observation: ". Dumper($observation));
    }
    $self->{result} |= $result;
    my $observation_time = $observation->get_time();

    if($self->{result} == 0){
        $self->set_start_time($observation_time);
    }
    if($result != 0) {
        $self->set_end_time($observation_time);
        $self->{end_threshold} = $observation_time->epoch() + $self->{timeout};
    }
    elsif($observation_time->epoch() > $self->{end_threshold}) {
        $self->{complete} = 1;
    }

    return $self->{result}; 
}

When I run my code, I get the following warning:

Use of uninitialized value in numeric gt (>) at Performance/BadSpan.pm line 67 (#1)

Line 67 equates to the if($result != 0) { line.

My problem is twofold:

  1. Why is Perl stating that $result is undefined, when there's some guard code preceding it that ensures that it is defined
  2. Why is Perl complaining about a numeric gt, when there is no numeric gt. The rubric associated with this warning does indicate that Perl might optimise your code and the warning refers to the optimized operators, but does that mean that a != is 'optimized' to a > and a <?
like image 775
Dancrumb Avatar asked Dec 15 '10 22:12

Dancrumb


1 Answers

What is the version of perl?

Given

use strict; use warnings;

my $x;

if ( $x ) {
    print "here\n";
}
elsif ( $x > 1 ) {
    print "there\n";
}

perl 5.10.1 correctly outputs:

Use of uninitialized value $x in numeric gt (>) at C:\Temp\t.pl line 8.

Given that elsif does not stand alone, but is part of the if statement, there might have been a bug in earlier versions which reported the line number of the enclosing if statement.

This post and this entry in perltodo seem relevant:

eliminate incorrect line numbers in warnings

This code

  1. use warnings;
  2. my $undef;
  3.
  4. if ($undef == 3) {
  5. } elsif ($undef == 0) {
  6. }

used to produce this output:

  Use of uninitialized value in numeric eq (==) at wrong.pl line 4.
  Use of uninitialized value in numeric eq (==) at wrong.pl line 4.

where the line of the second warning was misreported - it should be line 5. Rafael fixed this - the problem arose because there was no nextstate OP between the execution of the if and the elsif, hence PL_curcop still reports that the currently executing line is line 4. The solution was to inject a nextstate OPs for each elsif, although it turned out that the nextstate OP needed to be a nulled OP, rather than a live nextstate OP, else other line numbers became misreported. (Jenga!)

The problem is more general than elsif (although the elsif case is the most common and the most confusing). Ideally this code

   1. use warnings;
   2. my $undef;
   3.
   4. my $a = $undef + 1;
   5. my $b
   6. = $undef
   7. + 1;

would produce this output

   Use of uninitialized value $undef in addition (+) at wrong.pl line 4.
   Use of uninitialized value $undef in addition (+) at wrong.pl line 7.

(rather than lines 4 and 5), but this would seem to require every OP to carry (at least) line number information.

What might work is to have an optional line number in memory just before the BASEOP structure, with a flag bit in the op to say whether it's present. Initially during compile every OP would carry its line number. Then add a late pass to the optimiser (potentially combined with repack the optree) which looks at the two ops on every edge of the graph of the execution path. If the line number changes, flags the destination OP with this information. Once all paths are traced, replace every op with the flag with a nextstate-light op (that just updates PL_curcop), which in turn then passes control on to the true op. All ops would then be replaced by variants that do not store the line number. (Which, logically, why it would work best in conjunction with repack the optree, as that is already copying/reallocating all the OPs)

(Although I should note that we're not certain that doing this for the general case is worth it)

Here is the actual commit. Given that the commit happened in April 2008, I am assuming the fix was included in the 5.8.9 release (see perlhist).

like image 170
Sinan Ünür Avatar answered Sep 30 '22 07:09

Sinan Ünür