Skip Menu |
Report information
Id: 131785
Status: open
Priority: 0/
Queue: perl6

Owner: Nobody
Requestors: andynparker [at] googlemail.com
Cc:
AdminCc:

Severity: (no value)
Tag: Bug
Platform: (no value)
Patch Status: (no value)
VM: (no value)



To: "rakudobug [...] perl.org" <rakudobug [...] perl.org>
Subject: [BUG] - runtime looping.
Date: Sun, 23 Jul 2017 16:18:56 +0100
From: Andrew Parker <andynparker [...] googlemail.com>
Download (untitled) / with headers
text/plain 2.2k
Greetings! I have written a module to process some numeric data types and found that the run time will loop.  Here is minimal code for the module: use v6.c; unit module test1; our package EXPORT::DEFAULT { # export classes, sub etc.     class Fpa does Numeric {         has UInt $.var is rw = 0;     }     multi infix:<+> ( Fpa:D $left, Fpa:D $right --> Fpa ) is equiv(              &infix:«+» ) {         say "here 1!";         return my $newFpa = fpaAdd( $left, $right );     }     multi infix:<+> ( Any:D $left, Fpa:D $right --> Fpa ) is equiv(              &infix:«+» ) {         say "here 2!";         return my $newFpa = fpaAdd( $left, $right );     }     multi infix:<+> ( Fpa:D $left, Any:D $right --> Fpa ) is equiv(             &infix:«+» ) {         say "here 3!";         return my $newFpa = fpaAdd( $left, $right );     }     our sub fpaAdd ( Fpa:D $left, Fpa:D $right --> Fpa ) {         say "In fpaAdd";         my Int $l = $left.var;         my Int $r = $right.var;         my $z = Fpa.new();         $z.var = $l + $r;         return $z;     } } # end of package. Here is some test driver code to call the module (test2.pm6): use v6c; use test1; my Fpa $x = Fpa.new(); say $x.WHAT; my Fpa $y = Fpa.new(); say $y.WHAT; $y.var = 5; $x.var = 1; say "Object x set to " ~ $x.var ~ '.'; say "Object y set to " ~ $y.var ~ '.'; prompt "Press return and system will go into a loop."; $x = $x + $y; say "answer= " ~ $x.var ~ '.'; # This never happens. exit; Here is a run of the test2.pm6 code that calls the module: perl6-m test2.pm6 (Fpa) (Fpa) Object x set to 1. Object y set to 5. Press return and system will go into a loop. ^C $  Strangely, if the multi are taken out of the module and put in the test driver, it works as expected. If the "does Numeric" is removed from the class definition, it does not resolve a call to a multi infix:<+> despite correctly matching object types. The error message is: Cannot resolve caller Numeric(test1::EXPORT::DEFAULT::Fpa: ); none of these signatures match:     (Mu:U \v: *%_)   in block <unit> at test2.pm6 line 13 Is this expected behaviour? Regards, Andrew N Parker
Download (untitled) / with headers
text/plain 4.5k
Analysis... you've run into a nest of LTA/bugs. 1) First off, this will loop: perl6 -e 'class A does Numeric { }; say A.new + A.new' The reason it loops is because the default infix:<+> (provided by Numeric) does this: multi sub infix:<+>(\a, \b) { a.Numeric + b.Numeric } ...and the default .Numeric, also provided by Numeric, is just { self } This is where the loop comes from. The problem would be avoided if the proper infix:<+> candidate were being called, but the sanity of Numeric might be improved with something which breaks that loop and throws some reasonable exception telling you that you really need to define a specific candidate... how to implement that efficiently might be a challenge. 2) A simple explanation would have been that the EXPORT::DEFAULT needs to contain an &infix:<+> symbol, and the multis in this package do not provide that symbol. If you add this to the EXPORT::DEFAULT package: my constant &infix:<+> = &infix:<+>; ...then the multis will show up in &infix:<+>.candidates>>.signature.say Whether the presence of those multis should add the Stash key automatically when in an EXPORT package is arguably an LTA... probably they see the Setting's prototype and do not because a generic package shouldn't. 3) But it was not that simple... I know the docs say to, but I'm not sure what us gong on with using EXPORT::DEFAULT and "unit module" together. I know the intention was to make it work but maybe NYI? The first questionable thing to notice is that after a "use test1" an EXPORT::DEFAULT Stash is in your namespace: $ PERL6LIB="/tmp/" perl6 -e 'use v6.c; use test1; EXPORT::DEFAULT::.keys.say;' (Fpa &fpaAdd) ... I don't think this is supposed to happen; you should get test1::EXPORT::DEFAULT instead. That Stash is empty: $ PERL6LIB="/tmp/" perl6 -e 'use v6.c; use test1; test1::EXPORT::DEFAULT::.keys.say;' Could not find symbol '&DEFAULT' in block <unit> at -e line 1 But, despite this, the Fpa class does seem to be available and the multis seem to be added with this formulation, albeit with ugly test1::EXPORT::DEFAULT prefixes due to the location of the class definition. Now I'll assume you are using EXPORT::DEFAULT rather than just marking things as "is export" for a good reason, so we'll humor that and see whether we can find a formulation that works... because it should be possible to do things with this form if you really want to. 4) I found that even with the candidates in your module available, Numeric's infix:<+> was still being called. However, if you jam any additional random multi candidate into infix:<+> in your mainline, this seems to jog things into finding the candidates: ...but without that extra candidate it still ends up in Numeric::infix<+>(\a, \b). $ PERL6LIB="/tmp/" perl6 --ll-exception -e 'use v6.c; use test1; EXPORT::DEFAULT::.keys.say; my Fpa $x = Fpa.new(); my Fpa $y = Fpa.new(); $y.var = 5; $x.var = 1; say $x.perl, $y.perl; say &infix:<+>.candidates>>.signature; class foobar { }; multi sub infix:<+> (foobar $, foobar $) { }; say ($x + $y).perl' (Fpa &fpaAdd) test1::EXPORT::DEFAULT::Fpa.new(var => 1)test1::EXPORT::DEFAULT::Fpa.new(var => 5) (($x = 0) (\a, \b) (Real \a, Real \b) (Int:D \a, Int:D \b --> Int:D) (int $a, int $b --> int) (Num:D \a, Num:D \b) (num $a, num $b --> num) (Range:D \a, Real:D \b) (Real:D \a, Range:D \b) (Rational \a, Rational \b) (Rational \a, Int \b) (Int \a, Rational \b) (Complex:D \a, Complex:D \b --> Complex:D) (Complex:D \a, Real \b --> Complex:D) (Real \a, Complex:D \b --> Complex:D) (Instant:D $a, Real:D $b) (Real:D $a, Instant:D $b) (Instant:D $a, Duration:D $b) (Duration:D $a, Instant:D $b) (Duration:D $a, Real $b) (Real $a, Duration:D $b) (Duration:D $a, Duration:D $b) (DateTime:D \a, Duration:D \b) (Duration:D \a, DateTime:D \b) (Date:D $d, Int:D $x) (Int:D $x, Date:D $d) (test1::EXPORT::DEFAULT::Fpa:D $left, test1::EXPORT::DEFAULT::Fpa:D $right --> test1::EXPORT::DEFAULT::Fpa) (Any:D $left, test1::EXPORT::DEFAULT::Fpa:D $right --> test1::EXPORT::DEFAULT::Fpa) (test1::EXPORT::DEFAULT::Fpa:D $left, Any:D $right --> test1::EXPORT::DEFAULT::Fpa) (foobar, foobar)) here 1! In fpaAdd test1::EXPORT::DEFAULT::Fpa.new(var => 6) ...this, however, might be a consequence of the value of the 'infix:<+>' constant I installed earlier to fix #2 above... may somehow have gotten glued to a too-specific compiler construct (fold/spesh/opt) Do note that the (Any:D $, Fpa:D $) and (Fpa:D $, Any:D $) will fail as written since they don't convert the non-Fpa parameter or do anything special, but probably this was expected and you just wanted to see it reaching the "say." I'd dig deeper but I must rest now.
Great analysis!

The first issue seems to be already mentioned in RT #126112 (and I vaguely recall one more ticket, but I'm failing to find it now).

This ticket seems to be getting a bit unmanageable, please file smaller tickets for every issue you discover.


On 2017-08-10 21:36:34, bri@abrij.org wrote:
Show quoted text
>
> Analysis... you've run into a nest of LTA/bugs.
>
> 1) First off, this will loop:
>
> perl6 -e 'class A does Numeric { }; say A.new + A.new'
>
> The reason it loops is because the default infix:<+> (provided by
> Numeric) does this:
>
> multi sub infix:<+>(\a, \b) { a.Numeric + b.Numeric }
>
> ...and the default .Numeric, also provided by Numeric, is just { self
> }
>
> This is where the loop comes from.
>
> The problem would be avoided if the proper infix:<+> candidate were
> being
> called, but the sanity of Numeric might be improved with something
> which
> breaks that loop and throws some reasonable exception telling you that
> you really need to define a specific candidate... how to implement
> that
> efficiently might be a challenge.
>
> 2) A simple explanation would have been that the EXPORT::DEFAULT needs
> to contain an &infix:<+>
> symbol, and the multis in this package do not provide that symbol. If
> you add this
> to the EXPORT::DEFAULT package:
>
> my constant &infix:<+> = &infix:<+>;
>
> ...then the multis will show up in
> &infix:<+>.candidates>>.signature.say
>
> Whether the presence of those multis should add the Stash key
> automatically
> when in an EXPORT package is arguably an LTA... probably they see the
> Setting's
> prototype and do not because a generic package shouldn't.
>
> 3) But it was not that simple... I know the docs say to, but I'm not
> sure
> what us gong on with using EXPORT::DEFAULT and "unit module" together.
> I know
> the intention was to make it work but maybe NYI? The first
> questionable thing
> to notice is that after a "use test1" an EXPORT::DEFAULT Stash is in
> your namespace:
>
> $ PERL6LIB="/tmp/" perl6 -e 'use v6.c; use test1;
> EXPORT::DEFAULT::.keys.say;'
> (Fpa &fpaAdd)
>
> ... I don't think this is supposed to happen; you should get
> test1::EXPORT::DEFAULT
> instead. That Stash is empty:
>
> $ PERL6LIB="/tmp/" perl6 -e 'use v6.c; use test1;
> test1::EXPORT::DEFAULT::.keys.say;'
>
> Could not find symbol '&DEFAULT'
> in block <unit> at -e line 1
>
> But, despite this, the Fpa class does seem to be available and the
> multis seem to be
> added with this formulation, albeit with ugly test1::EXPORT::DEFAULT
> prefixes due to
> the location of the class definition.
>
> Now I'll assume you are using EXPORT::DEFAULT rather than just marking
> things as
> "is export" for a good reason, so we'll humor that and see whether we
> can
> find a formulation that works... because it should be possible to do
> things
> with this form if you really want to.
>
> 4) I found that even with the candidates in your module available,
> Numeric's infix:<+>
> was still being called. However, if you jam any additional random
> multi candidate into
> infix:<+> in your mainline, this seems to jog things into finding the
> candidates:
>
> ...but without that extra candidate it still ends up in
> Numeric::infix<+>(\a, \b).
>
> $ PERL6LIB="/tmp/" perl6 --ll-exception -e 'use v6.c; use test1;
> EXPORT::DEFAULT::.keys.say; my Fpa $x = Fpa.new(); my Fpa $y =
> Fpa.new(); $y.var = 5; $x.var = 1; say $x.perl, $y.perl; say
> &infix:<+>.candidates>>.signature; class foobar { }; multi sub
> infix:<+> (foobar $, foobar $) { }; say ($x + $y).perl'
> (Fpa &fpaAdd)
> test1::EXPORT::DEFAULT::Fpa.new(var =>
> 1)test1::EXPORT::DEFAULT::Fpa.new(var => 5)
> (($x = 0) (\a, \b) (Real \a, Real \b) (Int:D \a, Int:D \b --> Int:D)
> (int $a, int $b --> int) (Num:D \a, Num:D \b) (num $a, num $b --> num)
> (Range:D \a, Real:D \b) (Real:D \a, Range:D \b) (Rational \a, Rational
> \b) (Rational \a, Int \b) (Int \a, Rational \b) (Complex:D \a,
> Complex:D \b --> Complex:D) (Complex:D \a, Real \b --> Complex:D)
> (Real \a, Complex:D \b --> Complex:D) (Instant:D $a, Real:D $b)
> (Real:D $a, Instant:D $b) (Instant:D $a, Duration:D $b) (Duration:D
> $a, Instant:D $b) (Duration:D $a, Real $b) (Real $a, Duration:D $b)
> (Duration:D $a, Duration:D $b) (DateTime:D \a, Duration:D \b)
> (Duration:D \a, DateTime:D \b) (Date:D $d, Int:D $x) (Int:D $x, Date:D
> $d) (test1::EXPORT::DEFAULT::Fpa:D $left,
> test1::EXPORT::DEFAULT::Fpa:D $right --> test1::EXPORT::DEFAULT::Fpa)
> (Any:D $left, test1::EXPORT::DEFAULT::Fpa:D $right -->
> test1::EXPORT::DEFAULT::Fpa) (test1::EXPORT::DEFAULT::Fpa:D $left,
> Any:D $right --> test1::EXPORT::DEFAULT::Fpa) (foobar, foobar))
> here 1!
> In fpaAdd
> test1::EXPORT::DEFAULT::Fpa.new(var => 6)
>
> ...this, however, might be a consequence of the value of the
> 'infix:<+>' constant
> I installed earlier to fix #2 above... may somehow have gotten glued
> to a too-specific
> compiler construct (fold/spesh/opt)
>
> Do note that the (Any:D $, Fpa:D $) and (Fpa:D $, Any:D $) will fail
> as written
> since they don't convert the non-Fpa parameter or do anything special,
> but probably
> this was expected and you just wanted to see it reaching the "say."
>
> I'd dig deeper but I must rest now.




This service is sponsored and maintained by Best Practical Solutions and runs on Perl.org infrastructure.

For issues related to this RT instance (aka "perlbug"), please contact perlbug-admin at perl.org