Skip Menu |

This queue is for tickets about the Moo CPAN distribution.

Report information
The Basics
Id: 128278
Status: open
Priority: 0/
Queue: Moo

People
Owner: Nobody in particular
Requestors: djerius [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: (no value)
Fixed in: (no value)



Subject: "overwrite a locally defined method" exception thrown if used in class composed with role at compile time
(To be honest, I'm not sure if the subject is 100% accurate; it's as close as I could get) Perl 5.28 Moo 2.003004 MooX::TypeTiny 0.001003 Role::Tiny 2.000006 The following code, package Common { use Moo::Role; has _meta => ( is => 'ro' ); } package Cmd { use Moo; use Moo::Role (); BEGIN { Moo::Role->apply_roles_to_package( __PACKAGE__, 'MyRole' ); } use MooX::TypeTiny; with 'Common'; } where MyRole.pm is package MyRole; use Moo::Role; has _meta => ( is => 'ro', ); 1; results in You cannot overwrite a locally defined method (_meta) with a reader at /[...]/lib/site_perl/5.28.0/Role/Tiny.pm line 113. Without the use of MooX::TypeTiny it compiles with no error. If I load MooX::TypeTiny via: with 'Common'; require MooX::TypeTiny; MooX::TypeTiny->import(); It also compiles with no error. The (mis-)behavior is triggered by the following lines in MooX::TypeTiny: 16 Moo::Role->apply_roles_to_object( 17 Moo->_accessor_maker_for($target), 18 'Method::Generate::Accessor::Role::TypeTiny', 19 ); even when Method::Generate::Accessor::Role::TypeTiny is simplified to this package Method::Generate::Accessor::Role::TypeTiny; use Moo::Role; 1; I'm afraid I've run out of tuits to persue this any deeper... Thanks, Diab
On Wed Jan 16 18:02:43 2019, DJERIUS wrote: Show quoted text
> >
> Perl 5.28 > Moo 2.003004 > MooX::TypeTiny 0.001003 > Role::Tiny 2.000006 > > The following code, > >
I've reduced the code to something much more direct, and realized that it's not about when the role is composed, but rather there are two roles which add the same attribute. Here's the new code: package Role1 { use Moo::Role; has _meta => ( is => 'ro', default => 2 ); } package Role2 { use Moo::Role; has _meta => ( is => 'ro', default => 1); } package Cmd { use Moo; use Moo::Role (); use MooX::TypeTiny; with 'Role1'; with 'Role2'; } Show quoted text
> > results in > > You cannot overwrite a locally defined method (_meta) with a > reader at /[...]/lib/site_perl/5.28.0/Role/Tiny.pm line 113. > > Without the use of MooX::TypeTiny it compiles with no error. If > I load MooX::TypeTiny via: > > with 'Common'; > require MooX::TypeTiny; > MooX::TypeTiny->import(); > > It also compiles with no error. The (mis-)behavior is triggered > by the following lines in MooX::TypeTiny: > > 16 Moo::Role->apply_roles_to_object( > 17 Moo->_accessor_maker_for($target), > 18 'Method::Generate::Accessor::Role::TypeTiny', > 19 ); > > even when Method::Generate::Accessor::Role::TypeTiny is simplified to > this > > package Method::Generate::Accessor::Role::TypeTiny; > use Moo::Role; > 1; > > I'm afraid I've run out of tuits to persue this any deeper... > > Thanks, > > Diab
On Thu Jan 17 10:18:21 2019, DJERIUS wrote: Show quoted text
> On Wed Jan 16 18:02:43 2019, DJERIUS wrote:
> > >
> > Perl 5.28 > > Moo 2.003004 > > MooX::TypeTiny 0.001003 > > Role::Tiny 2.000006 > > > > The following code, > > > >
> > I've reduced the code to something much more direct, and realized that > it's not about when the role is composed, but rather there are two roles > which add the same attribute. Here's the new code: >
And even further, but after this it's really going down the rabbit hole: package Role1 { use Moo::Role; has _meta => ( is => 'ro', default => 1 ); } package Role2 { use Moo::Role; has _meta => ( is => 'ro', default => 2 ); } package Role0 { use Moo::Role; } package Cmd { use Moo; use Moo::Role (); Moo::Role->apply_roles_to_object( Moo->_accessor_maker_for( __PACKAGE__ ), 'Role0' ); with 'Role1'; with 'Role2'; } Show quoted text
>
> > > > results in > > > > You cannot overwrite a locally defined method (_meta) with a > > reader at /[...]/lib/site_perl/5.28.0/Role/Tiny.pm line 113. > > > > Without the use of MooX::TypeTiny it compiles with no error. If > > I load MooX::TypeTiny via: > > > > with 'Common'; > > require MooX::TypeTiny; > > MooX::TypeTiny->import(); > > > > It also compiles with no error. The (mis-)behavior is triggered > > by the following lines in MooX::TypeTiny: > > > > 16 Moo::Role->apply_roles_to_object( > > 17 Moo->_accessor_maker_for($target), > > 18 'Method::Generate::Accessor::Role::TypeTiny', > > 19 ); > > > > even when Method::Generate::Accessor::Role::TypeTiny is simplified to > > this > > > > package Method::Generate::Accessor::Role::TypeTiny; > > use Moo::Role; > > 1; > > > > I'm afraid I've run out of tuits to persue this any deeper... > > > > Thanks, > > > > Diab
> >
On Thu Jan 17 10:52:55 2019, DJERIUS wrote: Show quoted text
> On Thu Jan 17 10:18:21 2019, DJERIUS wrote:
> > On Wed Jan 16 18:02:43 2019, DJERIUS wrote:
> > > >
> > > Perl 5.28 > > > Moo 2.003004 > > > MooX::TypeTiny 0.001003 > > > Role::Tiny 2.000006 > > > > > > The following code, > > > > > >
> > > > I've reduced the code to something much more direct, and realized > > that > > it's not about when the role is composed, but rather there are two > > roles > > which add the same attribute. Here's the new code: > >
>
I've found the direct cause of this behavior. It comes down to accessors being created prematurely, before all of the roles have been specified. 001 package Cmd { 002 003 use Moo; 004 use Moo::Role (); 005 006 Moo::Role->apply_roles_to_object( Moo->_accessor_maker_for( __PACKAGE__ ), 'Role0' ); 007 008 with 'Role1'; 009 with 'Role2'; 010 } Prior to line 06, the class' accessor maker class name is 'Method::Generate::Accessor'. Afterwards, it's 'Method::Generate::Accessor__WITH__Role0'. This matters, because in Moo::Role, line 232, there's an explicit check against the class name: 226 sub _maybe_make_accessors { 227 my ($self, $target, $role) = @_; 228 my $m; 229 if ($INFO{$role} && $INFO{$role}{inhaled_from_moose} 230 or $INC{"Moo.pm"} 231 and $m = Moo->_accessor_maker_for($target) 232 and ref($m) ne 'Method::Generate::Accessor') { 233 $self->_make_accessors($target, $role); 234 } 235 } and accessors are generated if the class isn't exactly 'Method::Generate::Accessor'. Changing line 232 to 232 and ! $m->isa( 'Method::Generate::Accessor') ) { restores the expected behavior. What it breaks, I don't know. The commit comment for this bit of code doesn't explain the reasoning behind it: commit a41e15c32a4553d493d9a44b3865280212c2f553 Author: Matt S Trout <mst@shadowcat.co.uk> Date: Thu May 3 18:40:58 2012 +0000 regenerate accessors during role application if the accessor generator is non-standard
On Thu Mar 14 23:16:08 2019, DJERIUS wrote: Show quoted text
> On Thu Jan 17 10:52:55 2019, DJERIUS wrote:
> > On Thu Jan 17 10:18:21 2019, DJERIUS wrote:
> > > On Wed Jan 16 18:02:43 2019, DJERIUS wrote:
> > > > >
> > > > Perl 5.28 > > > > Moo 2.003004 > > > > MooX::TypeTiny 0.001003 > > > > Role::Tiny 2.000006 > > > > > > > > The following code, > > > > > > > >
> > > > > > I've reduced the code to something much more direct, and realized > > > that > > > it's not about when the role is composed, but rather there are two > > > roles > > > which add the same attribute. Here's the new code: > > >
> >
Show quoted text
> What it breaks, I don't know.
Well, at the least it breaks t/compose-roles.t: $ prove -l t/compose-roles.t t/compose-roles.t .. 1/? # Failed test 'apply_roles_to_object correctly calls accessor generator' # at t/compose-roles.t line 93. # got: 'Can't apply RoleWithReq to ClassWithExtension - missing _attr1_marker at /home/dj/Work/Moo/lib/Moo/Role.pm line 280. # ' # expected: undef # Looks like you failed 1 test of 25.
Moving this to the Moo queue, since it's not really a MooX::TypeTiny issue.