This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
allow wrap-around of PL_cop_seqmax
[perl5.git] / op.c
diff --git a/op.c b/op.c
index 3121cb4..2e15a8d 100644 (file)
--- a/op.c
+++ b/op.c
@@ -310,6 +310,12 @@ Perl_Slab_Free(pTHX_ void *op)
 
 #define RETURN_UNLIMITED_NUMBER (PERL_INT_MAX / 2)
 
+#define CHANGE_TYPE(o,type) \
+    STMT_START {                               \
+       o->op_type = (OPCODE)type;              \
+       o->op_ppaddr = PL_ppaddr[type];         \
+    } STMT_END
+
 STATIC const char*
 S_gv_ename(pTHX_ GV *gv)
 {
@@ -645,6 +651,7 @@ Perl_op_clear(pTHX_ OP *o)
            break;
        /* FALL THROUGH */
     case OP_TRANS:
+    case OP_TRANSR:
        if (o->op_private & (OPpTRANS_FROM_UTF|OPpTRANS_TO_UTF)) {
 #ifdef USE_ITHREADS
            if (cPADOPo->op_padix > 0) {
@@ -717,7 +724,7 @@ S_cop_free(pTHX_ COP* cop)
     CopSTASH_free(cop);
     if (! specialWARN(cop->cop_warnings))
        PerlMemShared_free(cop->cop_warnings);
-    Perl_refcounted_he_free(aTHX_ cop->cop_hints_hash);
+    cophh_free(CopHINTHASH_get(cop));
 }
 
 STATIC void
@@ -903,7 +910,8 @@ S_scalarboolean(pTHX_ OP *o)
 
     PERL_ARGS_ASSERT_SCALARBOOLEAN;
 
-    if (o->op_type == OP_SASSIGN && cBINOPo->op_first->op_type == OP_CONST) {
+    if (o->op_type == OP_SASSIGN && cBINOPo->op_first->op_type == OP_CONST
+     && !(cBINOPo->op_first->op_flags & OPf_SPECIAL)) {
        if (ckWARN(WARN_SYNTAX)) {
            const line_t oldline = CopLINE(PL_curcop);
 
@@ -1138,7 +1146,7 @@ Perl_scalarvoid(pTHX_ OP *o)
     case OP_NOT:
        kid = cUNOPo->op_first;
        if (kid->op_type != OP_MATCH && kid->op_type != OP_SUBST &&
-           kid->op_type != OP_TRANS) {
+           kid->op_type != OP_TRANS && kid->op_type != OP_TRANSR) {
                goto func_ops;
        }
        useless = "negative pattern binding (!~)";
@@ -1146,7 +1154,11 @@ Perl_scalarvoid(pTHX_ OP *o)
 
     case OP_SUBST:
        if (cPMOPo->op_pmflags & PMf_NONDESTRUCT)
-           useless = "Non-destructive substitution (s///r)";
+           useless = "non-destructive substitution (s///r)";
+       break;
+
+    case OP_TRANSR:
+       useless = "non-destructive transliteration (tr///r)";
        break;
 
     case OP_RV2GV:
@@ -1397,24 +1409,32 @@ S_modkids(pTHX_ OP *o, I32 type)
     if (o && o->op_flags & OPf_KIDS) {
         OP *kid;
        for (kid = cLISTOPo->op_first; kid; kid = kid->op_sibling)
-           mod(kid, type);
+           op_lvalue(kid, type);
     }
     return o;
 }
 
-/* Propagate lvalue ("modifiable") context to an op and its children.
- * 'type' represents the context type, roughly based on the type of op that
- * would do the modifying, although local() is represented by OP_NULL.
- * It's responsible for detecting things that can't be modified,  flag
- * things that need to behave specially in an lvalue context (e.g., "$$x = 5"
- * might have to vivify a reference in $x), and so on.
- *
- * For example, "$a+1 = 2" would cause mod() to be called with o being
- * OP_ADD and type being OP_SASSIGN, and would output an error.
- */
+/*
+=for apidoc Amx|OP *|op_lvalue|OP *o|I32 type
+
+Propagate lvalue ("modifiable") context to an op and its children.
+I<type> represents the context type, roughly based on the type of op that
+would do the modifying, although C<local()> is represented by OP_NULL,
+because it has no op type of its own (it is signalled by a flag on
+the lvalue op).
+
+This function detects things that can't be modified, such as C<$x+1>, and
+generates errors for them. For example, C<$x+1 = 2> would cause it to be
+called with an op of type OP_ADD and a C<type> argument of OP_SASSIGN.
+
+It also flags things that need to behave specially in an lvalue context,
+such as C<$$x = 5> which might have to vivify a reference in C<$x>.
+
+=cut
+*/
 
 OP *
-Perl_mod(pTHX_ OP *o, I32 type)
+Perl_op_lvalue(pTHX_ OP *o, I32 type)
 {
     dVAR;
     OP *kid;
@@ -1598,7 +1618,7 @@ Perl_mod(pTHX_ OP *o, I32 type)
     case OP_COND_EXPR:
        localize = 1;
        for (kid = cUNOPo->op_first->op_sibling; kid; kid = kid->op_sibling)
-           mod(kid, type);
+           op_lvalue(kid, type);
        break;
 
     case OP_RV2AV:
@@ -1686,7 +1706,7 @@ Perl_mod(pTHX_ OP *o, I32 type)
        o->op_targ = pad_alloc(o->op_type, SVs_PADMY);
        assert(SvTYPE(PAD_SV(o->op_targ)) == SVt_NULL);
        if (o->op_flags & OPf_KIDS)
-           mod(cBINOPo->op_first->op_sibling, type);
+           op_lvalue(cBINOPo->op_first->op_sibling, type);
        break;
 
     case OP_AELEM:
@@ -1707,7 +1727,7 @@ Perl_mod(pTHX_ OP *o, I32 type)
     case OP_LINESEQ:
        localize = 0;
        if (o->op_flags & OPf_KIDS)
-           mod(cLISTOPo->op_last, type);
+           op_lvalue(cLISTOPo->op_last, type);
        break;
 
     case OP_NULL:
@@ -1717,20 +1737,20 @@ Perl_mod(pTHX_ OP *o, I32 type)
        else if (!(o->op_flags & OPf_KIDS))
            break;
        if (o->op_targ != OP_LIST) {
-           mod(cBINOPo->op_first, type);
+           op_lvalue(cBINOPo->op_first, type);
            break;
        }
        /* FALL THROUGH */
     case OP_LIST:
        localize = 0;
        for (kid = cLISTOPo->op_first; kid; kid = kid->op_sibling)
-           mod(kid, type);
+           op_lvalue(kid, type);
        break;
 
     case OP_RETURN:
        if (type != OP_LEAVESUBLV)
            goto nomod;
-       break; /* mod()ing was handled by ck_return() */
+       break; /* op_lvalue()ing was handled by ck_return() */
     }
 
     /* [20011101.069] File test operators interpret OPf_REF to mean that
@@ -1765,6 +1785,14 @@ Perl_mod(pTHX_ OP *o, I32 type)
     return o;
 }
 
+/* Do not use this. It will be removed after 5.14. */
+OP *
+Perl_mod(pTHX_ OP *o, I32 type)
+{
+    return op_lvalue(o,type);
+}
+
+
 STATIC bool
 S_scalar_mod_type(const OP *o, I32 type)
 {
@@ -1803,6 +1831,7 @@ S_scalar_mod_type(const OP *o, I32 type)
     case OP_CONCAT:
     case OP_SUBST:
     case OP_TRANS:
+    case OP_TRANSR:
     case OP_READ:
     case OP_SYSREAD:
     case OP_RECV:
@@ -2044,7 +2073,7 @@ S_apply_attrs_my(pTHX_ HV *stash, OP *target, OP *attrs, OP **imopsp)
                       newSVOP(OP_CONST, 0, stashsv),
                       op_prepend_elem(OP_LIST,
                                    newUNOP(OP_REFGEN, 0,
-                                           mod(arg, OP_REFGEN)),
+                                           op_lvalue(arg, OP_REFGEN)),
                                    dup_attrlist(attrs)));
 
     /* Fake up a method call to import */
@@ -2114,6 +2143,7 @@ S_my_kid(pTHX_ OP *o, OP *attrs, OP **imopsp)
 {
     dVAR;
     I32 type;
+    const bool stately = PL_parser && PL_parser->in_my == KEY_state;
 
     PERL_ARGS_ASSERT_MY_KID;
 
@@ -2184,7 +2214,7 @@ S_my_kid(pTHX_ OP *o, OP *attrs, OP **imopsp)
     }
     o->op_flags |= OPf_MOD;
     o->op_private |= OPpLVAL_INTRO;
-    if (PL_parser->in_my == KEY_state)
+    if (stately)
        o->op_private |= OPpPAD_STATE;
     return o;
 }
@@ -2248,7 +2278,10 @@ Perl_bind_match(pTHX_ I32 type, OP *left, OP *right)
          || ltype == OP_PADHV) && ckWARN(WARN_MISC))
     {
       const char * const desc
-         = PL_op_desc[(rtype == OP_SUBST || rtype == OP_TRANS)
+         = PL_op_desc[(
+                         rtype == OP_SUBST || rtype == OP_TRANS
+                      || rtype == OP_TRANSR
+                      )
                       ? (int)rtype : OP_MATCH];
       const char * const sample = ((ltype == OP_RV2AV || ltype == OP_PADAV)
             ? "@array" : "%hash");
@@ -2264,14 +2297,16 @@ Perl_bind_match(pTHX_ I32 type, OP *left, OP *right)
        no_bareword_allowed(right);
     }
 
-    /* !~ doesn't make sense with s///r, so error on it for now */
+    /* !~ doesn't make sense with /r, so error on it for now */
     if (rtype == OP_SUBST && (cPMOPx(right)->op_pmflags & PMf_NONDESTRUCT) &&
        type == OP_NOT)
        yyerror("Using !~ with s///r doesn't make sense");
+    if (rtype == OP_TRANSR && type == OP_NOT)
+       yyerror("Using !~ with tr///r doesn't make sense");
 
     ismatchop = (rtype == OP_MATCH ||
                 rtype == OP_SUBST ||
-                rtype == OP_TRANS)
+                rtype == OP_TRANS || rtype == OP_TRANSR)
             && !(right->op_flags & OPf_SPECIAL);
     if (ismatchop && right->op_private & OPpTARGET_MY) {
        right->op_targ = 0;
@@ -2281,15 +2316,15 @@ Perl_bind_match(pTHX_ I32 type, OP *left, OP *right)
        OP *newleft;
 
        right->op_flags |= OPf_STACKED;
-       if (rtype != OP_MATCH &&
+       if (rtype != OP_MATCH && rtype != OP_TRANSR &&
             ! (rtype == OP_TRANS &&
                right->op_private & OPpTRANS_IDENTICAL) &&
            ! (rtype == OP_SUBST &&
               (cPMOPx(right)->op_pmflags & PMf_NONDESTRUCT)))
-           newleft = mod(left, rtype);
+           newleft = op_lvalue(left, rtype);
        else
            newleft = left;
-       if (right->op_type == OP_TRANS)
+       if (right->op_type == OP_TRANS || right->op_type == OP_TRANSR)
            o = newBINOP(OP_NULL, OPf_STACKED, scalar(newleft), right);
        else
            o = op_prepend_elem(rtype, scalar(newleft), right);
@@ -2310,8 +2345,22 @@ Perl_invert(pTHX_ OP *o)
     return newUNOP(OP_NOT, OPf_SPECIAL, scalar(o));
 }
 
+/*
+=for apidoc Amx|OP *|op_scope|OP *o
+
+Wraps up an op tree with some additional ops so that at runtime a dynamic
+scope will be created.  The original ops run in the new dynamic scope,
+and then, provided that they exit normally, the scope will be unwound.
+The additional ops used to create and unwind the dynamic scope will
+normally be an C<enter>/C<leave> pair, but a C<scope> op may be used
+instead if the ops are simple enough to not need the full dynamic scope
+structure.
+
+=cut
+*/
+
 OP *
-Perl_scope(pTHX_ OP *o)
+Perl_op_scope(pTHX_ OP *o)
 {
     dVAR;
     if (o) {
@@ -2381,7 +2430,7 @@ Perl_block_end(pTHX_ I32 floor, OP *seq)
 /*
 =head1 Compile-time scope hooks
 
-=for apidoc Ao||blockhook_register
+=for apidoc Aox||blockhook_register
 
 Register a set of hooks to be called when the Perl lexical scope changes
 at compile time. See L<perlguts/"Compile-time scope hooks">.
@@ -2441,7 +2490,7 @@ Perl_newPROG(pTHX_ OP *o)
            S_op_destroy(aTHX_ o);
            return;
        }
-       PL_main_root = scope(sawparens(scalarvoid(o)));
+       PL_main_root = op_scope(sawparens(scalarvoid(o)));
        PL_curcop = &PL_compiling;
        PL_main_start = LINKLIST(PL_main_root);
        PL_main_root->op_private |= OPpREFCOUNTED;
@@ -2520,7 +2569,7 @@ Perl_localize(pTHX_ OP *o, I32 lex)
     if (lex)
        o = my(o);
     else
-       o = mod(o, OP_NULL);            /* a bit kludgey */
+       o = op_lvalue(o, OP_NULL);              /* a bit kludgey */
     PL_parser->in_my = FALSE;
     PL_parser->in_my_stash = NULL;
     return o;
@@ -2588,6 +2637,7 @@ S_fold_constants(pTHX_ register OP *o)
     case OP_SLE:
     case OP_SGE:
     case OP_SCMP:
+    case OP_SPRINTF:
        /* XXX what about the numeric ops? */
        if (PL_hints & HINT_LOCALE)
            goto nope;
@@ -2694,12 +2744,12 @@ S_gen_constant_list(pTHX_ register OP *o)
     PL_op = curop = LINKLIST(o);
     o->op_next = 0;
     CALL_PEEP(curop);
-    pp_pushmark();
+    Perl_pp_pushmark(aTHX);
     CALLRUNOPS(aTHX);
     PL_op = curop;
     assert (!(curop->op_flags & OPf_SPECIAL));
     assert(curop->op_type == OP_RANGE);
-    pp_anonlist();
+    Perl_pp_anonlist(aTHX);
     PL_tmps_floor = oldtmps_floor;
 
     o->op_type = OP_RV2AV;
@@ -3477,8 +3527,7 @@ S_pmtrans(pTHX_ OP *o, OP *expr, OP *repl)
                U8 range_mark = UTF_TO_NATIVE(0xff);
                sv_catpvn(transv, (char *)&range_mark, 1);
            }
-           t = uvuni_to_utf8_flags(tmpbuf, 0x7fffffff,
-                                   UNICODE_ALLOW_SUPER);
+           t = uvuni_to_utf8(tmpbuf, 0x7fffffff);
            sv_catpvn(transv, (char*)tmpbuf, t - tmpbuf);
            t = (const U8*)SvPVX_const(transv);
            tlen = SvCUR(transv);
@@ -3729,10 +3778,22 @@ Perl_newPMOP(pTHX_ I32 type, I32 flags)
     if (PL_hints & HINT_RE_TAINT)
        pmop->op_pmflags |= PMf_RETAINT;
     if (PL_hints & HINT_LOCALE) {
-       pmop->op_pmflags |= PMf_LOCALE;
+       set_regex_charset(&(pmop->op_pmflags), REGEX_LOCALE_CHARSET);
     }
     else if ((! (PL_hints & HINT_BYTES)) && (PL_hints & HINT_UNI_8_BIT)) {
-        pmop->op_pmflags |= RXf_PMf_UNICODE;
+       set_regex_charset(&(pmop->op_pmflags), REGEX_UNICODE_CHARSET);
+    }
+    if (PL_hints & HINT_RE_FLAGS) {
+        SV *reflags = Perl_refcounted_he_fetch_pvn(aTHX_
+         PL_compiling.cop_hints_hash, STR_WITH_LEN("reflags"), 0, 0
+        );
+        if (reflags && SvOK(reflags)) pmop->op_pmflags |= SvIV(reflags);
+        reflags = Perl_refcounted_he_fetch_pvn(aTHX_
+         PL_compiling.cop_hints_hash, STR_WITH_LEN("reflags_charset"), 0, 0
+        );
+        if (reflags && SvOK(reflags)) {
+            set_regex_charset(&(pmop->op_pmflags), (regex_charset)SvIV(reflags));
+        }
     }
 
 
@@ -3786,7 +3847,10 @@ Perl_pmruntime(pTHX_ OP *o, OP *expr, bool isreg)
 
     PERL_ARGS_ASSERT_PMRUNTIME;
 
-    if (o->op_type == OP_SUBST || o->op_type == OP_TRANS) {
+    if (
+        o->op_type == OP_SUBST
+     || o->op_type == OP_TRANS || o->op_type == OP_TRANSR
+    ) {
        /* last element in list is the replacement; pop it */
        OP* kid;
        repl = cLISTOPx(expr)->op_last;
@@ -3808,7 +3872,7 @@ Perl_pmruntime(pTHX_ OP *o, OP *expr, bool isreg)
        op_free(oe);
     }
 
-    if (o->op_type == OP_TRANS) {
+    if (o->op_type == OP_TRANS || o->op_type == OP_TRANSR) {
        return pmtrans(o, expr, repl);
     }
 
@@ -3821,7 +3885,7 @@ Perl_pmruntime(pTHX_ OP *o, OP *expr, bool isreg)
 
     if (expr->op_type == OP_CONST) {
        SV *pat = ((SVOP*)expr)->op_sv;
-       U32 pm_flags = pm->op_pmflags & PMf_COMPILETIME;
+       U32 pm_flags = pm->op_pmflags & RXf_PMf_COMPILETIME;
 
        if (o->op_flags & OPf_SPECIAL)
            pm_flags |= RXf_SPLIT;
@@ -3865,7 +3929,7 @@ Perl_pmruntime(pTHX_ OP *o, OP *expr, bool isreg)
            rcop->op_targ = pad_alloc(rcop->op_type, SVs_PADTMP);
 
        /* /$x/ may cause an eval, since $x might be qr/(?{..})/  */
-       PL_cv_has_eval = 1;
+       if (PL_hints & HINT_RE_EVAL) PL_cv_has_eval = 1;
 
        /* establish postfix order */
        if (pm->op_pmflags & PMf_KEEP || !(PL_hints & HINT_RE_EVAL)) {
@@ -4170,6 +4234,7 @@ Perl_utilize(pTHX_ int aver, I32 floor, OP *version, OP *idop, OP *arg)
 #ifdef PERL_MAD
     OP *pegop = newOP(OP_NULL,0);
 #endif
+    SV *use_version = NULL;
 
     PERL_ARGS_ASSERT_UTILIZE;
 
@@ -4216,7 +4281,9 @@ Perl_utilize(pTHX_ int aver, I32 floor, OP *version, OP *idop, OP *arg)
     }
     else if (SvNIOKp(((SVOP*)idop)->op_sv)) {
        imop = NULL;            /* use 5.0; */
-       if (!aver)
+       if (aver)
+           use_version = ((SVOP*)idop)->op_sv;
+       else
            idop->op_private |= OPpCONST_NOVER;
     }
     else {
@@ -4248,6 +4315,26 @@ Perl_utilize(pTHX_ int aver, I32 floor, OP *version, OP *idop, OP *arg)
                newSTATEOP(0, NULL, veop)),
            newSTATEOP(0, NULL, imop) ));
 
+    if (use_version) {
+       /* If we request a version >= 5.9.5, load feature.pm with the
+        * feature bundle that corresponds to the required version. */
+       use_version = sv_2mortal(new_version(use_version));
+
+       if (vcmp(use_version,
+                sv_2mortal(upg_version(newSVnv(5.009005), FALSE))) >= 0) {
+           SV *const importsv = vnormal(use_version);
+           *SvPVX_mutable(importsv) = ':';
+           ENTER_with_name("load_feature");
+           Perl_load_module(aTHX_ 0, newSVpvs("feature"), NULL, importsv, NULL);
+           LEAVE_with_name("load_feature");
+       }
+       /* If a version >= 5.11.0 is requested, strictures are on by default! */
+       if (vcmp(use_version,
+                sv_2mortal(upg_version(newSVnv(5.011000), FALSE))) >= 0) {
+           PL_hints |= (HINT_STRICT_REFS | HINT_STRICT_SUBS | HINT_STRICT_VARS);
+       }
+    }
+
     /* The "did you use incorrect case?" warning used to be here.
      * The problem is that on case-insensitive filesystems one
      * might get false positives for "use" (and "require"):
@@ -4269,6 +4356,8 @@ Perl_utilize(pTHX_ int aver, I32 floor, OP *version, OP *idop, OP *arg)
     PL_parser->copline = NOLINE;
     PL_parser->expect = XSTATE;
     PL_cop_seqmax++; /* Purely for B::*'s benefit */
+    if (PL_cop_seqmax == PERL_PADSEQ_INTRO) /* not a legal value */
+       PL_cop_seqmax++;
 
 #ifdef PERL_MAD
     if (!PL_madskills) {
@@ -4364,7 +4453,7 @@ Perl_vload_module(pTHX_ U32 flags, SV *name, SV *ver, va_list *args)
 
     ENTER;
     SAVEVPTR(PL_curcop);
-    lex_start(NULL, NULL, FALSE);
+    lex_start(NULL, NULL, 0);
     utilize(!(flags & PERL_LOADMOD_DENY), start_subparse(FALSE, 0),
            veop, modname, imop);
     LEAVE;
@@ -4499,12 +4588,12 @@ Perl_newASSIGNOP(pTHX_ I32 flags, OP *left, I32 optype, OP *right)
     if (optype) {
        if (optype == OP_ANDASSIGN || optype == OP_ORASSIGN || optype == OP_DORASSIGN) {
            return newLOGOP(optype, 0,
-               mod(scalar(left), optype),
+               op_lvalue(scalar(left), optype),
                newUNOP(OP_SASSIGN, 0, scalar(right)));
        }
        else {
            return newBINOP(optype, OPf_STACKED,
-               mod(scalar(left), optype), scalar(right));
+               op_lvalue(scalar(left), optype), scalar(right));
        }
     }
 
@@ -4518,7 +4607,7 @@ Perl_newASSIGNOP(pTHX_ I32 flags, OP *left, I32 optype, OP *right)
        /* Grandfathering $[ assignment here.  Bletch.*/
        /* Only simple assignments like C<< ($[) = 1 >> are allowed */
        PL_eval_start = (left->op_type == OP_CONST) ? right : NULL;
-       left = mod(left, OP_AASSIGN);
+       left = op_lvalue(left, OP_AASSIGN);
        if (PL_eval_start)
            PL_eval_start = 0;
        else if (left->op_type == OP_CONST) {
@@ -4718,12 +4807,13 @@ Perl_newASSIGNOP(pTHX_ I32 flags, OP *left, I32 optype, OP *right)
        right = newOP(OP_UNDEF, 0);
     if (right->op_type == OP_READLINE) {
        right->op_flags |= OPf_STACKED;
-       return newBINOP(OP_NULL, flags, mod(scalar(left), OP_SASSIGN), scalar(right));
+       return newBINOP(OP_NULL, flags, op_lvalue(scalar(left), OP_SASSIGN),
+               scalar(right));
     }
     else {
        PL_eval_start = right;  /* Grandfathering $[ assignment here.  Bletch.*/
        o = newBINOP(OP_SASSIGN, flags,
-           scalar(right), mod(scalar(left), OP_SASSIGN) );
+           scalar(right), op_lvalue(scalar(left), OP_SASSIGN) );
        if (PL_eval_start)
            PL_eval_start = 0;
        else {
@@ -4785,12 +4875,7 @@ Perl_newSTATEOP(pTHX_ I32 flags, char *label, OP *o)
        CopHINTS and a possible value in cop_hints_hash, so no need to copy it.
     */
     cop->cop_warnings = DUP_WARNINGS(PL_curcop->cop_warnings);
-    cop->cop_hints_hash = PL_curcop->cop_hints_hash;
-    if (cop->cop_hints_hash) {
-       HINTS_REFCNT_LOCK;
-       cop->cop_hints_hash->refcounted_he_refcnt++;
-       HINTS_REFCNT_UNLOCK;
-    }
+    CopHINTHASH_set(cop, cophh_copy(CopHINTHASH_get(PL_curcop)));
     if (label) {
        Perl_store_cop_label(aTHX_ cop, label, strlen(label), 0);
                                                     
@@ -4962,6 +5047,7 @@ S_new_logop(pTHX_ I32 type, I32 flags, OP** firstp, OP** otherp)
                other = newUNOP(OP_NULL, OPf_SPECIAL, other);
            else if (other->op_type == OP_MATCH
                  || other->op_type == OP_SUBST
+                 || other->op_type == OP_TRANSR
                  || other->op_type == OP_TRANS)
                /* Mark the op as being unbindable with =~ */
                other->op_flags |= OPf_SPECIAL;
@@ -5118,7 +5204,7 @@ Perl_newCONDOP(pTHX_ I32 flags, OP *first, OP *trueop, OP *falseop)
        if (live->op_type == OP_LEAVE)
            live = newUNOP(OP_NULL, OPf_SPECIAL, live);
        else if (live->op_type == OP_MATCH || live->op_type == OP_SUBST
-             || live->op_type == OP_TRANS)
+             || live->op_type == OP_TRANS || live->op_type == OP_TRANSR)
            /* Mark the op as being unbindable with =~ */
            live->op_flags |= OPf_SPECIAL;
        return live;
@@ -5287,13 +5373,13 @@ Perl_newLOOPOP(pTHX_ I32 flags, I32 debuggable, OP *expr, OP *block)
        o = newUNOP(OP_NULL, 0, o);     /* or do {} while 1 loses outer block */
 
     o->op_flags |= flags;
-    o = scope(o);
+    o = op_scope(o);
     o->op_flags |= OPf_SPECIAL;        /* suppress POPBLOCK curpm restoration*/
     return o;
 }
 
 /*
-=for apidoc Am|OP *|newWHILEOP|I32 flags|I32 debuggable|LOOP *loop|I32 whileline|OP *expr|OP *block|OP *cont|I32 has_my
+=for apidoc Am|OP *|newWHILEOP|I32 flags|I32 debuggable|LOOP *loop|OP *expr|OP *block|OP *cont|I32 has_my
 
 Constructs, checks, and returns an op tree expressing a C<while> loop.
 This is a heavyweight loop, with structure that allows exiting the loop
@@ -5310,16 +5396,15 @@ I<flags> gives the eight bits of C<op_flags> for the C<leaveloop>
 op and, shifted up eight bits, the eight bits of C<op_private> for
 the C<leaveloop> op, except that (in both cases) some bits will be set
 automatically.  I<debuggable> is currently unused and should always be 1.
-I<whileline> is the line number that should be attributed to the loop's
-controlling expression.  I<has_my> can be supplied as true to force the
+I<has_my> can be supplied as true to force the
 loop body to be enclosed in its own scope.
 
 =cut
 */
 
 OP *
-Perl_newWHILEOP(pTHX_ I32 flags, I32 debuggable, LOOP *loop, I32
-whileline, OP *expr, OP *block, OP *cont, I32 has_my)
+Perl_newWHILEOP(pTHX_ I32 flags, I32 debuggable, LOOP *loop,
+       OP *expr, OP *block, OP *cont, I32 has_my)
 {
     dVAR;
     OP *redo;
@@ -5362,7 +5447,7 @@ whileline, OP *expr, OP *block, OP *cont, I32 has_my)
     if (!block)
        block = newOP(OP_NULL, 0);
     else if (cont || has_my) {
-       block = scope(block);
+       block = op_scope(block);
     }
 
     if (cont) {
@@ -5381,7 +5466,6 @@ whileline, OP *expr, OP *block, OP *cont, I32 has_my)
     redo = LINKLIST(listop);
 
     if (expr) {
-       PL_parser->copline = (line_t)whileline;
        scalar(listop);
        o = new_logop(OP_AND, 0, &expr, &listop);
        if (o == expr && o->op_type == OP_CONST && !SvTRUE(cSVOPo->op_sv)) {
@@ -5421,7 +5505,7 @@ whileline, OP *expr, OP *block, OP *cont, I32 has_my)
 }
 
 /*
-=for apidoc Am|OP *|newFOROP|I32 flags|char *label|line_t forline|OP *sv|OP *expr|OP *block|OP *cont
+=for apidoc Am|OP *|newFOROP|I32 flags|OP *sv|OP *expr|OP *block|OP *cont
 
 Constructs, checks, and returns an op tree expressing a C<foreach>
 loop (iteration through a list of values).  This is a heavyweight loop,
@@ -5438,17 +5522,13 @@ op tree.
 I<flags> gives the eight bits of C<op_flags> for the C<leaveloop>
 op and, shifted up eight bits, the eight bits of C<op_private> for
 the C<leaveloop> op, except that (in both cases) some bits will be set
-automatically.  I<forline> is the line number that should be attributed
-to the loop's list expression.  If I<label> is non-null, it supplies
-the name of a label to attach to the state op at the start of the loop;
-this function takes ownership of the memory pointed at by I<label>,
-and will free it.
+automatically.
 
 =cut
 */
 
 OP *
-Perl_newFOROP(pTHX_ I32 flags, char *label, line_t forline, OP *sv, OP *expr, OP *block, OP *cont)
+Perl_newFOROP(pTHX_ I32 flags, OP *sv, OP *expr, OP *block, OP *cont)
 {
     dVAR;
     LOOP *loop;
@@ -5509,7 +5589,7 @@ Perl_newFOROP(pTHX_ I32 flags, char *label, line_t forline, OP *sv, OP *expr, OP
        iterpflags |= OPpITER_DEF;
     }
     if (expr->op_type == OP_RV2AV || expr->op_type == OP_PADAV) {
-       expr = mod(force_list(scalar(ref(expr, OP_ITER))), OP_GREPSTART);
+       expr = op_lvalue(force_list(scalar(ref(expr, OP_ITER))), OP_GREPSTART);
        iterflags |= OPf_STACKED;
     }
     else if (expr->op_type == OP_NULL &&
@@ -5545,7 +5625,7 @@ Perl_newFOROP(pTHX_ I32 flags, char *label, line_t forline, OP *sv, OP *expr, OP
        iterflags |= OPf_STACKED;
     }
     else {
-        expr = mod(force_list(expr), OP_GREPSTART);
+        expr = op_lvalue(force_list(expr), OP_GREPSTART);
     }
 
     loop = (LOOP*)list(convert(OP_ENTERITER, iterflags,
@@ -5566,11 +5646,10 @@ Perl_newFOROP(pTHX_ I32 flags, char *label, line_t forline, OP *sv, OP *expr, OP
     loop = (LOOP*)PerlMemShared_realloc(loop, sizeof(LOOP));
 #endif
     loop->op_targ = padoff;
-    wop = newWHILEOP(flags, 1, loop, forline, newOP(OP_ITER, 0), block, cont, 0);
+    wop = newWHILEOP(flags, 1, loop, newOP(OP_ITER, 0), block, cont, 0);
     if (madsv)
        op_getmad(madsv, (OP*)loop, 'v');
-    PL_parser->copline = forline;
-    return newSTATEOP(0, label, wop);
+    return wop;
 }
 
 /*
@@ -5613,7 +5692,7 @@ Perl_newLOOPEX(pTHX_ I32 type, OP *label)
        /* Check whether it's going to be a goto &function */
        if (label->op_type == OP_ENTERSUB
                && !(label->op_flags & OPf_STACKED))
-           label = newUNOP(OP_REFGEN, 0, mod(label, OP_REFGEN));
+           label = newUNOP(OP_REFGEN, 0, op_lvalue(label, OP_REFGEN));
        o = newUNOP(type, OPf_STACKED, label);
     }
     PL_hints |= HINT_BLOCK_SCOPE;
@@ -5632,8 +5711,7 @@ S_ref_array_or_hash(pTHX_ OP *cond)
     ||  cond->op_type == OP_RV2HV
     ||  cond->op_type == OP_PADHV))
 
-       return newUNOP(OP_REFGEN,
-           0, mod(cond, OP_REFGEN));
+       return newUNOP(OP_REFGEN, 0, op_lvalue(cond, OP_REFGEN));
 
     else if(cond
     && (cond->op_type == OP_ASLICE
@@ -5644,7 +5722,7 @@ S_ref_array_or_hash(pTHX_ OP *cond)
        cond->op_flags |= ~(OPf_WANT_SCALAR | OPf_REF);
        cond->op_flags |= OPf_WANT_LIST;
 
-       return newANONLIST(mod(cond, OP_ANONLIST));
+       return newANONLIST(op_lvalue(cond, OP_ANONLIST));
     }
 
     else
@@ -5846,74 +5924,6 @@ Perl_newWHENOP(pTHX_ OP *cond, OP *block)
        OP_ENTERWHEN, OP_LEAVEWHEN, 0);
 }
 
-/*
-=head1 Embedding Functions
-
-=for apidoc cv_undef
-
-Clear out all the active components of a CV. This can happen either
-by an explicit C<undef &foo>, or by the reference count going to zero.
-In the former case, we keep the CvOUTSIDE pointer, so that any anonymous
-children can still follow the full lexical scope chain.
-
-=cut
-*/
-
-void
-Perl_cv_undef(pTHX_ CV *cv)
-{
-    dVAR;
-
-    PERL_ARGS_ASSERT_CV_UNDEF;
-
-    DEBUG_X(PerlIO_printf(Perl_debug_log,
-         "CV undef: cv=0x%"UVxf" comppad=0x%"UVxf"\n",
-           PTR2UV(cv), PTR2UV(PL_comppad))
-    );
-
-#ifdef USE_ITHREADS
-    if (CvFILE(cv) && !CvISXSUB(cv)) {
-       /* for XSUBs CvFILE point directly to static memory; __FILE__ */
-       Safefree(CvFILE(cv));
-    }
-    CvFILE(cv) = NULL;
-#endif
-
-    if (!CvISXSUB(cv) && CvROOT(cv)) {
-       if (SvTYPE(cv) == SVt_PVCV && CvDEPTH(cv))
-           Perl_croak(aTHX_ "Can't undef active subroutine");
-       ENTER;
-
-       PAD_SAVE_SETNULLPAD();
-
-       op_free(CvROOT(cv));
-       CvROOT(cv) = NULL;
-       CvSTART(cv) = NULL;
-       LEAVE;
-    }
-    SvPOK_off(MUTABLE_SV(cv));         /* forget prototype */
-    CvGV_set(cv, NULL);
-
-    pad_undef(cv);
-
-    /* remove CvOUTSIDE unless this is an undef rather than a free */
-    if (!SvREFCNT(cv) && CvOUTSIDE(cv)) {
-       if (!CvWEAKOUTSIDE(cv))
-           SvREFCNT_dec(CvOUTSIDE(cv));
-       CvOUTSIDE(cv) = NULL;
-    }
-    if (CvCONST(cv)) {
-       SvREFCNT_dec(MUTABLE_SV(CvXSUBANY(cv).any_ptr));
-       CvCONST_off(cv);
-    }
-    if (CvISXSUB(cv) && CvXSUB(cv)) {
-       CvXSUB(cv) = NULL;
-    }
-    /* delete all flags except WEAKOUTSIDE and CVGV_RC, which indicate the
-     * ref status of CvOUTSIDE and CvGV */
-    CvFLAGS(cv) &= (CVf_WEAKOUTSIDE|CVf_CVGV_RC);
-}
-
 void
 Perl_cv_ckproto_len(pTHX_ const CV *cv, const GV *gv, const char *p,
                    const STRLEN len)
@@ -5990,7 +6000,7 @@ Perl_cv_const_sv(pTHX_ const CV *const cv)
  * cv && CvCONST(cv)
  *
  *     We have just cloned an anon prototype that was marked as a const
- *     candidiate. Try to grab the current value, and in the case of
+ *     candidate. Try to grab the current value, and in the case of
  *     PADSV, ignore it if it has multiple references. Return the value.
  */
 
@@ -6015,7 +6025,9 @@ Perl_op_const_sv(pTHX_ const OP *o, CV *cv)
        if (sv && o->op_next == o)
            return sv;
        if (o->op_next != o) {
-           if (type == OP_NEXTSTATE || type == OP_NULL || type == OP_PUSHMARK)
+           if (type == OP_NEXTSTATE
+            || (type == OP_NULL && !(o->op_flags & OPf_KIDS))
+            || type == OP_PUSHMARK)
                continue;
            if (type == OP_DBSTATE)
                continue;
@@ -6083,12 +6095,6 @@ Perl_newMYSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block)
 }
 
 CV *
-Perl_newSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *block)
-{
-    return Perl_newATTRSUB(aTHX_ floor, o, proto, NULL, block);
-}
-
-CV *
 Perl_newATTRSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block)
 {
     dVAR;
@@ -6240,7 +6246,7 @@ Perl_newATTRSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block)
            CvISXSUB_on(cv);
        }
        else {
-           GvCV(gv) = NULL;
+           GvCV_set(gv, NULL);
            cv = newCONSTSUB(NULL, name, const_sv);
        }
         mro_method_changed_in( /* sub Foo::Bar () { 123 } */
@@ -6265,21 +6271,34 @@ Perl_newATTRSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block)
 #endif
        ) {
            cv_flags_t existing_builtin_attrs = CvFLAGS(cv) & CVf_BUILTIN_ATTRS;
-           cv_undef(cv);
+           AV *const temp_av = CvPADLIST(cv);
+           CV *const temp_cv = CvOUTSIDE(cv);
+
+           assert(!CvWEAKOUTSIDE(cv));
+           assert(!CvCVGV_RC(cv));
+           assert(CvGV(cv) == gv);
+
+           SvPOK_off(cv);
            CvFLAGS(cv) = CvFLAGS(PL_compcv) | existing_builtin_attrs;
-           if (!CvWEAKOUTSIDE(cv))
-               SvREFCNT_dec(CvOUTSIDE(cv));
            CvOUTSIDE(cv) = CvOUTSIDE(PL_compcv);
            CvOUTSIDE_SEQ(cv) = CvOUTSIDE_SEQ(PL_compcv);
-           CvOUTSIDE(PL_compcv) = 0;
            CvPADLIST(cv) = CvPADLIST(PL_compcv);
-           CvPADLIST(PL_compcv) = 0;
+           CvOUTSIDE(PL_compcv) = temp_cv;
+           CvPADLIST(PL_compcv) = temp_av;
+
+#ifdef USE_ITHREADS
+           if (CvFILE(cv) && !CvISXSUB(cv)) {
+               /* for XSUBs CvFILE point directly to static memory; __FILE__ */
+               Safefree(CvFILE(cv));
+    }
+#endif
+           CvFILE_set_from_cop(cv, PL_curcop);
+           CvSTASH_set(cv, PL_curstash);
+
            /* inner references to PL_compcv must be fixed up ... */
            pad_fixup_inner_anons(CvPADLIST(cv), PL_compcv, cv);
            if (PERLDB_INTER)/* Advice debugger on the new sub. */
              ++PL_sub_generation;
-           if (CvSTASH(cv))
-               sv_del_backref(MUTABLE_SV(CvSTASH(cv)), MUTABLE_SV(cv));
        }
        else {
            /* Might have had built-in attributes applied -- propagate them. */
@@ -6292,7 +6311,7 @@ Perl_newATTRSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block)
     else {
        cv = PL_compcv;
        if (name) {
-           GvCV(gv) = cv;
+           GvCV_set(gv, cv);
            if (PL_madskills) {
                if (strEQ(name, "import")) {
                    PL_formfeed = MUTABLE_SV(cv);
@@ -6307,9 +6326,7 @@ Perl_newATTRSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block)
     if (!CvGV(cv)) {
        CvGV_set(cv, gv);
        CvFILE_set_from_cop(cv, PL_curcop);
-       CvSTASH(cv) = PL_curstash;
-       if (PL_curstash)
-           Perl_sv_add_backref(aTHX_ MUTABLE_SV(PL_curstash), MUTABLE_SV(cv));
+       CvSTASH_set(cv, PL_curstash);
     }
     if (attrs) {
        /* Need to do a C<use attributes $stash_of_cv,\&cv,@attrs>. */
@@ -6351,7 +6368,7 @@ Perl_newATTRSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block)
     PL_breakable_sub_gen++;
     if (CvLVALUE(cv)) {
        CvROOT(cv) = newUNOP(OP_LEAVESUBLV, 0,
-                            mod(scalarseq(block), OP_LEAVESUBLV));
+                            op_lvalue(scalarseq(block), OP_LEAVESUBLV));
        block->op_attached = 1;
     }
     else {
@@ -6440,7 +6457,7 @@ S_process_special_blocks(pTHX_ const char *const fullname, GV *const gv,
 
            DEBUG_x( dump_sub(gv) );
            Perl_av_create_and_push(aTHX_ &PL_beginav, MUTABLE_SV(cv));
-           GvCV(gv) = 0;               /* cv has been hijacked */
+           GvCV_set(gv,0);             /* cv has been hijacked */
            call_list(oldscope, PL_beginav);
 
            PL_curcop = &PL_compiling;
@@ -6484,7 +6501,7 @@ S_process_special_blocks(pTHX_ const char *const fullname, GV *const gv,
        } else
            return;
        DEBUG_x( dump_sub(gv) );
-       GvCV(gv) = 0;           /* cv has been hijacked */
+       GvCV_set(gv,0);         /* cv has been hijacked */
     }
 }
 
@@ -6662,7 +6679,7 @@ Perl_newXS(pTHX_ const char *name, XSUBADDR_t subaddr, const char *filename)
     else {
        cv = MUTABLE_CV(newSV_type(SVt_PVCV));
        if (name) {
-           GvCV(gv) = cv;
+           GvCV_set(gv,cv);
            GvCVGEN(gv) = 0;
             mro_method_changed_in(GvSTASH(gv)); /* newXS */
        }
@@ -7282,6 +7299,8 @@ Perl_ck_rvconst(pTHX_ register OP *o)
 #endif
            kid->op_private = 0;
            kid->op_ppaddr = PL_ppaddr[OP_GV];
+           /* FAKE globs in the symbol table cause weird bugs (#77810) */
+           SvFAKE_off(gv);
        }
     }
     return o;
@@ -7417,7 +7436,7 @@ Perl_ck_fun(pTHX_ OP *o)
                }
                else if (kid->op_type != OP_RV2AV && kid->op_type != OP_PADAV)
                    bad_type(numargs, "array", PL_op_desc[type], kid);
-               mod(kid, type);
+               op_lvalue(kid, type);
                break;
            case OA_HVREF:
                if (kid->op_type == OP_CONST &&
@@ -7439,7 +7458,7 @@ Perl_ck_fun(pTHX_ OP *o)
                }
                else if (kid->op_type != OP_RV2HV && kid->op_type != OP_PADHV)
                    bad_type(numargs, "hash", PL_op_desc[type], kid);
-               mod(kid, type);
+               op_lvalue(kid, type);
                break;
            case OA_CVREF:
                {
@@ -7546,7 +7565,7 @@ Perl_ck_fun(pTHX_ OP *o)
                                      name = "__ANONIO__";
                                      len = 10;
                                 }
-                                mod(kid, type);
+                                op_lvalue(kid, type);
                            }
                            if (name) {
                                SV *namesv;
@@ -7569,7 +7588,7 @@ Perl_ck_fun(pTHX_ OP *o)
                scalar(kid);
                break;
            case OA_SCALARREF:
-               mod(scalar(kid), type);
+               op_lvalue(scalar(kid), type);
                break;
            }
            oa >>= 4;
@@ -7619,7 +7638,7 @@ Perl_ck_glob(pTHX_ OP *o)
 
     o = ck_fun(o);
     if ((o->op_flags & OPf_KIDS) && !cLISTOPo->op_first->op_sibling)
-       op_append_elem(OP_GLOB, o, newDEFSVOP());
+       op_append_elem(OP_GLOB, o, newDEFSVOP()); /* glob() => glob($_) */
 
     if (!((gv = gv_fetchpvs("glob", GV_NOTQUAL, SVt_PVCV))
          && GvCVu(gv) && GvIMPORTED_CV(gv)))
@@ -7636,7 +7655,7 @@ Perl_ck_glob(pTHX_ OP *o)
                newSVpvs("File::Glob"), NULL, NULL, NULL);
        if((glob_gv = gv_fetchpvs("File::Glob::csh_glob", 0, SVt_PVCV))) {
            gv = gv_fetchpvs("CORE::GLOBAL::glob", 0, SVt_PVCV);
-           GvCV(gv) = GvCV(glob_gv);
+           GvCV_set(gv, GvCV(glob_gv));
            SvREFCNT_inc_void(MUTABLE_SV(GvCV(gv)));
            GvIMPORTED_CV_on(gv);
        }
@@ -7644,20 +7663,31 @@ Perl_ck_glob(pTHX_ OP *o)
     }
 #endif /* PERL_EXTERNAL_GLOB */
 
+    assert(!(o->op_flags & OPf_SPECIAL));
     if (gv && GvCVu(gv) && GvIMPORTED_CV(gv)) {
+       /* convert
+        *     glob
+        *       \ null - const(wildcard)
+        * into
+        *     null
+        *       \ enter
+        *            \ list
+        *                 \ mark - glob - rv2cv
+        *                             |        \ gv(CORE::GLOBAL::glob)
+        *                             |
+        *                              \ null - const(wildcard) - const(ix)
+        */
+       o->op_flags |= OPf_SPECIAL;
+       o->op_targ = pad_alloc(OP_GLOB, SVs_PADTMP);
        op_append_elem(OP_GLOB, o,
                    newSVOP(OP_CONST, 0, newSViv(PL_glob_index++)));
-       o->op_type = OP_LIST;
-       o->op_ppaddr = PL_ppaddr[OP_LIST];
-       cLISTOPo->op_first->op_type = OP_PUSHMARK;
-       cLISTOPo->op_first->op_ppaddr = PL_ppaddr[OP_PUSHMARK];
-       cLISTOPo->op_first->op_targ = 0;
+       o = newLISTOP(OP_LIST, 0, o, NULL);
        o = newUNOP(OP_ENTERSUB, OPf_STACKED,
                    op_append_elem(OP_LIST, o,
                                scalar(newUNOP(OP_RV2CV, 0,
                                               newGVOP(OP_GV, 0, gv)))));
        o = newUNOP(OP_NULL, 0, ck_subr(o));
-       o->op_targ = OP_GLOB;           /* hint at what it used to be */
+       o->op_targ = OP_GLOB; /* hint at what it used to be: eg in newWHILEOP */
        return o;
     }
     gv = newGVgen("main");
@@ -7729,7 +7759,7 @@ Perl_ck_grep(pTHX_ OP *o)
     if (!kid || !kid->op_sibling)
        return too_few_arguments(o,OP_DESC(o));
     for (kid = kid->op_sibling; kid; kid = kid->op_sibling)
-       mod(kid, OP_GREPSTART);
+       op_lvalue(kid, OP_GREPSTART);
 
     return (OP*)gwop;
 }
@@ -7919,7 +7949,13 @@ Perl_ck_sassign(pTHX_ OP *o)
     }
     if (kid->op_sibling) {
        OP *kkid = kid->op_sibling;
-       if (kkid->op_type == OP_PADSV
+       /* For state variable assignment, kkid is a list op whose op_last
+          is a padsv. */
+       if ((kkid->op_type == OP_PADSV ||
+            (kkid->op_type == OP_LIST &&
+             (kkid = cLISTOPx(kkid)->op_last)->op_type == OP_PADSV
+            )
+           )
                && (kkid->op_private & OPpLVAL_INTRO)
                && SvPAD_STATE(*av_fetch(PL_comppad_name, kkid->op_targ, FALSE))) {
            const PADOFFSET target = kkid->op_targ;
@@ -7938,7 +7974,7 @@ Perl_ck_sassign(pTHX_ OP *o)
            other->op_targ = target;
 
            /* Because we change the type of the op here, we will skip the
-              assinment binop->op_last = binop->op_first->op_sibling; at the
+              assignment binop->op_last = binop->op_first->op_sibling; at the
               end of Perl_newBINOP(). So need to do it here. */
            cBINOPo->op_last = cBINOPo->op_first->op_sibling;
 
@@ -8169,7 +8205,7 @@ Perl_ck_return(pTHX_ OP *o)
     kid = cLISTOPo->op_first->op_sibling;
     if (CvLVALUE(PL_compcv)) {
        for (; kid; kid = kid->op_sibling)
-           mod(kid, OP_LEAVESUBLV);
+           op_lvalue(kid, OP_LEAVESUBLV);
     } else {
        for (; kid; kid = kid->op_sibling)
            if ((kid->op_type == OP_NULL)
@@ -8241,7 +8277,7 @@ Perl_ck_shift(pTHX_ OP *o)
        return newUNOP(type, 0, scalar(argop));
 #endif
     }
-    return scalar(modkids(ck_fun(o), type));
+    return scalar(modkids(ck_push(o), type));
 }
 
 OP *
@@ -8591,7 +8627,7 @@ Perl_ck_entersub_args_list(pTHX_ OP *entersubop)
     for (aop = aop->op_sibling; aop->op_sibling; aop = aop->op_sibling) {
        if (!(PL_madskills && aop->op_type == OP_STUB)) {
            list(aop);
-           mod(aop, OP_ENTERSUB);
+           op_lvalue(aop, OP_ENTERSUB);
        }
     }
     return entersubop;
@@ -8727,6 +8763,18 @@ Perl_ck_entersub_args_proto(pTHX_ OP *entersubop, GV *namegv, SV *protosv)
                }
                scalar(aop);
                break;
+           case '+':
+               proto++;
+               arg++;
+               if (o3->op_type == OP_RV2AV ||
+                   o3->op_type == OP_PADAV ||
+                   o3->op_type == OP_RV2HV ||
+                   o3->op_type == OP_PADHV
+               ) {
+                   goto wrapref;
+               }
+               scalar(aop);
+               break;
            case '[': case ']':
                goto oops;
                break;
@@ -8821,7 +8869,7 @@ Perl_ck_entersub_args_proto(pTHX_ OP *entersubop, GV *namegv, SV *protosv)
                        gv_ename(namegv), SVfARG(protosv));
        }
 
-       mod(aop, OP_ENTERSUB);
+       op_lvalue(aop, OP_ENTERSUB);
        prev = aop;
        aop = aop->op_sibling;
     }
@@ -9095,30 +9143,81 @@ Perl_ck_substr(pTHX_ OP *o)
 }
 
 OP *
-Perl_ck_each(pTHX_ OP *o)
+Perl_ck_push(pTHX_ OP *o)
 {
     dVAR;
     OP *kid = o->op_flags & OPf_KIDS ? cLISTOPo->op_first : NULL;
+    OP *cursor = NULL;
+    OP *proxy = NULL;
 
-    PERL_ARGS_ASSERT_CK_EACH;
+    PERL_ARGS_ASSERT_CK_PUSH;
 
+    /* If 1st kid is pushmark (e.g. push, unshift, splice), we need 2nd kid */
     if (kid) {
-       if (kid->op_type == OP_PADAV || kid->op_type == OP_RV2AV) {
-           const unsigned new_type = o->op_type == OP_EACH ? OP_AEACH
-               : o->op_type == OP_KEYS ? OP_AKEYS : OP_AVALUES;
-           o->op_type = new_type;
-           o->op_ppaddr = PL_ppaddr[new_type];
-       }
-       else if (!(kid->op_type == OP_PADHV || kid->op_type == OP_RV2HV
-                   || (kid->op_type == OP_CONST && kid->op_private & OPpCONST_BARE)
-                 )) {
-           bad_type(1, "hash or array", PL_op_desc[o->op_type], kid);
-           return o;
+       cursor = kid->op_type == OP_PUSHMARK ? kid->op_sibling : kid;
+    }
+
+    /* If not array or array deref, wrap it with an array deref.
+     * For OP_CONST, we only wrap arrayrefs */
+    if (cursor) {
+       if ( (    cursor->op_type != OP_PADAV
+              && cursor->op_type != OP_RV2AV
+              && cursor->op_type != OP_CONST
+            )
+            ||
+            (    cursor->op_type == OP_CONST
+              && SvROK(cSVOPx_sv(cursor))
+              && SvTYPE(SvRV(cSVOPx_sv(cursor))) == SVt_PVAV
+            )
+       ) {
+           proxy = newAVREF(cursor);
+           if ( cursor == kid ) {
+               cLISTOPx(o)->op_first = proxy;
+           }
+           else {
+               cLISTOPx(kid)->op_sibling = proxy;
+           }
+           cLISTOPx(proxy)->op_sibling = cLISTOPx(cursor)->op_sibling;
+           cLISTOPx(cursor)->op_sibling = NULL;
        }
     }
     return ck_fun(o);
 }
 
+OP *
+Perl_ck_each(pTHX_ OP *o)
+{
+    dVAR;
+    OP *kid = o->op_flags & OPf_KIDS ? cUNOPo->op_first : NULL;
+    const unsigned orig_type  = o->op_type;
+    const unsigned array_type = orig_type == OP_EACH ? OP_AEACH
+                             : orig_type == OP_KEYS ? OP_AKEYS : OP_AVALUES;
+    const unsigned ref_type   = orig_type == OP_EACH ? OP_REACH
+                             : orig_type == OP_KEYS ? OP_RKEYS : OP_RVALUES;
+
+    PERL_ARGS_ASSERT_CK_EACH;
+
+    if (kid) {
+       switch (kid->op_type) {
+           case OP_PADHV:
+           case OP_RV2HV:
+               break;
+           case OP_PADAV:
+           case OP_RV2AV:
+               CHANGE_TYPE(o, array_type);
+               break;
+           case OP_CONST:
+               if (kid->op_private == OPpCONST_BARE)
+                   /* we let ck_fun treat as hash */
+                   break;
+           default:
+               CHANGE_TYPE(o, ref_type);
+       }
+    }
+    /* if treating as a reference, defer additional checks to runtime */
+    return o->op_type == ref_type ? o : ck_fun(o);
+}
+
 /* caller is supposed to assign the return to the 
    container of the rep_op var */
 STATIC OP *
@@ -9251,7 +9350,7 @@ Perl_rpeep(pTHX_ register OP *o)
            /* Two NEXTSTATEs in a row serve no purpose. Except if they happen
               to carry two labels. For now, take the easier option, and skip
               this optimisation if the first NEXTSTATE has a label.  */
-           if (!CopLABEL((COP*)o)) {
+           if (!CopLABEL((COP*)o) && !PERLDB_NOOPT) {
                OP *nextop = o->op_next;
                while (nextop && nextop->op_type == OP_NULL)
                    nextop = nextop->op_next;
@@ -9583,7 +9682,8 @@ Perl_rpeep(pTHX_ register OP *o)
 
            /* Make the CONST have a shared SV */
            svp = cSVOPx_svp(((BINOP*)o)->op_last);
-           if (!SvFAKE(sv = *svp) || !SvREADONLY(sv)) {
+           if ((!SvFAKE(sv = *svp) || !SvREADONLY(sv))
+            && SvTYPE(sv) < SVt_PVMG && !SvROK(sv)) {
                key = SvPV_const(sv, keylen);
                lexname = newSVpvn_share(key,
                                         SvUTF8(sv) ? -(I32)keylen : (I32)keylen,
@@ -9918,6 +10018,15 @@ Perl_rpeep(pTHX_ register OP *o)
                assert (!cPMOP->op_pmstashstartu.op_pmreplstart);
            }
            break;
+
+       case OP_CUSTOM: {
+           Perl_cpeep_t cpeep = 
+               XopENTRY(Perl_custom_op_xop(aTHX_ o), xop_peep);
+           if (cpeep)
+               cpeep(aTHX_ o, oldop);
+           break;
+       }
+           
        }
        oldop = o;
     }
@@ -9930,48 +10039,88 @@ Perl_peep(pTHX_ register OP *o)
     CALL_RPEEP(o);
 }
 
-const char*
-Perl_custom_op_name(pTHX_ const OP* o)
-{
-    dVAR;
-    const IV index = PTR2IV(o->op_ppaddr);
-    SV* keysv;
-    HE* he;
+/*
+=head1 Custom Operators
 
-    PERL_ARGS_ASSERT_CUSTOM_OP_NAME;
+=for apidoc Ao||custom_op_xop
+Return the XOP structure for a given custom op. This function should be
+considered internal to OP_NAME and the other access macros: use them instead.
 
-    if (!PL_custom_op_names) /* This probably shouldn't happen */
-        return (char *)PL_op_name[OP_CUSTOM];
+=cut
+*/
 
-    keysv = sv_2mortal(newSViv(index));
+const XOP *
+Perl_custom_op_xop(pTHX_ const OP *o)
+{
+    SV *keysv;
+    HE *he = NULL;
+    XOP *xop;
+
+    static const XOP xop_null = { 0, 0, 0, 0, 0 };
+
+    PERL_ARGS_ASSERT_CUSTOM_OP_XOP;
+    assert(o->op_type == OP_CUSTOM);
+
+    /* This is wrong. It assumes a function pointer can be cast to IV,
+     * which isn't guaranteed, but this is what the old custom OP code
+     * did. In principle it should be safer to Copy the bytes of the
+     * pointer into a PV: since the new interface is hidden behind
+     * functions, this can be changed later if necessary.  */
+    /* Change custom_op_xop if this ever happens */
+    keysv = sv_2mortal(newSViv(PTR2IV(o->op_ppaddr)));
+
+    if (PL_custom_ops)
+       he = hv_fetch_ent(PL_custom_ops, keysv, 0, 0);
+
+    /* assume noone will have just registered a desc */
+    if (!he && PL_custom_op_names &&
+       (he = hv_fetch_ent(PL_custom_op_names, keysv, 0, 0))
+    ) {
+       const char *pv;
+       STRLEN l;
+
+       /* XXX does all this need to be shared mem? */
+       Newxz(xop, 1, XOP);
+       pv = SvPV(HeVAL(he), l);
+       XopENTRY_set(xop, xop_name, savepvn(pv, l));
+       if (PL_custom_op_descs &&
+           (he = hv_fetch_ent(PL_custom_op_descs, keysv, 0, 0))
+       ) {
+           pv = SvPV(HeVAL(he), l);
+           XopENTRY_set(xop, xop_desc, savepvn(pv, l));
+       }
+       Perl_custom_op_register(aTHX_ o->op_ppaddr, xop);
+       return xop;
+    }
 
-    he = hv_fetch_ent(PL_custom_op_names, keysv, 0, 0);
-    if (!he)
-        return (char *)PL_op_name[OP_CUSTOM]; /* Don't know who you are */
+    if (!he) return &xop_null;
 
-    return SvPV_nolen(HeVAL(he));
+    xop = INT2PTR(XOP *, SvIV(HeVAL(he)));
+    return xop;
 }
 
-const char*
-Perl_custom_op_desc(pTHX_ const OP* o)
-{
-    dVAR;
-    const IV index = PTR2IV(o->op_ppaddr);
-    SV* keysv;
-    HE* he;
+/*
+=for apidoc Ao||custom_op_register
+Register a custom op. See L<perlguts/"Custom Operators">.
+
+=cut
+*/
 
-    PERL_ARGS_ASSERT_CUSTOM_OP_DESC;
+void
+Perl_custom_op_register(pTHX_ Perl_ppaddr_t ppaddr, const XOP *xop)
+{
+    SV *keysv;
 
-    if (!PL_custom_op_descs)
-        return (char *)PL_op_desc[OP_CUSTOM];
+    PERL_ARGS_ASSERT_CUSTOM_OP_REGISTER;
 
-    keysv = sv_2mortal(newSViv(index));
+    /* see the comment in custom_op_xop */
+    keysv = sv_2mortal(newSViv(PTR2IV(ppaddr)));
 
-    he = hv_fetch_ent(PL_custom_op_descs, keysv, 0, 0);
-    if (!he)
-        return (char *)PL_op_desc[OP_CUSTOM];
+    if (!PL_custom_ops)
+       PL_custom_ops = newHV();
 
-    return SvPV_nolen(HeVAL(he));
+    if (!hv_store_ent(PL_custom_ops, keysv, newSViv(PTR2IV(xop)), 0))
+       Perl_croak(aTHX_ "panic: can't register custom OP %s", xop->xop_name);
 }
 
 #include "XSUB.h"