Fixed the example showing parameter type constraints
[p5sagit/Function-Parameters.git] / Parameters.xs
1 /*
2 Copyright 2012 Lukas Mai.
3
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of either: the GNU General Public License as published
6 by the Free Software Foundation; or the Artistic License.
7
8 See http://dev.perl.org/licenses/ for more information.
9  */
10
11 #ifdef __GNUC__
12  #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ >= 5
13   #define PRAGMA_GCC_(X) _Pragma(#X)
14   #define PRAGMA_GCC(X) PRAGMA_GCC_(GCC X)
15  #endif
16 #endif
17
18 #ifndef PRAGMA_GCC
19  #define PRAGMA_GCC(X)
20 #endif
21
22 #ifdef DEVEL
23  #define WARNINGS_RESET PRAGMA_GCC(diagnostic pop)
24  #define WARNINGS_ENABLEW(X) PRAGMA_GCC(diagnostic warning #X)
25  #define WARNINGS_ENABLE \
26         WARNINGS_ENABLEW(-Wall) \
27         WARNINGS_ENABLEW(-Wextra) \
28         WARNINGS_ENABLEW(-Wundef) \
29         /* WARNINGS_ENABLEW(-Wshadow) :-( */ \
30         WARNINGS_ENABLEW(-Wbad-function-cast) \
31         WARNINGS_ENABLEW(-Wcast-align) \
32         WARNINGS_ENABLEW(-Wwrite-strings) \
33         /* WARNINGS_ENABLEW(-Wnested-externs) wtf? */ \
34         WARNINGS_ENABLEW(-Wstrict-prototypes) \
35         WARNINGS_ENABLEW(-Wmissing-prototypes) \
36         WARNINGS_ENABLEW(-Winline) \
37         WARNINGS_ENABLEW(-Wdisabled-optimization)
38
39 #else
40  #define WARNINGS_RESET
41  #define WARNINGS_ENABLE
42 #endif
43
44
45 #define PERL_NO_GET_CONTEXT
46 #include "EXTERN.h"
47 #include "perl.h"
48 #include "XSUB.h"
49
50 #include <string.h>
51
52
53 WARNINGS_ENABLE
54
55
56 #define HAVE_PERL_VERSION(R, V, S) \
57         (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
58
59 #if HAVE_PERL_VERSION(5, 16, 0)
60  #define IF_HAVE_PERL_5_16(YES, NO) YES
61 #else
62  #define IF_HAVE_PERL_5_16(YES, NO) NO
63 #endif
64
65 #if 0
66  #if HAVE_PERL_VERSION(5, 17, 6)
67   #error "internal error: missing definition of KEY_my (your perl is too new)"
68  #elif HAVE_PERL_VERSION(5, 15, 8)
69   #define S_KEY_my 134
70  #elif HAVE_PERL_VERSION(5, 15, 6)
71   #define S_KEY_my 133
72  #elif HAVE_PERL_VERSION(5, 15, 5)
73   #define S_KEY_my 132
74  #elif HAVE_PERL_VERSION(5, 13, 0)
75   #define S_KEY_my 131
76  #else
77   #error "internal error: missing definition of KEY_my (your perl is too old)"
78  #endif
79 #endif
80
81
82 #define MY_PKG "Function::Parameters"
83
84 #define HINTK_KEYWORDS MY_PKG "/keywords"
85 #define HINTK_FLAGS_   MY_PKG "/flags:"
86 #define HINTK_SHIFT_   MY_PKG "/shift:"
87 #define HINTK_ATTRS_   MY_PKG "/attrs:"
88 #define HINTK_REIFY_   MY_PKG "/reify:"
89
90 #define DEFSTRUCT(T) typedef struct T T; struct T
91
92 enum {
93         FLAG_NAME_OK      = 0x01,
94         FLAG_ANON_OK      = 0x02,
95         FLAG_DEFAULT_ARGS = 0x04,
96         FLAG_CHECK_NARGS  = 0x08,
97         FLAG_INVOCANT     = 0x10,
98         FLAG_NAMED_PARAMS = 0x20,
99         FLAG_TYPES_OK     = 0x40,
100         FLAG_CHECK_TARGS  = 0x80
101 };
102
103 DEFSTRUCT(KWSpec) {
104         unsigned flags;
105         I32 reify_type;
106         SV *shift;
107         SV *attrs;
108 };
109
110 static int (*next_keyword_plugin)(pTHX_ char *, STRLEN, OP **);
111
112 DEFSTRUCT(Resource) {
113         Resource *next;
114         void *data;
115         void (*destroy)(pTHX_ void *);
116 };
117
118 typedef Resource *Sentinel[1];
119
120 static void sentinel_clear_void(pTHX_ void *p) {
121         Resource **pp = p;
122         while (*pp) {
123                 Resource *cur = *pp;
124                 if (cur->destroy) {
125                         cur->destroy(aTHX_ cur->data);
126                 }
127                 cur->data = (void *)"no";
128                 cur->destroy = NULL;
129                 *pp = cur->next;
130                 Safefree(cur);
131         }
132 }
133
134 static Resource *sentinel_register(Sentinel sen, void *data, void (*destroy)(pTHX_ void *)) {
135         Resource *cur;
136
137         Newx(cur, 1, Resource);
138         cur->data = data;
139         cur->destroy = destroy;
140         cur->next = *sen;
141         *sen = cur;
142
143         return cur;
144 }
145
146 static void sentinel_disarm(Resource *p) {
147         p->destroy = NULL;
148 }
149
150 static void my_sv_refcnt_dec_void(pTHX_ void *p) {
151         SV *sv = p;
152         SvREFCNT_dec(sv);
153 }
154
155 static SV *sentinel_mortalize(Sentinel sen, SV *sv) {
156         sentinel_register(sen, sv, my_sv_refcnt_dec_void);
157         return sv;
158 }
159
160
161 #if HAVE_PERL_VERSION(5, 17, 2)
162  #define MY_OP_SLABBED(O) ((O)->op_slabbed)
163 #else
164  #define MY_OP_SLABBED(O) 0
165 #endif
166
167 DEFSTRUCT(OpGuard) {
168         OP *op;
169         bool needs_freed;
170 };
171
172 static void op_guard_init(OpGuard *p) {
173         p->op = NULL;
174         p->needs_freed = FALSE;
175 }
176
177 static OpGuard op_guard_transfer(OpGuard *p) {
178         OpGuard r = *p;
179         op_guard_init(p);
180         return r;
181 }
182
183 static OP *op_guard_relinquish(OpGuard *p) {
184         OP *o = p->op;
185         op_guard_init(p);
186         return o;
187 }
188
189 static void op_guard_update(OpGuard *p, OP *o) {
190         p->op = o;
191         p->needs_freed = o && !MY_OP_SLABBED(o);
192 }
193
194 static void op_guard_clear(pTHX_ OpGuard *p) {
195         if (p->needs_freed) {
196                 op_free(p->op);
197         }
198 }
199
200 static void free_op_guard_void(pTHX_ void *vp) {
201         OpGuard *p = vp;
202         op_guard_clear(aTHX_ p);
203         Safefree(p);
204 }
205
206 static void free_op_void(pTHX_ void *vp) {
207         OP *p = vp;
208         op_free(p);
209 }
210
211 #define sv_eq_pvs(SV, S) my_sv_eq_pvn(aTHX_ SV, "" S "", sizeof (S) - 1)
212
213 static int my_sv_eq_pvn(pTHX_ SV *sv, const char *p, STRLEN n) {
214         STRLEN sv_len;
215         const char *sv_p = SvPV(sv, sv_len);
216         return memcmp(sv_p, p, n) == 0;
217 }
218
219
220 #include "padop_on_crack.c.inc"
221
222
223 enum {
224         MY_ATTR_LVALUE = 0x01,
225         MY_ATTR_METHOD = 0x02,
226         MY_ATTR_SPECIAL = 0x04
227 };
228
229 static void my_sv_cat_c(pTHX_ SV *sv, U32 c) {
230         char ds[UTF8_MAXBYTES + 1], *d;
231         d = (char *)uvchr_to_utf8((U8 *)ds, c);
232         if (d - ds > 1) {
233                 sv_utf8_upgrade(sv);
234         }
235         sv_catpvn(sv, ds, d - ds);
236 }
237
238
239 #define MY_UNI_IDFIRST(C) isIDFIRST_uni(C)
240 #define MY_UNI_IDCONT(C)  isALNUM_uni(C)
241
242 static SV *my_scan_word(pTHX_ Sentinel sen, bool allow_package) {
243         bool at_start, at_substart;
244         I32 c;
245         SV *sv = sentinel_mortalize(sen, newSVpvs(""));
246         if (lex_bufutf8()) {
247                 SvUTF8_on(sv);
248         }
249
250         at_start = at_substart = TRUE;
251         c = lex_peek_unichar(0);
252
253         while (c != -1) {
254                 if (at_substart ? MY_UNI_IDFIRST(c) : MY_UNI_IDCONT(c)) {
255                         lex_read_unichar(0);
256                         my_sv_cat_c(aTHX_ sv, c);
257                         at_substart = FALSE;
258                         c = lex_peek_unichar(0);
259                 } else if (allow_package && !at_substart && c == '\'') {
260                         lex_read_unichar(0);
261                         c = lex_peek_unichar(0);
262                         if (!MY_UNI_IDFIRST(c)) {
263                                 lex_stuff_pvs("'", 0);
264                                 break;
265                         }
266                         sv_catpvs(sv, "'");
267                         at_substart = TRUE;
268                 } else if (allow_package && (at_start || !at_substart) && c == ':') {
269                         lex_read_unichar(0);
270                         if (lex_peek_unichar(0) != ':') {
271                                 lex_stuff_pvs(":", 0);
272                                 break;
273                         }
274                         lex_read_unichar(0);
275                         c = lex_peek_unichar(0);
276                         if (!MY_UNI_IDFIRST(c)) {
277                                 lex_stuff_pvs("::", 0);
278                                 break;
279                         }
280                         sv_catpvs(sv, "::");
281                         at_substart = TRUE;
282                 } else {
283                         break;
284                 }
285                 at_start = FALSE;
286         }
287
288         return SvCUR(sv) ? sv : NULL;
289 }
290
291 static SV *my_scan_parens_tail(pTHX_ Sentinel sen, bool keep_backslash) {
292         I32 c, nesting;
293         SV *sv;
294         line_t start;
295
296         start = CopLINE(PL_curcop);
297
298         sv = sentinel_mortalize(sen, newSVpvs(""));
299         if (lex_bufutf8()) {
300                 SvUTF8_on(sv);
301         }
302
303         nesting = 0;
304         for (;;) {
305                 c = lex_read_unichar(0);
306                 if (c == EOF) {
307                         CopLINE_set(PL_curcop, start);
308                         return NULL;
309                 }
310
311                 if (c == '\\') {
312                         c = lex_read_unichar(0);
313                         if (c == EOF) {
314                                 CopLINE_set(PL_curcop, start);
315                                 return NULL;
316                         }
317                         if (keep_backslash || (c != '(' && c != ')')) {
318                                 sv_catpvs(sv, "\\");
319                         }
320                 } else if (c == '(') {
321                         nesting++;
322                 } else if (c == ')') {
323                         if (!nesting) {
324                                 break;
325                         }
326                         nesting--;
327                 }
328
329                 my_sv_cat_c(aTHX_ sv, c);
330         }
331
332         return sv;
333 }
334
335 static void my_check_prototype(pTHX_ Sentinel sen, const SV *declarator, SV *proto) {
336         char *start, *r, *w, *end;
337         STRLEN len;
338
339         /* strip spaces */
340         start = SvPV(proto, len);
341         end = start + len;
342
343         for (w = r = start; r < end; r++) {
344                 if (!isSPACE(*r)) {
345                         *w++ = *r;
346                 }
347         }
348         *w = '\0';
349         SvCUR_set(proto, w - start);
350         end = w;
351         len = end - start;
352
353         if (!ckWARN(WARN_ILLEGALPROTO)) {
354                 return;
355         }
356
357         /* check for bad characters */
358         if (strspn(start, "$@%*;[]&\\_+") != len) {
359                 SV *dsv = sentinel_mortalize(sen, newSVpvs(""));
360                 warner(
361                         packWARN(WARN_ILLEGALPROTO),
362                         "Illegal character in prototype for %"SVf" : %s",
363                         SVfARG(declarator),
364                         SvUTF8(proto)
365                                 ? sv_uni_display(
366                                         dsv,
367                                         proto,
368                                         len,
369                                         UNI_DISPLAY_ISPRINT
370                                 )
371                                 : pv_pretty(dsv, start, len, 60, NULL, NULL,
372                                         PERL_PV_ESCAPE_NONASCII
373                                 )
374                 );
375                 return;
376         }
377
378         for (r = start; r < end; r++) {
379                 switch (*r) {
380                         default:
381                                 warner(
382                                         packWARN(WARN_ILLEGALPROTO),
383                                         "Illegal character in prototype for %"SVf" : %s",
384                                         SVfARG(declarator), r
385                                 );
386                                 return;
387
388                         case '_':
389                                 if (r[1] && !strchr(";@%", *r)) {
390                                         warner(
391                                                 packWARN(WARN_ILLEGALPROTO),
392                                                 "Illegal character after '_' in prototype for %"SVf" : %s",
393                                                 SVfARG(declarator), r
394                                         );
395                                         return;
396                                 }
397                                 break;
398
399                         case '@':
400                         case '%':
401                                 if (r[1]) {
402                                         warner(
403                                                 packWARN(WARN_ILLEGALPROTO),
404                                                 "prototype after '%c' for %"SVf": %s",
405                                                 *r, SVfARG(declarator), r + 1
406                                         );
407                                         return;
408                                 }
409                                 break;
410
411                         case '\\':
412                                 r++;
413                                 if (strchr("$@%&*", *r)) {
414                                         break;
415                                 }
416                                 if (*r == '[') {
417                                         r++;
418                                         for (; r < end && *r != ']'; r++) {
419                                                 if (!strchr("$@%&*", *r)) {
420                                                         break;
421                                                 }
422                                         }
423                                         if (*r == ']' && r[-1] != '[') {
424                                                 break;
425                                         }
426                                 }
427                                 warner(
428                                         packWARN(WARN_ILLEGALPROTO),
429                                         "Illegal character after '\\' in prototype for %"SVf" : %s",
430                                         SVfARG(declarator), r
431                                 );
432                                 return;
433
434                         case '$':
435                         case '*':
436                         case '&':
437                         case ';':
438                         case '+':
439                                 break;
440                 }
441         }
442 }
443
444 static SV *parse_type(pTHX_ Sentinel, const SV *);
445
446 static SV *parse_type_paramd(pTHX_ Sentinel sen, const SV *declarator) {
447         I32 c;
448         SV *t;
449
450         t = my_scan_word(aTHX_ sen, TRUE);
451         lex_read_space(0);
452
453         c = lex_peek_unichar(0);
454         if (c == '[') {
455                 SV *u;
456
457                 lex_read_unichar(0);
458                 lex_read_space(0);
459                 my_sv_cat_c(aTHX_ t, c);
460
461                 u = parse_type(aTHX_ sen, declarator);
462                 sv_catsv(t, u);
463
464                 c = lex_peek_unichar(0);
465                 if (c != ']') {
466                         croak("In %"SVf": missing ']' after '%"SVf"'", SVfARG(declarator), SVfARG(t));
467                 }
468                 lex_read_unichar(0);
469                 lex_read_space(0);
470
471                 my_sv_cat_c(aTHX_ t, c);
472         }
473
474         return t;
475 }
476
477 static SV *parse_type(pTHX_ Sentinel sen, const SV *declarator) {
478         I32 c;
479         SV *t;
480
481         t = parse_type_paramd(aTHX_ sen, declarator);
482
483         c = lex_peek_unichar(0);
484         while (c == '|') {
485                 SV *u;
486
487                 lex_read_unichar(0);
488                 lex_read_space(0);
489
490                 my_sv_cat_c(aTHX_ t, c);
491                 u = parse_type_paramd(aTHX_ sen, declarator);
492                 sv_catsv(t, u);
493
494                 c = lex_peek_unichar(0);
495         }
496
497         return t;
498 }
499
500 static SV *reify_type(pTHX_ Sentinel sen, const SV *declarator, const KWSpec *spec, SV *name) {
501         AV *type_reifiers;
502         SV *t, *sv, **psv;
503         int n;
504         dSP;
505
506         type_reifiers = get_av(MY_PKG "::type_reifiers", 0);
507         assert(type_reifiers != NULL);
508
509         if (spec->reify_type < 0 || spec->reify_type > av_len(type_reifiers)) {
510                 croak("In %"SVf": internal error: reify_type [%ld] out of range [%ld]", SVfARG(declarator), (long)spec->reify_type, (long)(av_len(type_reifiers) + 1));
511         }
512
513         psv = av_fetch(type_reifiers, spec->reify_type, 0);
514         assert(psv != NULL);
515         sv = *psv;
516
517         ENTER;
518         SAVETMPS;
519
520         PUSHMARK(SP);
521         EXTEND(SP, 2);
522         PUSHs(name);
523         PUSHs(PL_curstname);
524         PUTBACK;
525
526         n = call_sv(sv, G_SCALAR);
527         SPAGAIN;
528
529         assert(n == 1);
530         /* don't warn about n being unused if assert() is compiled out */
531         n = n;
532
533         t = sentinel_mortalize(sen, SvREFCNT_inc(POPs));
534
535         PUTBACK;
536         FREETMPS;
537         LEAVE;
538
539         if (!SvTRUE(t)) {
540                 croak("In %"SVf": undefined type '%"SVf"'", SVfARG(declarator), SVfARG(name));
541         }
542
543         return t;
544 }
545
546
547 DEFSTRUCT(Param) {
548         SV *name;
549         PADOFFSET padoff;
550         SV *type;
551 };
552
553 DEFSTRUCT(ParamInit) {
554         Param param;
555         OpGuard init;
556 };
557
558 #define VEC(B) B ## _Vec
559
560 #define DEFVECTOR(B) DEFSTRUCT(VEC(B)) { \
561         B (*data); \
562         size_t used, size; \
563 }
564
565 DEFVECTOR(Param);
566 DEFVECTOR(ParamInit);
567
568 #define DEFVECTOR_INIT(N, B) static void N(VEC(B) *p) { \
569         p->used = 0; \
570         p->size = 23; \
571         Newx(p->data, p->size, B); \
572 } static void N(VEC(B) *)
573
574 DEFSTRUCT(ParamSpec) {
575         Param invocant;
576         VEC(Param) positional_required;
577         VEC(ParamInit) positional_optional;
578         VEC(Param) named_required;
579         VEC(ParamInit) named_optional;
580         Param slurpy;
581         PADOFFSET rest_hash;
582 };
583
584 DEFVECTOR_INIT(pv_init, Param);
585 DEFVECTOR_INIT(piv_init, ParamInit);
586
587 static void p_init(Param *p) {
588         p->name = NULL;
589         p->padoff = NOT_IN_PAD;
590         p->type = NULL;
591 }
592
593 static void ps_init(ParamSpec *ps) {
594         p_init(&ps->invocant);
595         pv_init(&ps->positional_required);
596         piv_init(&ps->positional_optional);
597         pv_init(&ps->named_required);
598         piv_init(&ps->named_optional);
599         p_init(&ps->slurpy);
600         ps->rest_hash = NOT_IN_PAD;
601 }
602
603 #define DEFVECTOR_EXTEND(N, B) static B (*N(VEC(B) *p)) { \
604         assert(p->used <= p->size); \
605         if (p->used == p->size) { \
606                 const size_t n = p->size / 2 * 3 + 1; \
607                 Renew(p->data, n, B); \
608                 p->size = n; \
609         } \
610         return &p->data[p->used]; \
611 } static B (*N(VEC(B) *))
612
613 DEFVECTOR_EXTEND(pv_extend, Param);
614 DEFVECTOR_EXTEND(piv_extend, ParamInit);
615
616 #define DEFVECTOR_CLEAR(N, B, F) static void N(pTHX_ VEC(B) *p) { \
617         while (p->used) { \
618                 p->used--; \
619                 F(aTHX_ &p->data[p->used]); \
620         } \
621         Safefree(p->data); \
622         p->data = NULL; \
623         p->size = 0; \
624 } static void N(pTHX_ VEC(B) *)
625
626 static void p_clear(pTHX_ Param *p) {
627         p->name = NULL;
628         p->padoff = NOT_IN_PAD;
629         p->type = NULL;
630 }
631
632 static void pi_clear(pTHX_ ParamInit *pi) {
633         p_clear(aTHX_ &pi->param);
634         op_guard_clear(aTHX_ &pi->init);
635 }
636
637 DEFVECTOR_CLEAR(pv_clear, Param, p_clear);
638 DEFVECTOR_CLEAR(piv_clear, ParamInit, pi_clear);
639
640 static void ps_clear(pTHX_ ParamSpec *ps) {
641         p_clear(aTHX_ &ps->invocant);
642
643         pv_clear(aTHX_ &ps->positional_required);
644         piv_clear(aTHX_ &ps->positional_optional);
645
646         pv_clear(aTHX_ &ps->named_required);
647         piv_clear(aTHX_ &ps->named_optional);
648
649         p_clear(aTHX_ &ps->slurpy);
650 }
651
652 static int ps_contains(pTHX_ const ParamSpec *ps, SV *sv) {
653         size_t i, lim;
654
655         if (ps->invocant.name && sv_eq(sv, ps->invocant.name)) {
656                 return 1;
657         }
658
659         for (i = 0, lim = ps->positional_required.used; i < lim; i++) {
660                 if (sv_eq(sv, ps->positional_required.data[i].name)) {
661                         return 1;
662                 }
663         }
664
665         for (i = 0, lim = ps->positional_optional.used; i < lim; i++) {
666                 if (sv_eq(sv, ps->positional_optional.data[i].param.name)) {
667                         return 1;
668                 }
669         }
670
671         for (i = 0, lim = ps->named_required.used; i < lim; i++) {
672                 if (sv_eq(sv, ps->named_required.data[i].name)) {
673                         return 1;
674                 }
675         }
676
677         for (i = 0, lim = ps->named_optional.used; i < lim; i++) {
678                 if (sv_eq(sv, ps->named_optional.data[i].param.name)) {
679                         return 1;
680                 }
681         }
682
683         return 0;
684 }
685
686 static void ps_free_void(pTHX_ void *p) {
687         ps_clear(aTHX_ p);
688         Safefree(p);
689 }
690
691 static int args_min(pTHX_ const ParamSpec *ps, const KWSpec *ks) {
692         int n = 0;
693         if (!ps) {
694                 return SvTRUE(ks->shift) ? 1 : 0;
695         }
696         if (ps->invocant.name) {
697                 n++;
698         }
699         n += ps->positional_required.used;
700         n += ps->named_required.used * 2;
701         return n;
702 }
703
704 static int args_max(const ParamSpec *ps) {
705         int n = 0;
706         if (!ps) {
707                 return -1;
708         }
709         if (ps->invocant.name) {
710                 n++;
711         }
712         n += ps->positional_required.used;
713         n += ps->positional_optional.used;
714         if (ps->named_required.used || ps->named_optional.used || ps->slurpy.name) {
715                 n = -1;
716         }
717         return n;
718 }
719
720 static size_t count_positional_params(const ParamSpec *ps) {
721         return ps->positional_required.used + ps->positional_optional.used;
722 }
723
724 static size_t count_named_params(const ParamSpec *ps) {
725         return ps->named_required.used + ps->named_optional.used;
726 }
727
728 static SV *my_eval(pTHX_ Sentinel sen, I32 floor, OP *op) {
729         SV *sv;
730         CV *cv;
731         dSP;
732
733         cv = newATTRSUB(floor, NULL, NULL, NULL, op);
734
735         ENTER;
736         SAVETMPS;
737
738         PUSHMARK(SP);
739         call_sv((SV *)cv, G_SCALAR | G_NOARGS);
740         SPAGAIN;
741         sv = sentinel_mortalize(sen, SvREFCNT_inc(POPs));
742
743         PUTBACK;
744         FREETMPS;
745         LEAVE;
746
747         return sv;
748 }
749
750 enum {
751         PARAM_INVOCANT = 0x01,
752         PARAM_NAMED    = 0x02
753 };
754
755 static PADOFFSET parse_param(
756         pTHX_
757         Sentinel sen,
758         const SV *declarator, const KWSpec *spec, ParamSpec *param_spec,
759         int *pflags, SV **pname, OpGuard *ginit, SV **ptype
760 ) {
761         I32 c;
762         char sigil;
763         SV *name;
764
765         assert(!ginit->op);
766         *pflags = 0;
767         *ptype = NULL;
768
769         c = lex_peek_unichar(0);
770
771         if (spec->flags & FLAG_TYPES_OK) {
772                 if (c == '(') {
773                         I32 floor;
774                         OP *expr;
775                         Resource *expr_sentinel;
776
777                         lex_read_unichar(0);
778
779                         floor = start_subparse(FALSE, 0);
780                         SAVEFREESV(PL_compcv);
781                         CvSPECIAL_on(PL_compcv);
782
783                         if (!(expr = parse_fullexpr(PARSE_OPTIONAL))) {
784                                 croak("In %"SVf": invalid type expression", SVfARG(declarator));
785                         }
786                         if (MY_OP_SLABBED(expr)) {
787                                 expr_sentinel = NULL;
788                         } else {
789                                 expr_sentinel = sentinel_register(sen, expr, free_op_void);
790                         }
791
792                         lex_read_space(0);
793                         c = lex_peek_unichar(0);
794                         if (c != ')') {
795                                 croak("In %"SVf": missing ')' after type expression", SVfARG(declarator));
796                         }
797                         lex_read_unichar(0);
798                         lex_read_space(0);
799
800                         SvREFCNT_inc_simple_void(PL_compcv);
801                         if (expr_sentinel) {
802                                 sentinel_disarm(expr_sentinel);
803                         }
804                         *ptype = my_eval(aTHX_ sen, floor, expr);
805                         if (!SvROK(*ptype)) {
806                                 *ptype = reify_type(aTHX_ sen, declarator, spec, *ptype);
807                         }
808                         if (!sv_isobject(*ptype)) {
809                                 croak("In %"SVf": (%"SVf") doesn't look like a type object", SVfARG(declarator), SVfARG(*ptype));
810                         }
811
812                         c = lex_peek_unichar(0);
813                 } else if (MY_UNI_IDFIRST(c)) {
814                         *ptype = parse_type(aTHX_ sen, declarator);
815                         *ptype = reify_type(aTHX_ sen, declarator, spec, *ptype);
816
817                         c = lex_peek_unichar(0);
818                 }
819         }
820
821         if (c == ':') {
822                 lex_read_unichar(0);
823                 lex_read_space(0);
824
825                 *pflags |= PARAM_NAMED;
826
827                 c = lex_peek_unichar(0);
828         }
829
830         if (c == -1) {
831                 croak("In %"SVf": unterminated parameter list", SVfARG(declarator));
832         }
833
834         if (!(c == '$' || c == '@' || c == '%')) {
835                 croak("In %"SVf": unexpected '%c' in parameter list (expecting a sigil)", SVfARG(declarator), (int)c);
836         }
837
838         sigil = c;
839
840         lex_read_unichar(0);
841         lex_read_space(0);
842
843         if (!(name = my_scan_word(aTHX_ sen, FALSE))) {
844                 croak("In %"SVf": missing identifier after '%c'", SVfARG(declarator), sigil);
845         }
846         sv_insert(name, 0, 0, &sigil, 1);
847         *pname = name;
848
849         lex_read_space(0);
850         c = lex_peek_unichar(0);
851
852         if (c == '=') {
853                 lex_read_unichar(0);
854                 lex_read_space(0);
855
856
857                 if (!param_spec->invocant.name && SvTRUE(spec->shift)) {
858                         param_spec->invocant.name = spec->shift;
859                         param_spec->invocant.padoff = pad_add_name_sv(param_spec->invocant.name, 0, NULL, NULL);
860                 }
861
862                 op_guard_update(ginit, parse_termexpr(0));
863
864                 lex_read_space(0);
865                 c = lex_peek_unichar(0);
866         }
867
868         if (c == ':') {
869                 *pflags |= PARAM_INVOCANT;
870                 lex_read_unichar(0);
871                 lex_read_space(0);
872         } else if (c == ',') {
873                 lex_read_unichar(0);
874                 lex_read_space(0);
875         } else if (c != ')') {
876                 if (c == -1) {
877                         croak("In %"SVf": unterminated parameter list", SVfARG(declarator));
878                 }
879                 croak("In %"SVf": unexpected '%c' in parameter list (expecting ',')", SVfARG(declarator), (int)c);
880         }
881
882         return pad_add_name_sv(*pname, IF_HAVE_PERL_5_16(padadd_NO_DUP_CHECK, 0), NULL, NULL);
883 }
884
885 static OP *my_var_g(pTHX_ I32 type, I32 flags, PADOFFSET padoff) {
886         OP *var = newOP(type, flags);
887         var->op_targ = padoff;
888         return var;
889 }
890
891 static OP *my_var(pTHX_ I32 flags, PADOFFSET padoff) {
892         return my_var_g(aTHX_ OP_PADSV, flags, padoff);
893 }
894
895 static OP *mkhvelem(pTHX_ PADOFFSET h, OP *k) {
896         OP *hv = my_var_g(aTHX_ OP_PADHV, OPf_REF, h);
897         return newBINOP(OP_HELEM, 0, hv, k);
898 }
899
900 static OP *mkconstsv(pTHX_ SV *sv) {
901         return newSVOP(OP_CONST, 0, sv);
902 }
903
904 static OP *mkconstiv(pTHX_ IV i) {
905         return mkconstsv(aTHX_ newSViv(i));
906 }
907
908 static OP *mkconstpv(pTHX_ const char *p, size_t n) {
909         return mkconstsv(aTHX_ newSVpv(p, n));
910 }
911
912 #define mkconstpvs(S) mkconstpv(aTHX_ "" S "", sizeof S - 1)
913
914 static OP *mktypecheck(pTHX_ const SV *declarator, int nr, SV *name, PADOFFSET padoff, SV *type) {
915         /* $type->check($value) or Carp::croak "...: " . $type->get_message($value) */
916         OP *chk, *err, *msg, *xcroak;
917
918         err = mkconstsv(aTHX_ newSVpvf("In %"SVf": parameter %d (%"SVf"): ", SVfARG(declarator), nr, SVfARG(name)));
919         {
920                 OP *args = NULL;
921
922                 args = op_append_elem(OP_LIST, args, mkconstsv(aTHX_ SvREFCNT_inc_simple_NN(type)));
923                 args = op_append_elem(
924                         OP_LIST, args,
925                         padoff == NOT_IN_PAD
926                                 ? S_newDEFSVOP(aTHX)
927                                 : my_var(aTHX_ 0, padoff)
928                 );
929                 args = op_append_elem(OP_LIST, args, newUNOP(OP_METHOD, 0, mkconstpvs("get_message")));
930
931                 msg = args;
932                 msg->op_type = OP_ENTERSUB;
933                 msg->op_ppaddr = PL_ppaddr[OP_ENTERSUB];
934                 msg->op_flags |= OPf_STACKED;
935         }
936
937         msg = newBINOP(OP_CONCAT, 0, err, msg);
938
939         xcroak = newCVREF(
940                 OPf_WANT_SCALAR,
941                 newGVOP(OP_GV, 0, gv_fetchpvs("Carp::croak", 0, SVt_PVCV))
942         );
943         xcroak = newUNOP(OP_ENTERSUB, OPf_STACKED, op_append_elem(OP_LIST, msg, xcroak));
944
945         {
946                 OP *args = NULL;
947
948                 args = op_append_elem(OP_LIST, args, mkconstsv(aTHX_ SvREFCNT_inc_simple_NN(type)));
949                 args = op_append_elem(
950                         OP_LIST, args,
951                         padoff == NOT_IN_PAD
952                                 ? S_newDEFSVOP(aTHX)
953                                 : my_var(aTHX_ 0, padoff)
954                 );
955                 args = op_append_elem(OP_LIST, args, newUNOP(OP_METHOD, 0, mkconstpvs("check")));
956
957                 chk = args;
958                 chk->op_type = OP_ENTERSUB;
959                 chk->op_ppaddr = PL_ppaddr[OP_ENTERSUB];
960                 chk->op_flags |= OPf_STACKED;
961         }
962
963         chk = newLOGOP(OP_OR, 0, chk, xcroak);
964         return chk;
965 }
966
967 static OP *mktypecheckp(pTHX_ const SV *declarator, int nr, const Param *param) {
968         return mktypecheck(aTHX_ declarator, nr, param->name, param->padoff, param->type);
969 }
970
971 static void register_info(pTHX_ UV key, SV *declarator, const KWSpec *kws, const ParamSpec *ps) {
972         dSP;
973
974         ENTER;
975         SAVETMPS;
976
977         PUSHMARK(SP);
978         EXTEND(SP, 10);
979
980         /* 0 */ {
981                 mPUSHu(key);
982         }
983         /* 1 */ {
984                 STRLEN n;
985                 char *p = SvPV(declarator, n);
986                 char *q = memchr(p, ' ', n);
987                 SV *tmp = newSVpvn_utf8(p, q ? (size_t)(q - p) : n, SvUTF8(declarator));
988                 mPUSHs(tmp);
989         }
990         if (!ps) {
991                 if (SvTRUE(kws->shift)) {
992                         PUSHs(kws->shift);
993                 } else {
994                         PUSHmortal;
995                 }
996                 PUSHmortal;
997                 mPUSHs(newRV_noinc((SV *)newAV()));
998                 mPUSHs(newRV_noinc((SV *)newAV()));
999                 mPUSHs(newRV_noinc((SV *)newAV()));
1000                 mPUSHs(newRV_noinc((SV *)newAV()));
1001                 mPUSHp("@_", 2);
1002                 PUSHmortal;
1003         } else {
1004                 /* 2, 3 */ {
1005                         if (ps->invocant.name) {
1006                                 PUSHs(ps->invocant.name);
1007                                 if (ps->invocant.type) {
1008                                         PUSHs(ps->invocant.type);
1009                                 } else {
1010                                         PUSHmortal;
1011                                 }
1012                         } else {
1013                                 PUSHmortal;
1014                                 PUSHmortal;
1015                         }
1016                 }
1017                 /* 4 */ {
1018                         size_t i, lim;
1019                         AV *av;
1020
1021                         lim = ps->positional_required.used;
1022
1023                         av = newAV();
1024                         if (lim) {
1025                                 av_extend(av, (lim - 1) * 2);
1026                                 for (i = 0; i < lim; i++) {
1027                                         Param *cur = &ps->positional_required.data[i];
1028                                         av_push(av, SvREFCNT_inc_simple_NN(cur->name));
1029                                         av_push(av, cur->type ? SvREFCNT_inc_simple_NN(cur->type) : &PL_sv_undef);
1030                                 }
1031                         }
1032
1033                         mPUSHs(newRV_noinc((SV *)av));
1034                 }
1035                 /* 5 */ {
1036                         size_t i, lim;
1037                         AV *av;
1038
1039                         lim = ps->positional_optional.used;
1040
1041                         av = newAV();
1042                         if (lim) {
1043                                 av_extend(av, (lim - 1) * 2);
1044                                 for (i = 0; i < lim; i++) {
1045                                         Param *cur = &ps->positional_optional.data[i].param;
1046                                         av_push(av, SvREFCNT_inc_simple_NN(cur->name));
1047                                         av_push(av, cur->type ? SvREFCNT_inc_simple_NN(cur->type) : &PL_sv_undef);
1048                                 }
1049                         }
1050
1051                         mPUSHs(newRV_noinc((SV *)av));
1052                 }
1053                 /* 6 */ {
1054                         size_t i, lim;
1055                         AV *av;
1056
1057                         lim = ps->named_required.used;
1058
1059                         av = newAV();
1060                         if (lim) {
1061                                 av_extend(av, (lim - 1) * 2);
1062                                 for (i = 0; i < lim; i++) {
1063                                         Param *cur = &ps->named_required.data[i];
1064                                         av_push(av, SvREFCNT_inc_simple_NN(cur->name));
1065                                         av_push(av, cur->type ? SvREFCNT_inc_simple_NN(cur->type) : &PL_sv_undef);
1066                                 }
1067                         }
1068
1069                         mPUSHs(newRV_noinc((SV *)av));
1070                 }
1071                 /* 7 */ {
1072                         size_t i, lim;
1073                         AV *av;
1074
1075                         lim = ps->named_optional.used;
1076
1077                         av = newAV();
1078                         if (lim) {
1079                                 av_extend(av, (lim - 1) * 2);
1080                                 for (i = 0; i < lim; i++) {
1081                                         Param *cur = &ps->named_optional.data[i].param;
1082                                         av_push(av, SvREFCNT_inc_simple_NN(cur->name));
1083                                         av_push(av, cur->type ? SvREFCNT_inc_simple_NN(cur->type) : &PL_sv_undef);
1084                                 }
1085                         }
1086
1087                         mPUSHs(newRV_noinc((SV *)av));
1088                 }
1089                 /* 8, 9 */ {
1090                         if (ps->slurpy.name) {
1091                                 PUSHs(ps->slurpy.name);
1092                                 if (ps->slurpy.type) {
1093                                         PUSHs(ps->slurpy.type);
1094                                 } else {
1095                                         PUSHmortal;
1096                                 }
1097                         } else {
1098                                 PUSHmortal;
1099                                 PUSHmortal;
1100                         }
1101                 }
1102         }
1103         PUTBACK;
1104
1105         call_pv(MY_PKG "::_register_info", G_VOID);
1106
1107         FREETMPS;
1108         LEAVE;
1109 }
1110
1111 static int parse_fun(pTHX_ Sentinel sen, OP **pop, const char *keyword_ptr, STRLEN keyword_len, const KWSpec *spec) {
1112         ParamSpec *param_spec;
1113         SV *declarator;
1114         I32 floor_ix;
1115         int save_ix;
1116         SV *saw_name;
1117         OpGuard *prelude_sentinel;
1118         SV *proto;
1119         OpGuard *attrs_sentinel;
1120         OP *body;
1121         unsigned builtin_attrs;
1122         I32 c;
1123
1124         declarator = sentinel_mortalize(sen, newSVpvn(keyword_ptr, keyword_len));
1125         if (lex_bufutf8()) {
1126                 SvUTF8_on(declarator);
1127         }
1128
1129         lex_read_space(0);
1130
1131         builtin_attrs = 0;
1132
1133         /* function name */
1134         saw_name = NULL;
1135         if ((spec->flags & FLAG_NAME_OK) && (saw_name = my_scan_word(aTHX_ sen, TRUE))) {
1136
1137                 if (PL_parser->expect != XSTATE) {
1138                         /* bail out early so we don't predeclare $saw_name */
1139                         croak("In %"SVf": I was expecting a function body, not \"%"SVf"\"", SVfARG(declarator), SVfARG(saw_name));
1140                 }
1141
1142                 sv_catpvs(declarator, " ");
1143                 sv_catsv(declarator, saw_name);
1144
1145                 if (
1146                         sv_eq_pvs(saw_name, "BEGIN") ||
1147                         sv_eq_pvs(saw_name, "END") ||
1148                         sv_eq_pvs(saw_name, "INIT") ||
1149                         sv_eq_pvs(saw_name, "CHECK") ||
1150                         sv_eq_pvs(saw_name, "UNITCHECK")
1151                 ) {
1152                         builtin_attrs |= MY_ATTR_SPECIAL;
1153                 }
1154
1155                 lex_read_space(0);
1156         } else if (!(spec->flags & FLAG_ANON_OK)) {
1157                 croak("I was expecting a function name, not \"%.*s\"", (int)(PL_parser->bufend - PL_parser->bufptr), PL_parser->bufptr);
1158         } else {
1159                 sv_catpvs(declarator, " (anon)");
1160         }
1161
1162         /* we're a subroutine declaration */
1163         floor_ix = start_subparse(FALSE, saw_name ? 0 : CVf_ANON);
1164         SAVEFREESV(PL_compcv);
1165
1166         /* create outer block: '{' */
1167         save_ix = S_block_start(aTHX_ TRUE);
1168
1169         /* initialize synthetic optree */
1170         Newx(prelude_sentinel, 1, OpGuard);
1171         op_guard_init(prelude_sentinel);
1172         sentinel_register(sen, prelude_sentinel, free_op_guard_void);
1173
1174         /* parameters */
1175         param_spec = NULL;
1176
1177         c = lex_peek_unichar(0);
1178         if (c == '(') {
1179                 OpGuard *init_sentinel;
1180
1181                 Newx(init_sentinel, 1, OpGuard);
1182                 op_guard_init(init_sentinel);
1183                 sentinel_register(sen, init_sentinel, free_op_guard_void);
1184
1185                 Newx(param_spec, 1, ParamSpec);
1186                 ps_init(param_spec);
1187                 sentinel_register(sen, param_spec, ps_free_void);
1188
1189                 lex_read_unichar(0);
1190                 lex_read_space(0);
1191
1192                 while ((c = lex_peek_unichar(0)) != ')') {
1193                         int flags;
1194                         SV *name, *type;
1195                         char sigil;
1196                         PADOFFSET padoff;
1197
1198                         padoff = parse_param(aTHX_ sen, declarator, spec, param_spec, &flags, &name, init_sentinel, &type);
1199
1200                         S_intro_my(aTHX);
1201
1202                         sigil = SvPV_nolen(name)[0];
1203
1204                         /* internal consistency */
1205                         if (flags & PARAM_NAMED) {
1206                                 if (flags & PARAM_INVOCANT) {
1207                                         croak("In %"SVf": invocant %"SVf" can't be a named parameter", SVfARG(declarator), SVfARG(name));
1208                                 }
1209                                 if (sigil != '$') {
1210                                         croak("In %"SVf": named parameter %"SVf" can't be a%s", SVfARG(declarator), SVfARG(name), sigil == '@' ? "n array" : " hash");
1211                                 }
1212                         } else if (flags & PARAM_INVOCANT) {
1213                                 if (init_sentinel->op) {
1214                                         croak("In %"SVf": invocant %"SVf" can't have a default value", SVfARG(declarator), SVfARG(name));
1215                                 }
1216                                 if (sigil != '$') {
1217                                         croak("In %"SVf": invocant %"SVf" can't be a%s", SVfARG(declarator), SVfARG(name), sigil == '@' ? "n array" : " hash");
1218                                 }
1219                         } else if (sigil != '$' && init_sentinel->op) {
1220                                 croak("In %"SVf": %s %"SVf" can't have a default value", SVfARG(declarator), sigil == '@' ? "array" : "hash", SVfARG(name));
1221                         }
1222
1223                         /* external constraints */
1224                         if (param_spec->slurpy.name) {
1225                                 croak("In %"SVf": I was expecting \")\" after \"%"SVf"\", not \"%"SVf"\"", SVfARG(declarator), SVfARG(param_spec->slurpy.name), SVfARG(name));
1226                         }
1227                         if (sigil != '$') {
1228                                 assert(!init_sentinel->op);
1229                                 param_spec->slurpy.name = name;
1230                                 param_spec->slurpy.padoff = padoff;
1231                                 param_spec->slurpy.type = type;
1232                                 continue;
1233                         }
1234
1235                         if (!(flags & PARAM_NAMED) && count_named_params(param_spec)) {
1236                                 croak("In %"SVf": positional parameter %"SVf" can't appear after named parameter %"SVf"", SVfARG(declarator), SVfARG(name), SVfARG((param_spec->named_required.used ? param_spec->named_required.data[0] : param_spec->named_optional.data[0].param).name));
1237                         }
1238
1239                         if (flags & PARAM_INVOCANT) {
1240                                 if (param_spec->invocant.name) {
1241                                         croak("In %"SVf": invalid double invocants %"SVf", %"SVf"", SVfARG(declarator), SVfARG(param_spec->invocant.name), SVfARG(name));
1242                                 }
1243                                 if (count_positional_params(param_spec) || count_named_params(param_spec)) {
1244                                         croak("In %"SVf": invocant %"SVf" must be first in parameter list", SVfARG(declarator), SVfARG(name));
1245                                 }
1246                                 if (!(spec->flags & FLAG_INVOCANT)) {
1247                                         croak("In %"SVf": invocant %"SVf" not allowed here", SVfARG(declarator), SVfARG(name));
1248                                 }
1249                                 param_spec->invocant.name = name;
1250                                 param_spec->invocant.padoff = padoff;
1251                                 param_spec->invocant.type = type;
1252                                 continue;
1253                         }
1254
1255                         if (init_sentinel->op && !(spec->flags & FLAG_DEFAULT_ARGS)) {
1256                                 croak("In %"SVf": default argument for %"SVf" not allowed here", SVfARG(declarator), SVfARG(name));
1257                         }
1258
1259                         if (ps_contains(aTHX_ param_spec, name)) {
1260                                 croak("In %"SVf": %"SVf" can't appear twice in the same parameter list", SVfARG(declarator), SVfARG(name));
1261                         }
1262
1263                         if (flags & PARAM_NAMED) {
1264                                 if (!(spec->flags & FLAG_NAMED_PARAMS)) {
1265                                         croak("In %"SVf": named parameter :%"SVf" not allowed here", SVfARG(declarator), SVfARG(name));
1266                                 }
1267
1268                                 if (init_sentinel->op) {
1269                                         ParamInit *pi = piv_extend(&param_spec->named_optional);
1270                                         pi->param.name = name;
1271                                         pi->param.padoff = padoff;
1272                                         pi->param.type = type;
1273                                         pi->init = op_guard_transfer(init_sentinel);
1274                                         param_spec->named_optional.used++;
1275                                 } else {
1276                                         Param *p;
1277
1278                                         if (param_spec->positional_optional.used) {
1279                                                 croak("In %"SVf": can't combine optional positional (%"SVf") and required named (%"SVf") parameters", SVfARG(declarator), SVfARG(param_spec->positional_optional.data[0].param.name), SVfARG(name));
1280                                         }
1281
1282                                         p = pv_extend(&param_spec->named_required);
1283                                         p->name = name;
1284                                         p->padoff = padoff;
1285                                         p->type = type;
1286                                         param_spec->named_required.used++;
1287                                 }
1288                         } else {
1289                                 if (init_sentinel->op || param_spec->positional_optional.used) {
1290                                         ParamInit *pi = piv_extend(&param_spec->positional_optional);
1291                                         pi->param.name = name;
1292                                         pi->param.padoff = padoff;
1293                                         pi->param.type = type;
1294                                         pi->init = op_guard_transfer(init_sentinel);
1295                                         param_spec->positional_optional.used++;
1296                                 } else {
1297                                         Param *p = pv_extend(&param_spec->positional_required);
1298                                         p->name = name;
1299                                         p->padoff = padoff;
1300                                         p->type = type;
1301                                         param_spec->positional_required.used++;
1302                                 }
1303                         }
1304
1305                 }
1306                 lex_read_unichar(0);
1307                 lex_read_space(0);
1308
1309                 if (!param_spec->invocant.name && SvTRUE(spec->shift)) {
1310                         if (ps_contains(aTHX_ param_spec, spec->shift)) {
1311                                 croak("In %"SVf": %"SVf" can't appear twice in the same parameter list", SVfARG(declarator), SVfARG(spec->shift));
1312                         }
1313
1314                         param_spec->invocant.name = spec->shift;
1315                         param_spec->invocant.padoff = pad_add_name_sv(param_spec->invocant.name, 0, NULL, NULL);
1316                 }
1317         }
1318
1319         /* prototype */
1320         proto = NULL;
1321         c = lex_peek_unichar(0);
1322         if (c == ':') {
1323                 lex_read_unichar(0);
1324                 lex_read_space(0);
1325
1326                 c = lex_peek_unichar(0);
1327                 if (c != '(') {
1328                         lex_stuff_pvs(":", 0);
1329                         c = ':';
1330                 } else {
1331                         lex_read_unichar(0);
1332                         if (!(proto = my_scan_parens_tail(aTHX_ sen, FALSE))) {
1333                                 croak("In %"SVf": prototype not terminated", SVfARG(declarator));
1334                         }
1335                         my_check_prototype(aTHX_ sen, declarator, proto);
1336                         lex_read_space(0);
1337                         c = lex_peek_unichar(0);
1338                         if (!(c == ':' || c == '{')) {
1339                                 lex_stuff_pvs(":", 0);
1340                                 c = ':';
1341                         }
1342                 }
1343         }
1344
1345         /* attributes */
1346         Newx(attrs_sentinel, 1, OpGuard);
1347         op_guard_init(attrs_sentinel);
1348         sentinel_register(sen, attrs_sentinel, free_op_guard_void);
1349
1350         if (c == ':' || c == '{') /* '}' - hi, vim */ {
1351
1352                 /* kludge default attributes in */
1353                 if (SvTRUE(spec->attrs) && SvPV_nolen(spec->attrs)[0] == ':') {
1354                         lex_stuff_sv(spec->attrs, 0);
1355                         c = ':';
1356                 }
1357
1358                 if (c == ':') {
1359                         lex_read_unichar(0);
1360                         lex_read_space(0);
1361                         c = lex_peek_unichar(0);
1362
1363                         for (;;) {
1364                                 SV *attr;
1365
1366                                 if (!(attr = my_scan_word(aTHX_ sen, FALSE))) {
1367                                         break;
1368                                 }
1369
1370                                 lex_read_space(0);
1371                                 c = lex_peek_unichar(0);
1372
1373                                 if (c != '(') {
1374                                         if (sv_eq_pvs(attr, "lvalue")) {
1375                                                 builtin_attrs |= MY_ATTR_LVALUE;
1376                                                 attr = NULL;
1377                                         } else if (sv_eq_pvs(attr, "method")) {
1378                                                 builtin_attrs |= MY_ATTR_METHOD;
1379                                                 attr = NULL;
1380                                         }
1381                                 } else {
1382                                         SV *sv;
1383                                         lex_read_unichar(0);
1384                                         if (!(sv = my_scan_parens_tail(aTHX_ sen, TRUE))) {
1385                                                 croak("In %"SVf": unterminated attribute parameter in attribute list", SVfARG(declarator));
1386                                         }
1387                                         sv_catpvs(attr, "(");
1388                                         sv_catsv(attr, sv);
1389                                         sv_catpvs(attr, ")");
1390
1391                                         lex_read_space(0);
1392                                         c = lex_peek_unichar(0);
1393                                 }
1394
1395                                 if (attr) {
1396                                         op_guard_update(attrs_sentinel, op_append_elem(OP_LIST, attrs_sentinel->op, mkconstsv(aTHX_ SvREFCNT_inc_simple_NN(attr))));
1397                                 }
1398
1399                                 if (c == ':') {
1400                                         lex_read_unichar(0);
1401                                         lex_read_space(0);
1402                                         c = lex_peek_unichar(0);
1403                                 }
1404                         }
1405                 }
1406         }
1407
1408         /* body */
1409         if (c != '{') /* '}' - hi, vim */ {
1410                 croak("In %"SVf": I was expecting a function body, not \"%c\"", SVfARG(declarator), (int)c);
1411         }
1412
1413         /* surprise predeclaration! */
1414         if (saw_name) {
1415                 /* 'sub NAME (PROTO);' to make name/proto known to perl before it
1416                    starts parsing the body */
1417                 const I32 sub_ix = start_subparse(FALSE, 0);
1418                 SAVEFREESV(PL_compcv);
1419
1420                 SvREFCNT_inc_simple_void(PL_compcv);
1421
1422                 newATTRSUB(
1423                         sub_ix,
1424                         mkconstsv(aTHX_ SvREFCNT_inc_simple_NN(saw_name)),
1425                         proto ? mkconstsv(aTHX_ SvREFCNT_inc_simple_NN(proto)) : NULL,
1426                         NULL,
1427                         NULL
1428                 );
1429         }
1430
1431         if (builtin_attrs & MY_ATTR_LVALUE) {
1432                 CvLVALUE_on(PL_compcv);
1433         }
1434         if (builtin_attrs & MY_ATTR_METHOD) {
1435                 CvMETHOD_on(PL_compcv);
1436         }
1437         if (builtin_attrs & MY_ATTR_SPECIAL) {
1438                 CvSPECIAL_on(PL_compcv);
1439         }
1440
1441         /* check number of arguments */
1442         if (spec->flags & FLAG_CHECK_NARGS) {
1443                 int amin, amax;
1444
1445                 amin = args_min(aTHX_ param_spec, spec);
1446                 if (amin > 0) {
1447                         OP *chk, *cond, *err, *xcroak;
1448
1449                         err = mkconstsv(aTHX_ newSVpvf("Not enough arguments for %"SVf" (expected %d, got ", SVfARG(declarator), amin));
1450                         err = newBINOP(
1451                                 OP_CONCAT, 0,
1452                                 err,
1453                                 newAVREF(newGVOP(OP_GV, 0, PL_defgv))
1454                         );
1455                         err = newBINOP(
1456                                 OP_CONCAT, 0,
1457                                 err,
1458                                 mkconstpvs(")")
1459                         );
1460
1461                         xcroak = newCVREF(OPf_WANT_SCALAR,
1462                                           newGVOP(OP_GV, 0, gv_fetchpvs("Carp::croak", 0, SVt_PVCV)));
1463                         err = newUNOP(OP_ENTERSUB, OPf_STACKED,
1464                                       op_append_elem(OP_LIST, err, xcroak));
1465
1466                         cond = newBINOP(OP_LT, 0,
1467                                         newAVREF(newGVOP(OP_GV, 0, PL_defgv)),
1468                                         mkconstiv(aTHX_ amin));
1469                         chk = newLOGOP(OP_AND, 0, cond, err);
1470
1471                         op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, chk)));
1472                 }
1473
1474                 amax = args_max(param_spec);
1475                 if (amax >= 0) {
1476                         OP *chk, *cond, *err, *xcroak;
1477
1478                         err = mkconstsv(aTHX_ newSVpvf("Too many arguments for %"SVf" (expected %d, got ", SVfARG(declarator), amax));
1479                         err = newBINOP(
1480                                 OP_CONCAT, 0,
1481                                 err,
1482                                 newAVREF(newGVOP(OP_GV, 0, PL_defgv))
1483                         );
1484                         err = newBINOP(
1485                                 OP_CONCAT, 0,
1486                                 err,
1487                                 mkconstpvs(")")
1488                         );
1489
1490                         xcroak = newCVREF(
1491                                 OPf_WANT_SCALAR,
1492                                 newGVOP(OP_GV, 0, gv_fetchpvs("Carp::croak", 0, SVt_PVCV))
1493                         );
1494                         err = newUNOP(OP_ENTERSUB, OPf_STACKED,
1495                         op_append_elem(OP_LIST, err, xcroak));
1496
1497                         cond = newBINOP(
1498                                 OP_GT, 0,
1499                                 newAVREF(newGVOP(OP_GV, 0, PL_defgv)),
1500                                 mkconstiv(aTHX_ amax)
1501                         );
1502                         chk = newLOGOP(OP_AND, 0, cond, err);
1503
1504                         op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, chk)));
1505                 }
1506
1507                 if (param_spec && (count_named_params(param_spec) || (param_spec->slurpy.name && SvPV_nolen(param_spec->slurpy.name)[0] == '%'))) {
1508                         OP *chk, *cond, *err, *xcroak;
1509                         const UV fixed = count_positional_params(param_spec) + !!param_spec->invocant.name;
1510
1511                         err = mkconstsv(aTHX_ newSVpvf("Odd number of paired arguments for %"SVf"", SVfARG(declarator)));
1512
1513                         xcroak = newCVREF(
1514                                 OPf_WANT_SCALAR,
1515                                 newGVOP(OP_GV, 0, gv_fetchpvs("Carp::croak", 0, SVt_PVCV))
1516                         );
1517                         err = newUNOP(OP_ENTERSUB, OPf_STACKED,
1518                         op_append_elem(OP_LIST, err, xcroak));
1519
1520                         cond = newBINOP(OP_GT, 0,
1521                                         newAVREF(newGVOP(OP_GV, 0, PL_defgv)),
1522                                         mkconstiv(aTHX_ fixed));
1523                         cond = newLOGOP(OP_AND, 0,
1524                                         cond,
1525                                         newBINOP(OP_MODULO, 0,
1526                                                  fixed
1527                                                  ? newBINOP(OP_SUBTRACT, 0,
1528                                                             newAVREF(newGVOP(OP_GV, 0, PL_defgv)),
1529                                                             mkconstiv(aTHX_ fixed))
1530                                                  : newAVREF(newGVOP(OP_GV, 0, PL_defgv)),
1531                                                  mkconstiv(aTHX_ 2)));
1532                         chk = newLOGOP(OP_AND, 0, cond, err);
1533
1534                         op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, chk)));
1535                 }
1536         }
1537
1538         if (!param_spec) {
1539                 /* my $invocant = shift; */
1540                 if (SvTRUE(spec->shift)) {
1541                         OP *var;
1542
1543                         var = my_var(
1544                                 aTHX_
1545                                 OPf_MOD | (OPpLVAL_INTRO << 8),
1546                                 pad_add_name_sv(spec->shift, 0, NULL, NULL)
1547                         );
1548                         var = newASSIGNOP(OPf_STACKED, var, 0, newOP(OP_SHIFT, 0));
1549
1550                         op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, var)));
1551                 }
1552         } else {
1553                 /* my $invocant = shift; */
1554                 if (param_spec->invocant.name) {
1555                         OP *var;
1556
1557                         var = my_var(
1558                                 aTHX_
1559                                 OPf_MOD | (OPpLVAL_INTRO << 8),
1560                                 param_spec->invocant.padoff
1561                         );
1562                         var = newASSIGNOP(OPf_STACKED, var, 0, newOP(OP_SHIFT, 0));
1563
1564                         op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, var)));
1565
1566                         if (param_spec->invocant.type && (spec->flags & FLAG_CHECK_TARGS)) {
1567                                 op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, mktypecheckp(aTHX_ declarator, 0, &param_spec->invocant))));
1568                         }
1569                 }
1570
1571                 /* my (...) = @_; */
1572                 {
1573                         OP *lhs;
1574                         size_t i, lim;
1575
1576                         lhs = NULL;
1577
1578                         for (i = 0, lim = param_spec->positional_required.used; i < lim; i++) {
1579                                 OP *const var = my_var(
1580                                         aTHX_
1581                                         OPf_WANT_LIST | (OPpLVAL_INTRO << 8),
1582                                         param_spec->positional_required.data[i].padoff
1583                                 );
1584                                 lhs = op_append_elem(OP_LIST, lhs, var);
1585                         }
1586
1587                         for (i = 0, lim = param_spec->positional_optional.used; i < lim; i++) {
1588                                 OP *const var = my_var(
1589                                         aTHX_
1590                                         OPf_WANT_LIST | (OPpLVAL_INTRO << 8),
1591                                         param_spec->positional_optional.data[i].param.padoff
1592                                 );
1593                                 lhs = op_append_elem(OP_LIST, lhs, var);
1594                         }
1595
1596                         {
1597                                 PADOFFSET padoff;
1598                                 I32 type;
1599                                 bool slurpy_hash;
1600
1601                                 /*
1602                                  * cases:
1603                                  *  1) no named params
1604                                  *   1.1) slurpy
1605                                  *       => put it in
1606                                  *   1.2) no slurpy
1607                                  *       => nop
1608                                  *  2) named params
1609                                  *   2.1) no slurpy
1610                                  *       => synthetic %{rest}
1611                                  *   2.2) slurpy is a hash
1612                                  *       => put it in
1613                                  *   2.3) slurpy is an array
1614                                  *       => synthetic %{rest}
1615                                  *          remember to declare array later
1616                                  */
1617
1618                                 slurpy_hash = param_spec->slurpy.name && SvPV_nolen(param_spec->slurpy.name)[0] == '%';
1619                                 if (!count_named_params(param_spec)) {
1620                                         if (param_spec->slurpy.name) {
1621                                                 padoff = param_spec->slurpy.padoff;
1622                                                 type = slurpy_hash ? OP_PADHV : OP_PADAV;
1623                                         } else {
1624                                                 padoff = NOT_IN_PAD;
1625                                                 type = OP_PADSV;
1626                                         }
1627                                 } else if (slurpy_hash) {
1628                                         padoff = param_spec->slurpy.padoff;
1629                                         type = OP_PADHV;
1630                                 } else {
1631                                         padoff = param_spec->rest_hash = pad_add_name_pvs("%{rest}", 0, NULL, NULL);
1632                                         type = OP_PADHV;
1633                                 }
1634
1635                                 if (padoff != NOT_IN_PAD) {
1636                                         OP *const var = my_var_g(
1637                                                 aTHX_
1638                                                 type,
1639                                                 OPf_WANT_LIST | (OPpLVAL_INTRO << 8),
1640                                                 padoff
1641                                         );
1642
1643                                         lhs = op_append_elem(OP_LIST, lhs, var);
1644
1645                                         if (type == OP_PADHV) {
1646                                                 param_spec->rest_hash = padoff;
1647                                         }
1648                                 }
1649                         }
1650
1651                         if (lhs) {
1652                                 OP *rhs;
1653                                 lhs->op_flags |= OPf_PARENS;
1654                                 rhs = newAVREF(newGVOP(OP_GV, 0, PL_defgv));
1655
1656                                 op_guard_update(prelude_sentinel, op_append_list(
1657                                         OP_LINESEQ, prelude_sentinel->op,
1658                                         newSTATEOP(
1659                                                 0, NULL,
1660                                                 newASSIGNOP(OPf_STACKED, lhs, 0, rhs)
1661                                         )
1662                                 ));
1663                         }
1664                 }
1665
1666                 /* default positional arguments */
1667                 {
1668                         size_t i, lim, req;
1669                         OP *nest;
1670
1671                         nest = NULL;
1672
1673                         req = param_spec->positional_required.used;
1674                         for (i = 0, lim = param_spec->positional_optional.used; i < lim; i++) {
1675                                 ParamInit *cur = &param_spec->positional_optional.data[i];
1676                                 OP *var, *cond;
1677
1678                                 cond = newBINOP(
1679                                         OP_LT, 0,
1680                                         newAVREF(newGVOP(OP_GV, 0, PL_defgv)),
1681                                         mkconstiv(aTHX_ req + i + 1)
1682                                 );
1683
1684                                 var = my_var(aTHX_ 0, cur->param.padoff);
1685
1686                                 nest = op_append_list(
1687                                         OP_LINESEQ, nest,
1688                                         newASSIGNOP(OPf_STACKED, var, 0, op_guard_relinquish(&cur->init))
1689                                 );
1690                                 nest = newCONDOP(
1691                                         0,
1692                                         cond,
1693                                         nest,
1694                                         NULL
1695                                 );
1696                         }
1697
1698                         op_guard_update(prelude_sentinel, op_append_list(
1699                                 OP_LINESEQ, prelude_sentinel->op,
1700                                 nest
1701                         ));
1702                 }
1703
1704                 /* named parameters */
1705                 if (count_named_params(param_spec)) {
1706                         size_t i, lim;
1707
1708                         assert(param_spec->rest_hash != NOT_IN_PAD);
1709
1710                         for (i = 0, lim = param_spec->named_required.used; i < lim; i++) {
1711                                 Param *cur = &param_spec->named_required.data[i];
1712                                 size_t n;
1713                                 char *p = SvPV(cur->name, n);
1714                                 OP *var, *cond;
1715
1716                                 cond = mkhvelem(aTHX_ param_spec->rest_hash, mkconstpv(aTHX_ p + 1, n - 1));
1717
1718                                 if (spec->flags & FLAG_CHECK_NARGS) {
1719                                         OP *xcroak, *msg;
1720
1721                                         var = mkhvelem(aTHX_ param_spec->rest_hash, mkconstpv(aTHX_ p + 1, n - 1));
1722                                         var = newUNOP(OP_DELETE, 0, var);
1723
1724                                         msg = mkconstsv(aTHX_ newSVpvf("In %"SVf": missing named parameter: %.*s", SVfARG(declarator), (int)(n - 1), p + 1));
1725                                         xcroak = newCVREF(
1726                                                 OPf_WANT_SCALAR,
1727                                                 newGVOP(OP_GV, 0, gv_fetchpvs("Carp::croak", 0, SVt_PVCV))
1728                                         );
1729                                         xcroak = newUNOP(OP_ENTERSUB, OPf_STACKED, op_append_elem(OP_LIST, msg, xcroak));
1730
1731                                         cond = newUNOP(OP_EXISTS, 0, cond);
1732
1733                                         cond = newCONDOP(0, cond, var, xcroak);
1734                                 }
1735
1736                                 var = my_var(
1737                                         aTHX_
1738                                         OPf_MOD | (OPpLVAL_INTRO << 8),
1739                                         cur->padoff
1740                                 );
1741                                 var = newASSIGNOP(OPf_STACKED, var, 0, cond);
1742
1743                                 op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, var)));
1744                         }
1745
1746                         for (i = 0, lim = param_spec->named_optional.used; i < lim; i++) {
1747                                 ParamInit *cur = &param_spec->named_optional.data[i];
1748                                 size_t n;
1749                                 char *p = SvPV(cur->param.name, n);
1750                                 OP *var, *cond;
1751
1752                                 var = mkhvelem(aTHX_ param_spec->rest_hash, mkconstpv(aTHX_ p + 1, n - 1));
1753                                 var = newUNOP(OP_DELETE, 0, var);
1754
1755                                 cond = mkhvelem(aTHX_ param_spec->rest_hash, mkconstpv(aTHX_ p + 1, n - 1));
1756                                 cond = newUNOP(OP_EXISTS, 0, cond);
1757
1758                                 cond = newCONDOP(0, cond, var, op_guard_relinquish(&cur->init));
1759
1760                                 var = my_var(
1761                                         aTHX_
1762                                         OPf_MOD | (OPpLVAL_INTRO << 8),
1763                                         cur->param.padoff
1764                                 );
1765                                 var = newASSIGNOP(OPf_STACKED, var, 0, cond);
1766
1767                                 op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, var)));
1768                         }
1769
1770                         if (!param_spec->slurpy.name) {
1771                                 if (spec->flags & FLAG_CHECK_NARGS) {
1772                                         /* croak if %{rest} */
1773                                         OP *xcroak, *cond, *keys, *msg;
1774
1775                                         keys = newUNOP(OP_KEYS, 0, my_var_g(aTHX_ OP_PADHV, 0, param_spec->rest_hash));
1776                                         keys = newLISTOP(OP_SORT, 0, newOP(OP_PUSHMARK, 0), keys);
1777                                         {
1778                                                 OP *first, *mid, *last;
1779
1780                                                 last = keys;
1781
1782                                                 mid = mkconstpvs(", ");
1783                                                 mid->op_sibling = last;
1784
1785                                                 first = newOP(OP_PUSHMARK, 0);
1786
1787                                                 keys = newLISTOP(OP_JOIN, 0, first, mid);
1788                                                 keys->op_targ = pad_alloc(OP_JOIN, SVs_PADTMP);
1789                                                 ((LISTOP *)keys)->op_last = last;
1790                                         }
1791
1792                                         msg = mkconstsv(aTHX_ newSVpvf("In %"SVf": no such named parameter: ", SVfARG(declarator)));
1793                                         msg = newBINOP(OP_CONCAT, 0, msg, keys);
1794
1795                                         xcroak = newCVREF(
1796                                                 OPf_WANT_SCALAR,
1797                                                 newGVOP(OP_GV, 0, gv_fetchpvs("Carp::croak", 0, SVt_PVCV))
1798                                         );
1799                                         xcroak = newUNOP(OP_ENTERSUB, OPf_STACKED, op_append_elem(OP_LIST, msg, xcroak));
1800
1801                                         cond = newUNOP(OP_KEYS, 0, my_var_g(aTHX_ OP_PADHV, 0, param_spec->rest_hash));
1802                                         xcroak = newCONDOP(0, cond, xcroak, NULL);
1803
1804                                         op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, xcroak)));
1805                                 } else {
1806                                         OP *clear;
1807
1808                                         clear = newASSIGNOP(
1809                                                 OPf_STACKED,
1810                                                 my_var_g(aTHX_ OP_PADHV, 0, param_spec->rest_hash),
1811                                                 0,
1812                                                 newNULLLIST()
1813                                         );
1814
1815                                         op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, clear)));
1816                                 }
1817                         } else if (param_spec->slurpy.padoff != param_spec->rest_hash) {
1818                                 OP *var, *clear;
1819
1820                                 assert(SvPV_nolen(param_spec->slurpy.name)[0] == '@');
1821
1822                                 var = my_var_g(
1823                                         aTHX_
1824                                         OP_PADAV,
1825                                         OPf_MOD | (OPpLVAL_INTRO << 8),
1826                                         param_spec->slurpy.padoff
1827                                 );
1828
1829                                 var = newASSIGNOP(OPf_STACKED, var, 0, my_var_g(aTHX_ OP_PADHV, 0, param_spec->rest_hash));
1830
1831                                 op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, var)));
1832
1833                                 clear = newASSIGNOP(
1834                                         OPf_STACKED,
1835                                         my_var_g(aTHX_ OP_PADHV, 0, param_spec->rest_hash),
1836                                         0,
1837                                         newNULLLIST()
1838                                 );
1839
1840                                 op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, clear)));
1841                         }
1842                 }
1843
1844                 if (spec->flags & FLAG_CHECK_TARGS) {
1845                         size_t i, lim, base;
1846
1847                         base = 1;
1848                         for (i = 0, lim = param_spec->positional_required.used; i < lim; i++) {
1849                                 Param *cur = &param_spec->positional_required.data[i];
1850
1851                                 if (cur->type) {
1852                                         op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, mktypecheckp(aTHX_ declarator, base + i, cur))));
1853                                 }
1854                         }
1855                         base += i;
1856
1857                         for (i = 0, lim = param_spec->positional_optional.used; i < lim; i++) {
1858                                 Param *cur = &param_spec->positional_optional.data[i].param;
1859
1860                                 if (cur->type) {
1861                                         op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, mktypecheckp(aTHX_ declarator, base + i, cur))));
1862                                 }
1863                         }
1864                         base += i;
1865
1866                         for (i = 0, lim = param_spec->named_required.used; i < lim; i++) {
1867                                 Param *cur = &param_spec->named_required.data[i];
1868
1869                                 if (cur->type) {
1870                                         op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, mktypecheckp(aTHX_ declarator, base + i, cur))));
1871                                 }
1872                         }
1873                         base += i;
1874
1875                         for (i = 0, lim = param_spec->named_optional.used; i < lim; i++) {
1876                                 Param *cur = &param_spec->named_optional.data[i].param;
1877
1878                                 if (cur->type) {
1879                                         op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, mktypecheckp(aTHX_ declarator, base + i, cur))));
1880                                 }
1881                         }
1882                         base += i;
1883
1884                         if (param_spec->slurpy.type) {
1885                                 /* $type->valid($_) or croak $type->get_message($_) for @rest / values %rest */
1886                                 OP *check, *list, *loop;
1887
1888                                 check = mktypecheck(aTHX_ declarator, base, param_spec->slurpy.name, NOT_IN_PAD, param_spec->slurpy.type);
1889
1890                                 if (SvPV_nolen(param_spec->slurpy.name)[0] == '@') {
1891                                         list = my_var_g(aTHX_ OP_PADAV, 0, param_spec->slurpy.padoff);
1892                                 } else {
1893                                         list = my_var_g(aTHX_ OP_PADHV, 0, param_spec->slurpy.padoff);
1894                                         list = newUNOP(OP_VALUES, 0, list);
1895                                 }
1896
1897                                 loop = newFOROP(0, NULL, list, check, NULL);
1898
1899                                 op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, loop)));
1900                         }
1901                 }
1902         }
1903
1904         /* finally let perl parse the actual subroutine body */
1905         body = parse_block(0);
1906
1907         /* add '();' to make function return nothing by default */
1908         /* (otherwise the invisible parameter initialization can "leak" into
1909            the return value: fun ($x) {}->("asdf", 0) == 2) */
1910         if (prelude_sentinel->op) {
1911                 body = newSTATEOP(0, NULL, body);
1912         }
1913
1914         body = op_append_list(OP_LINESEQ, op_guard_relinquish(prelude_sentinel), body);
1915
1916         /* it's go time. */
1917         {
1918                 CV *cv;
1919                 OP *const attrs = op_guard_relinquish(attrs_sentinel);
1920
1921                 SvREFCNT_inc_simple_void(PL_compcv);
1922
1923                 /* close outer block: '}' */
1924                 S_block_end(aTHX_ save_ix, body);
1925
1926                 cv = newATTRSUB(
1927                         floor_ix,
1928                         saw_name ? newSVOP(OP_CONST, 0, SvREFCNT_inc_simple_NN(saw_name)) : NULL,
1929                         proto ? newSVOP(OP_CONST, 0, SvREFCNT_inc_simple_NN(proto)) : NULL,
1930                         attrs,
1931                         body
1932                 );
1933
1934                 if (cv) {
1935                         register_info(aTHX_ PTR2UV(CvROOT(cv)), declarator, spec, param_spec);
1936                 }
1937
1938                 if (saw_name) {
1939                         *pop = newOP(OP_NULL, 0);
1940                         return KEYWORD_PLUGIN_STMT;
1941                 }
1942
1943                 *pop = newUNOP(
1944                         OP_REFGEN, 0,
1945                         newSVOP(
1946                                 OP_ANONCODE, 0,
1947                                 (SV *)cv
1948                         )
1949                 );
1950                 return KEYWORD_PLUGIN_EXPR;
1951         }
1952 }
1953
1954 static int kw_flags_enter(pTHX_ Sentinel sen, const char *kw_ptr, STRLEN kw_len, KWSpec *spec) {
1955         HV *hints;
1956         SV *sv, **psv;
1957         const char *p, *kw_active;
1958         STRLEN kw_active_len;
1959         bool kw_is_utf8;
1960
1961         if (!(hints = GvHV(PL_hintgv))) {
1962                 return FALSE;
1963         }
1964         if (!(psv = hv_fetchs(hints, HINTK_KEYWORDS, 0))) {
1965                 return FALSE;
1966         }
1967         sv = *psv;
1968         kw_active = SvPV(sv, kw_active_len);
1969         if (kw_active_len <= kw_len) {
1970                 return FALSE;
1971         }
1972
1973         kw_is_utf8 = lex_bufutf8();
1974
1975         for (
1976                 p = kw_active;
1977                 (p = strchr(p, *kw_ptr)) &&
1978                 p < kw_active + kw_active_len - kw_len;
1979                 p++
1980         ) {
1981                 if (
1982                         (p == kw_active || p[-1] == ' ') &&
1983                         p[kw_len] == ' ' &&
1984                         memcmp(kw_ptr, p, kw_len) == 0
1985                 ) {
1986                         ENTER;
1987                         SAVETMPS;
1988
1989                         SAVEDESTRUCTOR_X(sentinel_clear_void, sen);
1990
1991                         spec->flags = 0;
1992                         spec->reify_type = 0;
1993                         spec->shift = sentinel_mortalize(sen, newSVpvs(""));
1994                         spec->attrs = sentinel_mortalize(sen, newSVpvs(""));
1995
1996 #define FETCH_HINTK_INTO(NAME, PTR, LEN, X) STMT_START { \
1997         const char *fk_ptr_; \
1998         STRLEN fk_len_; \
1999         I32 fk_xlen_; \
2000         SV *fk_sv_; \
2001         fk_sv_ = sentinel_mortalize(sen, newSVpvs(HINTK_ ## NAME)); \
2002         sv_catpvn(fk_sv_, PTR, LEN); \
2003         fk_ptr_ = SvPV(fk_sv_, fk_len_); \
2004         fk_xlen_ = fk_len_; \
2005         if (kw_is_utf8) { \
2006                 fk_xlen_ = -fk_xlen_; \
2007         } \
2008         if (!((X) = hv_fetch(hints, fk_ptr_, fk_xlen_, 0))) { \
2009                 croak("%s: internal error: $^H{'%.*s'} not set", MY_PKG, (int)fk_len_, fk_ptr_); \
2010         } \
2011 } STMT_END
2012
2013                         FETCH_HINTK_INTO(FLAGS_, kw_ptr, kw_len, psv);
2014                         spec->flags = SvIV(*psv);
2015
2016                         FETCH_HINTK_INTO(REIFY_, kw_ptr, kw_len, psv);
2017                         spec->reify_type = SvIV(*psv);
2018
2019                         FETCH_HINTK_INTO(SHIFT_, kw_ptr, kw_len, psv);
2020                         SvSetSV(spec->shift, *psv);
2021
2022                         FETCH_HINTK_INTO(ATTRS_, kw_ptr, kw_len, psv);
2023                         SvSetSV(spec->attrs, *psv);
2024
2025 #undef FETCH_HINTK_INTO
2026                         return TRUE;
2027                 }
2028         }
2029         return FALSE;
2030 }
2031
2032 static int my_keyword_plugin(pTHX_ char *keyword_ptr, STRLEN keyword_len, OP **op_ptr) {
2033         Sentinel sen = { NULL };
2034         KWSpec spec;
2035         int ret;
2036
2037         if (kw_flags_enter(aTHX_ sen, keyword_ptr, keyword_len, &spec)) {
2038                 /* scope was entered, 'sen' and 'spec' are initialized */
2039                 ret = parse_fun(aTHX_ sen, op_ptr, keyword_ptr, keyword_len, &spec);
2040                 FREETMPS;
2041                 LEAVE;
2042         } else {
2043                 /* not one of our keywords, no allocation done */
2044                 ret = next_keyword_plugin(aTHX_ keyword_ptr, keyword_len, op_ptr);
2045         }
2046
2047         return ret;
2048 }
2049
2050 WARNINGS_RESET
2051
2052 MODULE = Function::Parameters   PACKAGE = Function::Parameters   PREFIX = fp_
2053 PROTOTYPES: ENABLE
2054
2055 UV
2056 fp__cv_root(sv)
2057         SV * sv
2058         PREINIT:
2059                 CV *xcv;
2060                 HV *hv;
2061                 GV *gv;
2062         CODE:
2063                 xcv = sv_2cv(sv, &hv, &gv, 0);
2064                 RETVAL = PTR2UV(xcv ? CvROOT(xcv) : NULL);
2065         OUTPUT:
2066                 RETVAL
2067
2068 BOOT:
2069 WARNINGS_ENABLE {
2070         HV *const stash = gv_stashpvs(MY_PKG, GV_ADD);
2071         /**/
2072         newCONSTSUB(stash, "FLAG_NAME_OK",      newSViv(FLAG_NAME_OK));
2073         newCONSTSUB(stash, "FLAG_ANON_OK",      newSViv(FLAG_ANON_OK));
2074         newCONSTSUB(stash, "FLAG_DEFAULT_ARGS", newSViv(FLAG_DEFAULT_ARGS));
2075         newCONSTSUB(stash, "FLAG_CHECK_NARGS",  newSViv(FLAG_CHECK_NARGS));
2076         newCONSTSUB(stash, "FLAG_INVOCANT",     newSViv(FLAG_INVOCANT));
2077         newCONSTSUB(stash, "FLAG_NAMED_PARAMS", newSViv(FLAG_NAMED_PARAMS));
2078         newCONSTSUB(stash, "FLAG_TYPES_OK",     newSViv(FLAG_TYPES_OK));
2079         newCONSTSUB(stash, "FLAG_CHECK_TARGS",  newSViv(FLAG_CHECK_TARGS));
2080         newCONSTSUB(stash, "HINTK_KEYWORDS", newSVpvs(HINTK_KEYWORDS));
2081         newCONSTSUB(stash, "HINTK_FLAGS_",   newSVpvs(HINTK_FLAGS_));
2082         newCONSTSUB(stash, "HINTK_SHIFT_",   newSVpvs(HINTK_SHIFT_));
2083         newCONSTSUB(stash, "HINTK_ATTRS_",   newSVpvs(HINTK_ATTRS_));
2084         newCONSTSUB(stash, "HINTK_REIFY_",   newSVpvs(HINTK_REIFY_));
2085         /**/
2086         next_keyword_plugin = PL_keyword_plugin;
2087         PL_keyword_plugin = my_keyword_plugin;
2088 } WARNINGS_RESET