Drop optimized subs for all builtins - instead use the inlining code to generate...
[gitmo/Moose.git] / lib / Moose / Meta / TypeConstraint.pm
CommitLineData
4e036ee4 1
2package Moose::Meta::TypeConstraint;
3
4use strict;
5use warnings;
6use metaclass;
7
ed90007b 8use overload '0+' => sub { refaddr(shift) }, # id an object
9 '""' => sub { shift->name }, # stringify to tc name
72e389bf 10 bool => sub { 1 },
b644e331 11 fallback => 1;
900466d6 12
dabed765 13use Scalar::Util qw(blessed refaddr);
9f2230e9 14use Sub::Name qw(subname);
5a18346b 15use Try::Tiny;
66811d63 16
e606ae5f 17use base qw(Class::MOP::Object);
18
3726f905 19__PACKAGE__->meta->add_attribute('name' => (reader => 'name'));
20__PACKAGE__->meta->add_attribute('parent' => (
21 reader => 'parent',
22 predicate => 'has_parent',
23));
baf26cc6 24
25my $null_constraint = sub { 1 };
d67145ed 26__PACKAGE__->meta->add_attribute('constraint' => (
8de73ff1 27 reader => 'constraint',
28 writer => '_set_constraint',
baf26cc6 29 default => sub { $null_constraint }
d67145ed 30));
76d37e5a 31__PACKAGE__->meta->add_attribute('message' => (
32 accessor => 'message',
33 predicate => 'has_message'
34));
a27aa600 35__PACKAGE__->meta->add_attribute('coercion' => (
36 accessor => 'coercion',
37 predicate => 'has_coercion'
38));
70ea9161 39
c8cf9aaa 40__PACKAGE__->meta->add_attribute('hand_optimized_type_constraint' => (
41 init_arg => 'optimized',
42 accessor => 'hand_optimized_type_constraint',
e27dfc11 43 predicate => 'has_hand_optimized_type_constraint',
c8cf9aaa 44));
45
4e36cf24 46__PACKAGE__->meta->add_attribute('inlined' => (
47 accessor => 'inlined',
48 predicate => 'has_inlined_type_constraint',
49));
50
bd72f3c8 51sub parents {
52 my $self;
53 $self->parent;
54}
55
3726f905 56# private accessors
57
58__PACKAGE__->meta->add_attribute('compiled_type_constraint' => (
59 accessor => '_compiled_type_constraint',
60 predicate => '_has_compiled_type_constraint'
61));
22aed3c0 62__PACKAGE__->meta->add_attribute('package_defined_in' => (
63 accessor => '_package_defined_in'
64));
65
e27dfc11 66sub new {
a27aa600 67 my $class = shift;
8534c69a 68 my ($first, @rest) = @_;
69 my %args = ref $first ? %$first : $first ? ($first, @rest) : ();
70 $args{name} = $args{name} ? "$args{name}" : "__ANON__";
70ea9161 71
8534c69a 72 my $self = $class->_new(%args);
3726f905 73 $self->compile_type_constraint()
74 unless $self->_has_compiled_type_constraint;
66811d63 75 return $self;
76}
77
8534c69a 78
79
70ea9161 80sub coerce {
81 my $self = shift;
82
83 my $coercion = $self->coercion;
84
85 unless ($coercion) {
86 require Moose;
87 Moose->throw_error("Cannot coerce without a type coercion");
88 }
89
02679ba4 90 return $_[0] if $self->check($_[0]);
91
70ea9161 92 return $coercion->coerce(@_);
93}
a1257460 94
bb6ac54a 95sub assert_coerce {
96 my $self = shift;
97
98 my $coercion = $self->coercion;
99
100 unless ($coercion) {
101 require Moose;
102 Moose->throw_error("Cannot coerce without a type coercion");
103 }
104
105 return $_[0] if $self->check($_[0]);
106
107 my $result = $coercion->coerce(@_);
108
109 $self->assert_valid($result);
110
111 return $result;
112}
113
a1257460 114sub check {
115 my ($self, @args) = @_;
116 my $constraint_subref = $self->_compiled_type_constraint;
117 return $constraint_subref->(@args) ? 1 : undef;
118}
119
e27dfc11 120sub validate {
76d37e5a 121 my ($self, $value) = @_;
122 if ($self->_compiled_type_constraint->($value)) {
123 return undef;
124 }
125 else {
688fcdda 126 $self->get_message($value);
76d37e5a 127 }
128}
129
4e36cf24 130sub _inline_check {
131 my $self = shift;
132
3f9c18f3 133 unless ( $self->has_inlined_type_constraint ) {
134 require Moose;
135 Moose->throw_error( 'Cannot inline a type constraint check for ' . $self->name );
136 }
4e36cf24 137
964294c1 138 return $self->inlined->( $self, @_ );
4e36cf24 139}
140
c24269b3 141sub assert_valid {
142 my ($self, $value) = @_;
143
144 my $error = $self->validate($value);
145 return 1 if ! defined $error;
146
147 require Moose;
148 Moose->throw_error($error);
149}
150
688fcdda 151sub get_message {
152 my ($self, $value) = @_;
688fcdda 153 if (my $msg = $self->message) {
154 local $_ = $value;
155 return $msg->($value);
156 }
157 else {
5a18346b 158 # have to load it late like this, since it uses Moose itself
a1829d2f 159 my $can_partialdump = try {
160 # versions prior to 0.14 had a potential infinite loop bug
161 Class::MOP::load_class('Devel::PartialDump', { -version => 0.14 });
162 1;
163 };
164 if ($can_partialdump) {
5a18346b 165 $value = Devel::PartialDump->new->dump($value);
166 }
167 else {
168 $value = (defined $value ? overload::StrVal($value) : 'undef');
169 }
24a9f75e 170 return "Validation failed for '" . $self->name . "' with value $value";
d03bd989 171 }
688fcdda 172}
173
3726f905 174## type predicates ...
175
d9e17f80 176sub equals {
177 my ( $self, $type_or_name ) = @_;
178
e606ae5f 179 my $other = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
dabed765 180
0677975f 181 return 1 if $self == $other;
dabed765 182
183 if ( $self->has_hand_optimized_type_constraint and $other->has_hand_optimized_type_constraint ) {
184 return 1 if $self->hand_optimized_type_constraint == $other->hand_optimized_type_constraint;
185 }
186
187 return unless $self->constraint == $other->constraint;
188
189 if ( $self->has_parent ) {
190 return unless $other->has_parent;
191 return unless $self->parent->equals( $other->parent );
192 } else {
193 return if $other->has_parent;
194 }
d9e17f80 195
05a5763c 196 return;
d9e17f80 197}
198
b26e162e 199sub is_a_type_of {
d9e17f80 200 my ($self, $type_or_name) = @_;
201
e606ae5f 202 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
d9e17f80 203
204 ($self->equals($type) || $self->is_subtype_of($type));
b26e162e 205}
206
cce8198b 207sub is_subtype_of {
d9e17f80 208 my ($self, $type_or_name) = @_;
209
e606ae5f 210 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
d9e17f80 211
cce8198b 212 my $current = $self;
d9e17f80 213
cce8198b 214 while (my $parent = $current->parent) {
d9e17f80 215 return 1 if $parent->equals($type);
cce8198b 216 $current = $parent;
217 }
d9e17f80 218
cce8198b 219 return 0;
220}
221
3726f905 222## compiling the type constraint
223
224sub compile_type_constraint {
225 my $self = shift;
226 $self->_compiled_type_constraint($self->_actually_compile_type_constraint);
451c8248 227}
228
3726f905 229## type compilers ...
230
231sub _actually_compile_type_constraint {
232 my $self = shift;
e27dfc11 233
3726f905 234 return $self->_compile_hand_optimized_type_constraint
235 if $self->has_hand_optimized_type_constraint;
e27dfc11 236
43837b8a 237 if ( $self->has_inlined_type_constraint ) {
238 local $@;
239 my $sub = eval 'sub { ' . $self->_inline_check('$_[0]') . '}';
240 die $@ if $@;
241
242 return $sub;
243 }
244
3726f905 245 my $check = $self->constraint;
70ea9161 246 unless ( defined $check ) {
247 require Moose;
248 Moose->throw_error( "Could not compile type constraint '"
e27dfc11 249 . $self->name
70ea9161 250 . "' because no constraint check" );
251 }
e27dfc11 252
3726f905 253 return $self->_compile_subtype($check)
254 if $self->has_parent;
e27dfc11 255
3726f905 256 return $self->_compile_type($check);
257}
258
259sub _compile_hand_optimized_type_constraint {
260 my $self = shift;
e27dfc11 261
3726f905 262 my $type_constraint = $self->hand_optimized_type_constraint;
e27dfc11 263
70ea9161 264 unless ( ref $type_constraint ) {
265 require Moose;
70ea9161 266 Moose->throw_error("Hand optimized type constraint is not a code reference");
267 }
42bc21a4 268
269 return $type_constraint;
3726f905 270}
271
272sub _compile_subtype {
273 my ($self, $check) = @_;
e27dfc11 274
baf26cc6 275 # gather all the parent constraintss in order
3726f905 276 my @parents;
baf26cc6 277 my $optimized_parent;
3726f905 278 foreach my $parent ($self->_collect_all_parents) {
baf26cc6 279 # if a parent is optimized, the optimized constraint already includes
280 # all of its parents tcs, so we can break the loop
3726f905 281 if ($parent->has_hand_optimized_type_constraint) {
baf26cc6 282 push @parents => $optimized_parent = $parent->hand_optimized_type_constraint;
e27dfc11 283 last;
3726f905 284 }
285 else {
baf26cc6 286 push @parents => $parent->constraint;
3726f905 287 }
288 }
e27dfc11 289
baf26cc6 290 @parents = grep { $_ != $null_constraint } reverse @parents;
291
292 unless ( @parents ) {
293 return $self->_compile_type($check);
294 } elsif( $optimized_parent and @parents == 1 ) {
295 # the case of just one optimized parent is optimized to prevent
296 # looping and the unnecessary localization
b5981f07 297 if ( $check == $null_constraint ) {
298 return $optimized_parent;
299 } else {
9f2230e9 300 return subname($self->name, sub {
b5981f07 301 return undef unless $optimized_parent->($_[0]);
a1257460 302 my (@args) = @_;
303 local $_ = $args[0];
304 $check->(@args);
b5981f07 305 });
306 }
baf26cc6 307 } else {
308 # general case, check all the constraints, from the first parent to ourselves
b5981f07 309 my @checks = @parents;
310 push @checks, $check if $check != $null_constraint;
9f2230e9 311 return subname($self->name => sub {
a1257460 312 my (@args) = @_;
313 local $_ = $args[0];
baf26cc6 314 foreach my $check (@checks) {
a1257460 315 return undef unless $check->(@args);
baf26cc6 316 }
317 return 1;
318 });
319 }
3726f905 320}
321
322sub _compile_type {
323 my ($self, $check) = @_;
baf26cc6 324
325 return $check if $check == $null_constraint; # Item, Any
326
9f2230e9 327 return subname($self->name => sub {
a1257460 328 my (@args) = @_;
329 local $_ = $args[0];
330 $check->(@args);
1b2aea39 331 });
3726f905 332}
333
334## other utils ...
335
336sub _collect_all_parents {
337 my $self = shift;
338 my @parents;
339 my $current = $self->parent;
340 while (defined $current) {
341 push @parents => $current;
342 $current = $current->parent;
343 }
344 return @parents;
345}
346
85a9908f 347sub create_child_type {
9ceb576e 348 my ($self, %opts) = @_;
349 my $class = ref $self;
350 return $class->new(%opts, parent => $self);
351}
352
4e036ee4 3531;
354
ad46f524 355# ABSTRACT: The Moose Type Constraint metaclass
356
4e036ee4 357__END__
358
359=pod
360
4e036ee4 361=head1 DESCRIPTION
362
cfd006f0 363This class represents a single type constraint. Moose's built-in type
76127c77 364constraints, as well as constraints you define, are all stored in a
cfd006f0 365L<Moose::Meta::TypeConstraint::Registry> object as objects of this
366class.
6ba6d68c 367
baf46b9e 368=head1 INHERITANCE
369
370C<Moose::Meta::TypeConstraint> is a subclass of L<Class::MOP::Object>.
371
4e036ee4 372=head1 METHODS
373
374=over 4
375
cfd006f0 376=item B<< Moose::Meta::TypeConstraint->new(%options) >>
4e036ee4 377
cfd006f0 378This creates a new type constraint based on the provided C<%options>:
4e036ee4 379
cfd006f0 380=over 8
d9e17f80 381
cfd006f0 382=item * name
e606ae5f 383
cfd006f0 384The constraint name. If a name is not provided, it will be set to
385"__ANON__".
b26e162e 386
cfd006f0 387=item * parent
b26e162e 388
cfd006f0 389A C<Moose::Meta::TypeConstraint> object which is the parent type for
390the type being created. This is optional.
cce8198b 391
cfd006f0 392=item * constraint
e606ae5f 393
cfd006f0 394This is the subroutine reference that implements the actual constraint
395check. This defaults to a subroutine which always returns true.
6ba6d68c 396
cfd006f0 397=item * message
0a5bd159 398
cfd006f0 399A subroutine reference which is used to generate an error message when
400the constraint fails. This is optional.
0a5bd159 401
cfd006f0 402=item * coercion
76d37e5a 403
cfd006f0 404A L<Moose::Meta::TypeCoercion> object representing the coercions to
405the type. This is optional.
76d37e5a 406
cfd006f0 407=item * optimized
76d37e5a 408
cfd006f0 409This is a variant of the C<constraint> parameter that is somehow
410optimized. Typically, this means incorporating both the type's
411constraint and all of its parents' constraints into a single
412subroutine reference.
6ba6d68c 413
cfd006f0 414=back
4e036ee4 415
cfd006f0 416=item B<< $constraint->equals($type_name_or_object) >>
e606ae5f 417
cfd006f0 418Returns true if the supplied name or type object is the same as the
419current type.
66811d63 420
cfd006f0 421=item B<< $constraint->is_subtype_of($type_name_or_object) >>
e606ae5f 422
cfd006f0 423Returns true if the supplied name or type object is a parent of the
424current type.
3726f905 425
cfd006f0 426=item B<< $constraint->is_a_type_of($type_name_or_object) >>
e606ae5f 427
cfd006f0 428Returns true if the given type is the same as the current type, or is
429a parent of the current type. This is a shortcut for checking
430C<equals> and C<is_subtype_of>.
d9e17f80 431
cfd006f0 432=item B<< $constraint->coerce($value) >>
2f7e4042 433
b2894aea 434This will attempt to coerce the value to the type. If the type does not
cfd006f0 435have any defined coercions this will throw an error.
66811d63 436
572b5187 437If no coercion can produce a value matching C<$constraint>, the original
438value is returned.
439
086c6b6c 440=item B<< $constraint->assert_coerce($value) >>
572b5187 441
442This method behaves just like C<coerce>, but if the result is not valid
443according to C<$constraint>, an error is thrown.
444
cfd006f0 445=item B<< $constraint->check($value) >>
2f7e4042 446
cfd006f0 447Returns true if the given value passes the constraint for the type.
76d37e5a 448
cfd006f0 449=item B<< $constraint->validate($value) >>
2f7e4042 450
cfd006f0 451This is similar to C<check>. However, if the type I<is valid> then the
452method returns an explicit C<undef>. If the type is not valid, we call
453C<< $self->get_message($value) >> internally to generate an error
454message.
76d37e5a 455
952320e0 456=item B<< $constraint->assert_valid($value) >>
457
458Like C<check> and C<validate>, this method checks whether C<$value> is
459valid under the constraint. If it is, it will return true. If it is not,
460an exception will be thrown with the results of
461C<< $self->get_message($value) >>.
462
cfd006f0 463=item B<< $constraint->name >>
2f7e4042 464
cfd006f0 465Returns the type's name, as provided to the constructor.
688fcdda 466
cfd006f0 467=item B<< $constraint->parent >>
2f7e4042 468
cfd006f0 469Returns the type's parent, as provided to the constructor, if any.
4e036ee4 470
cfd006f0 471=item B<< $constraint->has_parent >>
2f7e4042 472
cfd006f0 473Returns true if the type has a parent type.
a27aa600 474
cfd006f0 475=item B<< $constraint->parents >>
2f7e4042 476
cfd006f0 477A synonym for C<parent>. This is useful for polymorphism with types
478that can have more than one parent.
c8cf9aaa 479
cfd006f0 480=item B<< $constraint->constraint >>
c8cf9aaa 481
cfd006f0 482Returns the type's constraint, as provided to the constructor.
9ceb576e 483
cfd006f0 484=item B<< $constraint->get_message($value) >>
4e036ee4 485
cfd006f0 486This generates a method for the given value. If the type does not have
487an explicit message, we generate a default message.
3726f905 488
cfd006f0 489=item B<< $constraint->has_message >>
490
491Returns true if the type has a message.
492
493=item B<< $constraint->message >>
494
495Returns the type's message as a subroutine reference.
496
497=item B<< $constraint->coercion >>
498
499Returns the type's L<Moose::Meta::TypeCoercion> object, if one
500exists.
501
502=item B<< $constraint->has_coercion >>
503
504Returns true if the type has a coercion.
505
506=item B<< $constraint->hand_optimized_type_constraint >>
507
508Returns the type's hand optimized constraint, as provided to the
509constructor via the C<optimized> option.
510
511=item B<< $constraint->has_hand_optimized_type_constraint >>
512
513Returns true if the type has an optimized constraint.
514
515=item B<< $constraint->create_child_type(%options) >>
451c8248 516
cfd006f0 517This returns a new type constraint of the same class using the
518provided C<%options>. The C<parent> option will be the current type.
3726f905 519
cfd006f0 520This method exists so that subclasses of this class can override this
521behavior and change how child types are created.
451c8248 522
523=back
524
4e036ee4 525=head1 BUGS
526
d4048ef3 527See L<Moose/BUGS> for details on reporting bugs.
4e036ee4 528
c8cf9aaa 529=cut