Inherited arrays of callables are buggy
Nobody has claimed this yet.
- Dominant language
- Perl
- Stars
- 126
- Forks
- 2
- Avg merge
- 15m
- Merged PRs (30d)
- 3
Description
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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the inheritance and callable-array examples from the issue, then trace the compiler or runtime paths for inherited fields, block invocation, and initializer dispatch. Done means callable identities and values remain distinct in Bottom, each block runs once, and overriding init does not recurse indefinitely.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- perl
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100