Rewrite type constraint parser for union parameter: ArrayRef[ Int | Str ]
gfx [Tue, 29 Sep 2009 06:57:51 +0000 (15:57 +0900)]
lib/Mouse/Meta/TypeConstraint.pm
lib/Mouse/Util/TypeConstraints.pm

index 13b4495..c9afe63 100644 (file)
@@ -2,31 +2,47 @@ package Mouse::Meta::TypeConstraint;
 use strict;
 use warnings;
 
-use overload '""'     => sub { shift->{name} },   # stringify to tc name
-             fallback => 1;
+use overload
+    '""'     => sub { shift->{name} },   # stringify to tc name
+    fallback => 1;
 
-use Carp ();
+use Carp qw(confess);
+use Scalar::Util qw(blessed reftype);
 
 use Mouse::Util qw(:meta);
 
+my $null_check = sub { 1 };
+
 sub new {
-    my $class = shift;
-    my %args = @_;
-    my $name = $args{name} || '__ANON__';
+    my($class, %args) = @_;
+
+    $args{name} = '__ANON__' if !defined $args{name};
 
-    my $check = $args{_compiled_type_constraint} or Carp::croak("missing _compiled_type_constraint");
-    if (ref $check eq 'Mouse::Meta::TypeConstraint') {
+    my $check = $args{_compiled_type_constraint} || $args{constraint};
+
+    # FIXME
+    if(blessed($check)){
         $check = $check->{_compiled_type_constraint};
     }
 
-    bless +{
-        name                      => $name,
-        _compiled_type_constraint => $check,
-        message                   => $args{message}
-    }, $class;
+    if(defined($check) && ref($check) ne 'CODE'){
+        confess("Type constraint for $args{name} is not a CODE reference");
+    }
+
+    my $self = bless \%args, $class;
+    $self->{_compiled_type_constraint} ||= $self->_compile();
+
+    return $self;
+}
+
+sub create_child_type{
+    my $self = shift;
+    return ref($self)->new(@_, parent => $self);
 }
 
-sub name { shift->{name} }
+sub name    { $_[0]->{name}    }
+sub parent  { $_[0]->{parent}  }
+sub message { $_[0]->{message} }
 
 sub check {
     my $self = shift;
@@ -49,12 +65,7 @@ sub assert_valid {
     my $error = $self->validate($value);
     return 1 if ! defined $error;
 
-    Carp::confess($error);
-}
-
-
-sub message {
-    return $_[0]->{message};
+    confess($error);
 }
 
 sub get_message {
@@ -73,10 +84,69 @@ sub get_message {
 }
 
 sub is_a_type_of{
-    my($self, $tc_name) = @_;
+    my($self, $other) = @_;
+
+    # ->is_a_type_of('__ANON__') is always false
+    return 0 if !blessed($other) && $other eq '__ANON__';
+
+    (my $other_name = $other) =~ s/\s+//g;
 
-    return $self->name eq $tc_name
-        || $self->name =~ /\A $tc_name \[/xms; # "ArrayRef" =~ "ArrayRef[Foo]"
+    return 1 if $self->name eq $other_name;
+
+    if(exists $self->{type_constraints}){ # union
+        foreach my $type(@{$self->{type_constraints}}){
+            return 1 if $type->name eq $other_name;
+        }
+    }
+
+    for(my $parent = $self->parent; defined $parent; $parent = $parent->parent){
+        return 1 if $parent->name eq $other_name;
+    }
+
+    return 0;
+}
+
+sub _compile{
+    my($self) = @_;
+
+    # add parents first
+    my @checks;
+    for(my $parent = $self->parent; defined $parent; $parent = $parent->parent){
+        if($parent->{constraint}){
+            push @checks, $parent->{constraint};
+         }
+         elsif($parent->{_compiled_type_constraint} && $parent->{_compiled_type_constraint} != $null_check){
+            # hand-optimized constraint
+            push @checks, $parent->{_compiled_type_constraint};
+            last;
+        }
+    }
+    # then add child
+    if($self->{constraint}){
+        push @checks, $self->{constraint};
+    }
+
+    if(@checks == 0){
+        return $null_check;
+    }
+    elsif(@checks == 1){
+        my $c = $checks[0];
+        return sub{
+            my(@args) = @_;
+            local $_ = $args[0];
+            return $c->(@args);
+        };
+    }
+    else{
+        return sub{
+            my(@args) = @_;
+            local $_ = $args[0];
+            foreach my $c(@checks){
+                return undef if !$c->(@args);
+            }
+            return 1;
+        };
+    }
 }
 
 1;
index 54246b8..fedf769 100644 (file)
@@ -4,13 +4,15 @@ use warnings;
 
 use Exporter;
 
-use Carp ();
+use Carp qw(confess);
 use Scalar::Util qw/blessed looks_like_number openhandle/;
 
 use Mouse::Util qw(does_role not_supported);
 use Mouse::Meta::Module; # class_of
 use Mouse::Meta::TypeConstraint;
 
+use constant _DEBUG => !!$ENV{TC_DEBUG};
+
 our @ISA    = qw(Exporter);
 our @EXPORT = qw(
     as where message from via type subtype coerce class_type role_type enum
@@ -18,7 +20,6 @@ our @EXPORT = qw(
 );
 
 my %TYPE;
-my %TYPE_SOURCE;
 my %COERCE;
 my %COERCE_KEYS;
 
@@ -37,8 +38,9 @@ sub via (&) { $_[0] }
 
 BEGIN {
     my %builtins = (
-        Any        => sub { 1 },
-        Item       => sub { 1 },
+        Any        => undef, # null check
+        Item       => undef, # null check
+        Maybe      => undef, # null check
 
         Bool       => sub { $_[0] ? $_[0] eq '1' : 1 },
         Undef      => sub { !defined($_[0]) },
@@ -72,11 +74,14 @@ BEGIN {
         $TYPE{$name} = Mouse::Meta::TypeConstraint->new(
             name                      => $name,
             _compiled_type_constraint => $code,
+            package_defined_in        => __PACKAGE__,
         );
-        $TYPE_SOURCE{$name} = __PACKAGE__;
     }
 
-    sub optimized_constraints { \%TYPE }
+    sub optimized_constraints {
+        Carp::cluck('optimized_constraints() has been deprecated');
+        return \%TYPE;
+    }
 
     my @builtins = keys %TYPE;
     sub list_all_builtin_type_constraints { @builtins }
@@ -84,109 +89,74 @@ BEGIN {
     sub list_all_type_constraints         { keys %TYPE }
 }
 
-sub type {
+sub _create_type{
+    my $mode = shift;
+
     my $name;
-    my %conf;
+    my %args;
 
-    if(@_ == 1 && ref $_[0]){ # type { where => ... }
-        %conf = %{$_[0]};
+    if(@_ == 1 && ref $_[0]){   # @_ : { name => $name, where => ... }
+        %args = %{$_[0]};
     }
-    elsif(@_ == 2 && ref $_[1]){ # type $name => { where => ... }*
+    elsif(@_ == 2 && ref $_[1]){ # @_ : $name => { where => ... }
         $name = $_[0];
-        %conf = %{$_[1]};
+        %args = %{$_[1]};
     }
-    elsif(@_ % 2){ # odd number of arguments
-        $name = shift;
-        %conf = @_;
+    elsif(@_ % 2){               # @_ : $name => ( where => ... )
+        ($name, %args) = @_;
     }
-    else{
-        %conf = @_;
+    else{                        # @_ : (name => $name, where => ...)
+        %args = @_;
     }
 
-    $name = '__ANON__' if !defined $name;
-
-    my $pkg = caller;
-
-    if ($TYPE{$name} && $TYPE_SOURCE{$name} ne $pkg) {
-        Carp::croak "The type constraint '$name' has already been created in $TYPE_SOURCE{$name} and cannot be created again in $pkg";
+    if(!defined $name){
+        if(!defined($name = $args{name})){
+            $name = '__ANON__';
+        }
     }
 
-    my $constraint = $conf{where} || do {
-        my $as = delete $conf{as} || 'Any';
-        ($TYPE{$as} ||= _build_type_constraint($as))->{_compiled_type_constraint};
-    };
+    $args{name} = $name;
 
-    my $tc = Mouse::Meta::TypeConstraint->new(
-        name                      => $name,
-        _compiled_type_constraint => sub {
-            local $_ = $_[0];
-            return &{$constraint};
-        },
-    );
+    my $package_defined_in = $args{package_defined_in} ||= caller(1);
 
-    $TYPE_SOURCE{$name} = $pkg;
-    $TYPE{$name}        = $tc;
+    my $existing = $TYPE{$name};
+    if($existing && $existing->{package_defined_in} ne $package_defined_in){
+        confess("The type constraint '$name' has already been created in "
+              . "$existing->{package_defined_in} and cannot be created again in $package_defined_in");
+    }
 
-    return $tc;
-}
+    $args{constraint}                = delete($args{where})
+        if exists $args{where};
+    $args{_compiled_type_constraint} = delete $args{optimized_as}
+        if exists $args{optimized_as};
 
-sub subtype {
-    my $name;
-    my %conf;
+    my $constraint;
+    if($mode eq 'subtype'){
+        my $parent = exists($args{as}) ? delete($args{as}) : delete($args{name});
 
-    if(@_ == 1 && ref $_[0]){ # type { where => ... }
-        %conf = %{$_[0]};
-    }
-    elsif(@_ == 2 && ref $_[1]){ # type $name => { where => ... }*
-        $name = $_[0];
-        %conf = %{$_[1]};
-    }
-    elsif(@_ % 2){ # odd number of arguments
-        $name = shift;
-        %conf = @_;
+        $parent = blessed($parent) ? $parent : find_or_create_isa_type_constraint($parent);
+        $constraint = $parent->create_child_type(%args);
     }
     else{
-        %conf = @_;
-    }
-
-    $name = '__ANON__' if !defined $name;
-
-    my $pkg = caller;
-
-    if ($TYPE{$name} && $TYPE_SOURCE{$name} ne $pkg) {
-        Carp::croak "The type constraint '$name' has already been created in $TYPE_SOURCE{$name} and cannot be created again in $pkg";
+        $constraint = Mouse::Meta::TypeConstraint->new(%args);
     }
 
-    my $constraint    = delete $conf{where};
-    my $as_constraint = find_or_create_isa_type_constraint(delete $conf{as} || 'Any')
-        ->{_compiled_type_constraint};
-
-    my $tc = Mouse::Meta::TypeConstraint->new(
-        name => $name,
-        _compiled_type_constraint => (
-            $constraint ? 
-            sub {
-                local $_ = $_[0];
-                $as_constraint->($_[0]) && $constraint->($_[0])
-            } :
-            sub {
-                local $_ = $_[0];
-                $as_constraint->($_[0]);
-            }
-        ),
-        %conf,
-    );
+    return $TYPE{$name} = $constraint;
+}
 
-    $TYPE_SOURCE{$name} = $pkg;
-    $TYPE{$name}        = $tc;
+sub type {
+    return _create_type('type', @_);
+}
 
-    return $tc;
+sub subtype {
+    return _create_type('subtype', @_);
 }
 
 sub coerce {
     my $name = shift;
 
-    Carp::croak "Cannot find type '$name', perhaps you forgot to load it."
+    $name =~ s/\s+//g;
+    confess "Cannot find type '$name', perhaps you forgot to load it."
         unless $TYPE{$name};
 
     unless ($COERCE{$name}) {
@@ -194,21 +164,23 @@ sub coerce {
         $COERCE_KEYS{$name} = [];
     }
 
-    while (my($type, $code) = splice @_, 0, 2) {
-        Carp::croak "A coercion action already exists for '$type'"
-            if $COERCE{$name}->{$type};
+    my $package_defined_in = caller;
 
-        if (! $TYPE{$type}) {
-            # looks parameterized
-            if ($type =~ /^[^\[]+\[.+\]$/) {
-                $TYPE{$type} = _build_type_constraint($type);
-            } else {
-                Carp::croak "Could not find the type constraint ($type) to coerce from"
-            }
+    while (my($from, $code) = splice @_, 0, 2) {
+        $from =~ s/\s+//g;
+
+        confess "A coercion action already exists for '$from'"
+            if $COERCE{$name}->{$from};
+
+        my $type = find_or_parse_type_constraint($from, $package_defined_in);
+        if (!$type) {
+            confess "Could not find the type constraint ($from) to coerce from"
         }
 
+        warn "# REGISTER COERCE $name, from $type\n" if _DEBUG;
+
         push @{ $COERCE_KEYS{$name} }, $type;
-        $COERCE{$name}->{$type} = $code;
+        $COERCE{$name}->{$from} = $code;
     }
     return;
 }
@@ -218,20 +190,28 @@ sub class_type {
     if ($conf && $conf->{class}) {
         # No, you're using this wrong
         warn "class_type() should be class_type(ClassName). Perhaps you're looking for subtype $name => as '$conf->{class}'?";
-        subtype $name => (as => $conf->{class});
+        _create_type 'type', $name => (
+            as   => $conf->{class},
+
+            type => 'Class',
+       );
     }
     else {
-        subtype $name => (
-            where => sub { blessed($_) && $_->isa($name) },
+        _create_type 'type', $name => (
+            optimized_as => sub { blessed($_[0]) && $_[0]->isa($name) },
+
+            type => 'Class',
         );
     }
 }
 
 sub role_type {
     my($name, $conf) = @_;
-    my $role = $conf->{role};
-    subtype $name => (
-        where => sub { does_role($_, $role) },
+    my $role = ($conf && $conf->{role}) ? $conf->{role} : $name;
+    _create_type 'type', $name => (
+        optimized_as => sub { blessed($_[0]) && does_role($_[0], $role) },
+
+        type => 'Role',
     );
 }
 
@@ -241,171 +221,257 @@ sub typecast_constraints {
     Carp::croak("wrong arguments count") unless @_ == 4;
 
     local $_;
-    for my $type ( split /\|/, $types ) {
-        next unless $COERCE{$type};
+    for my $type ($types, ($types->{type_constraints} ? @{$types->{type_constraints}} : ()) ) {
         for my $coerce_type (@{ $COERCE_KEYS{$type}}) {
+
+            if(_DEBUG){
+                warn sprintf "# COERCE: from %s to %s for %s (%s)\n",
+                    $coerce_type, $type, defined($value) ? $value : 'undef',
+                    $coerce_type->check($value) ? "try" : "skip";
+            }
+
+            next if !$coerce_type->check($value);
+
+            # try to coerce
             $_ = $value;
-            next unless $TYPE{$coerce_type}->check($value);
-            $_ = $value;
-            $_ = $COERCE{$type}->{$coerce_type}->($value);
-            return $_ if $types->check($_);
+            $_ = $COERCE{$type}->{$coerce_type}->($_); # coerce
+
+            if(_DEBUG){
+                warn sprintf "# COERCE: got %s, which is%s %s\n",
+                    defined($_) ? $_ : 'undef', $types->check($_) ? '' : ' not', $types;
+            }
+
+            return $_ if $types->check($_); # check for $types, not $constraint
         }
     }
     return $value;
 }
 
-my $serial_enum = 0;
 sub enum {
+    my($name, %valid);
+
     # enum ['small', 'medium', 'large']
     if (ref($_[0]) eq 'ARRAY') {
-        my @elements = @{ shift @_ };
+        %valid = map{ $_ => undef } @{ $_[0] };
+        $name  = sprintf '(%s)', join '|', sort @{$_[0]};
+    }
+    # enum size => 'small', 'medium', 'large'
+    else{
+        $name  = shift;
+        %valid = map{ $_ => undef } @_;
+    }
+    return _create_type 'type', $name => (
+        optimized_as  => sub{ defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]} },
+
+        type => 'Enum',
+    );
+}
 
-        my $name = 'Mouse::Util::TypeConstaints::Enum::Serial::'
-                 . ++$serial_enum;
-        enum($name, @elements);
-        return $name;
+sub _find_or_create_regular_type{
+    my($spec)  = @_;
+
+    return $TYPE{$spec} if exists $TYPE{$spec};
+
+    my $meta  = Mouse::Meta::Module::class_of($spec);
+
+    if(!$meta){
+        return;
     }
 
-    # enum size => 'small', 'medium', 'large'
-    my $name = shift;
-    my %is_valid = map { $_ => 1 } @_;
+    my $check;
+    my $type;
+    if($meta && $meta->isa('Mouse::Meta::Role')){
+        $check = sub{
+            return blessed($_[0]) && $_[0]->does($spec);
+        };
+        $type = 'Role';
+    }
+    else{
+        $check = sub{
+            return blessed($_[0]) && $_[0]->isa($spec);
+        };
+        $type = 'Class';
+    }
+
+    warn "#CREATE a $type type for $spec\n" if _DEBUG;
 
-    subtype(
-        $name => where => sub { $is_valid{$_} }
+    return $TYPE{$spec} = Mouse::Meta::TypeConstraint->new(
+        name                      => $spec,
+        _compiled_type_constraint => $check,
+
+        type                      => $type,
     );
 }
 
-sub _build_type_constraint {
-    my($spec) = @_;
+$TYPE{ArrayRef}{constraint_generator} = sub {
+    my($type_parameter) = @_;
+    my $check = $type_parameter->{_compiled_type_constraint};
 
-    my $code;
-    $spec =~ s/\s+//g;
+    return sub{
+        foreach my $value (@{$_}) {
+            return undef unless $check->($value);
+        }
+        return 1;
+    }
+};
+$TYPE{HashRef}{constraint_generator} = sub {
+    my($type_parameter) = @_;
+    my $check = $type_parameter->{_compiled_type_constraint};
+
+    return sub{
+        foreach my $value(values %{$_}){
+            return undef unless $check->($value);
+        }
+        return 1;
+    };
+};
+
+# 'Maybe' type accepts 'Any', so it requires parameters
+$TYPE{Maybe}{constraint_generator} = sub {
+    my($type_parameter) = @_;
+    my $check = $type_parameter->{_compiled_type_constraint};
 
-    if ($spec =~ /\A (\w+) \[ (.+) \] \z/xms) {
-        # parameterized
-        my $constraint = $1;
-        my $param      = $2;
-        my $parent;
+    return sub{
+        return !defined($_) || $check->($_);
+    };
+};
+
+sub _find_or_create_parameterized_type{
+    my($base, $param) = @_;
+
+    my $name = sprintf '%s[%s]', $base->name, $param->name;
+
+    $TYPE{$name} ||= do{
+        warn "#CREATE a Parameterized type for $name\n" if _DEBUG;
 
-        if ($constraint eq 'Maybe') {
-            $parent = _build_type_constraint('Undef');
+        my $generator = $base->{constraint_generator};
+
+        if(!$generator){
+            confess("The $name constraint cannot be used, because $param doesn't subtype from a parameterizable type");
         }
-        else {
-            $parent = _build_type_constraint($constraint);
+
+        Mouse::Meta::TypeConstraint->new(
+            name               => $name,
+            parent             => $base,
+            constraint         => $generator->($param),
+
+            type               => 'Parameterized',
+        );
+    }
+}
+sub _find_or_create_union_type{
+    my @types              = map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
+
+    my $name = join '|', map{ $_->name } @types;
+
+    $TYPE{$name} ||= do{
+        warn "# CREATE a Union type for ", Mouse::Util::english_list(@types),"\n" if _DEBUG;
+
+        my $check = sub{
+            foreach my $type(@types){
+                return 1 if $type->check($_[0]);
+            }
+            return 0;
+        };
+
+        return Mouse::Meta::TypeConstraint->new(
+            name                      => $name,
+            _compiled_type_constraint => $check,
+            type_constraints          => \@types,
+
+            type                      => 'Union',
+        );
+    };
+}
+
+# The type parser
+sub _parse_type{
+    my($spec, $start) = @_;
+
+    my @list;
+    my $subtype;
+
+    my $len = length $spec;
+    my $i;
+
+    for($i = $start; $i < $len; $i++){
+        my $char = substr($spec, $i, 1);
+
+        if($char eq '['){
+            my $base = _find_or_create_regular_type( substr($spec, $start, $i - $start))
+                or return;
+
+            ($i, $subtype) = _parse_type($spec, $i+1)
+                or return;
+            $start = $i+1; # reset
+
+            push @list, _find_or_create_parameterized_type($base => $subtype);
         }
-        my $child = _build_type_constraint($param);
-        if ($constraint eq 'ArrayRef') {
-            my $code_str = 
-                "#line " . __LINE__ . ' "' . __FILE__ . "\"\n" .
-                "sub {\n" .
-                "    if (\$parent->check(\$_[0])) {\n" .
-                "        foreach my \$e (\@{\$_[0]}) {\n" .
-                "            return () unless \$child->check(\$e);\n" .
-                "        }\n" .
-                "        return 1;\n" .
-                "    }\n" .
-                "    return ();\n" .
-                "};\n"
-            ;
-            $code = eval $code_str or Carp::confess("Failed to generate inline type constraint: $@");
-        } elsif ($constraint eq 'HashRef') {
-            my $code_str = 
-                "#line " . __LINE__ . ' "' . __FILE__ . "\"\n" .
-                "sub {\n" .
-                "    if (\$parent->check(\$_[0])) {\n" .
-                "        foreach my \$e (values \%{\$_[0]}) {\n" .
-                "            return () unless \$child->check(\$e);\n" .
-                "        }\n" .
-                "        return 1;\n" .
-                "    }\n" .
-                "    return ();\n" .
-                "};\n"
-            ;
-            $code = eval $code_str or Carp::confess($@);
-        } elsif ($constraint eq 'Maybe') {
-            my $code_str =
-                "#line " . __LINE__ . ' "' . __FILE__ . "\"\n" .
-                "sub {\n" .
-                "    return \$child->check(\$_[0]) || \$parent->check(\$_[0]);\n" .
-                "};\n"
-            ;
-            $code = eval $code_str or Carp::confess($@);
-        } else {
-            Carp::confess("Support for parameterized types other than Maybe, ArrayRef or HashRef is not implemented yet");
+        elsif($char eq ']'){
+            $len = $i+1;
+            last;
         }
-        $TYPE{$spec} = Mouse::Meta::TypeConstraint->new( _compiled_type_constraint => $code, name => $spec );
-    } else {
-        $code = $TYPE{ $spec };
-        if (! $code) {
-            # is $spec a known role?  If so, constrain with 'does' instead of 'isa'
-            require Mouse::Meta::Role;
-            my $check = Mouse::Meta::Role->_metaclass_cache($spec)? 
-                'does' : 'isa';
-            my $code_str = 
-                "#line " . __LINE__ . ' "' . __FILE__ . "\"\n" .
-                "sub {\n" .
-                "    Scalar::Util::blessed(\$_[0]) && \$_[0]->$check('$spec');\n" .
-                "}"
-            ;
-            $code = eval $code_str  or Carp::confess($@);
-            $TYPE{$spec} = Mouse::Meta::TypeConstraint->new( _compiled_type_constraint => $code, name => $spec );
+        elsif($char eq '|'){
+            my $type = _find_or_create_regular_type( substr($spec, $start, $i - $start))
+                or return;
+
+            push @list, $type;
+
+            ($i, $subtype) = _parse_type($spec, $i+1)
+                or return;
+
+            $start = $i+1; # reset
+
+            push @list, $subtype;
         }
     }
-    return Mouse::Meta::TypeConstraint->new( _compiled_type_constraint => $code, name => $spec );
-}
+    if($i - $start){
+        push @list, _find_or_create_regular_type(substr $spec, $start, $i - $start);
+    }
 
-sub find_type_constraint {
-    my($type) = @_;
-    if(blessed($type) && $type->isa('Mouse::Meta::TypeConstraint')){
-        return $type;
+    if(@list == 0){
+       return;
+    }
+    elsif(@list == 1){
+        return ($len, $list[0]);
     }
     else{
-        return $TYPE{$type};
+        return ($len, _find_or_create_union_type(@list));
     }
 }
 
-sub find_or_create_does_type_constraint{
-    not_supported;
+
+sub find_type_constraint {
+    my($spec) = @_;
+    return $spec if blessed($spec);
+
+    $spec =~ s/\s+//g;
+    return $TYPE{$spec};
 }
 
-sub find_or_create_isa_type_constraint {
-    my $type_constraint = shift;
+sub find_or_parse_type_constraint {
+    my($spec) = @_;
 
-    Carp::confess("Got isa => type_constraints, but Mouse does not yet support parameterized types for containers other than ArrayRef and HashRef and Maybe (rt.cpan.org #39795)")
-        if $type_constraint =~ /\A ( [^\[]+ ) \[\.+\] \z/xms &&
-           $1 ne 'ArrayRef' &&
-           $1 ne 'HashRef'  &&
-           $1 ne 'Maybe'
-    ;
+    return $spec if blessed($spec);
 
+    $spec =~ s/\s+//g;
+    return $TYPE{$spec} || do{
+        my($pos, $type) = _parse_type($spec, 0);
+        $type;
+    };
+}
 
-    $type_constraint =~ s/\s+//g;
+sub find_or_create_does_type_constraint{
+    my $type = find_or_parse_type_constriant(@_) || role_type(@_);
 
-    my $tc =  find_type_constraint($type_constraint);
-    if (!$tc) {
-        my @type_constraints = split /\|/, $type_constraint;
-        if (@type_constraints == 1) {
-            $tc = $TYPE{$type_constraints[0]} ||
-                _build_type_constraint($type_constraints[0]);
-        }
-        else {
-            my @code_list = map {
-                $TYPE{$_} || _build_type_constraint($_)
-            } @type_constraints;
-
-            $tc = Mouse::Meta::TypeConstraint->new(
-                name => $type_constraint,
-
-                _compiled_type_constraint => sub {
-                    foreach my $code (@code_list) {
-                        return 1 if $code->check($_[0]);
-                    }
-                    return 0;
-                },
-            );
-        }
+    if($type->{type} && $type->{type} ne 'Role'){
+        Carp::cluck("$type is not a role type");
     }
-    return $tc;
+    return $type;
+}
+
+sub find_or_create_isa_type_constraint {
+    return find_or_parse_type_constraint(@_) || class_type(@_);
 }
 
 1;