I found a bug in Perl 6 recently. Really I independently discovered one that was already reported.
Here’s how to trigger it:
|
|
Any numerator of 231 or greater causes that error. Note that Perl 6 is perfectly happy to represent rationals of that size or larger:
|
|
So the problem was clearly somewhere in the compiler.
Here’s a quick guide to how I fixed this.
First, I added a test to the Perl 6 test suite. Unlike many programming languages, the primary test suite for Perl 6 is not in the same repo as the compiler. Instead, it has its own repo, roast. Roast is The Official Perl 6 Test Suite, and any toolchain which can pass the test suite is a valid Perl 6.
In this particular case, I added a test to S32-num/rat.t
. The test makes sure that a Rat value
with a large numerator can round trip via the .perl
method and EVAL
:
|
|
Running this with the latest Rakudo caused the test to blow up with the same error as my first example. Success! Well, failure, but success at failing the way I wanted it to.
Next I had to figure out where this error was happening. This can be a bit tricky with compiler
errors like this. The best way to get a clue to the problem’s location is to pass --ll-exception
as a CLI flag:
|
|
If we look at the top of the trace we see references to Perl6/Actions.moarvm:bare_rat_number
.
Looking in the rakudo repo, we can find a src/Perl6/Actions.nqp
file that contains
method bare_rat_number($/) {...}
. This seemed like a pretty good guess at where the error was
coming from.
Here’s the method in full before my patch:
|
|
After doing some dumping of values in the AST with code like note($<nu>.dump)
, I realized that the
numerator could end up being passed in as either a QAST::Want
or QAST::WVal
object. What are
these and how do they differ? Why is there a break at 231? I have no clue!
However, I could see that while a QAST::Want
object could be treated as an array, a QAST::WVal
could not. Fortunately, both objects support a compile_time_value
method. Looking at this method’s
implementation in QAST::Want
I could see that it was getting the first array element from the
object’s internals, while QAST::WVal
implemented this differently. But since they both implement
the same method why not just call it and be done with it?
Here’s the patched method:
|
|
All the tests passed, so I think I fixed it.
Overall, this wasn’t too hard. Because much of Perl 6 is either written in Perl 6 or in NQP (a subset of Perl 6), fixing the core can be much easier than with many other languages, especially most dynamic languages which are implemented in C.