Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

- runtime looping. #6407

Open
p6rt opened this issue Jul 23, 2017 · 4 comments
Open

- runtime looping. #6407

p6rt opened this issue Jul 23, 2017 · 4 comments
Labels

Comments

@p6rt
Copy link

p6rt commented Jul 23, 2017

Migrated from rt.perl.org#131785 (status was 'open')

Searchable as RT131785$

@p6rt
Copy link
Author

p6rt commented Jul 23, 2017

From andynparker@googlemail.com

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

@p6rt
Copy link
Author

p6rt commented Aug 11, 2017

From @skids

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 &amp;infix​:&lt;+&gt;.candidates&gt;&gt;.signature; class foobar { }; multi sub infix​:&lt;+&gt; (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.

@p6rt
Copy link
Author

p6rt commented Aug 11, 2017

The RT System itself - Status changed from 'new' to 'open'

@p6rt
Copy link
Author

p6rt commented Aug 11, 2017

From @AlexDaniel

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​:

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.

@p6rt p6rt added the Bug label Jan 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant