This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
In A && B, stop special-casing boolean-ness of A
authorDavid Mitchell <davem@iabyn.com>
Fri, 6 Jan 2017 11:35:11 +0000 (11:35 +0000)
committerDavid Mitchell <davem@iabyn.com>
Fri, 6 Jan 2017 16:28:27 +0000 (16:28 +0000)
commitb243b19395066bedc2a6dc3051cd0678692aa7d5
treef4d7cda87ff8b3b0c34483a7b615983103012237
parent9d692a7f83cf8fe36c59aaa07bca533887350e9b
In A && B, stop special-casing boolean-ness of A

Some ops, (currently PADHV and RV2HV) can be flagged as being in boolean
context, and if so, may return a simple truth value which may be more
efficient to calculate than a full scalar value. (This was originally
motivated by code like if (%h) {...}, where the scalar context %h returned a
bucket ratio string, which involved counting how many HvARRAY buckets were
non-empty, which was slow in large hashes. It's been made less important
since %h in scalar context now just returns a key count, which is quick to
calculate.)

There is an issue with the A argument of  A||B, A//B and A&&B, in that,
although A checked by the logop in boolean context, depending on its
truth value the original A may be passed through to the next op. So in
something like $x = (%h || -1), it's not sufficient for %h to return a
truth value; it must return a full scalar value which may get assigned to
$x.

So in general, we only mark the A op as being in boolean context if the
logop is in void context, or if the returned A would only be consumed in
boolean context; so !(A||B) would be ok for example.

However, && is a special case of this, since it will return the original A
only if A was false. Before this commit, && was special-cased to mark A as
being in boolean context regardless of the context of (A&&B). The downside
of this is that the A op can't just return &PL_sv_no as a false value;
it has to return something that is usable in scalar context too. For
example with %h, it returns sv_2mortal(newSViv(0))), which stringifies to
"0" while &PL_sv_no stringifies to "".

This commit removes that special case and makes && behave like || and //
again.

The upside is that some ops in boolean context will be able to more
cheaply return a false value (e.g. just &PL_sv_no verses
sv_2mortal(newSViv(0))).

The main downside is that && in unknown context (typically an
'if (%h} {...}' as the last statement in a sub) will have to check at
runtime whether the caller context is slower.  It will also have to return
a scalar value for something like $y = (%h && $x), but that's a relatively
uncommon occurrence, and now that %h in scalar context doesn't have to
count used buckets, the extra cost in these rare cases is minor.
op.c
pp.c
pp_hot.c
t/perf/optree.t