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

Inherited arrays of callables are buggy #79

Open
catb0t opened this issue Mar 1, 2019 · 4 comments
Open

Inherited arrays of callables are buggy #79

catb0t opened this issue Mar 1, 2019 · 4 comments

Comments

@catb0t
Copy link
Contributor

catb0t commented Mar 1, 2019

class Top {
  method init {
    say "Top init"
  }
}

class Next < Top {
  has Array array = [
    { say "asd" },
    { say "different" }
  ]
  method start  {
    say "Next: start"
    self.array.each{ .say }
    self.array.each{ .run }
  }
}

Next().start

If you run this code you get,

Top: init
Next: start
{|_| #`(__BLOCK__|94915947207360) ... }
{|_| #`(__BLOCK__|94915947207912) ... }
asd
different

This is correct; init is called on our parent Top, then our start method call, two blocks are printed and run.

If we added a class to the bottom of the inheritance tree, so the full code is:

class Top {
  method init {
    say "Top: #{__METHOD_NAME__}"
  }
}

class Next < Top {
  has Array array = [
    { say "asd" },
    { say "different" }
  ]
  method start  {
    say "Next: #{__METHOD_NAME__}"
    self.array.each{ .say }
    self.array.each{ .run }
  }
}

class Bottom < Next { }

Bottom().start

The output is

Top: init
Next: start
{|_| #`(__BLOCK__|94583961119968) ... }
{|_| #`(__BLOCK__|94583961119968) ... }
Top: init
Top: init

A couple of things are wrong. First of all, the identities of the blocks are somehow the same, but they weren't when we invoked Next() directly. (Also, they are not the same code). Worse, if you change the blocks to be func cN () { ... }, where N is different for each, self.array is [nil, nil] only in Bottom, but not if you constructed it inside Next.

The second thing which is wrong with this output is that Top: init is called three times. Once correctly at the start, and two more times, once for each block we try to .run. Also, the blocks themselves are not called; I guess their invocation is replaced with the construction of the class that originated them ??

There is one more bad thing that can happen with this code. If we rename method start to method init, the code recurses forever, for no reason that is obvious.

Next: init
{|_| #`(__BLOCK__|94434009372496) ... }
{|_| #`(__BLOCK__|94434009372496) ... }
Next: init
{|_| #`(__BLOCK__|94434009372496) ... }
{|_| #`(__BLOCK__|94434009372496) ... }
Next: init
{|_| #`(__BLOCK__|94434009372496) ... }
{|_| #`(__BLOCK__|94434009372496) ... }
Next: init
{|_| #`(__BLOCK__|94434009372496) ... }
{|_| #`(__BLOCK__|94434009372496) ... }
Next: init
{|_| #`(__BLOCK__|94434009372496) ... }
{|_| #`(__BLOCK__|94434009372496) ... }
Next: init
{|_| #`(__BLOCK__|94434009372496) ... }
{|_| #`(__BLOCK__|94434009372496) ... }
Next: init
{|_| #`(__BLOCK__|94434009372496) ... }
{|_| #`(__BLOCK__|94434009372496) ... }

And on and on forever. Notice that Top.init is correctly overridden by Next.init and is not being called, but it recurses anyway.

@catb0t catb0t changed the title Inherited arrays of blocks are buggy Inherited arrays of callables are buggy Mar 1, 2019
@catb0t
Copy link
Contributor Author

catb0t commented Mar 1, 2019

The workaround, which might help debugging, is to simply not declare the array of callables in any of the parent classes, but to declare it in the bottom class, and don't inherit from it.

class Top {
  method init {
    say "Top: #{__METHOD_NAME__}"
  }
}

class Next < Top {
  method init {
    say "Next: #{__METHOD_NAME__}"
    self.array.each{ .say }
    self.array.each{ .run }
  }
}

class Bottom < Next {
  has Array array = [
    func c1 () { say "asd" },
    func c2 () { say "different" }
  ]

}

Bottom()
Next: init
func () { #`(x1|94865196094224) ... }
func () { #`(x2|94865196094632) ... }
asd
different

@catb0t
Copy link
Contributor Author

catb0t commented Mar 4, 2019

Hmm, in my opinion that workaround (using init, but not inheriting the array) can still cause inexplicable infinite recursion. I haven't gotten a small piece of code that can reproduce it, but basically every 100 times the full version of that code is run, 4 times it will infinitely recurse, and 10 times it will give Not a HASH reference for no reason.

I think I'll rewrite my code and try again, because 4% and 10% are not really ideal failure rates for deterministic, relatively straightforward code that doesn't actually screw with Sidef/Perl internals.

@catb0t
Copy link
Contributor Author

catb0t commented Mar 8, 2019

class Top {
  method init {
    say "Top: #{__METHOD_NAME__}"
  }
}

const Array global_array = [
  { say 'asd' },
  { say 'different' }
]
class Next < Top {
  has Array bx = global_array
  method init {
    say "Next: #{__METHOD_NAME__}"
    self.bx.each{ .say }
    self.bx.each{ .run }
  }
}

class Bottom < Next { }

Bottom() 

Success

Next: init
{|_| #`(__BLOCK__|93950116575624) ... }
{|_| #`(__BLOCK__|93950118451640) ... }
asd
different

@trizen
Copy link
Owner

trizen commented Apr 22, 2019

Partially fixed in 5911c5c.

class Top {
  method init {
    say "Top: #{__METHOD_NAME__}"
  }
}

class Next < Top {
  has Array array = [
    { say "asd" },
    { say "different" }
  ]
  method start  {
    say "Next: #{__METHOD_NAME__}"
    self.array.each{ .say }
    self.array.each{ .run }
  }
}

class Bottom < Next { }

Next()              # needs to be initialized first
Bottom().start

Output:

Top: init
Top: init
Next: start
{|_| #`(__BLOCK__|94522831472928) ... }
{|_| #`(__BLOCK__|94522831473360) ... }
asd
different

A better fix is still needed that automatically initializes the values of the parent classes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants