Skip to content

Commit

Permalink
Fix andthen-orelse chaining; make orelse 2.4x faster
Browse files Browse the repository at this point in the history
Fixes RT#130798: https://rt.perl.org/Ticket/Display.html?id=130798

The OP issue in the ticket is due to `Empty` from andthen messing
up positions of args in orelse, making the thunked args shift
up and be returned as blocks instead of evaled.

Fix by using nqp::p6argvarray op to preserve Empties and then
emulating the original +@foo slurpy param.

This is also an alternate and better fix for RT#130034:
https://rt.perl.org/Ticket/Display.html?id=130034
  • Loading branch information
zoffixznet committed Apr 29, 2017
1 parent 6ef2abd commit 37316f8
Showing 1 changed file with 24 additions and 26 deletions.
50 changes: 24 additions & 26 deletions src/core/operators.pm
Expand Up @@ -693,32 +693,30 @@ sub infix:<notandthen>(+a) {
$current;
}

sub infix:<orelse>(+a) {
my $ai := a.iterator;
my Mu $current := $ai.pull-one;
return Nil if $current =:= IterationEnd;

# Flag for heuristic when we were passed an Empty as LHS
my int $handle-empty = 1;
nqp::until(
(($_ := $ai.pull-one) =:= IterationEnd),
nqp::stmts(
(return $current if $current.defined),
($handle-empty = 0),
($current := $_ ~~ Callable
?? (.count ?? $_($current) !! $_())
!! $_
),
),
:nohandler, # do not handle control stuff in thunks
);

if $handle-empty and $current ~~ Callable {
$_ := Empty; # set $_ in the Callable
$current := $current.count ?? $current($_) !! $current();
}

$current;
sub infix:<orelse>(+$) {
# We need to be able to process `Empty` in our args, which we can get
# when we're chained with, say, `andthen`. Since Empty disappears in normal
# arg handling, we use nqp::p6argvmarray op to fetch the args, and then
# emulate the `+@foo` slurpy by inspecting the list the op gave us.
nqp::if(
(my int $els = nqp::elems(my $args := nqp::p6argvmarray)),
nqp::stmts(
(my $current := nqp::atpos($args, 0)),
nqp::if( # emulate the +@foo slurpy
nqp::iseq_i($els, 1) && nqp::istype($current, Iterable),
nqp::stmts(
($args := $current),
$current := $args[0])),
(my int $i),
nqp::until(
$current.defined || nqp::iseq_i($els, $i = nqp::add_i($i, 1)),
($current := nqp::if(
nqp::istype(($_ := $args[$i]), Callable),
nqp::if(.count, $_($current), $_()),
$_)),
:nohandler), # do not handle control stuff in thunks
$current),
Nil) # We were given no args, return Nil
}

# next three sub would belong to traits.pm if PseudoStash were available
Expand Down

0 comments on commit 37316f8

Please sign in to comment.