pp_negate: Don’t treat nummified str as num
authorFather Chrysostomos <sprout@cpan.org>
Wed, 6 Jun 2012 05:38:12 +0000 (22:38 -0700)
committerFather Chrysostomos <sprout@cpan.org>
Thu, 7 Jun 2012 15:18:54 +0000 (08:18 -0700)
I think it’s a bug that this prints 0:

$ ./perl -lIlib -MDevel::Peek -e '$x = "dogs"; 0+$x; Dump $x; print -$x'
SV = PVNV(0x802340) at 0x821b90
  REFCNT = 1
  FLAGS = (POK,pIOK,pNOK,pPOK)
  IV = 0
  NV = 0
  PV = 0x301620 "dogs"\0
  CUR = 4
  LEN = 16
0

This variable is a string, not a number.  The number 0 is just a
cached value.  It lacks the IOK flag precisely because the IV is not
representative of the actual value of the scalar.

This logic here is a little bit odd:

        if( !SvNIOK( sv ) && looks_like_number( sv ) ){
           SvIV_please( sv );
        }

if ((flags & SVf_IOK) || ((flags & (SVp_IOK | SVp_NOK)) == SVp_IOK)) {

SvIV_please sets the flags on sv but then they are ignored when check-
ing for integrality.

To fix the bug mentioned above, I had to change this logic to use sv
directly, rather than the saved flags.

That meant that this bug was also fixed at the same time, since the
integer code is no longer bypassed when it is SvIV_please that sets
the integer flags:

$ ./perl -Ilib -le 'print -97656250000000000'
-97656250000000000
$ ./perl -Ilib -le 'print -"97656250000000000"'
-9.765625e+16

pp.c
t/op/negate.t

diff --git a/pp.c b/pp.c
index fbeb3ae..48fd8c6 100644 (file)
--- a/pp.c
+++ b/pp.c
@@ -2156,14 +2156,13 @@ PP(pp_negate)
     tryAMAGICun_MG(neg_amg, AMGf_numeric);
     {
        SV * const sv = TOPs;
-       const int flags = SvFLAGS(sv);
 
         if( !SvNIOK( sv ) && looks_like_number( sv ) ){
            SvIV_please( sv );
         }   
 
-       if ((flags & SVf_IOK) || ((flags & (SVp_IOK | SVp_NOK)) == SVp_IOK)) {
-           /* It's publicly an integer, or privately an integer-not-float */
+       if (SvIOK(sv) || (SvOKp(sv) == SVp_IOK)) {
+           /* It's publicly an integer, or privately just an integer */
        oops_its_an_int:
            if (SvIsUV(sv)) {
                if (SvIVX(sv) == IV_MIN) {
@@ -2187,7 +2186,7 @@ PP(pp_negate)
            }
 #endif
        }
-       if (SvNIOKp(sv))
+       if (SvNIOKp(sv) && (SvNIOK(sv) || !SvPOK(sv)))
            SETn(-SvNV_nomg(sv));
        else if (SvPOKp(sv)) {
            STRLEN len;
index 8a0ef2b..62dc418 100644 (file)
@@ -6,7 +6,7 @@ BEGIN {
     require './test.pl';
 }
 
-plan tests => 16;
+plan tests => 18;
 
 # Some of these will cause warnings if left on.  Here we're checking the
 # functionality, not the warnings.
@@ -28,4 +28,11 @@ is(-bareword, "-bareword", "Negation of bareword treated like a string");
 is(- -bareword, "+bareword", "Negation of -bareword returns string +bareword");
 is(-" -10", 10, "Negation of a whitespace-lead numeric string");
 is(-" -10.0", 10, "Negation of a whitespace-lead decimal string");
-is(-" -10foo", 10, "Negation of a whitespace-lead sting starting with a numeric")
+is(-" -10foo", 10,
+    "Negation of a whitespace-lead sting starting with a numeric");
+
+$x = "dogs";
+()=0+$x;
+is -$x, '-dogs', 'cached numeric value does not sabotage string negation';
+
+is(-"97656250000000000", -97656250000000000, '-bigint vs -"bigint"');