Return from sort block gives "Use of uninitialized value in sort" warning, but only with explicit return

Viewed 274

I'm getting a strange behavior from Perl when I try to return from my sort() block:

#!/usr/bin/perl
use strict;
use warnings;

my @data = sort {
    return &special_sort;
} qw/ 6 1 2 /;

use Data::Dumper;
print STDERR Data::Dumper->Dump([\@data]);

sub special_sort {
    # (imagine some custom sorting logic here)
    return $a <=> $b;
}

This gives the following messages:

Use of uninitialized value in sort at ./mcve.pl line 6.
Use of uninitialized value in sort at ./mcve.pl line 6.
Use of uninitialized value in sort at ./mcve.pl line 6.
$VAR1 = [
          6,
          1,
          2
        ];

As you can see, it also failed to sort the list. The odd thing is, the following sort clauses work without any warnings, and sort the data properly:

my @data = sort {
    &special_sort;
} qw/ 6 1 2 /;

my @data = sort {
    my $res = &special_sort;
    return $res;
} qw/ 6 1 2 /;

my @data = sort {
    return $a <=> $b;
} qw/ 6 1 2 /;

my @data = sort {
    eval { return &special_sort; }
} qw/ 6 1 2 /;

Question 0

What's going on here? Why do those four work, but the first example doesn't? It shouldn't be the return statement causing undefined behavior, since two of the working ones have that.

Question 1

If it turns out the world isn't perfect and I can't (or shouldn't) return from a block, is there an elegant way to effectively do that?

Edit: It was a bug in Perl. (Note: I did some checking, and I'm slowly figuring out this is down to my confusion between how Perl handles BLOCKs vs. SUBs, hence Question 1. The question is still interesting IMO, and confusing behavior on Perl's part.)

For clarity, I'm using Perl 5.32.0.

1 Answers

This is a bug. A fix has been applied, resolving the issue in upcoming Perl 5.34.

In the meantime, you can replace

return &special_sort;

with

&special_sort;                    # Avoids the issue.

or

return scalar( &special_sort );   # Force scalar context.

or

return 0+&special_sort;           # Force scalar context.

The problem is that special_sort is incorrectly being called in void context, which is avoided or circumvented by the above workarounds.


I initially claimed that you can't use return in a sort block because it's not a sub. I also indicated I was confused by the fact that no error was reported. @Håkon Hægland points out that the documentation for return indicates it can be used in sort block. And indeed it can be used in other circumstances. There's a bug, which I've reported and which has now been fixed.

Related