This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Add string- and number-specific bitop types
[perl5.git] / pp.c
diff --git a/pp.c b/pp.c
index 08ca123..2cd2d5e 100644 (file)
--- a/pp.c
+++ b/pp.c
@@ -1298,7 +1298,8 @@ PP(pp_multiply)
                    alow = aiv;
                    auvok = TRUE; /* effectively it's a UV now */
                } else {
-                   alow = -aiv; /* abs, auvok == false records sign */
+                    /* abs, auvok == false records sign */
+                   alow = (aiv == IV_MIN) ? (UV)aiv : (UV)(-aiv);
                }
            }
            if (buvok) {
@@ -1309,7 +1310,8 @@ PP(pp_multiply)
                    blow = biv;
                    buvok = TRUE; /* effectively it's a UV now */
                } else {
-                   blow = -biv; /* abs, buvok == false records sign */
+                    /* abs, buvok == false records sign */
+                   blow = (biv == IV_MIN) ? (UV)biv : (UV)(-biv);
                }
            }
 
@@ -1335,6 +1337,10 @@ PP(pp_multiply)
                    /* 2s complement assumption that (UV)-IV_MIN is correct.  */
                    /* -ve result, which could overflow an IV  */
                    SP--;
+                    /* can't negate IV_MIN, but there are aren't two
+                     * integers such that !ahigh && !bhigh, where the
+                     * product equals 0x800....000 */
+                    assert(product != (UV)IV_MIN);
                    SETi( -(IV)product );
                    RETURN;
                } /* else drop to NVs below. */
@@ -1372,7 +1378,8 @@ PP(pp_multiply)
                            /* 2s complement assumption again  */
                            /* -ve result, which could overflow an IV  */
                            SP--;
-                           SETi( -(IV)product_low );
+                           SETi(product_low == (UV)IV_MIN
+                                    ? IV_MIN : -(IV)product_low);
                            RETURN;
                        } /* else drop to NVs below. */
                    }
@@ -1434,7 +1441,7 @@ PP(pp_divide)
                     right_non_neg = TRUE; /* effectively it's a UV now */
                 }
                else {
-                    right = -biv;
+                    right = (biv == IV_MIN) ? (UV)biv : (UV)(-biv);
                 }
             }
             /* historically undef()/0 gives a "Use of uninitialized value"
@@ -1455,7 +1462,7 @@ PP(pp_divide)
                     left_non_neg = TRUE; /* effectively it's a UV now */
                 }
                else {
-                    left = -aiv;
+                    left = (aiv == IV_MIN) ? (UV)aiv : (UV)(-aiv);
                 }
             }
 
@@ -1485,7 +1492,7 @@ PP(pp_divide)
                     }
                     /* 2s complement assumption */
                     if (result <= (UV)IV_MIN)
-                        SETi( -(IV)result );
+                        SETi(result == (UV)IV_MIN ? IV_MIN : -(IV)result);
                     else {
                         /* It's exact but too negative for IV. */
                         SETn( -(NV)result );
@@ -1535,7 +1542,7 @@ PP(pp_modulo)
                     right = biv;
                     right_neg = FALSE; /* effectively it's a UV now */
                 } else {
-                    right = -biv;
+                    right = (biv == IV_MIN) ? (UV)biv : (UV)(-biv);
                 }
             }
         }
@@ -1565,7 +1572,7 @@ PP(pp_modulo)
                         left = aiv;
                         left_neg = FALSE; /* effectively it's a UV now */
                     } else {
-                        left = -aiv;
+                        left = (aiv == IV_MIN) ? (UV)aiv : (UV)(-aiv);
                     }
                 }
         }
@@ -1642,6 +1649,7 @@ PP(pp_repeat)
     dSP; dATARGET;
     IV count;
     SV *sv;
+    bool infnan = FALSE;
 
     if (GIMME_V == G_ARRAY && PL_op->op_private & OPpREPEAT_DOLIST) {
        /* TODO: think of some way of doing list-repeat overloading ??? */
@@ -1684,19 +1692,27 @@ PP(pp_repeat)
         }
     }
     else if (SvNOKp(sv)) {
-        const NV nv = SvNV_nomg(sv);
-        if (nv < 0.0)
-              count = -1;   /* An arbitrary negative integer */
-        else
-             count = (IV)nv;
+        const NV nv = SvNV_nomg(sv);
+        infnan = Perl_isinfnan(nv);
+        if (UNLIKELY(infnan)) {
+            count = 0;
+        } else {
+            if (nv < 0.0)
+                count = -1;   /* An arbitrary negative integer */
+            else
+                count = (IV)nv;
+        }
     }
     else
-        count = SvIV_nomg(sv);
+       count = SvIV_nomg(sv);
 
-    if (count < 0) {
+    if (infnan) {
+        Perl_ck_warner(aTHX_ packWARN(WARN_NUMERIC),
+                       "Non-finite repeat count does nothing");
+    } else if (count < 0) {
         count = 0;
         Perl_ck_warner(aTHX_ packWARN(WARN_NUMERIC),
-                                         "Negative repeat count does nothing");
+                       "Negative repeat count does nothing");
     }
 
     if (GIMME_V == G_ARRAY && PL_op->op_private & OPpREPEAT_DOLIST) {
@@ -1797,7 +1813,7 @@ PP(pp_subtract)
                        auv = aiv;
                        auvok = 1;      /* Now acting as a sign flag.  */
                    } else { /* 2s complement assumption for IV_MIN */
-                       auv = (UV)-aiv;
+                       auv = (aiv == IV_MIN) ? (UV)aiv : (UV)-aiv;
                    }
                }
                a_valid = 1;
@@ -1817,7 +1833,7 @@ PP(pp_subtract)
                    buv = biv;
                    buvok = 1;
                } else
-                   buv = (UV)-biv;
+                    buv = (biv == IV_MIN) ? (UV)biv : (UV)-biv;
            }
            /* ?uvok if value is >= 0. basically, flagged as UV if it's +ve,
               else "IV" now, independent of how it came in.
@@ -1858,7 +1874,8 @@ PP(pp_subtract)
                else {
                    /* Negate result */
                    if (result <= (UV)IV_MIN)
-                       SETi( -(IV)result );
+                        SETi(result == (UV)IV_MIN
+                                ? IV_MIN : -(IV)result);
                    else {
                        /* result valid, but out of range for IV.  */
                        SETn( -(NV)result );
@@ -2207,6 +2224,34 @@ PP(pp_bit_and)
     }
 }
 
+PP(pp_nbit_and)
+{
+    dSP;
+    tryAMAGICbin_MG(band_amg, AMGf_assign);
+    {
+       dATARGET; dPOPTOPssrl;
+       if (PL_op->op_private & HINT_INTEGER) {
+         const IV i = SvIV_nomg(left) & SvIV_nomg(right);
+         SETi(i);
+       }
+       else {
+         const UV u = SvUV_nomg(left) & SvUV_nomg(right);
+         SETu(u);
+       }
+    }
+    RETURN;
+}
+
+PP(pp_sbit_and)
+{
+    dSP;
+    tryAMAGICbin_MG(sband_amg, AMGf_assign);
+    {
+       dATARGET; dPOPTOPssrl;
+       do_vop(OP_BIT_AND, TARG, left, right);
+       RETSETTARG;
+    }
+}
 
 /* also used for: pp_bit_xor() */
 
@@ -2244,6 +2289,50 @@ PP(pp_bit_or)
     }
 }
 
+/* also used for: pp_nbit_xor() */
+
+PP(pp_nbit_or)
+{
+    dSP;
+    const int op_type = PL_op->op_type;
+
+    tryAMAGICbin_MG((op_type == OP_NBIT_OR ? bor_amg : bxor_amg),
+                   AMGf_assign);
+    {
+       dATARGET; dPOPTOPssrl;
+       if (PL_op->op_private & HINT_INTEGER) {
+         const IV l = (USE_LEFT(left) ? SvIV_nomg(left) : 0);
+         const IV r = SvIV_nomg(right);
+         const IV result = op_type == OP_NBIT_OR ? (l | r) : (l ^ r);
+         SETi(result);
+       }
+       else {
+         const UV l = (USE_LEFT(left) ? SvUV_nomg(left) : 0);
+         const UV r = SvUV_nomg(right);
+         const UV result = op_type == OP_NBIT_OR ? (l | r) : (l ^ r);
+         SETu(result);
+       }
+    }
+    RETURN;
+}
+
+/* also used for: pp_sbit_xor() */
+
+PP(pp_sbit_or)
+{
+    dSP;
+    const int op_type = PL_op->op_type;
+
+    tryAMAGICbin_MG((op_type == OP_SBIT_OR ? sbor_amg : sbxor_amg),
+                   AMGf_assign);
+    {
+       dATARGET; dPOPTOPssrl;
+       do_vop(op_type == OP_SBIT_OR ? OP_BIT_OR : OP_BIT_XOR, TARG, left,
+              right);
+       RETSETTARG;
+    }
+}
+
 PERL_STATIC_INLINE bool
 S_negate_string(pTHX)
 {
@@ -2263,7 +2352,7 @@ S_negate_string(pTHX)
        *SvPV_force_nomg(TARG, len) = *s == '-' ? '+' : '-';
     }
     else return FALSE;
-    SETTARG; PUTBACK;
+    SETTARG;
     return TRUE;
 }
 
@@ -2283,21 +2372,21 @@ PP(pp_negate)
                    /* 2s complement assumption. */
                     SETi(SvIVX(sv));   /* special case: -((UV)IV_MAX+1) ==
                                            IV_MIN */
-                   RETURN;
+                    return NORMAL;
                }
                else if (SvUVX(sv) <= IV_MAX) {
                    SETi(-SvIVX(sv));
-                   RETURN;
+                   return NORMAL;
                }
            }
            else if (SvIVX(sv) != IV_MIN) {
                SETi(-SvIVX(sv));
-               RETURN;
+               return NORMAL;
            }
 #ifdef PERL_PRESERVE_IVUV
            else {
                SETu((UV)IV_MIN);
-               RETURN;
+               return NORMAL;
            }
 #endif
        }
@@ -2308,7 +2397,7 @@ PP(pp_negate)
        else
            SETn(-SvNV_nomg(sv));
     }
-    RETURN;
+    return NORMAL;
 }
 
 PP(pp_not)
@@ -2319,23 +2408,9 @@ PP(pp_not)
     return NORMAL;
 }
 
-PP(pp_complement)
+static void
+S_scomplement(pTHX_ SV *targ, SV *sv)
 {
-    dSP; dTARGET;
-    tryAMAGICun_MG(compl_amg, AMGf_numeric);
-    {
-      dTOPss;
-      if (SvNIOKp(sv)) {
-       if (PL_op->op_private & HINT_INTEGER) {
-         const IV i = ~SvIV_nomg(sv);
-         SETi(i);
-       }
-       else {
-         const UV u = ~SvUV_nomg(sv);
-         SETu(u);
-       }
-      }
-      else {
        U8 *tmps;
        I32 anum;
        STRLEN len;
@@ -2396,8 +2471,7 @@ PP(pp_complement)
              sv_usepvn_flags(TARG, (char*)result, nchar, SV_HAS_TRAILING_NUL);
              SvUTF8_off(TARG);
          }
-         SETTARG;
-         RETURN;
+         return;
        }
 #ifdef LIBERAL
        {
@@ -2412,9 +2486,59 @@ PP(pp_complement)
 #endif
        for ( ; anum > 0; anum--, tmps++)
            *tmps = ~*tmps;
+}
+
+PP(pp_complement)
+{
+    dSP; dTARGET;
+    tryAMAGICun_MG(compl_amg, AMGf_numeric);
+    {
+      dTOPss;
+      if (SvNIOKp(sv)) {
+       if (PL_op->op_private & HINT_INTEGER) {
+         const IV i = ~SvIV_nomg(sv);
+         SETi(i);
+       }
+       else {
+         const UV u = ~SvUV_nomg(sv);
+         SETu(u);
+       }
+      }
+      else {
+       S_scomplement(aTHX_ TARG, sv);
        SETTARG;
       }
-      RETURN;
+      return NORMAL;
+    }
+}
+
+PP(pp_ncomplement)
+{
+    dSP;
+    tryAMAGICun_MG(compl_amg, AMGf_numeric);
+    {
+       dTARGET; dTOPss;
+       if (PL_op->op_private & HINT_INTEGER) {
+         const IV i = ~SvIV_nomg(sv);
+         SETi(i);
+       }
+       else {
+         const UV u = ~SvUV_nomg(sv);
+         SETu(u);
+       }
+    }
+    return NORMAL;
+}
+
+PP(pp_scomplement)
+{
+    dSP;
+    tryAMAGICun_MG(scompl_amg, AMGf_numeric);
+    {
+       dTARGET; dTOPss;
+       S_scomplement(aTHX_ TARG, sv);
+       SETTARG;
+       return NORMAL;
     }
 }
 
@@ -2661,7 +2785,7 @@ PP(pp_i_negate)
        SV * const sv = TOPs;
        IV const i = SvIV_nomg(sv);
        SETi(-i);
-       RETURN;
+       return NORMAL;
     }
 }
 
@@ -2700,7 +2824,7 @@ PP(pp_sin)
 
     tryAMAGICun_MG(amg_type, 0);
     {
-      SV * const arg = POPs;
+      SV * const arg = TOPs;
       const NV value = SvNV_nomg(arg);
       NV result = NV_NAN;
       if (neg_report) { /* log or sqrt */
@@ -2722,8 +2846,8 @@ PP(pp_sin)
       case OP_LOG:  result = Perl_log(value);  break;
       case OP_SQRT: result = Perl_sqrt(value); break;
       }
-      XPUSHn(result);
-      RETURN;
+      SETn(result);
+      return NORMAL;
     }
 }
 
@@ -2857,7 +2981,7 @@ PP(pp_int)
          }
       }
     }
-    RETURN;
+    return NORMAL;
 }
 
 PP(pp_abs)
@@ -2897,7 +3021,7 @@ PP(pp_abs)
          SETn(value);
       }
     }
-    RETURN;
+    return NORMAL;
 }
 
 
@@ -2911,7 +3035,7 @@ PP(pp_oct)
     STRLEN len;
     NV result_nv;
     UV result_uv;
-    SV* const sv = POPs;
+    SV* const sv = TOPs;
 
     tmps = (SvPV_const(sv, len));
     if (DO_UTF8(sv)) {
@@ -2940,12 +3064,12 @@ PP(pp_oct)
         result_uv = grok_oct (tmps, &len, &flags, &result_nv);
 
     if (flags & PERL_SCAN_GREATER_THAN_UV_MAX) {
-        XPUSHn(result_nv);
+        SETn(result_nv);
     }
     else {
-        XPUSHu(result_uv);
+        SETu(result_uv);
     }
-    RETURN;
+    return NORMAL;
 }
 
 /* String stuff. */
@@ -3102,7 +3226,6 @@ PP(pp_substr)
        assert(!repl_sv);
        repl_sv = POPs;
     }
-    PUTBACK;
     if (lvalue && !repl_sv) {
        SV * ret;
        ret = sv_2mortal(newSV_type(SVt_PVLV));  /* Not TARG RT#67838 */
@@ -3118,7 +3241,6 @@ PP(pp_substr)
                ? (STRLEN)(UV)len_iv
                : (LvFLAGS(ret) |= 2, (STRLEN)(UV)-len_iv);
 
-       SPAGAIN;
        PUSHs(ret);    /* avoid SvSETMAGIC here */
        RETURN;
     }
@@ -3188,7 +3310,6 @@ PP(pp_substr)
            SvREFCNT_dec(repl_sv_copy);
        }
     }
-    SPAGAIN;
     if (PL_op->op_private & OPpSUBSTR_REPL_FIRST)
        SP++;
     else if (rvalue) {
@@ -3197,7 +3318,7 @@ PP(pp_substr)
     }
     RETURN;
 
-bound_fail:
+  bound_fail:
     if (repl)
        Perl_croak(aTHX_ "substr outside of string");
     Perl_ck_warner(aTHX_ packWARN(WARN_SUBSTR), "substr outside of string");
@@ -3369,7 +3490,7 @@ PP(pp_ord)
 {
     dSP; dTARGET;
 
-    SV *argsv = POPs;
+    SV *argsv = TOPs;
     STRLEN len;
     const U8 *s = (U8*)SvPV_const(argsv, len);
 
@@ -3380,11 +3501,11 @@ PP(pp_ord)
         argsv = tmpsv;
     }
 
-    XPUSHu(DO_UTF8(argsv)
+    SETu(DO_UTF8(argsv)
            ? utf8n_to_uvchr(s, len, 0, UTF8_ALLOW_ANYUV)
            : (UV)(*s));
 
-    RETURN;
+    return NORMAL;
 }
 
 PP(pp_chr)
@@ -3392,7 +3513,7 @@ PP(pp_chr)
     dSP; dTARGET;
     char *tmps;
     UV value;
-    SV *top = POPs;
+    SV *top = TOPs;
 
     SvGETMAGIC(top);
     if (UNLIKELY(SvAMAGIC(top)))
@@ -3429,8 +3550,8 @@ PP(pp_chr)
        *tmps = '\0';
        (void)SvPOK_only(TARG);
        SvUTF8_on(TARG);
-       XPUSHTARG;
-       RETURN;
+       SETTARG;
+       return NORMAL;
     }
 
     SvGROW(TARG,2);
@@ -3456,8 +3577,8 @@ PP(pp_chr)
        }
     }
 
-    XPUSHTARG;
-    RETURN;
+    SETTARG;
+    return NORMAL;
 }
 
 PP(pp_crypt)
@@ -3591,23 +3712,27 @@ PP(pp_ucfirst)
        if (op_type == OP_LCFIRST) {
 
            /* lower case the first letter: no trickiness for any character */
-            *tmpbuf =
 #ifdef USE_LOCALE_CTYPE
-                      (IN_LC_RUNTIME(LC_CTYPE))
-                      ? toLOWER_LC(*s)
-                      :
+            if (IN_LC_RUNTIME(LC_CTYPE)) {
+                _CHECK_AND_WARN_PROBLEMATIC_LOCALE;
+                *tmpbuf = toLOWER_LC(*s);
+            }
+            else
 #endif
-                         (IN_UNI_8_BIT)
-                         ? toLOWER_LATIN1(*s)
-                         : toLOWER(*s);
+            {
+                *tmpbuf = (IN_UNI_8_BIT)
+                          ? toLOWER_LATIN1(*s)
+                          : toLOWER(*s);
+            }
        }
-       /* is ucfirst() */
 #ifdef USE_LOCALE_CTYPE
+       /* is ucfirst() */
        else if (IN_LC_RUNTIME(LC_CTYPE)) {
             if (IN_UTF8_CTYPE_LOCALE) {
                 goto do_uni_rules;
             }
 
+            _CHECK_AND_WARN_PROBLEMATIC_LOCALE;
             *tmpbuf = (U8) toUPPER_LC(*s); /* This would be a bug if any
                                               locales have upper and title case
                                               different */
@@ -3766,7 +3891,7 @@ PP(pp_ucfirst)
     if (dest != source && SvTAINTED(source))
        SvTAINT(dest);
     SvSETMAGIC(dest);
-    RETURN;
+    return NORMAL;
 }
 
 /* There's so much setup/teardown code common between uc and lc, I wonder if
@@ -3912,6 +4037,7 @@ PP(pp_uc)
                 if (IN_UTF8_CTYPE_LOCALE) {
                     goto do_uni_rules;
                 }
+                _CHECK_AND_WARN_PROBLEMATIC_LOCALE;
                for (; s < send; d++, s++)
                     *d = (U8) toUPPER_LC(*s);
            }
@@ -4023,7 +4149,7 @@ PP(pp_uc)
     if (dest != source && SvTAINTED(source))
        SvTAINT(dest);
     SvSETMAGIC(dest);
-    RETURN;
+    return NORMAL;
 }
 
 PP(pp_lc)
@@ -4119,6 +4245,7 @@ PP(pp_lc)
             * whole thing in a tight loop, for speed, */
 #ifdef USE_LOCALE_CTYPE
             if (IN_LC_RUNTIME(LC_CTYPE)) {
+                _CHECK_AND_WARN_PROBLEMATIC_LOCALE;
                for (; s < send; d++, s++)
                    *d = toLOWER_LC(*s);
             }
@@ -4149,7 +4276,7 @@ PP(pp_lc)
     if (dest != source && SvTAINTED(source))
        SvTAINT(dest);
     SvSETMAGIC(dest);
-    RETURN;
+    return NORMAL;
 }
 
 PP(pp_quotemeta)
@@ -4227,7 +4354,7 @@ PP(pp_quotemeta)
     else
        sv_setpvn(TARG, s, len);
     SETTARG;
-    RETURN;
+    return NORMAL;
 }
 
 PP(pp_fc)
@@ -4301,6 +4428,7 @@ PP(pp_fc)
             if (IN_UTF8_CTYPE_LOCALE) {
                 goto do_uni_folding;
             }
+            _CHECK_AND_WARN_PROBLEMATIC_LOCALE;
             for (; s < send; d++, s++)
                 *d = (U8) toFOLD_LC(*s);
         }
@@ -4613,21 +4741,15 @@ PP(pp_each)
     HE *entry;
     const I32 gimme = GIMME_V;
 
-    PUTBACK;
-    /* might clobber stack_sp */
     entry = hv_iternext(hash);
-    SPAGAIN;
 
     EXTEND(SP, 2);
     if (entry) {
        SV* const sv = hv_iterkeysv(entry);
-       PUSHs(sv);      /* won't clobber stack_sp */
+       PUSHs(sv);
        if (gimme == G_ARRAY) {
            SV *val;
-           PUTBACK;
-           /* might clobber stack_sp */
            val = hv_iterval(hash, entry);
-           SPAGAIN;
            PUSHs(val);
        }
     }
@@ -5046,7 +5168,7 @@ PP(pp_anonhash)
            MARK++;
            SvGETMAGIC(*MARK);
            val = newSV(0);
-           sv_setsv(val, *MARK);
+           sv_setsv_nomg(val, *MARK);
        }
        else
        {
@@ -5560,15 +5682,18 @@ PP(pp_split)
 #ifdef USE_ITHREADS
     if (pm->op_pmreplrootu.op_pmtargetoff) {
        ary = GvAVn(MUTABLE_GV(PAD_SVl(pm->op_pmreplrootu.op_pmtargetoff)));
+       goto have_av;
     }
 #else
     if (pm->op_pmreplrootu.op_pmtargetgv) {
        ary = GvAVn(pm->op_pmreplrootu.op_pmtargetgv);
+       goto have_av;
     }
 #endif
     else if (pm->op_targ)
        ary = (AV *)PAD_SVl(pm->op_targ);
     if (ary) {
+       have_av:
        realarray = 1;
        PUTBACK;
        av_extend(ary,0);
@@ -6358,6 +6483,17 @@ PP(pp_lvavref)
     }
 }
 
+PP(pp_anonconst)
+{
+    dSP;
+    dTOPss;
+    SETs(sv_2mortal((SV *)newCONSTSUB(SvTYPE(CopSTASH(PL_curcop))==SVt_PVHV
+                                       ? CopSTASH(PL_curcop)
+                                       : NULL,
+                                     NULL, SvREFCNT_inc_simple_NN(sv))));
+    RETURN;
+}
+
 /*
  * Local variables:
  * c-indentation-style: bsd