This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Merge B::PMOP::precomp and B::PMOP::reflags, using the ALIAS mechanism.
[perl5.git] / op.c
diff --git a/op.c b/op.c
index 86b933f..ce9c220 100644 (file)
--- a/op.c
+++ b/op.c
@@ -717,7 +717,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
@@ -843,14 +843,22 @@ Perl_op_contextualize(pTHX_ OP *o, I32 context)
     }
 }
 
-#define LINKLIST(o) ((o)->op_next ? (o)->op_next : linklist((OP*)o))
+/*
+=head1 Optree Manipulation Functions
 
-static OP *
-S_linklist(pTHX_ OP *o)
+=for apidoc Am|OP*|op_linklist|OP *o
+This function is the implementation of the L</LINKLIST> macro. It should
+not be called directly.
+
+=cut
+*/
+
+OP *
+Perl_op_linklist(pTHX_ OP *o)
 {
     OP *first;
 
-    PERL_ARGS_ASSERT_LINKLIST;
+    PERL_ARGS_ASSERT_OP_LINKLIST;
 
     if (o->op_next)
        return o->op_next;
@@ -1389,24 +1397,28 @@ 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.  It also flags things
+that need to behave specially in an lvalue context, such as C<$$x>
+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;
@@ -1590,7 +1602,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:
@@ -1678,7 +1690,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:
@@ -1699,7 +1711,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:
@@ -1709,20 +1721,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
@@ -1959,7 +1971,7 @@ S_dup_attrlist(pTHX_ OP *o)
        rop = NULL;
        for (o = cLISTOPo->op_first; o; o=o->op_sibling) {
            if (o->op_type == OP_CONST)
-               rop = append_elem(OP_LIST, rop,
+               rop = op_append_elem(OP_LIST, rop,
                                  newSVOP(OP_CONST, o->op_flags,
                                          SvREFCNT_inc_NN(cSVOPo->op_sv)));
        }
@@ -1995,9 +2007,9 @@ S_apply_attrs(pTHX_ HV *stash, SV *target, OP *attrs, bool for_my)
        Perl_load_module(aTHX_ PERL_LOADMOD_IMPORT_OPS,
                         newSVpvs(ATTRSMODULE),
                         NULL,
-                        prepend_elem(OP_LIST,
+                        op_prepend_elem(OP_LIST,
                                      newSVOP(OP_CONST, 0, stashsv),
-                                     prepend_elem(OP_LIST,
+                                     op_prepend_elem(OP_LIST,
                                                   newSVOP(OP_CONST, 0,
                                                           newRV(target)),
                                                   dup_attrlist(attrs))));
@@ -2032,23 +2044,23 @@ S_apply_attrs_my(pTHX_ HV *stash, OP *target, OP *attrs, OP **imopsp)
 
     arg = newOP(OP_PADSV, 0);
     arg->op_targ = target->op_targ;
-    arg = prepend_elem(OP_LIST,
+    arg = op_prepend_elem(OP_LIST,
                       newSVOP(OP_CONST, 0, stashsv),
-                      prepend_elem(OP_LIST,
+                      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 */
     meth = newSVpvs_share("import");
     imop = convert(OP_ENTERSUB, OPf_STACKED|OPf_SPECIAL|OPf_WANT_VOID,
-                  append_elem(OP_LIST,
-                              prepend_elem(OP_LIST, pack, list(arg)),
+                  op_append_elem(OP_LIST,
+                              op_prepend_elem(OP_LIST, pack, list(arg)),
                               newSVOP(OP_METHOD_NAMED, 0, meth)));
     imop->op_private |= OPpENTERSUB_NOMOD;
 
     /* Combine the ops. */
-    *imopsp = append_elem(OP_LIST, *imopsp, imop);
+    *imopsp = op_append_elem(OP_LIST, *imopsp, imop);
 }
 
 /*
@@ -2085,7 +2097,7 @@ Perl_apply_attrs_string(pTHX_ const char *stashpv, CV *cv,
         if (len) {
             const char * const sstr = attrstr;
             for (; !isSPACE(*attrstr) && len; --len, ++attrstr) ;
-            attrs = append_elem(OP_LIST, attrs,
+            attrs = op_append_elem(OP_LIST, attrs,
                                 newSVOP(OP_CONST, 0,
                                         newSVpvn(sstr, attrstr-sstr)));
         }
@@ -2093,9 +2105,9 @@ Perl_apply_attrs_string(pTHX_ const char *stashpv, CV *cv,
 
     Perl_load_module(aTHX_ PERL_LOADMOD_IMPORT_OPS,
                     newSVpvs(ATTRSMODULE),
-                     NULL, prepend_elem(OP_LIST,
+                     NULL, op_prepend_elem(OP_LIST,
                                  newSVOP(OP_CONST, 0, newSVpv(stashpv,0)),
-                                 prepend_elem(OP_LIST,
+                                 op_prepend_elem(OP_LIST,
                                               newSVOP(OP_CONST, 0,
                                                       newRV(MUTABLE_SV(cv))),
                                                attrs)));
@@ -2206,11 +2218,11 @@ Perl_my_attrs(pTHX_ OP *o, OP *attrs)
     o = my_kid(o, attrs, &rops);
     if (rops) {
        if (maybe_scalar && o->op_type == OP_PADSV) {
-           o = scalar(append_list(OP_LIST, (LISTOP*)rops, (LISTOP*)o));
+           o = scalar(op_append_list(OP_LIST, rops, o));
            o->op_private |= OPpLVAL_INTRO;
        }
        else
-           o = append_list(OP_LIST, (LISTOP*)o, (LISTOP*)rops);
+           o = op_append_list(OP_LIST, o, rops);
     }
     PL_parser->in_my = FALSE;
     PL_parser->in_my_stash = NULL;
@@ -2278,13 +2290,13 @@ Perl_bind_match(pTHX_ I32 type, OP *left, OP *right)
                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)
            o = newBINOP(OP_NULL, OPf_STACKED, scalar(newleft), right);
        else
-           o = prepend_elem(rtype, scalar(newleft), right);
+           o = op_prepend_elem(rtype, scalar(newleft), right);
        if (type == OP_NOT)
            return newUNOP(OP_NOT, 0, scalar(o));
        return o;
@@ -2302,13 +2314,27 @@ 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) {
        if (o->op_flags & OPf_PARENS || PERLDB_NOOPT || PL_tainting) {
-           o = prepend_elem(OP_LINESEQ, newOP(OP_ENTER, 0), o);
+           o = op_prepend_elem(OP_LINESEQ, newOP(OP_ENTER, 0), o);
            o->op_type = OP_LEAVE;
            o->op_ppaddr = PL_ppaddr[OP_LEAVE];
        }
@@ -2345,7 +2371,7 @@ Perl_block_start(pTHX_ int full)
     SAVECOMPILEWARNINGS();
     PL_compiling.cop_warnings = DUP_WARNINGS(PL_compiling.cop_warnings);
 
-    CALL_BLOCK_HOOKS(start, full);
+    CALL_BLOCK_HOOKS(bhk_start, full);
 
     return retval;
 }
@@ -2357,7 +2383,7 @@ Perl_block_end(pTHX_ I32 floor, OP *seq)
     const int needblockscope = PL_hints & HINT_BLOCK_SCOPE;
     OP* retval = scalarseq(seq);
 
-    CALL_BLOCK_HOOKS(pre_end, &retval);
+    CALL_BLOCK_HOOKS(bhk_pre_end, &retval);
 
     LEAVE_SCOPE(floor);
     CopHINTS_set(&PL_compiling, PL_hints);
@@ -2365,7 +2391,7 @@ Perl_block_end(pTHX_ I32 floor, OP *seq)
        PL_hints |= HINT_BLOCK_SCOPE; /* propagate out */
     pad_leavemy();
 
-    CALL_BLOCK_HOOKS(post_end, &retval);
+    CALL_BLOCK_HOOKS(bhk_post_end, &retval);
 
     return retval;
 }
@@ -2417,7 +2443,10 @@ Perl_newPROG(pTHX_ OP *o)
        PL_eval_root = newUNOP(OP_LEAVEEVAL,
                               ((PL_in_eval & EVAL_KEEPERR)
                                ? OPf_SPECIAL : 0), o);
-       PL_eval_start = linklist(PL_eval_root);
+       /* don't use LINKLIST, since PL_eval_root might indirect through
+        * a rather expensive function call and LINKLIST evaluates its
+        * argument more than once */
+       PL_eval_start = op_linklist(PL_eval_root);
        PL_eval_root->op_private |= OPpREFCOUNTED;
        OpREFCNT_set(PL_eval_root, 1);
        PL_eval_root->op_next = 0;
@@ -2430,7 +2459,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;
@@ -2509,7 +2538,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;
@@ -2523,7 +2552,7 @@ Perl_jmaybe(pTHX_ OP *o)
     if (o->op_type == OP_LIST) {
        OP * const o2
            = newSVREF(newGVOP(OP_GV, 0, gv_fetchpvs(";", GV_ADD|GV_NOTQUAL, SVt_PV)));
-       o = convert(OP_JOIN, 0, prepend_elem(OP_LIST, o2, o));
+       o = convert(OP_JOIN, 0, op_prepend_elem(OP_LIST, o2, o));
     }
     return o;
 }
@@ -2577,6 +2606,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;
@@ -2703,7 +2733,7 @@ S_gen_constant_list(pTHX_ register OP *o)
 #else
     op_free(curop);
 #endif
-    linklist(o);
+    LINKLIST(o);
     return list(o);
 }
 
@@ -2730,10 +2760,27 @@ Perl_convert(pTHX_ I32 type, I32 flags, OP *o)
     return fold_constants(o);
 }
 
+/*
+=head1 Optree Manipulation Functions
+*/
+
 /* List constructors */
 
+/*
+=for apidoc Am|OP *|op_append_elem|I32 optype|OP *first|OP *last
+
+Append an item to the list of ops contained directly within a list-type
+op, returning the lengthened list.  I<first> is the list-type op,
+and I<last> is the op to append to the list.  I<optype> specifies the
+intended opcode for the list.  If I<first> is not already a list of the
+right type, it will be upgraded into one.  If either I<first> or I<last>
+is null, the other is returned unchanged.
+
+=cut
+*/
+
 OP *
-Perl_append_elem(pTHX_ I32 type, OP *first, OP *last)
+Perl_op_append_elem(pTHX_ I32 type, OP *first, OP *last)
 {
     if (!first)
        return last;
@@ -2757,48 +2804,74 @@ Perl_append_elem(pTHX_ I32 type, OP *first, OP *last)
     return first;
 }
 
+/*
+=for apidoc Am|OP *|op_append_list|I32 optype|OP *first|OP *last
+
+Concatenate the lists of ops contained directly within two list-type ops,
+returning the combined list.  I<first> and I<last> are the list-type ops
+to concatenate.  I<optype> specifies the intended opcode for the list.
+If either I<first> or I<last> is not already a list of the right type,
+it will be upgraded into one.  If either I<first> or I<last> is null,
+the other is returned unchanged.
+
+=cut
+*/
+
 OP *
-Perl_append_list(pTHX_ I32 type, LISTOP *first, LISTOP *last)
+Perl_op_append_list(pTHX_ I32 type, OP *first, OP *last)
 {
     if (!first)
-       return (OP*)last;
+       return last;
 
     if (!last)
-       return (OP*)first;
+       return first;
 
     if (first->op_type != (unsigned)type)
-       return prepend_elem(type, (OP*)first, (OP*)last);
+       return op_prepend_elem(type, first, last);
 
     if (last->op_type != (unsigned)type)
-       return append_elem(type, (OP*)first, (OP*)last);
+       return op_append_elem(type, first, last);
 
-    first->op_last->op_sibling = last->op_first;
-    first->op_last = last->op_last;
+    ((LISTOP*)first)->op_last->op_sibling = ((LISTOP*)last)->op_first;
+    ((LISTOP*)first)->op_last = ((LISTOP*)last)->op_last;
     first->op_flags |= (last->op_flags & OPf_KIDS);
 
 #ifdef PERL_MAD
-    if (last->op_first && first->op_madprop) {
-       MADPROP *mp = last->op_first->op_madprop;
+    if (((LISTOP*)last)->op_first && first->op_madprop) {
+       MADPROP *mp = ((LISTOP*)last)->op_first->op_madprop;
        if (mp) {
            while (mp->mad_next)
                mp = mp->mad_next;
            mp->mad_next = first->op_madprop;
        }
        else {
-           last->op_first->op_madprop = first->op_madprop;
+           ((LISTOP*)last)->op_first->op_madprop = first->op_madprop;
        }
     }
     first->op_madprop = last->op_madprop;
     last->op_madprop = 0;
 #endif
 
-    S_op_destroy(aTHX_ (OP*)last);
+    S_op_destroy(aTHX_ last);
 
-    return (OP*)first;
+    return first;
 }
 
+/*
+=for apidoc Am|OP *|op_prepend_elem|I32 optype|OP *first|OP *last
+
+Prepend an item to the list of ops contained directly within a list-type
+op, returning the lengthened list.  I<first> is the op to prepend to the
+list, and I<last> is the list-type op.  I<optype> specifies the intended
+opcode for the list.  If I<last> is not already a list of the right type,
+it will be upgraded into one.  If either I<first> or I<last> is null,
+the other is returned unchanged.
+
+=cut
+*/
+
 OP *
-Perl_prepend_elem(pTHX_ I32 type, OP *first, OP *last)
+Perl_op_prepend_elem(pTHX_ I32 type, OP *first, OP *last)
 {
     if (!first)
        return last;
@@ -3680,6 +3753,19 @@ Perl_newPMOP(pTHX_ I32 type, I32 flags)
     else if ((! (PL_hints & HINT_BYTES)) && (PL_hints & HINT_UNI_8_BIT)) {
         pmop->op_pmflags |= RXf_PMf_UNICODE;
     }
+    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_dul"), 0, 0
+        );
+        if (reflags && SvOK(reflags)) {
+            pmop->op_pmflags &= ~(RXf_PMf_LOCALE|RXf_PMf_UNICODE);
+            pmop->op_pmflags |= SvIV(reflags);
+        }
+    }
 
 
 #ifdef USE_ITHREADS
@@ -3824,7 +3910,7 @@ Perl_pmruntime(pTHX_ OP *o, OP *expr, bool isreg)
            expr->op_next = (OP*)rcop;
        }
 
-       prepend_elem(o->op_type, scalar((OP*)rcop), o);
+       op_prepend_elem(o->op_type, scalar((OP*)rcop), o);
     }
 
     if (repl) {
@@ -3878,7 +3964,7 @@ Perl_pmruntime(pTHX_ OP *o, OP *expr, bool isreg)
                     || RX_EXTFLAGS(PM_GETRE(pm)) & RXf_EVAL_SEEN)))
        {
            pm->op_pmflags |= PMf_CONST;        /* const for long enough */
-           prepend_elem(o->op_type, scalar(repl), o);
+           op_prepend_elem(o->op_type, scalar(repl), o);
        }
        else {
            if (curop == repl && !PM_GETRE(pm)) { /* Has variables. */
@@ -4148,8 +4234,8 @@ Perl_utilize(pTHX_ int aver, I32 floor, OP *version, OP *idop, OP *arg)
            /* Fake up a method call to VERSION */
            meth = newSVpvs_share("VERSION");
            veop = convert(OP_ENTERSUB, OPf_STACKED|OPf_SPECIAL,
-                           append_elem(OP_LIST,
-                                       prepend_elem(OP_LIST, pack, list(version)),
+                           op_append_elem(OP_LIST,
+                                       op_prepend_elem(OP_LIST, pack, list(version)),
                                        newSVOP(OP_METHOD_NAMED, 0, meth)));
        }
     }
@@ -4178,8 +4264,8 @@ Perl_utilize(pTHX_ int aver, I32 floor, OP *version, OP *idop, OP *arg)
        meth = aver
            ? newSVpvs_share("import") : newSVpvs_share("unimport");
        imop = convert(OP_ENTERSUB, OPf_STACKED|OPf_SPECIAL,
-                      append_elem(OP_LIST,
-                                  prepend_elem(OP_LIST, pack, list(arg)),
+                      op_append_elem(OP_LIST,
+                                  op_prepend_elem(OP_LIST, pack, list(arg)),
                                   newSVOP(OP_METHOD_NAMED, 0, meth)));
     }
 
@@ -4188,8 +4274,8 @@ Perl_utilize(pTHX_ int aver, I32 floor, OP *version, OP *idop, OP *arg)
        newSVOP(OP_CONST, 0, newSVpvs_share("BEGIN")),
        NULL,
        NULL,
-       append_elem(OP_LINESEQ,
-           append_elem(OP_LINESEQ,
+       op_append_elem(OP_LINESEQ,
+           op_append_elem(OP_LINESEQ,
                newSTATEOP(0, NULL, newUNOP(OP_REQUIRE, 0, idop)),
                newSTATEOP(0, NULL, veop)),
            newSTATEOP(0, NULL, imop) ));
@@ -4297,7 +4383,7 @@ Perl_vload_module(pTHX_ U32 flags, SV *name, SV *ver, va_list *args)
        imop = NULL;
        sv = va_arg(*args, SV*);
        while (sv) {
-           imop = append_elem(OP_LIST, imop, newSVOP(OP_CONST, 0, sv));
+           imop = op_append_elem(OP_LIST, imop, newSVOP(OP_CONST, 0, sv));
            sv = va_arg(*args, SV*);
        }
     }
@@ -4310,7 +4396,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;
@@ -4335,7 +4421,7 @@ Perl_dofile(pTHX_ OP *term, I32 force_builtin)
 
     if (gv && GvCVu(gv) && GvIMPORTED_CV(gv)) {
        doop = ck_subr(newUNOP(OP_ENTERSUB, OPf_STACKED,
-                              append_elem(OP_LIST, term,
+                              op_append_elem(OP_LIST, term,
                                           scalar(newUNOP(OP_RV2CV, 0,
                                                          newGVOP(OP_GV, 0, gv))))));
     }
@@ -4445,12 +4531,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));
        }
     }
 
@@ -4464,7 +4550,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) {
@@ -4664,12 +4750,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 {
@@ -4731,12 +4818,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);
                                                     
@@ -4775,7 +4857,7 @@ Perl_newSTATEOP(pTHX_ I32 flags, char *label, OP *o)
 
     if (flags & OPf_SPECIAL)
        op_null((OP*)cop);
-    return prepend_elem(OP_LINESEQ, (OP*)cop, o);
+    return op_prepend_elem(OP_LINESEQ, (OP*)cop, o);
 }
 
 /*
@@ -5137,7 +5219,7 @@ Perl_newRANGE(pTHX_ I32 flags, OP *left, OP *right)
     flip = newUNOP(OP_FLIP, flags, (OP*)range);
     flop = newUNOP(OP_FLOP, 0, flip);
     o = newUNOP(OP_NULL, 0, flop);
-    linklist(flop);
+    LINKLIST(flop);
     range->op_next = leftstart;
 
     left->op_next = flip;
@@ -5153,7 +5235,7 @@ Perl_newRANGE(pTHX_ I32 flags, OP *left, OP *right)
 
     flip->op_next = o;
     if (!flip->op_private || !flop->op_private)
-       linklist(o);            /* blow off optimizer unless constant */
+       LINKLIST(o);            /* blow off optimizer unless constant */
 
     return o;
 }
@@ -5216,11 +5298,11 @@ Perl_newLOOPOP(pTHX_ I32 flags, I32 debuggable, OP *expr, OP *block)
        }
     }
 
-    /* if block is null, the next append_elem() would put UNSTACK, a scalar
+    /* if block is null, the next op_append_elem() would put UNSTACK, a scalar
      * op, in listop. This is wrong. [perl #27024] */
     if (!block)
        block = newOP(OP_NULL, 0);
-    listop = append_elem(OP_LINESEQ, block, newOP(OP_UNSTACK, 0));
+    listop = op_append_elem(OP_LINESEQ, block, newOP(OP_UNSTACK, 0));
     o = new_logop(OP_AND, 0, &expr, &listop);
 
     if (listop)
@@ -5233,13 +5315,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
@@ -5256,16 +5338,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;
@@ -5308,7 +5389,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) {
@@ -5318,16 +5399,15 @@ whileline, OP *expr, OP *block, OP *cont, I32 has_my)
        OP * const unstack = newOP(OP_UNSTACK, 0);
        if (!next)
            next = unstack;
-       cont = append_elem(OP_LINESEQ, cont, unstack);
+       cont = op_append_elem(OP_LINESEQ, cont, unstack);
     }
 
     assert(block);
-    listop = append_list(OP_LINESEQ, (LISTOP*)block, (LISTOP*)cont);
+    listop = op_append_list(OP_LINESEQ, block, cont);
     assert(listop);
     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)) {
@@ -5367,7 +5447,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,
@@ -5384,17 +5464,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;
@@ -5455,7 +5531,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 &&
@@ -5491,11 +5567,11 @@ 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,
-                              append_elem(OP_LIST, expr, scalar(sv))));
+                              op_append_elem(OP_LIST, expr, scalar(sv))));
     assert(!loop->op_next);
     /* for my  $x () sets OPpLVAL_INTRO;
      * for our $x () sets OPpOUR_INTRO */
@@ -5512,11 +5588,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;
 }
 
 /*
@@ -5559,7 +5634,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;
@@ -5578,8 +5653,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
@@ -5590,7 +5664,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
@@ -5788,7 +5862,7 @@ Perl_newWHENOP(pTHX_ OP *cond, OP *block)
     
     return newGIVWHENOP(
        cond_op,
-       append_elem(block->op_type, block, newOP(OP_BREAK, OPf_SPECIAL)),
+       op_append_elem(block->op_type, block, newOP(OP_BREAK, OPf_SPECIAL)),
        OP_ENTERWHEN, OP_LEAVEWHEN, 0);
 }
 
@@ -6224,8 +6298,6 @@ Perl_newATTRSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block)
            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. */
@@ -6253,9 +6325,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>. */
@@ -6297,7 +6367,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 {
@@ -7032,7 +7102,7 @@ Perl_ck_eval(pTHX_ OP *o)
            /* establish postfix order */
            enter->op_next = (OP*)enter;
 
-           o = prepend_elem(OP_LINESEQ, (OP*)enter, (OP*)kid);
+           o = op_prepend_elem(OP_LINESEQ, (OP*)enter, (OP*)kid);
            o->op_type = OP_LEAVETRY;
            o->op_ppaddr = PL_ppaddr[OP_LEAVETRY];
            enter->op_other = o;
@@ -7228,6 +7298,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;
@@ -7363,7 +7435,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 &&
@@ -7385,13 +7457,13 @@ 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:
                {
                    OP * const newop = newUNOP(OP_NULL, 0, kid);
                    kid->op_sibling = 0;
-                   linklist(kid);
+                   LINKLIST(kid);
                    newop->op_next = newop;
                    kid = newop;
                    kid->op_sibling = sibl;
@@ -7492,7 +7564,7 @@ Perl_ck_fun(pTHX_ OP *o)
                                      name = "__ANONIO__";
                                      len = 10;
                                 }
-                                mod(kid, type);
+                                op_lvalue(kid, type);
                            }
                            if (name) {
                                SV *namesv;
@@ -7515,7 +7587,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;
@@ -7565,7 +7637,7 @@ Perl_ck_glob(pTHX_ OP *o)
 
     o = ck_fun(o);
     if ((o->op_flags & OPf_KIDS) && !cLISTOPo->op_first->op_sibling)
-       append_elem(OP_GLOB, o, newDEFSVOP());
+       op_append_elem(OP_GLOB, o, newDEFSVOP());
 
     if (!((gv = gv_fetchpvs("glob", GV_NOTQUAL, SVt_PVCV))
          && GvCVu(gv) && GvIMPORTED_CV(gv)))
@@ -7591,7 +7663,7 @@ Perl_ck_glob(pTHX_ OP *o)
 #endif /* PERL_EXTERNAL_GLOB */
 
     if (gv && GvCVu(gv) && GvIMPORTED_CV(gv)) {
-       append_elem(OP_GLOB, o,
+       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];
@@ -7599,7 +7671,7 @@ Perl_ck_glob(pTHX_ OP *o)
        cLISTOPo->op_first->op_ppaddr = PL_ppaddr[OP_PUSHMARK];
        cLISTOPo->op_first->op_targ = 0;
        o = newUNOP(OP_ENTERSUB, OPf_STACKED,
-                   append_elem(OP_LIST, o,
+                   op_append_elem(OP_LIST, o,
                                scalar(newUNOP(OP_RV2CV, 0,
                                               newGVOP(OP_GV, 0, gv)))));
        o = newUNOP(OP_NULL, 0, ck_subr(o));
@@ -7608,7 +7680,7 @@ Perl_ck_glob(pTHX_ OP *o)
     }
     gv = newGVgen("main");
     gv_IOadd(gv);
-    append_elem(OP_GLOB, o, newGVOP(OP_GV, 0, gv));
+    op_append_elem(OP_GLOB, o, newGVOP(OP_GV, 0, gv));
     scalarkids(o);
     return o;
 }
@@ -7675,7 +7747,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;
 }
@@ -7795,7 +7867,7 @@ Perl_ck_listiob(pTHX_ OP *o)
     }
 
     if (!kid)
-       append_elem(o->op_type, o, newDEFSVOP());
+       op_append_elem(o->op_type, o, newDEFSVOP());
 
     return listkids(o);
 }
@@ -8093,7 +8165,7 @@ Perl_ck_require(pTHX_ OP *o)
        op_free(o);
 #endif
        newop = ck_subr(newUNOP(OP_ENTERSUB, OPf_STACKED,
-                               append_elem(OP_LIST, kid,
+                               op_append_elem(OP_LIST, kid,
                                            scalar(newUNOP(OP_RV2CV, 0,
                                                           newGVOP(OP_GV, 0,
                                                                   gv))))));
@@ -8115,7 +8187,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)
@@ -8220,7 +8292,7 @@ Perl_ck_sort(pTHX_ OP *o)
        OP *kid = cUNOPx(firstkid)->op_first;           /* get past null */
 
        if (kid->op_type == OP_SCOPE || kid->op_type == OP_LEAVE) {
-           linklist(kid);
+           LINKLIST(kid);
            if (kid->op_type == OP_SCOPE) {
                k = kid->op_next;
                kid->op_next = 0;
@@ -8388,13 +8460,13 @@ Perl_ck_split(pTHX_ OP *o)
     }
 
     if (!kid->op_sibling)
-       append_elem(OP_SPLIT, o, newDEFSVOP());
+       op_append_elem(OP_SPLIT, o, newDEFSVOP());
 
     kid = kid->op_sibling;
     scalar(kid);
 
     if (!kid->op_sibling)
-       append_elem(OP_SPLIT, o, newSVOP(OP_CONST, 0, newSViv(0)));
+       op_append_elem(OP_SPLIT, o, newSVOP(OP_CONST, 0, newSViv(0)));
     assert(kid->op_sibling);
 
     kid = kid->op_sibling;
@@ -8537,7 +8609,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;
@@ -8607,167 +8679,179 @@ Perl_ck_entersub_args_proto(pTHX_ OP *entersubop, GV *namegv, SV *protosv)
            return too_many_arguments(entersubop, gv_ename(namegv));
 
        switch (*proto) {
-       case ';':
-           optional = 1;
-           proto++;
-           continue;
-       case '_':
-           /* _ must be at the end */
-           if (proto[1] && proto[1] != ';')
-               goto oops;
-       case '$':
-           proto++;
-           arg++;
-           scalar(aop);
-           break;
-       case '%':
-       case '@':
-           list(aop);
-           arg++;
-           break;
-       case '&':
-           proto++;
-           arg++;
-           if (o3->op_type != OP_REFGEN && o3->op_type != OP_UNDEF)
-               bad_type(arg,
-                   arg == 1 ? "block or sub {}" : "sub {}",
-                   gv_ename(namegv), o3);
-           break;
-       case '*':
-           /* '*' allows any scalar type, including bareword */
-           proto++;
-           arg++;
-           if (o3->op_type == OP_RV2GV)
-               goto wrapref;   /* autoconvert GLOB -> GLOBref */
-           else if (o3->op_type == OP_CONST)
-               o3->op_private &= ~OPpCONST_STRICT;
-           else if (o3->op_type == OP_ENTERSUB) {
-               /* accidental subroutine, revert to bareword */
-               OP *gvop = ((UNOP*)o3)->op_first;
-               if (gvop && gvop->op_type == OP_NULL) {
-                   gvop = ((UNOP*)gvop)->op_first;
-                   if (gvop) {
-                       for (; gvop->op_sibling; gvop = gvop->op_sibling)
-                           ;
-                       if (gvop &&
-                           (gvop->op_private & OPpENTERSUB_NOPAREN) &&
-                           (gvop = ((UNOP*)gvop)->op_first) &&
-                           gvop->op_type == OP_GV)
-                       {
-                           GV * const gv = cGVOPx_gv(gvop);
-                           OP * const sibling = aop->op_sibling;
-                           SV * const n = newSVpvs("");
+           case ';':
+               optional = 1;
+               proto++;
+               continue;
+           case '_':
+               /* _ must be at the end */
+               if (proto[1] && proto[1] != ';')
+                   goto oops;
+           case '$':
+               proto++;
+               arg++;
+               scalar(aop);
+               break;
+           case '%':
+           case '@':
+               list(aop);
+               arg++;
+               break;
+           case '&':
+               proto++;
+               arg++;
+               if (o3->op_type != OP_REFGEN && o3->op_type != OP_UNDEF)
+                   bad_type(arg,
+                           arg == 1 ? "block or sub {}" : "sub {}",
+                           gv_ename(namegv), o3);
+               break;
+           case '*':
+               /* '*' allows any scalar type, including bareword */
+               proto++;
+               arg++;
+               if (o3->op_type == OP_RV2GV)
+                   goto wrapref;       /* autoconvert GLOB -> GLOBref */
+               else if (o3->op_type == OP_CONST)
+                   o3->op_private &= ~OPpCONST_STRICT;
+               else if (o3->op_type == OP_ENTERSUB) {
+                   /* accidental subroutine, revert to bareword */
+                   OP *gvop = ((UNOP*)o3)->op_first;
+                   if (gvop && gvop->op_type == OP_NULL) {
+                       gvop = ((UNOP*)gvop)->op_first;
+                       if (gvop) {
+                           for (; gvop->op_sibling; gvop = gvop->op_sibling)
+                               ;
+                           if (gvop &&
+                                   (gvop->op_private & OPpENTERSUB_NOPAREN) &&
+                                   (gvop = ((UNOP*)gvop)->op_first) &&
+                                   gvop->op_type == OP_GV)
+                           {
+                               GV * const gv = cGVOPx_gv(gvop);
+                               OP * const sibling = aop->op_sibling;
+                               SV * const n = newSVpvs("");
 #ifdef PERL_MAD
-                           OP * const oldaop = aop;
+                               OP * const oldaop = aop;
 #else
-                           op_free(aop);
+                               op_free(aop);
 #endif
-                           gv_fullname4(n, gv, "", FALSE);
-                           aop = newSVOP(OP_CONST, 0, n);
-                           op_getmad(oldaop,aop,'O');
-                           prev->op_sibling = aop;
-                           aop->op_sibling = sibling;
+                               gv_fullname4(n, gv, "", FALSE);
+                               aop = newSVOP(OP_CONST, 0, n);
+                               op_getmad(oldaop,aop,'O');
+                               prev->op_sibling = aop;
+                               aop->op_sibling = sibling;
+                           }
                        }
                    }
                }
-           }
-           scalar(aop);
-           break;
-       case '[': case ']':
-            goto oops;
-            break;
-       case '\\':
-           proto++;
-           arg++;
-       again:
-           switch (*proto++) {
-           case '[':
-                if (contextclass++ == 0) {
-                     e = strchr(proto, ']');
-                     if (!e || e == proto)
-                          goto oops;
-                }
-                else
-                     goto oops;
-                goto again;
-                break;
-           case ']':
-                if (contextclass) {
-                    const char *p = proto;
-                    const char *const end = proto;
-                    contextclass = 0;
-                    while (*--p != '[') {}
-                    bad_type(arg, Perl_form(aTHX_ "one of %.*s",
-                                            (int)(end - p), p),
-                             gv_ename(namegv), o3);
-                } else
-                     goto oops;
-                break;
-           case '*':
-                if (o3->op_type == OP_RV2GV)
-                     goto wrapref;
-                if (!contextclass)
-                     bad_type(arg, "symbol", gv_ename(namegv), o3);
-                break;
-           case '&':
-                if (o3->op_type == OP_ENTERSUB)
-                     goto wrapref;
-                if (!contextclass)
-                     bad_type(arg, "subroutine entry", gv_ename(namegv),
-                              o3);
-                break;
-           case '$':
-               if (o3->op_type == OP_RV2SV ||
-                   o3->op_type == OP_PADSV ||
-                   o3->op_type == OP_HELEM ||
-                   o3->op_type == OP_AELEM)
-                    goto wrapref;
-               if (!contextclass)
-                   bad_type(arg, "scalar", gv_ename(namegv), o3);
-                break;
-           case '@':
+               scalar(aop);
+               break;
+           case '+':
+               proto++;
+               arg++;
                if (o3->op_type == OP_RV2AV ||
-                   o3->op_type == OP_PADAV)
-                    goto wrapref;
-               if (!contextclass)
-                   bad_type(arg, "array", gv_ename(namegv), o3);
+                   o3->op_type == OP_PADAV ||
+                   o3->op_type == OP_RV2HV ||
+                   o3->op_type == OP_PADHV
+               ) {
+                   goto wrapref;
+               }
+               scalar(aop);
                break;
-           case '%':
-               if (o3->op_type == OP_RV2HV ||
-                   o3->op_type == OP_PADHV)
-                    goto wrapref;
-               if (!contextclass)
-                    bad_type(arg, "hash", gv_ename(namegv), o3);
+           case '[': case ']':
+               goto oops;
                break;
-           wrapref:
-               {
-                   OP* const kid = aop;
-                   OP* const sib = kid->op_sibling;
-                   kid->op_sibling = 0;
-                   aop = newUNOP(OP_REFGEN, 0, kid);
-                   aop->op_sibling = sib;
-                   prev->op_sibling = aop;
-               }
-               if (contextclass && e) {
-                    proto = e + 1;
-                    contextclass = 0;
+           case '\\':
+               proto++;
+               arg++;
+           again:
+               switch (*proto++) {
+                   case '[':
+                       if (contextclass++ == 0) {
+                           e = strchr(proto, ']');
+                           if (!e || e == proto)
+                               goto oops;
+                       }
+                       else
+                           goto oops;
+                       goto again;
+                       break;
+                   case ']':
+                       if (contextclass) {
+                           const char *p = proto;
+                           const char *const end = proto;
+                           contextclass = 0;
+                           while (*--p != '[') {}
+                           bad_type(arg, Perl_form(aTHX_ "one of %.*s",
+                                       (int)(end - p), p),
+                                   gv_ename(namegv), o3);
+                       } else
+                           goto oops;
+                       break;
+                   case '*':
+                       if (o3->op_type == OP_RV2GV)
+                           goto wrapref;
+                       if (!contextclass)
+                           bad_type(arg, "symbol", gv_ename(namegv), o3);
+                       break;
+                   case '&':
+                       if (o3->op_type == OP_ENTERSUB)
+                           goto wrapref;
+                       if (!contextclass)
+                           bad_type(arg, "subroutine entry", gv_ename(namegv),
+                                   o3);
+                       break;
+                   case '$':
+                       if (o3->op_type == OP_RV2SV ||
+                               o3->op_type == OP_PADSV ||
+                               o3->op_type == OP_HELEM ||
+                               o3->op_type == OP_AELEM)
+                           goto wrapref;
+                       if (!contextclass)
+                           bad_type(arg, "scalar", gv_ename(namegv), o3);
+                       break;
+                   case '@':
+                       if (o3->op_type == OP_RV2AV ||
+                               o3->op_type == OP_PADAV)
+                           goto wrapref;
+                       if (!contextclass)
+                           bad_type(arg, "array", gv_ename(namegv), o3);
+                       break;
+                   case '%':
+                       if (o3->op_type == OP_RV2HV ||
+                               o3->op_type == OP_PADHV)
+                           goto wrapref;
+                       if (!contextclass)
+                           bad_type(arg, "hash", gv_ename(namegv), o3);
+                       break;
+                   wrapref:
+                       {
+                           OP* const kid = aop;
+                           OP* const sib = kid->op_sibling;
+                           kid->op_sibling = 0;
+                           aop = newUNOP(OP_REFGEN, 0, kid);
+                           aop->op_sibling = sib;
+                           prev->op_sibling = aop;
+                       }
+                       if (contextclass && e) {
+                           proto = e + 1;
+                           contextclass = 0;
+                       }
+                       break;
+                   default: goto oops;
                }
+               if (contextclass)
+                   goto again;
                break;
-           default: goto oops;
-           }
-           if (contextclass)
-                goto again;
-           break;
-       case ' ':
-           proto++;
-           continue;
-       default:
-         oops:
-           Perl_croak(aTHX_ "Malformed prototype for %s: %"SVf,
-                      gv_ename(namegv), SVfARG(protosv));
+           case ' ':
+               proto++;
+               continue;
+           default:
+           oops:
+               Perl_croak(aTHX_ "Malformed prototype for %s: %"SVf,
+                       gv_ename(namegv), SVfARG(protosv));
        }
 
-       mod(aop, OP_ENTERSUB);
+       op_lvalue(aop, OP_ENTERSUB);
        prev = aop;
        aop = aop->op_sibling;
     }