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