Moose used an incorrect cast at the C-level resulting in errors with >2**32 IV's...
[gitmo/Mouse.git] / xs-src / MouseTypeConstraints.xs
1 /*
2  *   full definition of built-in type constraints (ware in Moose::Util::TypeConstraints::OptimizedConstraints)
3  */
4
5 #include "mouse.h"
6
7 #if PERL_BCDVERSION >= 0x5008005
8 #define LooksLikeNumber(sv) looks_like_number(sv)
9 #else
10 #define LooksLikeNumber(sv) ( SvPOKp(sv) ? looks_like_number(sv) : SvNIOKp(sv) )
11 #endif
12
13 #ifndef SvRXOK
14 #define SvRXOK(sv) (SvROK(sv) && SvMAGICAL(SvRV(sv)) && mg_find(SvRV(sv), PERL_MAGIC_qr))
15 #endif
16
17 typedef int (*check_fptr_t)(pTHX_ SV* const data, SV* const sv);
18
19 /*
20     NOTE: mouse_tc_check() handles GETMAGIC
21 */
22 int
23 mouse_tc_check(pTHX_ SV* const tc_code, SV* const sv) {
24     CV* const cv = (CV*)SvRV(tc_code);
25     assert(SvTYPE(cv) == SVt_PVCV);
26
27     SvGETMAGIC(sv);
28     if(CvXSUB(cv) == XS_Mouse_constraint_check){ /* built-in type constraints */
29         MAGIC* const mg = (MAGIC*)CvXSUBANY(cv).any_ptr;
30
31         assert(CvXSUBANY(cv).any_ptr != NULL);
32         assert(mg->mg_ptr            != NULL);
33
34         /* call the check function directly, skipping call_sv() */
35         return CALL_FPTR((check_fptr_t)mg->mg_ptr)(aTHX_ mg->mg_obj, sv);
36     }
37     else { /* custom */
38         int ok;
39         dSP;
40
41         ENTER;
42         SAVETMPS;
43
44         PUSHMARK(SP);
45         XPUSHs(sv);
46         PUTBACK;
47
48         call_sv(tc_code, G_SCALAR);
49
50         SPAGAIN;
51         ok = sv_true(POPs);
52         PUTBACK;
53
54         FREETMPS;
55         LEAVE;
56
57         return ok;
58     }
59 }
60
61 /*
62     The following type check functions return an integer, not a bool, to keep them simple,
63     so if you assign these return value to bool variable, you must use "expr ? TRUE : FALSE".
64 */
65
66 int
67 mouse_tc_Any(pTHX_ SV* const data PERL_UNUSED_DECL, SV* const sv PERL_UNUSED_DECL) {
68     assert(sv);
69     return TRUE;
70 }
71
72 int
73 mouse_tc_Bool(pTHX_ SV* const data PERL_UNUSED_DECL, SV* const sv) {
74     assert(sv);
75
76     if(sv_true(sv)){
77         if(SvIOKp(sv)){
78             return SvIVX(sv) == 1;
79         }
80         else if(SvNOKp(sv)){
81             return SvNVX(sv) == 1.0;
82         }
83         else if(SvPOKp(sv)){ /* "1" */
84             return SvCUR(sv) == 1 && SvPVX(sv)[0] == '1';
85         }
86         else{
87             return FALSE;
88         }
89     }
90     else{
91         /* any false value must be boolean */
92         return TRUE;
93     }
94 }
95
96 int
97 mouse_tc_Undef(pTHX_ SV* const data PERL_UNUSED_DECL, SV* const sv) {
98     assert(sv);
99     return !SvOK(sv);
100 }
101
102 int
103 mouse_tc_Defined(pTHX_ SV* const data PERL_UNUSED_DECL, SV* const sv) {
104     assert(sv);
105     return SvOK(sv);
106 }
107
108 int
109 mouse_tc_Value(pTHX_ SV* const data PERL_UNUSED_DECL, SV* const sv) {
110     assert(sv);
111     return SvOK(sv) && !SvROK(sv);
112 }
113
114 int
115 mouse_tc_Num(pTHX_ SV* const data PERL_UNUSED_DECL, SV* const sv) {
116     assert(sv);
117     return LooksLikeNumber(sv);
118 }
119
120 int
121 mouse_tc_Int(pTHX_ SV* const data PERL_UNUSED_DECL, SV* const sv) {
122     assert(sv);
123     if(SvIOKp(sv) || SvNOKp(sv)){
124         return TRUE;
125     }
126     else if(SvPOKp(sv)){
127         int const num_type = grok_number(SvPVX(sv), SvCUR(sv), NULL);
128         if(num_type){
129             return !(num_type & IS_NUMBER_NOT_INT);
130         }
131     }
132     return FALSE;
133 }
134
135 int
136 mouse_tc_Str(pTHX_ SV* const data PERL_UNUSED_DECL, SV* const sv) {
137     assert(sv);
138     return SvOK(sv) && !SvROK(sv) && !isGV(sv);
139 }
140
141 int
142 mouse_tc_ClassName(pTHX_ SV* const data PERL_UNUSED_DECL, SV* const sv){ 
143     assert(sv);
144     return is_class_loaded(sv);
145 }
146
147 int
148 mouse_tc_RoleName(pTHX_ SV* const data PERL_UNUSED_DECL, SV* const sv) {
149     assert(sv);
150     if(is_class_loaded(sv)){
151         int ok;
152
153         ENTER;
154         SAVETMPS;
155
156         ok =  is_an_instance_of("Mouse::Meta::Role", get_metaclass(sv));
157
158         FREETMPS;
159         LEAVE;
160
161         return ok;
162     }
163     return FALSE;
164 }
165
166 int
167 mouse_tc_Ref(pTHX_ SV* const data PERL_UNUSED_DECL, SV* const sv) {
168     assert(sv);
169     return SvROK(sv);
170 }
171
172 int
173 mouse_tc_ScalarRef(pTHX_ SV* const data PERL_UNUSED_DECL, SV* sv) {
174     assert(sv);
175     if(SvROK(sv)){
176          sv = SvRV(sv);
177          return !SvOBJECT(sv) && (SvTYPE(sv) <= SVt_PVLV && !isGV(sv));
178     }
179     return FALSE;
180 }
181
182 int
183 mouse_tc_ArrayRef(pTHX_ SV* const data PERL_UNUSED_DECL, SV* const sv) {
184     assert(sv);
185     return IsArrayRef(sv);
186 }
187
188 int
189 mouse_tc_HashRef(pTHX_ SV* const data PERL_UNUSED_DECL, SV* const sv) {
190     assert(sv);
191     return IsHashRef(sv);
192 }
193
194 int
195 mouse_tc_CodeRef(pTHX_ SV* const data PERL_UNUSED_DECL, SV* const sv) {
196     assert(sv);
197     return IsCodeRef(sv);
198 }
199
200 int
201 mouse_tc_RegexpRef(pTHX_ SV* const data PERL_UNUSED_DECL, SV* const sv) {
202     assert(sv);
203     return SvRXOK(sv);
204 }
205
206 int
207 mouse_tc_GlobRef(pTHX_ SV* const data PERL_UNUSED_DECL, SV* const sv) {
208     assert(sv);
209     return SvROK(sv) && !SvOBJECT(SvRV(sv)) && isGV(SvRV(sv));
210 }
211
212 int
213 mouse_tc_FileHandle(pTHX_ SV* const data PERL_UNUSED_DECL, SV* const sv) {
214     GV* gv;
215     assert(sv);
216
217     /* see pp_fileno() in pp_sys.c and Scalar::Util::openhandle() */
218
219     gv = (GV*)(SvROK(sv) ? SvRV(sv) : sv);
220     if(isGV(gv) || SvTYPE(gv) == SVt_PVIO){
221         IO* const io = isGV(gv) ? GvIO(gv) : (IO*)gv;
222
223         if(io && ( IoIFP(io) || SvTIED_mg((SV*)io, PERL_MAGIC_tiedscalar) )){
224             return TRUE;
225         }
226     }
227
228     return is_an_instance_of("IO::Handle", sv);
229 }
230
231 int
232 mouse_tc_Object(pTHX_ SV* const data PERL_UNUSED_DECL, SV* const sv) {
233     assert(sv);
234     return SvROK(sv) && SvOBJECT(SvRV(sv)) && !SvRXOK(sv);
235 }
236
237 /* Parameterized type constraints */
238
239 static int
240 mouse_parameterized_ArrayRef(pTHX_ SV* const param, SV* const sv) {
241     if(IsArrayRef(sv)){
242         AV* const av  = (AV*)SvRV(sv);
243         I32 const len = av_len(av) + 1;
244         I32 i;
245         for(i = 0; i < len; i++){
246             SV* const value = *av_fetch(av, i, TRUE);
247             if(!mouse_tc_check(aTHX_ param, value)){
248                 return FALSE;
249             }
250         }
251         return TRUE;
252     }
253     return FALSE;
254 }
255
256 static int
257 mouse_parameterized_HashRef(pTHX_ SV* const param, SV* const sv) {
258     if(mouse_tc_HashRef(aTHX_ NULL, sv)){
259         HV* const hv  = (HV*)SvRV(sv);
260         HE* he;
261
262         hv_iterinit(hv);
263         while((he = hv_iternext(hv))){
264             SV* const value = hv_iterval(hv, he);
265             if(!mouse_tc_check(aTHX_ param, value)){
266                 hv_iterinit(hv); /* reset */
267                 return FALSE;
268             }
269         }
270         return TRUE;
271     }
272     return FALSE;
273 }
274
275 static int
276 mouse_parameterized_Maybe(pTHX_ SV* const param, SV* const sv) {
277     if(SvOK(sv)){
278         return mouse_tc_check(aTHX_ param, sv);
279     }
280     return TRUE;
281 }
282
283 static int
284 mouse_types_union_check(pTHX_ AV* const types, SV* const sv) {
285     I32 const len = AvFILLp(types) + 1;
286     I32 i;
287
288     for(i = 0; i < len; i++){
289         if(mouse_tc_check(aTHX_ AvARRAY(types)[i], sv)){
290             return TRUE;
291         }
292     }
293
294     return FALSE;
295 }
296
297 static int
298 mouse_types_check(pTHX_ AV* const types, SV* const sv) {
299     I32 const len = AvFILLp(types) + 1;
300     I32 i;
301
302     ENTER;
303     SAVE_DEFSV;
304     DEFSV_set(sv);
305
306     for(i = 0; i < len; i++){
307         if(!mouse_tc_check(aTHX_ AvARRAY(types)[i], sv)){
308             LEAVE;
309             return FALSE;
310         }
311     }
312
313     LEAVE;
314
315     return TRUE;
316 }
317
318 /*
319  *  This class_type generator is taken from Scalar::Util::Instance
320  */
321
322 #define MY_CXT_KEY "Mouse::Util::TypeConstraints::_guts" XS_VERSION
323 typedef struct sui_cxt{
324     GV* universal_isa;
325     GV* universal_can;
326 } my_cxt_t;
327 START_MY_CXT
328
329 #define MG_klass_stash(mg) ((HV*)(mg)->mg_obj)
330 #define MG_klass_pv(mg)    ((mg)->mg_ptr)
331 #define MG_klass_len(mg)   ((mg)->mg_len)
332
333 static const char*
334 mouse_canonicalize_package_name(const char* name){
335
336     /* "::Foo" -> "Foo" */
337     if(name[0] == ':' && name[1] == ':'){
338         name += 2;
339     }
340
341     /* "main::main::main::Foo" -> "Foo" */
342     while(strnEQ(name, "main::", sizeof("main::")-1)){
343         name += sizeof("main::")-1;
344     }
345
346     return name;
347 }
348
349 static int
350 mouse_lookup_isa(pTHX_ HV* const instance_stash, const char* const klass_pv){
351     AV*  const linearized_isa = mro_get_linear_isa(instance_stash);
352     SV**       svp            = AvARRAY(linearized_isa);
353     SV** const end            = svp + AvFILLp(linearized_isa) + 1;
354
355     while(svp != end){
356         assert(SvPVX(*svp));
357         if(strEQ(klass_pv, mouse_canonicalize_package_name(SvPVX(*svp)))){
358             return TRUE;
359         }
360         svp++;
361     }
362     return FALSE;
363 }
364
365 #define find_method_pvn(a, b, c) mouse_stash_find_method(aTHX_ a, b, c)
366 #define find_method_pvs(a, b)    mouse_stash_find_method(aTHX_ a, STR_WITH_LEN(b))
367
368 static inline GV*
369 mouse_stash_find_method(pTHX_ HV* const stash, const char* const name, I32 const namelen){
370     GV** const gvp = (GV**)hv_fetch(stash, name, namelen, FALSE);
371     if(gvp && isGV(*gvp) && GvCV(*gvp)){ /* shortcut */
372         return *gvp;
373     }
374
375     return gv_fetchmeth_autoload(stash, name, namelen, 0);
376 }
377
378 int
379 mouse_is_an_instance_of(pTHX_ HV* const stash, SV* const instance){
380     assert(stash);
381     assert(SvTYPE(stash) == SVt_PVHV);
382
383     if(IsObject(instance)){
384         dMY_CXT;
385         HV* const instance_stash = SvSTASH(SvRV(instance));
386         GV* const instance_isa   = find_method_pvs(instance_stash, "isa");
387
388         /* the instance has no own isa method */
389         if(instance_isa == NULL || GvCV(instance_isa) == GvCV(MY_CXT.universal_isa)){
390             return stash == instance_stash
391                 || mouse_lookup_isa(aTHX_ instance_stash, HvNAME_get(stash));
392         }
393         /* the instance has its own isa method */
394         else {
395             int retval;
396             dSP;
397
398             ENTER;
399             SAVETMPS;
400
401             PUSHMARK(SP);
402             EXTEND(SP, 2);
403             PUSHs(instance);
404             mPUSHp(HvNAME_get(stash), HvNAMELEN_get(stash));
405             PUTBACK;
406
407             call_sv((SV*)instance_isa, G_SCALAR);
408
409             SPAGAIN;
410             retval = sv_true(POPs);
411             PUTBACK;
412
413             FREETMPS;
414             LEAVE;
415
416             return retval;
417         }
418     }
419     return FALSE;
420 }
421
422 static int
423 mouse_is_an_instance_of_universal(pTHX_ SV* const data, SV* const sv){
424     PERL_UNUSED_ARG(data);
425     return SvROK(sv) && SvOBJECT(SvRV(sv));
426 }
427
428 static int
429 mouse_can_methods(pTHX_ AV* const methods, SV* const instance){
430     if(IsObject(instance)){
431         dMY_CXT;
432         HV* const mystash      = SvSTASH(SvRV(instance));
433         GV* const mycan        = find_method_pvs(mystash, "can");
434         bool const use_builtin = (mycan == NULL || GvCV(mycan) == GvCV(MY_CXT.universal_can)) ? TRUE : FALSE;
435         I32 const len           = AvFILLp(methods) + 1;
436         I32 i;
437         for(i = 0; i < len; i++){
438             SV* const name = MOUSE_av_at(methods, i);
439
440             if(use_builtin){
441                 if(!find_method_pvn(mystash, SvPVX(name), SvCUR(name))){
442                     return FALSE;
443                 }
444             }
445             else{
446                 bool ok;
447                 dSP;
448
449                 ENTER;
450                 SAVETMPS;
451
452                 PUSHMARK(SP);
453                 EXTEND(SP, 2);
454                 PUSHs(instance);
455                 PUSHs(sv_mortalcopy(name));
456                 PUTBACK;
457
458                 call_method("can", G_SCALAR);
459
460                 SPAGAIN;
461                 ok = sv_true(POPs);
462                 PUTBACK;
463
464                 FREETMPS;
465                 LEAVE;
466
467                 if(!ok){
468                     return FALSE;
469                 }
470             }
471         }
472         return TRUE;
473     }
474     return FALSE;
475 }
476
477 static MGVTBL mouse_util_type_constraints_vtbl; /* not used, only for identity */
478
479 static CV*
480 mouse_tc_generate(pTHX_ const char* const name, check_fptr_t const fptr, SV* const param) {
481     CV* xsub;
482
483     xsub = newXS(name, XS_Mouse_constraint_check, __FILE__);
484     CvXSUBANY(xsub).any_ptr = sv_magicext(
485         (SV*)xsub,
486         param,       /* mg_obj: refcnt will be increased */
487         PERL_MAGIC_ext,
488         &mouse_util_type_constraints_vtbl,
489         (char*)fptr, /* mg_ptr */
490         0            /* mg_len: 0 for static data */
491     );
492
493     if(!name){
494         sv_2mortal((SV*)xsub);
495     }
496
497     return xsub;
498 }
499
500 CV*
501 mouse_generate_isa_predicate_for(pTHX_ SV* const klass, const char* const predicate_name){
502     STRLEN klass_len;
503     const char* klass_pv = SvPV_const(klass, klass_len);
504     SV*   param;
505     check_fptr_t fptr;
506
507     klass_pv = mouse_canonicalize_package_name(klass_pv);
508
509     if(strNE(klass_pv, "UNIVERSAL")){
510         param = (SV*)gv_stashpvn(klass_pv, klass_len, GV_ADD);
511         fptr = (check_fptr_t)mouse_is_an_instance_of;
512
513     }
514     else{
515         param = NULL;
516         fptr = (check_fptr_t)mouse_is_an_instance_of_universal;
517     }
518
519     return mouse_tc_generate(aTHX_ predicate_name, fptr, param);
520 }
521
522 CV*
523 mouse_generate_can_predicate_for(pTHX_ SV* const methods, const char* const predicate_name){
524     AV* av;
525     AV* const param = newAV_mortal();
526     I32 len;
527     I32 i;
528
529     must_ref(methods, "an ARRAY ref for method names", SVt_PVAV);
530     av = (AV*)SvRV(methods);
531
532     len = av_len(av) + 1;
533     for(i = 0; i < len; i++){
534         SV* const name = *av_fetch(av, i, TRUE);
535         STRLEN pvlen;
536         const char* const pv = SvPV_const(name, pvlen);
537
538         av_push(param, newSVpvn_share(pv, pvlen, 0U));
539     }
540
541     return mouse_tc_generate(aTHX_ predicate_name, (check_fptr_t)mouse_can_methods, (SV*)param);
542 }
543
544
545 XS(XS_Mouse_constraint_check) {
546     dVAR;
547     dXSARGS;
548     MAGIC* const mg = (MAGIC*)XSANY.any_ptr;
549
550     if(items < 1){
551         croak("Too few arguments for type constraint check functions");
552     }
553
554     SvGETMAGIC( ST(0) );
555     ST(0) = boolSV( CALL_FPTR((check_fptr_t)mg->mg_ptr)(aTHX_ mg->mg_obj, ST(0)) );
556     XSRETURN(1);
557 }
558
559 static void
560 setup_my_cxt(pTHX_ pMY_CXT){
561     MY_CXT.universal_isa = gv_fetchpvs("UNIVERSAL::isa", GV_ADD, SVt_PVCV);
562     SvREFCNT_inc_simple_void_NN(MY_CXT.universal_isa);
563
564     MY_CXT.universal_can = gv_fetchpvs("UNIVERSAL::can", GV_ADD, SVt_PVCV);
565     SvREFCNT_inc_simple_void_NN(MY_CXT.universal_can);
566 }
567
568 #define DEFINE_TC(name) mouse_tc_generate(aTHX_ "Mouse::Util::TypeConstraints::" STRINGIFY(name), CAT2(mouse_tc_, name), NULL)
569
570 MODULE = Mouse::Util::TypeConstraints    PACKAGE = Mouse::Util::TypeConstraints
571
572 PROTOTYPES:   DISABLE
573 VERSIONCHECK: DISABLE
574
575 BOOT:
576 {
577     MY_CXT_INIT;
578     setup_my_cxt(aTHX_ aMY_CXT);
579
580     /* setup built-in type constraints */
581     DEFINE_TC(Any);
582     DEFINE_TC(Undef);
583     DEFINE_TC(Defined);
584     DEFINE_TC(Bool);
585     DEFINE_TC(Value);
586     DEFINE_TC(Ref);
587     DEFINE_TC(Str);
588     DEFINE_TC(Num);
589     DEFINE_TC(Int);
590     DEFINE_TC(ScalarRef);
591     DEFINE_TC(ArrayRef);
592     DEFINE_TC(HashRef);
593     DEFINE_TC(CodeRef);
594     DEFINE_TC(GlobRef);
595     DEFINE_TC(FileHandle);
596     DEFINE_TC(RegexpRef);
597     DEFINE_TC(Object);
598     DEFINE_TC(ClassName);
599     DEFINE_TC(RoleName);
600 }
601
602 #ifdef USE_ITHREADS
603
604 void
605 CLONE(...)
606 CODE:
607 {
608     MY_CXT_CLONE;
609     setup_my_cxt(aTHX_ aMY_CXT);
610     PERL_UNUSED_VAR(items);
611 }
612
613 #endif /* !USE_ITHREADS */
614
615 #define MOUSE_TC_MAYBE     0
616 #define MOUSE_TC_ARRAY_REF 1
617 #define MOUSE_TC_HASH_REF  2
618
619 CV*
620 _parameterize_ArrayRef_for(SV* param)
621 ALIAS:
622     _parameterize_ArrayRef_for = MOUSE_TC_ARRAY_REF
623     _parameterize_HashRef_for  = MOUSE_TC_HASH_REF
624     _parameterize_Maybe_for    = MOUSE_TC_MAYBE
625 CODE:
626 {
627     check_fptr_t fptr;
628     SV* const tc_code = mcall0s(param, "_compiled_type_constraint");
629     if(!IsCodeRef(tc_code)){
630         croak("_compiled_type_constraint didn't return a CODE reference");
631     }
632
633     switch(ix){
634     case MOUSE_TC_ARRAY_REF:
635         fptr = mouse_parameterized_ArrayRef;
636         break;
637     case MOUSE_TC_HASH_REF:
638         fptr = mouse_parameterized_HashRef;
639         break;
640     default: /* Maybe type */
641         fptr = mouse_parameterized_Maybe;
642     }
643     RETVAL = mouse_tc_generate(aTHX_ NULL, fptr, tc_code);
644 }
645 OUTPUT:
646     RETVAL
647
648 MODULE = Mouse::Util::TypeConstraints    PACKAGE = Mouse::Meta::TypeConstraint
649
650 BOOT:
651     INSTALL_SIMPLE_READER(TypeConstraint, name);
652     INSTALL_SIMPLE_READER(TypeConstraint, parent);
653     INSTALL_SIMPLE_READER(TypeConstraint, message);
654
655     INSTALL_SIMPLE_READER(TypeConstraint, type_parameter);
656
657     INSTALL_SIMPLE_READER_WITH_KEY(TypeConstraint, _compiled_type_constraint, compiled_type_constraint);
658     INSTALL_SIMPLE_READER(TypeConstraint, _compiled_type_coercion); /* Mouse specific */
659
660     INSTALL_SIMPLE_PREDICATE_WITH_KEY(TypeConstraint, has_coercion, _compiled_type_coercion);
661     INSTALL_SIMPLE_PREDICATE_WITH_KEY(TypeConstraint, __is_parameterized, type_parameter); /* Mouse specific */
662
663 void
664 compile_type_constraint(SV* self)
665 CODE:
666 {
667     AV* const checks = newAV_mortal();
668     SV* check; /* check function */
669     SV* parent;
670     SV* types_ref;
671
672     for(parent = get_slots(self, "parent"); parent; parent = get_slots(parent, "parent")){
673         check = get_slots(parent, "hand_optimized_type_constraint");
674         if(check && SvOK(check)){
675             if(!mouse_tc_CodeRef(aTHX_ NULL, check)){
676                 croak("Not a CODE reference");
677             }
678             av_unshift(checks, 1);
679             av_store(checks, 0, newSVsv(check));
680             break; /* a hand optimized constraint must include all the parent */
681         }
682
683         check = get_slots(parent, "constraint");
684         if(check && SvOK(check)){
685             if(!mouse_tc_CodeRef(aTHX_ NULL, check)){
686                 croak("Not a CODE reference");
687             }
688             av_unshift(checks, 1);
689             av_store(checks, 0, newSVsv(check));
690         }
691     }
692
693     check = get_slots(self, "constraint");
694     if(check && SvOK(check)){
695         if(!mouse_tc_CodeRef(aTHX_ NULL, check)){
696             croak("Not a CODE reference");
697         }
698         av_push(checks, newSVsv(check));
699     }
700
701     types_ref = get_slots(self, "type_constraints");
702     if(types_ref && SvOK(types_ref)){ /* union type */
703         AV* types;
704         AV* union_checks;
705         CV* union_check;
706         I32 len;
707         I32 i;
708
709         if(!IsArrayRef(types_ref)){
710             croak("Not an ARRAY reference");
711         }
712         types = (AV*)SvRV(types_ref);
713         len = av_len(types) + 1;
714
715         union_checks = newAV_mortal();
716
717         for(i = 0; i < len; i++){
718             SV* const tc = *av_fetch(types, i, TRUE);
719             SV* const c  = get_slots(tc, "compiled_type_constraint");
720             if(!(c && mouse_tc_CodeRef(aTHX_ NULL, c))){
721                 sv_dump(self);
722                 croak("'%"SVf"' has no compiled type constraint", self);
723             }
724             av_push(union_checks, newSVsv(c));
725         }
726
727         union_check = mouse_tc_generate(aTHX_ NULL, (check_fptr_t)mouse_types_union_check, (SV*)union_checks);
728         av_push(checks, newRV_inc((SV*)union_check));
729     }
730
731     if(AvFILLp(checks) < 0){
732         check = newRV_inc((SV*)get_cv("Mouse::Util::TypeConstraints::Any", TRUE));
733     }
734     else{
735         check = newRV_inc((SV*)mouse_tc_generate(aTHX_ NULL, (check_fptr_t)mouse_types_check, (SV*)checks));
736     }
737     (void)set_slots(self, "compiled_type_constraint", check);
738 }
739