This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Optimisation for 'my $x; my $y;' -> 'my ($x, $y)'.
[perl5.git] / op.c
diff --git a/op.c b/op.c
index a641647..60aef44 100644 (file)
--- a/op.c
+++ b/op.c
@@ -672,6 +672,14 @@ S_op_destroy(pTHX_ OP *o)
 
 /* Destructor */
 
+/*
+=for apidoc Am|void|op_free|OP *o
+
+Free an op. Only use this when an op is no longer linked to from any optree.
+
+=cut
+*/
+
 void
 Perl_op_free(pTHX_ OP *o)
 {
@@ -986,6 +994,15 @@ S_find_and_forget_pmops(pTHX_ OP *o)
     }
 }
 
+/*
+=for apidoc Am|void|op_null|OP *o
+
+Neutralizes an op when it is no longer needed, but is still linked to from
+other ops.
+
+=cut
+*/
+
 void
 Perl_op_null(pTHX_ OP *o)
 {
@@ -1050,7 +1067,7 @@ Perl_op_contextualize(pTHX_ OP *o, I32 context)
 =head1 Optree Manipulation Functions
 
 =for apidoc Am|OP*|op_linklist|OP *o
-This function is the implementation of the L</LINKLIST> macro. It should
+This function is the implementation of the L</LINKLIST> macro.  It should
 not be called directly.
 
 =cut
@@ -1123,6 +1140,116 @@ S_scalarboolean(pTHX_ OP *o)
     return scalar(o);
 }
 
+static SV *
+S_op_varname(pTHX_ const OP *o)
+{
+    assert(o);
+    assert(o->op_type == OP_PADAV || o->op_type == OP_RV2AV ||
+          o->op_type == OP_PADHV || o->op_type == OP_RV2HV);
+    {
+       const char funny  = o->op_type == OP_PADAV
+                        || o->op_type == OP_RV2AV ? '@' : '%';
+       if (o->op_type == OP_RV2AV || o->op_type == OP_RV2HV) {
+           GV *gv;
+           if (cUNOPo->op_first->op_type != OP_GV
+            || !(gv = cGVOPx_gv(cUNOPo->op_first)))
+               return NULL;
+           return varname(gv, funny, 0, NULL, 0, 1);
+       }
+       return
+           varname(MUTABLE_GV(PL_compcv), funny, o->op_targ, NULL, 0, 1);
+    }
+}
+
+static void
+S_op_pretty(pTHX_ const OP *o, SV **retsv, const char **retpv)
+{ /* or not so pretty :-) */
+    if (o->op_type == OP_CONST) {
+       *retsv = cSVOPo_sv;
+       if (SvPOK(*retsv)) {
+           SV *sv = *retsv;
+           *retsv = sv_newmortal();
+           pv_pretty(*retsv, SvPVX_const(sv), SvCUR(sv), 32, NULL, NULL,
+                     PERL_PV_PRETTY_DUMP |PERL_PV_ESCAPE_UNI_DETECT);
+       }
+       else if (!SvOK(*retsv))
+           *retpv = "undef";
+    }
+    else *retpv = "...";
+}
+
+static void
+S_scalar_slice_warning(pTHX_ const OP *o)
+{
+    OP *kid;
+    const char lbrack =
+       o->op_type == OP_HSLICE ? '{' : '[';
+    const char rbrack =
+       o->op_type == OP_HSLICE ? '}' : ']';
+    SV *name;
+    SV *keysv = NULL; /* just to silence compiler warnings */
+    const char *key = NULL;
+
+    if (!(o->op_private & OPpSLICEWARNING))
+       return;
+    if (PL_parser && PL_parser->error_count)
+       /* This warning can be nonsensical when there is a syntax error. */
+       return;
+
+    kid = cLISTOPo->op_first;
+    kid = kid->op_sibling; /* get past pushmark */
+    /* weed out false positives: any ops that can return lists */
+    switch (kid->op_type) {
+    case OP_BACKTICK:
+    case OP_GLOB:
+    case OP_READLINE:
+    case OP_MATCH:
+    case OP_RV2AV:
+    case OP_EACH:
+    case OP_VALUES:
+    case OP_KEYS:
+    case OP_SPLIT:
+    case OP_LIST:
+    case OP_SORT:
+    case OP_REVERSE:
+    case OP_ENTERSUB:
+    case OP_CALLER:
+    case OP_LSTAT:
+    case OP_STAT:
+    case OP_READDIR:
+    case OP_SYSTEM:
+    case OP_TMS:
+    case OP_LOCALTIME:
+    case OP_GMTIME:
+    case OP_ENTEREVAL:
+    case OP_REACH:
+    case OP_RKEYS:
+    case OP_RVALUES:
+       return;
+    }
+    assert(kid->op_sibling);
+    name = S_op_varname(aTHX_ kid->op_sibling);
+    if (!name) /* XS module fiddling with the op tree */
+       return;
+    S_op_pretty(aTHX_ kid, &keysv, &key);
+    assert(SvPOK(name));
+    sv_chop(name,SvPVX(name)+1);
+    if (key)
+       /* diag_listed_as: Scalar value @%s[%s] better written as $%s[%s] */
+       Perl_warner(aTHX_ packWARN(WARN_SYNTAX),
+                  "Scalar value @%"SVf"%c%s%c better written as $%"SVf
+                  "%c%s%c",
+                   SVfARG(name), lbrack, key, rbrack, SVfARG(name),
+                   lbrack, key, rbrack);
+    else
+       /* diag_listed_as: Scalar value @%s[%s] better written as $%s[%s] */
+       Perl_warner(aTHX_ packWARN(WARN_SYNTAX),
+                  "Scalar value @%"SVf"%c%"SVf"%c better written as $%"
+                   SVf"%c%"SVf"%c",
+                   SVfARG(name), lbrack, keysv, rbrack,
+                   SVfARG(name), lbrack, keysv, rbrack);
+}
+
 OP *
 Perl_scalar(pTHX_ OP *o)
 {
@@ -1185,6 +1312,46 @@ Perl_scalar(pTHX_ OP *o)
     case OP_SORT:
        Perl_ck_warner(aTHX_ packWARN(WARN_VOID), "Useless use of sort in scalar context");
        break;
+    case OP_KVHSLICE:
+    case OP_KVASLICE:
+    {
+       /* Warn about scalar context */
+       const char lbrack = o->op_type == OP_KVHSLICE ? '{' : '[';
+       const char rbrack = o->op_type == OP_KVHSLICE ? '}' : ']';
+       SV *name;
+       SV *keysv;
+       const char *key = NULL;
+
+       /* This warning can be nonsensical when there is a syntax error. */
+       if (PL_parser && PL_parser->error_count)
+           break;
+
+       if (!ckWARN(WARN_SYNTAX)) break;
+
+       kid = cLISTOPo->op_first;
+       kid = kid->op_sibling; /* get past pushmark */
+       assert(kid->op_sibling);
+       name = S_op_varname(aTHX_ kid->op_sibling);
+       if (!name) /* XS module fiddling with the op tree */
+           break;
+       S_op_pretty(aTHX_ kid, &keysv, &key);
+       assert(SvPOK(name));
+       sv_chop(name,SvPVX(name)+1);
+       if (key)
+  /* diag_listed_as: %%s[%s] in scalar context better written as $%s[%s] */
+           Perl_warner(aTHX_ packWARN(WARN_SYNTAX),
+                      "%%%"SVf"%c%s%c in scalar context better written "
+                      "as $%"SVf"%c%s%c",
+                       SVfARG(name), lbrack, key, rbrack, SVfARG(name),
+                       lbrack, key, rbrack);
+       else
+  /* diag_listed_as: %%s[%s] in scalar context better written as $%s[%s] */
+           Perl_warner(aTHX_ packWARN(WARN_SYNTAX),
+                      "%%%"SVf"%c%"SVf"%c in scalar context better "
+                      "written as $%"SVf"%c%"SVf"%c",
+                       SVfARG(name), lbrack, keysv, rbrack,
+                       SVfARG(name), lbrack, keysv, rbrack);
+    }
     }
     return o;
 }
@@ -1276,8 +1443,10 @@ Perl_scalarvoid(pTHX_ OP *o)
     case OP_AELEMFAST:
     case OP_AELEMFAST_LEX:
     case OP_ASLICE:
+    case OP_KVASLICE:
     case OP_HELEM:
     case OP_HSLICE:
+    case OP_KVHSLICE:
     case OP_UNPACK:
     case OP_PACK:
     case OP_JOIN:
@@ -1660,8 +1829,8 @@ S_modkids(pTHX_ OP *o, I32 type)
 /*
 =for apidoc finalize_optree
 
-This function finalizes the optree. Should be called directly after
-the complete optree is built. It does some additional
+This function finalizes the optree.  Should be called directly after
+the complete optree is built.  It does some additional
 checking which can't be done in the normal ck_xxx functions and makes
 the tree thread-safe.
 
@@ -1754,24 +1923,10 @@ S_finalize_op(pTHX_ OP* o)
         * for reference counts, sv_upgrade() etc. */
        if (cSVOPo->op_sv) {
            const PADOFFSET ix = pad_alloc(OP_CONST, SVf_READONLY);
-           if (o->op_type != OP_METHOD_NAMED
-               && cSVOPo->op_sv == &PL_sv_undef) {
-               /* PL_sv_undef is hack - it's unsafe to store it in the
-                  AV that is the pad, because av_fetch treats values of
-                  PL_sv_undef as a "free" AV entry and will merrily
-                  replace them with a new SV, causing pad_alloc to think
-                  that this pad slot is free. (When, clearly, it is not)
-               */
-               SvOK_off(PAD_SVl(ix));
-               SvPADTMP_on(PAD_SVl(ix));
-               SvREADONLY_on(PAD_SVl(ix));
-           }
-           else {
-               SvREFCNT_dec(PAD_SVl(ix));
-               PAD_SETSV(ix, cSVOPo->op_sv);
-               /* XXX I don't know how this isn't readonly already. */
-               if (!SvIsCOW(PAD_SVl(ix))) SvREADONLY_on(PAD_SVl(ix));
-           }
+           SvREFCNT_dec(PAD_SVl(ix));
+           PAD_SETSV(ix, cSVOPo->op_sv);
+           /* XXX I don't know how this isn't readonly already. */
+           if (!SvIsCOW(PAD_SVl(ix))) SvREADONLY_on(PAD_SVl(ix));
            cSVOPo->op_sv = NULL;
            o->op_targ = ix;
        }
@@ -1782,66 +1937,36 @@ S_finalize_op(pTHX_ OP* o)
        UNOP *rop;
        SV *lexname;
        GV **fields;
-       SV **svp, *sv;
-       const char *key = NULL;
-       STRLEN keylen;
+       SVOP *key_op;
+       OP *kid;
+       bool check_fields;
 
-       if (((BINOP*)o)->op_last->op_type != OP_CONST)
+       if ((key_op = cSVOPx(((BINOP*)o)->op_last))->op_type != OP_CONST)
            break;
 
-       /* Make the CONST have a shared SV */
-       svp = cSVOPx_svp(((BINOP*)o)->op_last);
-       if ((!SvIsCOW_shared_hash(sv = *svp))
-           && SvTYPE(sv) < SVt_PVMG && SvOK(sv) && !SvROK(sv)) {
-           key = SvPV_const(sv, keylen);
-           lexname = newSVpvn_share(key,
-               SvUTF8(sv) ? -(I32)keylen : (I32)keylen,
-               0);
-           SvREFCNT_dec_NN(sv);
-           *svp = lexname;
-       }
+       rop = (UNOP*)((BINOP*)o)->op_first;
 
-       if ((o->op_private & (OPpLVAL_INTRO)))
-           break;
+       goto check_keys;
 
-       rop = (UNOP*)((BINOP*)o)->op_first;
-       if (rop->op_type != OP_RV2HV || rop->op_first->op_type != OP_PADSV)
-           break;
-       lexname = *av_fetch(PL_comppad_name, rop->op_first->op_targ, TRUE);
-       if (!SvPAD_TYPED(lexname))
-           break;
-       fields = (GV**)hv_fetchs(SvSTASH(lexname), "FIELDS", FALSE);
-       if (!fields || !GvHV(*fields))
-           break;
-       key = SvPV_const(*svp, keylen);
-       if (!hv_fetch(GvHV(*fields), key,
-               SvUTF8(*svp) ? -(I32)keylen : (I32)keylen, FALSE)) {
-           Perl_croak(aTHX_ "No such class field \"%"SVf"\" " 
-                          "in variable %"SVf" of type %"HEKf, 
-                     SVfARG(*svp), SVfARG(lexname),
-                      HEKfARG(HvNAME_HEK(SvSTASH(lexname))));
-       }
-       break;
-    }
+    case OP_HSLICE:
+       S_scalar_slice_warning(aTHX_ o);
 
-    case OP_HSLICE: {
-       UNOP *rop;
-       SV *lexname;
-       GV **fields;
-       SV **svp;
-       const char *key;
-       STRLEN keylen;
-       SVOP *first_key_op, *key_op;
-
-       if ((o->op_private & (OPpLVAL_INTRO))
-           /* I bet there's always a pushmark... */
-           || ((LISTOP*)o)->op_first->op_sibling->op_type != OP_LIST)
-           /* hmmm, no optimization if list contains only one key. */
+    case OP_KVHSLICE:
+       if (/* I bet there's always a pushmark... */
+               (kid = cLISTOPo->op_first->op_sibling)->op_type != OP_LIST
+             && kid->op_type != OP_CONST)
            break;
+
+       key_op = (SVOP*)(kid->op_type == OP_CONST
+                               ? kid
+                               : kLISTOP->op_first->op_sibling);
+
        rop = (UNOP*)((LISTOP*)o)->op_last;
-       if (rop->op_type != OP_RV2HV)
-           break;
-       if (rop->op_first->op_type == OP_PADSV)
+
+      check_keys:      
+       if (o->op_private & OPpLVAL_INTRO || rop->op_type != OP_RV2HV)
+           rop = NULL;
+       else if (rop->op_first->op_type == OP_PADSV)
            /* @$hash{qw(keys here)} */
            rop = (UNOP*)rop->op_first;
        else {
@@ -1852,26 +1977,38 @@ S_finalize_op(pTHX_ OP* o)
                    rop = (UNOP*)cLISTOPx(rop->op_first)->op_last;
                }
            else
-               break;
+               rop = NULL;
        }
 
-       lexname = *av_fetch(PL_comppad_name, rop->op_targ, TRUE);
-       if (!SvPAD_TYPED(lexname))
-           break;
-       fields = (GV**)hv_fetchs(SvSTASH(lexname), "FIELDS", FALSE);
-       if (!fields || !GvHV(*fields))
-           break;
-       /* Again guessing that the pushmark can be jumped over.... */
-       first_key_op = (SVOP*)((LISTOP*)((LISTOP*)o)->op_first->op_sibling)
-           ->op_first->op_sibling;
-       for (key_op = first_key_op; key_op;
+        lexname = NULL; /* just to silence compiler warnings */
+        fields  = NULL; /* just to silence compiler warnings */
+
+       check_fields =
+           rop
+        && (lexname = *av_fetch(PL_comppad_name, rop->op_targ, TRUE),
+            SvPAD_TYPED(lexname))
+        && (fields = (GV**)hv_fetchs(SvSTASH(lexname), "FIELDS", FALSE))
+        && isGV(*fields) && GvHV(*fields);
+       for (; key_op;
             key_op = (SVOP*)key_op->op_sibling) {
+           SV **svp, *sv;
            if (key_op->op_type != OP_CONST)
                continue;
            svp = cSVOPx_svp(key_op);
-           key = SvPV_const(*svp, keylen);
-           if (!hv_fetch(GvHV(*fields), key,
-                   SvUTF8(*svp) ? -(I32)keylen : (I32)keylen, FALSE)) {
+
+           /* Make the CONST have a shared SV */
+           if ((!SvIsCOW_shared_hash(sv = *svp))
+            && SvTYPE(sv) < SVt_PVMG && SvOK(sv) && !SvROK(sv)) {
+               SSize_t keylen;
+               const char * const key = SvPV_const(sv, *(STRLEN*)&keylen);
+               SV *nsv = newSVpvn_share(key,
+                                        SvUTF8(sv) ? -keylen : keylen, 0);
+               SvREFCNT_dec_NN(sv);
+               *svp = nsv;
+           }
+
+           if (check_fields
+            && !hv_fetch_ent(GvHV(*fields), *svp, FALSE, 0)) {
                Perl_croak(aTHX_ "No such class field \"%"SVf"\" " 
                           "in variable %"SVf" of type %"HEKf, 
                      SVfARG(*svp), SVfARG(lexname),
@@ -1880,6 +2017,9 @@ S_finalize_op(pTHX_ OP* o)
        }
        break;
     }
+    case OP_ASLICE:
+       S_scalar_slice_warning(aTHX_ o);
+       break;
 
     case OP_SUBST: {
        if (cPMOPo->op_pmreplrootu.op_pmreplroot)
@@ -1907,7 +2047,7 @@ 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
+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,
@@ -2068,13 +2208,22 @@ Perl_op_lvalue_flags(pTHX_ OP *o, I32 type, U32 flags)
        localize = 1;
        /* FALL THROUGH */
     case OP_AASSIGN:
-       if (type == OP_LEAVESUBLV)
+       /* Do not apply the lvsub flag for rv2[ah]v in scalar context.  */
+       if (type == OP_LEAVESUBLV && (
+               (o->op_type != OP_RV2AV && o->op_type != OP_RV2HV)
+            || (o->op_flags & OPf_WANT) != OPf_WANT_SCALAR
+          ))
            o->op_private |= OPpMAYBE_LVSUB;
        /* FALL THROUGH */
     case OP_NEXTSTATE:
     case OP_DBSTATE:
        PL_modcount = RETURN_UNLIMITED_NUMBER;
        break;
+    case OP_KVHSLICE:
+    case OP_KVASLICE:
+       if (type == OP_LEAVESUBLV)
+           o->op_private |= OPpMAYBE_LVSUB;
+        goto nomod;
     case OP_AV2ARYLEN:
        PL_hints |= HINT_BLOCK_SCOPE;
        if (type == OP_LEAVESUBLV)
@@ -2107,7 +2256,8 @@ Perl_op_lvalue_flags(pTHX_ OP *o, I32 type, U32 flags)
            return o;           /* Treat \(@foo) like ordinary list. */
        if (scalar_mod_type(o, type))
            goto nomod;
-       if (type == OP_LEAVESUBLV)
+       if ((o->op_flags & OPf_WANT) != OPf_WANT_SCALAR
+         && type == OP_LEAVESUBLV)
            o->op_private |= OPpMAYBE_LVSUB;
        /* FALL THROUGH */
     case OP_PADSV:
@@ -2151,8 +2301,10 @@ Perl_op_lvalue_flags(pTHX_ OP *o, I32 type, U32 flags)
        PL_modcount++;
        break;
 
-    case OP_SCOPE:
     case OP_LEAVE:
+    case OP_LEAVELOOP:
+       o->op_private |= OPpLVALUE;
+    case OP_SCOPE:
     case OP_ENTER:
     case OP_LINESEQ:
        localize = 0;
@@ -2187,6 +2339,12 @@ Perl_op_lvalue_flags(pTHX_ OP *o, I32 type, U32 flags)
 
     case OP_COREARGS:
        return o;
+
+    case OP_AND:
+    case OP_OR:
+       op_lvalue(cLOGOPo->op_first,             type);
+       op_lvalue(cLOGOPo->op_first->op_sibling, type);
+       goto nomod;
     }
 
     /* [20011101.069] File test operators interpret OPf_REF to mean that
@@ -2447,7 +2605,6 @@ S_apply_attrs(pTHX_ HV *stash, SV *target, OP *attrs)
     PERL_ARGS_ASSERT_APPLY_ATTRS;
 
     /* fake up C<use attributes $pkg,$rv,@attrs> */
-    ENTER;             /* need to protect against side-effects of 'use' */
 
 #define ATTRSMODULE "attributes"
 #define ATTRSMODULE_PM "attributes.pm"
@@ -2461,7 +2618,6 @@ S_apply_attrs(pTHX_ HV *stash, SV *target, OP *attrs)
                                                   newSVOP(OP_CONST, 0,
                                                           newRV(target)),
                                                   dup_attrlist(attrs))));
-    LEAVE;
 }
 
 STATIC void
@@ -2481,7 +2637,6 @@ S_apply_attrs_my(pTHX_ HV *stash, OP *target, OP *attrs, OP **imopsp)
           target->op_type == OP_PADAV);
 
     /* Ensure that attributes.pm is loaded. */
-    ENTER;             /* need to protect against side-effects of 'use' */
     /* Don't force the C<use> if we don't need it. */
     svp = hv_fetchs(GvHVn(PL_incgv), ATTRSMODULE_PM, FALSE);
     if (svp && *svp != &PL_sv_undef)
@@ -2489,7 +2644,6 @@ S_apply_attrs_my(pTHX_ HV *stash, OP *target, OP *attrs, OP **imopsp)
     else
        Perl_load_module(aTHX_ PERL_LOADMOD_NOIMPORT,
                               newSVpvs(ATTRSMODULE), NULL);
-    LEAVE;
 
     /* Need package name for method call. */
     pack = newSVOP(OP_CONST, 0, newSVpvs(ATTRSMODULE));
@@ -2567,6 +2721,114 @@ Perl_apply_attrs_string(pTHX_ const char *stashpv, CV *cv,
                                                attrs)));
 }
 
+STATIC void
+S_move_proto_attr(pTHX_ OP **proto, OP **attrs, const GV * name)
+{
+    OP *new_proto = NULL;
+    STRLEN pvlen;
+    char *pv;
+    OP *o;
+
+    PERL_ARGS_ASSERT_MOVE_PROTO_ATTR;
+
+    if (!*attrs)
+        return;
+
+    o = *attrs;
+    if (o->op_type == OP_CONST) {
+        pv = SvPV(cSVOPo_sv, pvlen);
+        if (pvlen >= 10 && memEQ(pv, "prototype(", 10)) {
+            SV * const tmpsv = newSVpvn_flags(pv + 10, pvlen - 11, SvUTF8(cSVOPo_sv));
+            SV ** const tmpo = cSVOPx_svp(o);
+            SvREFCNT_dec(cSVOPo_sv);
+            *tmpo = tmpsv;
+            new_proto = o;
+            *attrs = NULL;
+        }
+    } else if (o->op_type == OP_LIST) {
+        OP * lasto = NULL;
+        assert(o->op_flags & OPf_KIDS);
+        assert(cLISTOPo->op_first->op_type == OP_PUSHMARK);
+        /* Counting on the first op to hit the lasto = o line */
+        for (o = cLISTOPo->op_first; o; o=o->op_sibling) {
+            if (o->op_type == OP_CONST) {
+                pv = SvPV(cSVOPo_sv, pvlen);
+                if (pvlen >= 10 && memEQ(pv, "prototype(", 10)) {
+                    SV * const tmpsv = newSVpvn_flags(pv + 10, pvlen - 11, SvUTF8(cSVOPo_sv));
+                    SV ** const tmpo = cSVOPx_svp(o);
+                    SvREFCNT_dec(cSVOPo_sv);
+                    *tmpo = tmpsv;
+                    if (new_proto && ckWARN(WARN_MISC)) {
+                        STRLEN new_len;
+                        const char * newp = SvPV(cSVOPo_sv, new_len);
+                        Perl_warner(aTHX_ packWARN(WARN_MISC),
+                            "Attribute prototype(%"UTF8f") discards earlier prototype attribute in same sub",
+                            UTF8fARG(SvUTF8(cSVOPo_sv), new_len, newp));
+                        op_free(new_proto);
+                    }
+                    else if (new_proto)
+                        op_free(new_proto);
+                    new_proto = o;
+                    lasto->op_sibling = o->op_sibling;
+                    continue;
+                }
+            }
+            lasto = o;
+        }
+        /* If the list is now just the PUSHMARK, scrap the whole thing; otherwise attributes.xs
+           would get pulled in with no real need */
+        if (!cLISTOPx(*attrs)->op_first->op_sibling) {
+            op_free(*attrs);
+            *attrs = NULL;
+        }
+    }
+
+    if (new_proto) {
+        SV *svname;
+        if (isGV(name)) {
+            svname = sv_newmortal();
+            gv_efullname3(svname, name, NULL);
+        }
+        else if (SvPOK(name) && *SvPVX((SV *)name) == '&')
+            svname = newSVpvn_flags(SvPVX((SV *)name)+1, SvCUR(name)-1, SvUTF8(name)|SVs_TEMP);
+        else
+            svname = (SV *)name;
+        if (ckWARN(WARN_ILLEGALPROTO))
+            (void)validate_proto(svname, cSVOPx_sv(new_proto), TRUE);
+        if (*proto && ckWARN(WARN_PROTOTYPE)) {
+            STRLEN old_len, new_len;
+            const char * oldp = SvPV(cSVOPx_sv(*proto), old_len);
+            const char * newp = SvPV(cSVOPx_sv(new_proto), new_len);
+
+            Perl_warner(aTHX_ packWARN(WARN_PROTOTYPE),
+                "Prototype '%"UTF8f"' overridden by attribute 'prototype(%"UTF8f")'"
+                " in %"SVf,
+                UTF8fARG(SvUTF8(cSVOPx_sv(*proto)), old_len, oldp),
+                UTF8fARG(SvUTF8(cSVOPx_sv(new_proto)), new_len, newp),
+                SVfARG(svname));
+        }
+        if (*proto)
+            op_free(*proto);
+        *proto = new_proto;
+    }
+}
+
+static void
+S_cant_declare(pTHX_ OP *o)
+{
+    if (o->op_type == OP_NULL
+     && (o->op_flags & (OPf_SPECIAL|OPf_KIDS)) == OPf_KIDS)
+        o = cUNOPo->op_first;
+    yyerror(Perl_form(aTHX_ "Can't declare %s in \"%s\"",
+                             o->op_type == OP_NULL
+                               && o->op_flags & OPf_SPECIAL
+                                 ? "do block"
+                                 : OP_DESC(o),
+                             PL_parser->in_my == KEY_our   ? "our"   :
+                             PL_parser->in_my == KEY_state ? "state" :
+                                                             "my"));
+}
+
 STATIC OP *
 S_my_kid(pTHX_ OP *o, OP *attrs, OP **imopsp)
 {
@@ -2596,11 +2858,7 @@ S_my_kid(pTHX_ OP *o, OP *attrs, OP **imopsp)
               type == OP_RV2AV ||
               type == OP_RV2HV) { /* XXX does this let anything illegal in? */
        if (cUNOPo->op_first->op_type != OP_GV) { /* MJD 20011224 */
-           yyerror(Perl_form(aTHX_ "Can't declare %s in \"%s\"",
-                       OP_DESC(o),
-                       PL_parser->in_my == KEY_our
-                           ? "our"
-                           : PL_parser->in_my == KEY_state ? "state" : "my"));
+           S_cant_declare(aTHX_ o);
        } else if (attrs) {
            GV * const gv = cGVOPx_gv(cUNOPo->op_first);
            PL_parser->in_my = FALSE;
@@ -2619,11 +2877,7 @@ S_my_kid(pTHX_ OP *o, OP *attrs, OP **imopsp)
             type != OP_PADHV &&
             type != OP_PUSHMARK)
     {
-       yyerror(Perl_form(aTHX_ "Can't declare %s in \"%s\"",
-                         OP_DESC(o),
-                         PL_parser->in_my == KEY_our
-                           ? "our"
-                           : PL_parser->in_my == KEY_state ? "state" : "my"));
+       S_cant_declare(aTHX_ o);
        return o;
     }
     else if (attrs && type != OP_PUSHMARK) {
@@ -2721,16 +2975,8 @@ Perl_bind_match(pTHX_ I32 type, OP *left, OP *right)
                       )
                       ? (int)rtype : OP_MATCH];
       const bool isary = ltype == OP_RV2AV || ltype == OP_PADAV;
-      GV *gv;
       SV * const name =
-       (ltype == OP_RV2AV || ltype == OP_RV2HV)
-        ?    cUNOPx(left)->op_first->op_type == OP_GV
-          && (gv = cGVOPx_gv(cUNOPx(left)->op_first))
-              ? varname(gv, isary ? '@' : '%', 0, NULL, 0, 1)
-              : NULL
-        : varname(
-           (GV *)PL_compcv, isary ? '@' : '%', left->op_targ, NULL, 0, 1
-          );
+       S_op_varname(aTHX_ left);
       if (name)
        Perl_warner(aTHX_ packWARN(WARN_MISC),
              "Applying %s to %"SVf" will act on scalar(%"SVf")",
@@ -2754,8 +3000,10 @@ Perl_bind_match(pTHX_ I32 type, OP *left, OP *right)
     /* !~ 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)
+       /* diag_listed_as: Using !~ with %s doesn't make sense */
        yyerror("Using !~ with s///r doesn't make sense");
     if (rtype == OP_TRANSR && type == OP_NOT)
+       /* diag_listed_as: Using !~ with %s doesn't make sense */
        yyerror("Using !~ with tr///r doesn't make sense");
 
     ismatchop = (rtype == OP_MATCH ||
@@ -2958,7 +3206,7 @@ Perl_block_end(pTHX_ I32 floor, OP *seq)
 =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">.
+at compile time.  See L<perlguts/"Compile-time scope hooks">.
 
 =cut
 */
@@ -3183,7 +3431,7 @@ S_op_integerize(pTHX_ OP *o)
     if ((PL_opargs[type] & OA_OTHERINT) && (PL_hints & HINT_INTEGER))
     {
        dVAR;
-       o->op_ppaddr = PL_ppaddr[type = ++(o->op_type)];
+       o->op_ppaddr = PL_ppaddr[++(o->op_type)];
     }
 
     if (type == OP_NEGATE)
@@ -3248,6 +3496,11 @@ S_fold_constants(pTHX_ OP *o)
        break;
     case OP_REPEAT:
        if (o->op_private & OPpREPEAT_DOLIST) goto nope;
+       break;
+    case OP_SREFGEN:
+       if (cUNOPx(cUNOPo->op_first)->op_first->op_type != OP_CONST
+        || SvPADTMP(cSVOPx_sv(cUNOPx(cUNOPo->op_first)->op_first)))
+           goto nope;
     }
 
     if (PL_parser && PL_parser->error_count)
@@ -3335,13 +3588,16 @@ S_fold_constants(pTHX_ OP *o)
 #endif
     assert(sv);
     if (type == OP_STRINGIFY) SvPADTMP_off(sv);
-    else if (!SvIMMORTAL(sv)) SvPADTMP_on(sv);
+    else if (!SvIMMORTAL(sv)) {
+       SvPADTMP_on(sv);
+       SvREADONLY_on(sv);
+    }
     if (type == OP_RV2GV)
        newop = newGVOP(OP_GV, 0, MUTABLE_GV(sv));
     else
     {
-       newop = newSVOP(OP_CONST, OPpCONST_FOLDED<<8, MUTABLE_SV(sv));
-       newop->op_folded = 1;
+       newop = newSVOP(OP_CONST, 0, MUTABLE_SV(sv));
+       if (type != OP_STRINGIFY) newop->op_folded = 1;
     }
     op_getmad(o,newop,'f');
     return newop;
@@ -3384,7 +3640,10 @@ S_gen_constant_list(pTHX_ OP *o)
     ((UNOP*)o)->op_first = newSVOP(OP_CONST, 0, (SV *)av);
     if (AvFILLp(av) != -1)
        for (svp = AvARRAY(av) + AvFILLp(av); svp >= AvARRAY(av); --svp)
+       {
            SvPADTMP_on(*svp);
+           SvREADONLY_on(*svp);
+       }
 #ifdef PERL_MAD
     op_getmad(curop,o,'O');
 #else
@@ -4000,7 +4259,7 @@ Perl_newBINOP(pTHX_ I32 type, I32 flags, OP *first, OP *last)
     dVAR;
     BINOP *binop;
 
-    assert((PL_opargs[type] & OA_CLASS_MASK) == OA_BINOP
+    ASSUME((PL_opargs[type] & OA_CLASS_MASK) == OA_BINOP
        || type == OP_SASSIGN || type == OP_NULL );
 
     NewOp(1101, binop, 1, BINOP);
@@ -5195,7 +5454,8 @@ Loads the module whose name is pointed to by the string part of name.
 Note that the actual module name, not its filename, should be given.
 Eg, "Foo::Bar" instead of "Foo/Bar.pm".  flags can be any of
 PERL_LOADMOD_DENY, PERL_LOADMOD_NOIMPORT, or PERL_LOADMOD_IMPORT_OPS
-(or 0 for no flags). ver, if specified and not NULL, provides version semantics
+(or 0 for no flags).  ver, if specified
+and not NULL, provides version semantics
 similar to C<use Foo::Bar VERSION>.  The optional trailing SV*
 arguments can be used to specify arguments to the module's import()
 method, similar to C<use Foo::Bar VERSION LIST>.  They must be
@@ -5268,7 +5528,8 @@ Perl_vload_module(pTHX_ U32 flags, SV *name, SV *ver, va_list *args)
      * that it has a PL_parser to play with while doing that, and also
      * that it doesn't mess with any existing parser, by creating a tmp
      * new parser with lex_start(). This won't actually be used for much,
-     * since pp_require() will create another parser for the real work. */
+     * since pp_require() will create another parser for the real work.
+     * The ENTER/LEAVE pair protect callers from any side effects of use.  */
 
     ENTER;
     SAVEVPTR(PL_curcop);
@@ -5278,28 +5539,26 @@ Perl_vload_module(pTHX_ U32 flags, SV *name, SV *ver, va_list *args)
     LEAVE;
 }
 
+PERL_STATIC_INLINE OP *
+S_new_entersubop(pTHX_ GV *gv, OP *arg)
+{
+    return newUNOP(OP_ENTERSUB, OPf_STACKED,
+                  newLISTOP(OP_LIST, 0, arg,
+                            newUNOP(OP_RV2CV, 0,
+                                    newGVOP(OP_GV, 0, gv))));
+}
+
 OP *
 Perl_dofile(pTHX_ OP *term, I32 force_builtin)
 {
     dVAR;
     OP *doop;
-    GV *gv = NULL;
+    GV *gv;
 
     PERL_ARGS_ASSERT_DOFILE;
 
-    if (!force_builtin) {
-       gv = gv_fetchpvs("do", GV_NOTQUAL, SVt_PVCV);
-       if (!(gv && GvCVu(gv) && GvIMPORTED_CV(gv))) {
-           GV * const * const gvp = (GV**)hv_fetchs(PL_globalstash, "do", FALSE);
-           gv = gvp ? *gvp : NULL;
-       }
-    }
-
-    if (gv && GvCVu(gv) && GvIMPORTED_CV(gv)) {
-       doop = newUNOP(OP_ENTERSUB, OPf_STACKED,
-                              op_append_elem(OP_LIST, term,
-                                          scalar(newUNOP(OP_RV2CV, 0,
-                                                         newGVOP(OP_GV, 0, gv)))));
+    if (!force_builtin && (gv = gv_override("do", 2))) {
+       doop = S_new_entersubop(aTHX_ gv, term);
     }
     else {
        doop = newUNOP(OP_DOFILE, 0, scalar(term));
@@ -5363,7 +5622,8 @@ S_is_list_assignment(pTHX_ const OP *o)
 
     if (type == OP_LIST || flags & OPf_PARENS ||
        type == OP_RV2AV || type == OP_RV2HV ||
-       type == OP_ASLICE || type == OP_HSLICE)
+       type == OP_ASLICE || type == OP_HSLICE ||
+        type == OP_KVASLICE || type == OP_KVHSLICE)
        return TRUE;
 
     if (type == OP_PADAV || type == OP_PADHV)
@@ -5488,6 +5748,9 @@ Perl_newASSIGNOP(pTHX_ I32 flags, OP *left, I32 optype, OP *right)
        OP *curop;
        bool maybe_common_vars = TRUE;
 
+       if (left->op_type == OP_ASLICE || left->op_type == OP_HSLICE)
+           left->op_private &= ~ OPpSLICEWARNING;
+
        PL_modcount = 0;
        left = op_lvalue(left, OP_AASSIGN);
        curop = list(force_list(left));
@@ -5691,6 +5954,9 @@ Perl_newSTATEOP(pTHX_ I32 flags, char *label, OP *o)
 #ifdef NATIVE_HINTS
     cop->op_private |= NATIVE_HINTS;
 #endif
+#ifdef VMS
+    if (VMSISH_HUSHED) cop->op_private |= OPpHUSH_VMSISH;
+#endif
     cop->op_next = (OP*)cop;
 
     cop->cop_seq = seq;
@@ -5706,7 +5972,11 @@ Perl_newSTATEOP(pTHX_ I32 flags, char *label, OP *o)
        SAVEFREEPV(label);
     }
 
-    if (PL_parser->copline == NOLINE)
+    if (PL_parser->preambling != NOLINE) {
+        CopLINE_set(cop, PL_parser->preambling);
+        PL_parser->copline = NOLINE;
+    }
+    else if (PL_parser->copline == NOLINE)
         CopLINE_set(cop, CopLINE(PL_curcop));
     else {
        CopLINE_set(cop, PL_parser->copline);
@@ -5719,7 +5989,7 @@ Perl_newSTATEOP(pTHX_ I32 flags, char *label, OP *o)
 #endif
     CopSTASH_set(cop, PL_curstash);
 
-    if ((PERLDB_LINE || PERLDB_SAVESRC) && PL_curstash != PL_debstash) {
+    if (cop->op_type == OP_DBSTATE) {
        /* this line can have a breakpoint - store the cop in IV */
        AV *av = CopFILEAVx(PL_curcop);
        if (av) {
@@ -5819,6 +6089,44 @@ S_new_logop(pTHX_ I32 type, I32 flags, OP** firstp, OP** otherp)
     first = *firstp;
     other = *otherp;
 
+    /* [perl #59802]: Warn about things like "return $a or $b", which
+       is parsed as "(return $a) or $b" rather than "return ($a or
+       $b)".  NB: This also applies to xor, which is why we do it
+       here.
+     */
+    switch (first->op_type) {
+    case OP_NEXT:
+    case OP_LAST:
+    case OP_REDO:
+       /* XXX: Perhaps we should emit a stronger warning for these.
+          Even with the high-precedence operator they don't seem to do
+          anything sensible.
+
+          But until we do, fall through here.
+         */
+    case OP_RETURN:
+    case OP_EXIT:
+    case OP_DIE:
+    case OP_GOTO:
+       /* XXX: Currently we allow people to "shoot themselves in the
+          foot" by explicitly writing "(return $a) or $b".
+
+          Warn unless we are looking at the result from folding or if
+          the programmer explicitly grouped the operators like this.
+          The former can occur with e.g.
+
+               use constant FEATURE => ( $] >= ... );
+               sub { not FEATURE and return or do_stuff(); }
+        */
+       if (!first->op_folded && !(first->op_flags & OPf_PARENS))
+           Perl_ck_warner(aTHX_ packWARN(WARN_SYNTAX),
+                          "Possible precedence issue with control flow operator");
+       /* XXX: Should we optimze this to "return $a;" (i.e. remove
+          the "or $b" part)?
+       */
+       break;
+    }
+
     if (type == OP_XOR)                /* Not short circuit, but here by precedence. */
        return newBINOP(type, flags, scalar(first), scalar(other));
 
@@ -5870,8 +6178,6 @@ S_new_logop(pTHX_ I32 type, I32 flags, OP** firstp, OP** otherp)
                  || other->op_type == OP_TRANS)
                /* Mark the op as being unbindable with =~ */
                other->op_flags |= OPf_SPECIAL;
-           else if (other->op_type == OP_CONST)
-               other->op_private |= OPpCONST_FOLDED;
 
            other->op_folded = 1;
            return other;
@@ -6033,8 +6339,6 @@ Perl_newCONDOP(pTHX_ I32 flags, OP *first, OP *trueop, OP *falseop)
              || live->op_type == OP_TRANS || live->op_type == OP_TRANSR)
            /* Mark the op as being unbindable with =~ */
            live->op_flags |= OPf_SPECIAL;
-       else if (live->op_type == OP_CONST)
-           live->op_private |= OPpCONST_FOLDED;
        live->op_folded = 1;
        return live;
     }
@@ -6156,12 +6460,20 @@ Perl_newLOOPOP(pTHX_ I32 flags, I32 debuggable, OP *expr, OP *block)
     OP* listop;
     OP* o;
     const bool once = block && block->op_flags & OPf_SPECIAL &&
-      (block->op_type == OP_ENTERSUB || block->op_type == OP_NULL);
+                     block->op_type == OP_NULL;
 
     PERL_UNUSED_ARG(debuggable);
 
     if (expr) {
-       if (once && expr->op_type == OP_CONST && !SvTRUE(((SVOP*)expr)->op_sv))
+       if (once && (
+             (expr->op_type == OP_CONST && !SvTRUE(((SVOP*)expr)->op_sv))
+          || (  expr->op_type == OP_NOT
+             && cUNOPx(expr)->op_first->op_type == OP_CONST
+             && SvTRUE(cSVOPx_sv(cUNOPx(expr)->op_first))
+             )
+          ))
+           /* Return the block now, so that S_new_logop does not try to
+              fold it away. */
            return block;       /* do {} while 0 does once */
        if (expr->op_type == OP_READLINE
            || expr->op_type == OP_READDIR
@@ -6200,11 +6512,19 @@ Perl_newLOOPOP(pTHX_ I32 flags, I32 debuggable, OP *expr, OP *block)
     listop = op_append_elem(OP_LINESEQ, block, newOP(OP_UNSTACK, 0));
     o = new_logop(OP_AND, 0, &expr, &listop);
 
+    if (once) {
+       ASSUME(listop);
+    }
+
     if (listop)
        ((LISTOP*)listop)->op_last->op_next = LINKLIST(o);
 
     if (once && o != listop)
+    {
+       assert(cUNOPo->op_first->op_type == OP_AND
+           || cUNOPo->op_first->op_type == OP_OR);
        o->op_next = ((LOGOP*)cUNOPo->op_first)->op_other;
+    }
 
     if (o == listop)
        o = newUNOP(OP_NULL, 0, o);     /* or do {} while 1 loses outer block */
@@ -6567,7 +6887,9 @@ S_ref_array_or_hash(pTHX_ OP *cond)
 
     else if(cond
     && (cond->op_type == OP_ASLICE
-    ||  cond->op_type == OP_HSLICE)) {
+    ||  cond->op_type == OP_KVASLICE
+    ||  cond->op_type == OP_HSLICE
+    ||  cond->op_type == OP_KVHSLICE)) {
 
        /* anonlist now needs a list from this op, was previously used in
         * scalar context */
@@ -6844,7 +7166,7 @@ static void const_av_xsub(pTHX_ CV* cv);
 
 =for apidoc cv_const_sv
 
-If C<cv> is a constant sub eligible for inlining. returns the constant
+If C<cv> is a constant sub eligible for inlining, returns the constant
 value returned by the sub.  Otherwise, returns NULL.
 
 Constant subs can be created with C<newCONSTSUB> or as described in
@@ -6973,7 +7295,7 @@ S_already_defined(pTHX_ CV *const cv, OP * const block, OP * const o,
 #endif
     {
        /* (PL_madskills unset in used file.) */
-       SvREFCNT_dec(cv);
+       SAVEFREESV(cv);
     }
     return TRUE;
 }
@@ -7019,6 +7341,9 @@ Perl_newMYSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block)
                        [CvDEPTH(outcv) ? CvDEPTH(outcv) : 1])[pax];
     spot = (CV **)svspot;
 
+    if (!(PL_parser && PL_parser->error_count))
+        move_proto_attr(&proto, &attrs, (GV *)name);
+
     if (proto) {
        assert(proto->op_type == OP_CONST);
        ps = SvPV_const(((SVOP*)proto)->op_sv, ps_len);
@@ -7094,7 +7419,7 @@ Perl_newMYSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block)
             cv_ckproto_len_flags(cv, (GV *)name, ps, ps_len, ps_utf8);
        /* already defined? */
        if (exists) {
-           if (S_already_defined(aTHX_ cv, block, NULL, name, &const_sv))
+           if (S_already_defined(aTHX_ cv,block,NULL,name,&const_sv))
                cv = NULL;
            else {
                if (attrs) goto attrs;
@@ -7322,15 +7647,10 @@ Perl_newMYSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block)
     return cv;
 }
 
+/* _x = extended */
 CV *
-Perl_newATTRSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block)
-{
-    return newATTRSUB_flags(floor, o, proto, attrs, block, 0);
-}
-
-CV *
-Perl_newATTRSUB_flags(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs,
-                           OP *block, U32 flags)
+Perl_newATTRSUB_x(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs,
+                           OP *block, bool o_is_gv)
 {
     dVAR;
     GV *gv;
@@ -7351,7 +7671,6 @@ Perl_newATTRSUB_flags(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs,
           || PL_madskills)
        ? GV_ADDMULTI : GV_ADDMULTI | GV_NOINIT;
     STRLEN namlen = 0;
-    const bool o_is_gv = flags & 1;
     const char * const name =
         o ? SvPV_const(o_is_gv ? (SV *)o : cSVOPo->op_sv, namlen) : NULL;
     bool has_name;
@@ -7360,14 +7679,6 @@ Perl_newATTRSUB_flags(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs,
     OPSLAB *slab = NULL;
 #endif
 
-    if (proto) {
-       assert(proto->op_type == OP_CONST);
-       ps = SvPV_const(((SVOP*)proto)->op_sv, ps_len);
-        ps_utf8 = SvUTF8(((SVOP*)proto)->op_sv);
-    }
-    else
-       ps = NULL;
-
     if (o_is_gv) {
        gv = (GV*)o;
        o = NULL;
@@ -7390,6 +7701,17 @@ Perl_newATTRSUB_flags(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs,
        has_name = FALSE;
     }
 
+    if (!ec)
+        move_proto_attr(&proto, &attrs, gv);
+
+    if (proto) {
+       assert(proto->op_type == OP_CONST);
+       ps = SvPV_const(((SVOP*)proto)->op_sv, ps_len);
+        ps_utf8 = SvUTF8(((SVOP*)proto)->op_sv);
+    }
+    else
+       ps = NULL;
+
     if (!PL_madskills) {
        if (o)
            SAVEFREEOP(o);
@@ -7674,8 +7996,10 @@ S_process_special_blocks(pTHX_ I32 floor, const char *const fullname,
     if (*name == 'B') {
        if (strEQ(name, "BEGIN")) {
            const I32 oldscope = PL_scopestack_ix;
+            dSP;
            if (floor) LEAVE_SCOPE(floor);
            ENTER;
+            PUSHSTACKi(PERLSI_REQUIRE);
            SAVECOPFILE(&PL_compiling);
            SAVECOPLINE(&PL_compiling);
            SAVEVPTR(PL_curcop);
@@ -7685,6 +8009,7 @@ S_process_special_blocks(pTHX_ I32 floor, const char *const fullname,
            GvCV_set(gv,0);             /* cv has been hijacked */
            call_list(oldscope, PL_beginav);
 
+            POPSTACK;
            LEAVE;
        }
        else
@@ -7832,6 +8157,7 @@ Perl_newXS_len_flags(pTHX_ const char *name, STRLEN len,
                           U32 flags)
 {
     CV *cv;
+    bool interleave = FALSE;
 
     PERL_ARGS_ASSERT_NEWXS_LEN_FLAGS;
 
@@ -7861,7 +8187,9 @@ Perl_newXS_len_flags(pTHX_ const char *name, STRLEN len,
                                         ),
                                         cv, const_svp);
                 }
-                SvREFCNT_dec_NN(cv);
+                interleave = TRUE;
+                ENTER;
+                SAVEFREESV(cv);
                 cv = NULL;
             }
         }
@@ -7896,6 +8224,7 @@ Perl_newXS_len_flags(pTHX_ const char *name, STRLEN len,
        CvDYNFILE_on(cv);
     }
     sv_setpv(MUTABLE_SV(cv), proto);
+    if (interleave) LEAVE;
     return cv;
 }
 
@@ -8048,11 +8377,13 @@ Perl_oopsAV(pTHX_ OP *o)
 
     switch (o->op_type) {
     case OP_PADSV:
+    case OP_PADHV:
        o->op_type = OP_PADAV;
        o->op_ppaddr = PL_ppaddr[OP_PADAV];
        return ref(o, OP_RV2AV);
 
     case OP_RV2SV:
+    case OP_RV2HV:
        o->op_type = OP_RV2AV;
        o->op_ppaddr = PL_ppaddr[OP_RV2AV];
        ref(o, OP_RV2AV);
@@ -8179,6 +8510,62 @@ Perl_ck_anoncode(pTHX_ OP *o)
     return o;
 }
 
+static void
+S_io_hints(pTHX_ OP *o)
+{
+    HV * const table =
+       PL_hints & HINT_LOCALIZE_HH ? GvHV(PL_hintgv) : NULL;;
+    if (table) {
+       SV **svp = hv_fetchs(table, "open_IN", FALSE);
+       if (svp && *svp) {
+           STRLEN len = 0;
+           const char *d = SvPV_const(*svp, len);
+           const I32 mode = mode_from_discipline(d, len);
+           if (mode & O_BINARY)
+               o->op_private |= OPpOPEN_IN_RAW;
+           else if (mode & O_TEXT)
+               o->op_private |= OPpOPEN_IN_CRLF;
+       }
+
+       svp = hv_fetchs(table, "open_OUT", FALSE);
+       if (svp && *svp) {
+           STRLEN len = 0;
+           const char *d = SvPV_const(*svp, len);
+           const I32 mode = mode_from_discipline(d, len);
+           if (mode & O_BINARY)
+               o->op_private |= OPpOPEN_OUT_RAW;
+           else if (mode & O_TEXT)
+               o->op_private |= OPpOPEN_OUT_CRLF;
+       }
+    }
+}
+
+OP *
+Perl_ck_backtick(pTHX_ OP *o)
+{
+    GV *gv;
+    OP *newop = NULL;
+    PERL_ARGS_ASSERT_CK_BACKTICK;
+    /* qx and `` have a null pushmark; CORE::readpipe has only one kid. */
+    if (o->op_flags & OPf_KIDS && cUNOPo->op_first->op_sibling
+     && (gv = gv_override("readpipe",8))) {
+       newop = S_new_entersubop(aTHX_ gv, cUNOPo->op_first->op_sibling);
+       cUNOPo->op_first->op_sibling = NULL;
+    }
+    else if (!(o->op_flags & OPf_KIDS))
+       newop = newUNOP(OP_BACKTICK, 0, newDEFSVOP());
+    if (newop) {
+#ifdef PERL_MAD
+       op_getmad(o,newop,'O');
+#else
+       op_free(o);
+#endif
+       return newop;
+    }
+    S_io_hints(aTHX_ o);
+    return o;
+}
+
 OP *
 Perl_ck_bitop(pTHX_ OP *o)
 {
@@ -8306,9 +8693,15 @@ Perl_ck_delete(pTHX_ OP *o)
            /* FALL THROUGH */
        case OP_HELEM:
            break;
+       case OP_KVASLICE:
+           Perl_croak(aTHX_ "delete argument is index/value array slice,"
+                            " use array slice");
+       case OP_KVHSLICE:
+           Perl_croak(aTHX_ "delete argument is key/value hash slice, use"
+                            " hash slice");
        default:
-           Perl_croak(aTHX_ "%s argument is not a HASH or ARRAY element or slice",
-                 OP_DESC(o));
+           Perl_croak(aTHX_ "delete argument is not a HASH or ARRAY "
+                            "element or slice");
        }
        if (kid->op_private & OPpLVAL_INTRO)
            o->op_private |= OPpLVAL_INTRO;
@@ -8318,17 +8711,6 @@ Perl_ck_delete(pTHX_ OP *o)
 }
 
 OP *
-Perl_ck_die(pTHX_ OP *o)
-{
-    PERL_ARGS_ASSERT_CK_DIE;
-
-#ifdef VMS
-    if (VMSISH_HUSHED) o->op_private |= OPpHUSH_VMSISH;
-#endif
-    return ck_fun(o);
-}
-
-OP *
 Perl_ck_eof(pTHX_ OP *o)
 {
     dVAR;
@@ -8425,23 +8807,6 @@ Perl_ck_eval(pTHX_ OP *o)
 }
 
 OP *
-Perl_ck_exit(pTHX_ OP *o)
-{
-    PERL_ARGS_ASSERT_CK_EXIT;
-
-#ifdef VMS
-    HV * const table = GvHV(PL_hintgv);
-    if (table) {
-       SV * const * const svp = hv_fetchs(table, "vmsish_exit", FALSE);
-       if (svp && *svp && SvTRUE(*svp))
-           o->op_private |= OPpEXIT_VMSISH;
-    }
-    if (VMSISH_HUSHED) o->op_private |= OPpHUSH_VMSISH;
-#endif
-    return ck_fun(o);
-}
-
-OP *
 Perl_ck_exec(pTHX_ OP *o)
 {
     PERL_ARGS_ASSERT_CK_EXEC;
@@ -8472,15 +8837,15 @@ Perl_ck_exists(pTHX_ OP *o)
            (void) ref(kid, o->op_type);
            if (kid->op_type != OP_RV2CV
                        && !(PL_parser && PL_parser->error_count))
-               Perl_croak(aTHX_ "%s argument is not a subroutine name",
-                           OP_DESC(o));
+               Perl_croak(aTHX_
+                         "exists argument is not a subroutine name");
            o->op_private |= OPpEXISTS_SUB;
        }
        else if (kid->op_type == OP_AELEM)
            o->op_flags |= OPf_SPECIAL;
        else if (kid->op_type != OP_HELEM)
-           Perl_croak(aTHX_ "%s argument is not a HASH or ARRAY element or a subroutine",
-                       OP_DESC(o));
+           Perl_croak(aTHX_ "exists argument is not a HASH or ARRAY "
+                            "element or a subroutine");
        op_null(kid);
     }
     return o;
@@ -8715,7 +9080,7 @@ Perl_ck_fun(pTHX_ OP *o)
                {
                    return too_many_arguments_pv(o,PL_op_desc[type], 0);
                }
-               scalar(kid);
+               if (type != OP_DELETE) scalar(kid);
                break;
            case OA_LIST:
                if (oa < 16) {
@@ -8757,7 +9122,14 @@ Perl_ck_fun(pTHX_ OP *o)
                /* Defer checks to run-time if we have a scalar arg */
                if (kid->op_type == OP_RV2AV || kid->op_type == OP_PADAV)
                    op_lvalue(kid, type);
-               else scalar(kid);
+               else {
+                   scalar(kid);
+                   /* diag_listed_as: push on reference is experimental */
+                   Perl_ck_warner_d(aTHX_
+                                    packWARN(WARN_EXPERIMENTAL__AUTODEREF),
+                                   "%s on reference is experimental",
+                                    PL_op_desc[type]);
+               }
                break;
            case OA_HVREF:
                if (kid->op_type == OP_CONST &&
@@ -8965,7 +9337,6 @@ Perl_ck_glob(pTHX_ OP *o)
 {
     dVAR;
     GV *gv;
-    const bool core = o->op_flags & OPf_SPECIAL;
 
     PERL_ARGS_ASSERT_CK_GLOB;
 
@@ -8973,16 +9344,8 @@ Perl_ck_glob(pTHX_ OP *o)
     if ((o->op_flags & OPf_KIDS) && !cLISTOPo->op_first->op_sibling)
        op_append_elem(OP_GLOB, o, newDEFSVOP()); /* glob() => glob($_) */
 
-    if (core) gv = NULL;
-    else if (!((gv = gv_fetchpvs("glob", GV_NOTQUAL, SVt_PVCV))
-         && GvCVu(gv) && GvIMPORTED_CV(gv)))
+    if (!(o->op_flags & OPf_SPECIAL) && (gv = gv_override("glob", 4)))
     {
-       GV * const * const gvp =
-           (GV **)hv_fetchs(PL_globalstash, "glob", FALSE);
-       gv = gvp ? *gvp : NULL;
-    }
-
-    if (gv && GvCVu(gv) && GvIMPORTED_CV(gv)) {
        /* convert
         *     glob
         *       \ null - const(wildcard)
@@ -8997,11 +9360,7 @@ Perl_ck_glob(pTHX_ OP *o)
         */
        o->op_flags |= OPf_SPECIAL;
        o->op_targ = pad_alloc(OP_GLOB, SVs_PADTMP);
-       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 = S_new_entersubop(aTHX_ gv, o);
        o = newUNOP(OP_NULL, 0, o);
        o->op_targ = OP_GLOB; /* hint at what it used to be: eg in newWHILEOP */
        return o;
@@ -9376,45 +9735,10 @@ OP *
 Perl_ck_open(pTHX_ OP *o)
 {
     dVAR;
-    HV * const table = GvHV(PL_hintgv);
 
     PERL_ARGS_ASSERT_CK_OPEN;
 
-    if (table) {
-       SV **svp = hv_fetchs(table, "open_IN", FALSE);
-       if (svp && *svp) {
-           STRLEN len = 0;
-           const char *d = SvPV_const(*svp, len);
-           const I32 mode = mode_from_discipline(d, len);
-           if (mode & O_BINARY)
-               o->op_private |= OPpOPEN_IN_RAW;
-           else if (mode & O_TEXT)
-               o->op_private |= OPpOPEN_IN_CRLF;
-       }
-
-       svp = hv_fetchs(table, "open_OUT", FALSE);
-       if (svp && *svp) {
-           STRLEN len = 0;
-           const char *d = SvPV_const(*svp, len);
-           const I32 mode = mode_from_discipline(d, len);
-           if (mode & O_BINARY)
-               o->op_private |= OPpOPEN_OUT_RAW;
-           else if (mode & O_TEXT)
-               o->op_private |= OPpOPEN_OUT_CRLF;
-       }
-    }
-    if (o->op_type == OP_BACKTICK) {
-       if (!(o->op_flags & OPf_KIDS)) {
-           OP * const newop = newUNOP(OP_BACKTICK, 0, newDEFSVOP());
-#ifdef PERL_MAD
-           op_getmad(o,newop,'O');
-#else
-           op_free(o);
-#endif
-           return newop;
-       }
-       return o;
-    }
+    S_io_hints(aTHX_ o);
     {
         /* In case of three-arg dup open remove strictness
          * from the last arg if it is a bareword. */
@@ -9456,7 +9780,7 @@ OP *
 Perl_ck_require(pTHX_ OP *o)
 {
     dVAR;
-    GV* gv = NULL;
+    GV* gv;
 
     PERL_ARGS_ASSERT_CK_REQUIRE;
 
@@ -9491,16 +9815,9 @@ Perl_ck_require(pTHX_ OP *o)
        }
     }
 
-    if (!(o->op_flags & OPf_SPECIAL)) { /* Wasn't written as CORE::require */
+    if (!(o->op_flags & OPf_SPECIAL) /* Wasn't written as CORE::require */
        /* handle override, if any */
-       gv = gv_fetchpvs("require", GV_NOTQUAL, SVt_PVCV);
-       if (!(gv && GvCVu(gv) && GvIMPORTED_CV(gv))) {
-           GV * const * const gvp = (GV**)hv_fetchs(PL_globalstash, "require", FALSE);
-           gv = gvp ? *gvp : NULL;
-       }
-    }
-
-    if (gv && GvCVu(gv) && GvIMPORTED_CV(gv)) {
+     && (gv = gv_override("require", 7))) {
        OP *kid, *newop;
        if (o->op_flags & OPf_KIDS) {
            kid = cUNOPo->op_first;
@@ -9512,11 +9829,7 @@ Perl_ck_require(pTHX_ OP *o)
 #ifndef PERL_MAD
        op_free(o);
 #endif
-       newop = newUNOP(OP_ENTERSUB, OPf_STACKED,
-                               op_append_elem(OP_LIST, kid,
-                                           scalar(newUNOP(OP_RV2CV, 0,
-                                                          newGVOP(OP_GV, 0,
-                                                                  gv)))));
+       newop = S_new_entersubop(aTHX_ gv, kid);
        op_getmad(o,newop,'O');
        return newop;
     }
@@ -9665,8 +9978,6 @@ S_simplify_sort(pTHX_ OP *o)
 
     PERL_ARGS_ASSERT_SIMPLIFY_SORT;
 
-    GvMULTI_on(gv_fetchpvs("a", GV_ADD|GV_NOTQUAL, SVt_PV));
-    GvMULTI_on(gv_fetchpvs("b", GV_ADD|GV_NOTQUAL, SVt_PV));
     kid = kUNOP->op_first;                             /* get past null */
     if (!(have_scopeop = kid->op_type == OP_SCOPE)
      && kid->op_type != OP_LEAVE)
@@ -10333,7 +10644,7 @@ Perl_ck_entersub_args_core(pTHX_ OP *entersubop, GV *namegv, SV *protosv)
                                   )
                                );
        }
-       assert(0);
+       NOT_REACHED;
     }
     else {
        OP *prev, *cvop;
@@ -10563,6 +10874,9 @@ Perl_ck_svconst(pTHX_ OP *o)
     if (!SvREADONLY(sv) && !SvIsCOW(sv) && SvCANCOW(sv)) {
        SvIsCOW_on(sv);
        CowREFCNT(sv) = 0;
+# ifdef PERL_DEBUG_READONLY_COW
+       sv_buf_to_ro(sv);
+# endif
     }
 #endif
     SvREADONLY_on(sv);
@@ -10657,7 +10971,13 @@ Perl_ck_each(pTHX_ OP *o)
        }
     }
     /* if treating as a reference, defer additional checks to runtime */
-    return o->op_type == ref_type ? o : ck_fun(o);
+    if (o->op_type == ref_type) {
+       /* diag_listed_as: keys on reference is experimental */
+       Perl_ck_warner_d(aTHX_ packWARN(WARN_EXPERIMENTAL__AUTODEREF),
+                             "%s is experimental", PL_op_desc[ref_type]);
+       return o;
+    }
+    return ck_fun(o);
 }
 
 OP *
@@ -10677,19 +10997,9 @@ Perl_ck_length(pTHX_ OP *o)
             switch (kid->op_type) {
                 case OP_PADHV:
                 case OP_PADAV:
-                    name = varname(
-                        (GV *)PL_compcv, hash ? '%' : '@', kid->op_targ,
-                        NULL, 0, 1
-                    );
-                    break;
                 case OP_RV2HV:
                 case OP_RV2AV:
-                    if (cUNOPx(kid)->op_first->op_type != OP_GV) break;
-                    {
-                        GV *gv = cGVOPx_gv(cUNOPx(kid)->op_first);
-                        if (!gv) break;
-                        name = varname(gv, hash?'%':'@', 0, NULL, 0, 1);
-                    }
+                   name = S_op_varname(aTHX_ kid);
                     break;
                 default:
                     return o;
@@ -10809,6 +11119,9 @@ S_inplace_aassign(pTHX_ OP *o) {
     defer_queue[(defer_base + ++defer_ix) % MAX_DEFERRED] = o; \
   } STMT_END
 
+#define IS_AND_OP(o)   (o->op_type == OP_AND)
+#define IS_OR_OP(o)    (o->op_type == OP_OR)
+
 /* A peephole optimizer.  We visit the ops in the order they're to execute.
  * See the comments at the top of this file for more details about when
  * peep() is called */
@@ -10848,6 +11161,110 @@ Perl_rpeep(pTHX_ OP *o)
        case OP_NEXTSTATE:
            PL_curcop = ((COP*)o);              /* for warnings */
 
+           /* Optimise a "return ..." at the end of a sub to just be "...".
+            * This saves 2 ops. Before:
+            * 1  <;> nextstate(main 1 -e:1) v ->2
+            * 4  <@> return K ->5
+            * 2    <0> pushmark s ->3
+            * -    <1> ex-rv2sv sK/1 ->4
+            * 3      <#> gvsv[*cat] s ->4
+            *
+            * After:
+            * -  <@> return K ->-
+            * -    <0> pushmark s ->2
+            * -    <1> ex-rv2sv sK/1 ->-
+            * 2      <$> gvsv(*cat) s ->3
+            */
+           {
+               OP *next = o->op_next;
+               OP *sibling = o->op_sibling;
+               if (   OP_TYPE_IS(next, OP_PUSHMARK)
+                   && OP_TYPE_IS(sibling, OP_RETURN)
+                   && OP_TYPE_IS(sibling->op_next, OP_LINESEQ)
+                   && OP_TYPE_IS(sibling->op_next->op_next, OP_LEAVESUB)
+                   && cUNOPx(sibling)->op_first == next
+                   && next->op_sibling && next->op_sibling->op_next
+                   && next->op_next
+               ) {
+                   /* Look through the PUSHMARK's siblings for one that
+                    * points to the RETURN */
+                   OP *top = next->op_sibling;
+                   while (top && top->op_next) {
+                       if (top->op_next == sibling) {
+                           top->op_next = sibling->op_next;
+                           o->op_next = next->op_next;
+                           break;
+                       }
+                       top = top->op_sibling;
+                   }
+               }
+           }
+
+           /* Optimise 'my $x; my $y;' into 'my ($x, $y);'
+             *
+            * This latter form is then suitable for conversion into padrange
+            * later on. Convert:
+            *
+            *   nextstate1 -> padop1 -> nextstate2 -> padop2 -> nextstate3
+            *
+            * into:
+            *
+            *   nextstate1 ->     listop     -> nextstate3
+            *                 /            \
+            *         pushmark -> padop1 -> padop2
+            */
+           if (o->op_next && (
+                   o->op_next->op_type == OP_PADSV
+                || o->op_next->op_type == OP_PADAV
+                || o->op_next->op_type == OP_PADHV
+               )
+               && !(o->op_next->op_private & ~OPpLVAL_INTRO)
+               && o->op_next->op_next && o->op_next->op_next->op_type == OP_NEXTSTATE
+               && o->op_next->op_next->op_next && (
+                   o->op_next->op_next->op_next->op_type == OP_PADSV
+                || o->op_next->op_next->op_next->op_type == OP_PADAV
+                || o->op_next->op_next->op_next->op_type == OP_PADHV
+               )
+               && !(o->op_next->op_next->op_next->op_private & ~OPpLVAL_INTRO)
+               && o->op_next->op_next->op_next->op_next && o->op_next->op_next->op_next->op_next->op_type == OP_NEXTSTATE
+               && (!CopLABEL((COP*)o)) /* Don't mess with labels */
+               && (!CopLABEL((COP*)o->op_next->op_next)) /* ... */
+           ) {
+               OP *first;
+               OP *last;
+               OP *newop;
+
+               first = o->op_next;
+               last = o->op_next->op_next->op_next;
+
+               newop = newLISTOP(OP_LIST, 0, first, last);
+               newop->op_flags |= OPf_PARENS;
+               newop->op_flags = (newop->op_flags & ~OPf_WANT) | OPf_WANT_VOID;
+
+               /* Kill nextstate2 between padop1/padop2 */
+               op_free(first->op_next);
+
+               first->op_next = last;                /* padop2 */
+               first->op_sibling = last;             /* ... */
+               o->op_next = cUNOPx(newop)->op_first; /* pushmark */
+               o->op_next->op_next = first;          /* padop1 */
+               o->op_next->op_sibling = first;       /* ... */
+               newop->op_next = last->op_next;       /* nextstate3 */
+               newop->op_sibling = last->op_sibling;
+               last->op_next = newop;                /* listop */
+               last->op_sibling = NULL;
+               o->op_sibling = newop;                /* ... */
+
+               newop->op_flags = (newop->op_flags & ~OPf_WANT) | OPf_WANT_VOID;
+
+               /* Ensure pushmark has this flag if padops do */
+               if (first->op_flags & OPf_MOD && last->op_flags & OPf_MOD) {
+                   o->op_next->op_flags |= OPf_MOD;
+               }
+
+               break;
+           }
+
            /* 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.  */
@@ -11027,7 +11444,7 @@ Perl_rpeep(pTHX_ OP *o)
                 )
                     break;
 
-                /* let $a[N] potentially be optimised into ALEMFAST_LEX
+                /* let $a[N] potentially be optimised into AELEMFAST_LEX
                  * instead */
                 if (   p->op_type == OP_PADAV
                     && p->op_next
@@ -11122,9 +11539,14 @@ Perl_rpeep(pTHX_ OP *o)
 
                         old_count
                             = (oldoldop->op_private & OPpPADRANGE_COUNTMASK);
-                        assert(oldoldop->op_targ + old_count == base);
 
-                        if (old_count < OPpPADRANGE_COUNTMASK - count) {
+                       /* Do not assume pad offsets for $c and $d are con-
+                          tiguous in
+                            my ($a,$b,$c);
+                            my ($d,$e,$f);
+                        */
+                        if (  oldoldop->op_targ + old_count == base
+                           && old_count < OPpPADRANGE_COUNTMASK - count) {
                             base = oldoldop->op_targ;
                             count += old_count;
                             reuse = 1;
@@ -11271,6 +11693,21 @@ Perl_rpeep(pTHX_ OP *o)
            while (o->op_next && (   o->op_type == o->op_next->op_type
                                  || o->op_next->op_type == OP_NULL))
                o->op_next = o->op_next->op_next;
+
+           /* if we're an OR and our next is a AND in void context, we'll
+              follow it's op_other on short circuit, same for reverse.
+              We can't do this with OP_DOR since if it's true, its return
+              value is the underlying value which must be evaluated
+              by the next op */
+           if (o->op_next &&
+               (
+                   (IS_AND_OP(o) && IS_OR_OP(o->op_next))
+                || (IS_OR_OP(o) && IS_AND_OP(o->op_next))
+               )
+               && (o->op_next->op_flags & OPf_WANT) == OPf_WANT_VOID
+           ) {
+               o->op_next = ((LOGOP*)o->op_next)->op_other;
+           }
            DEFER(cLOGOP->op_other);
           
            o->op_opt = 1;
@@ -11533,7 +11970,7 @@ Perl_rpeep(pTHX_ OP *o)
 
        case OP_CUSTOM: {
            Perl_cpeep_t cpeep = 
-               XopENTRY(Perl_custom_op_xop(aTHX_ o), xop_peep);
+               XopENTRYCUSTOM(o, xop_peep);
            if (cpeep)
                cpeep(aTHX_ o, oldop);
            break;
@@ -11556,14 +11993,17 @@ Perl_peep(pTHX_ OP *o)
 =head1 Custom Operators
 
 =for apidoc Ao||custom_op_xop
-Return the XOP structure for a given custom op. This function should be
+Return the XOP structure for a given custom op.  This macro should be
 considered internal to OP_NAME and the other access macros: use them instead.
+This macro does call a function.  Prior
+to 5.19.9, this was implemented as a
+function.
 
 =cut
 */
 
-const XOP *
-Perl_custom_op_xop(pTHX_ const OP *o)
+XOPRETANY
+Perl_custom_op_get_field(pTHX_ const OP *o, const xop_flags_enum field)
 {
     SV *keysv;
     HE *he = NULL;
@@ -11571,7 +12011,7 @@ Perl_custom_op_xop(pTHX_ const OP *o)
 
     static const XOP xop_null = { 0, 0, 0, 0, 0 };
 
-    PERL_ARGS_ASSERT_CUSTOM_OP_XOP;
+    PERL_ARGS_ASSERT_CUSTOM_OP_GET_FIELD;
     assert(o->op_type == OP_CUSTOM);
 
     /* This is wrong. It assumes a function pointer can be cast to IV,
@@ -11603,18 +12043,64 @@ Perl_custom_op_xop(pTHX_ const OP *o)
            XopENTRY_set(xop, xop_desc, savepvn(pv, l));
        }
        Perl_custom_op_register(aTHX_ o->op_ppaddr, xop);
-       return xop;
     }
-
-    if (!he) return &xop_null;
-
-    xop = INT2PTR(XOP *, SvIV(HeVAL(he)));
-    return xop;
+    else {
+       if (!he)
+           xop = (XOP *)&xop_null;
+       else
+           xop = INT2PTR(XOP *, SvIV(HeVAL(he)));
+    }
+    {
+       XOPRETANY any;
+       if(field == XOPe_xop_ptr) {
+           any.xop_ptr = xop;
+       } else {
+           const U32 flags = XopFLAGS(xop);
+           if(flags & field) {
+               switch(field) {
+               case XOPe_xop_name:
+                   any.xop_name = xop->xop_name;
+                   break;
+               case XOPe_xop_desc:
+                   any.xop_desc = xop->xop_desc;
+                   break;
+               case XOPe_xop_class:
+                   any.xop_class = xop->xop_class;
+                   break;
+               case XOPe_xop_peep:
+                   any.xop_peep = xop->xop_peep;
+                   break;
+               default:
+                   NOT_REACHED;
+                   break;
+               }
+           } else {
+               switch(field) {
+               case XOPe_xop_name:
+                   any.xop_name = XOPd_xop_name;
+                   break;
+               case XOPe_xop_desc:
+                   any.xop_desc = XOPd_xop_desc;
+                   break;
+               case XOPe_xop_class:
+                   any.xop_class = XOPd_xop_class;
+                   break;
+               case XOPe_xop_peep:
+                   any.xop_peep = XOPd_xop_peep;
+                   break;
+               default:
+                   NOT_REACHED;
+                   break;
+               }
+           }
+       }
+       return any;
+    }
 }
 
 /*
 =for apidoc Ao||custom_op_register
-Register a custom op. See L<perlguts/"Custom Operators">.
+Register a custom op.  See L<perlguts/"Custom Operators">.
 
 =cut
 */
@@ -11643,7 +12129,7 @@ Perl_custom_op_register(pTHX_ Perl_ppaddr_t ppaddr, const XOP *xop)
 This function assigns the prototype of the named core function to C<sv>, or
 to a new mortal SV if C<sv> is NULL.  It returns the modified C<sv>, or
 NULL if the core function has no prototype.  C<code> is a code as returned
-by C<keyword()>.  It must not be equal to 0 or -KEY_CORE.
+by C<keyword()>.  It must not be equal to 0.
 
 =cut
 */
@@ -11660,7 +12146,7 @@ Perl_core_prototype(pTHX_ SV *sv, const char *name, const int code,
 
     PERL_ARGS_ASSERT_CORE_PROTOTYPE;
 
-    assert (code && code != -KEY_CORE);
+    assert (code);
 
     if (!sv) sv = sv_newmortal();