“P6opaque, Str” vs simple “Str” types in Perl 6
Asked Answered
F

1

7

This is a follow-up to my previous question.

I am finally able to reproduce the error here:

my @recentList = prompt("Get recentList: e.g. 1 2 3: ").words || (2,4,6);    
say "the list is: ", @recentList;    
for @recentList -> $x {
    say "one element is:  ", $x;
    say "element type is: ", $x.WHAT;
    say "test (1,2,3).tail(\"2\") : ", (1,2,3).tail("2");
    say ( (10.rand.Int xx 10) xx 15 ).map: { @($_.tail($x)); };
}

And the results are ok as long as I use the default list by just hitting return at the prompt and not entering anything. But if I enter a number, it gives this error:

Get recentList: e.g. 1 2 3: 2
the list is: [2]
one element is:  2
element type is: (Str)
test (1,2,3).tail("2") : (2 3)
This type cannot unbox to a native integer: P6opaque, Str
  in block  at intType.p6 line 9
  in block <unit> at intType.p6 line 5

If tail("2") works, why does tail($x) fail? Also, in my original code, tail($x.Int) wouldn't correct the problem, but it did here.

Ferroconcrete answered 21/7, 2019 at 18:50 Comment(2)
Cool. :) Step #1 was to have code that exhibited the problem. Step #2 is to golf it. So far I have (1 xx 1).tail('1'). :)Sessile
OK, back to this question. say Any.tail('0') displays () but say Any.tail('1') yields This type cannot unbox to a native integer: P6opaque, Str. That's enough golf methinks. Next, some code spelunking...Sessile
S
6

This is at best a nanswer. It is a thus-far failed attempt to figure out this problem. I may have just wandered off into the weeds. But I'll publish what I have. If nothing else, maybe it can serve as a reminder that the first three steps below are sensible ones; thereafter I'm gambling on my ability to work my way forward by spelunking source code when I would probably make much faster and more reliable progress by directly debugging the compiler as discussed in the third step.


OK, the first step was an MRE. What you've provided was an E that was fully R and sufficiently M. :)


Step #2 was increasing the M (golfing). I got it down to:

Any.tail('0');    # OK
Any.tail('1');    # BOOM

Note that it can be actual values:

1.tail('1');      # BOOM
(1..2).tail('1'); # BOOM

But some values work:

(1,2).tail('1');  # OK

Step #3 probably should be to follow the instructions in Playing with the code of Rakudo Perl 6 to track the compiler's execution, eg by sticking says in its source code and recompiling it.

You may also want to try out App::MoarVM::Debug. (I haven't.)

Using these approaches you'll have the power to track with absolute precision what the compiler does for any code you throw at it. I recommend you do this even though I didn't. Maybe you can figure out where I've gone wrong.


In the following I trace this problem by just directly spelunking the Rakudo compiler's source code.

A search for "method tail" in the Rakudo sources yielded 4 matches. For my golf the matching method is a match in core/AnyIterableMethods.pm6.

The tail parameter $n clearly isn't a Callable so the pertinent line that continues our spelunking is Rakudo::Iterator.LastNValues(self.iterator,$n,'tail').

A search for this leads to this method in core/Iterator.pm6.

This in turn calls this .new routine.

These three lines:

nqp::if(
  n <= 0,                 # must be HLL comparison
  Rakudo::Iterator.Empty, # negative is just nothing

explain why '0' works. The <= operator coerces its operands to numeric before doing the numeric comparison. So '0' coerces to 0, the condition is True, the result is Rakudo::Iterator.Empty, and the Any.tail('0') yields () and doesn't complain.

The code that immediately follows the above three lines is the else branch of the nqp::if. It closes with nqp::create(self)!SET-SELF(iterator,n,f).

That in turn calls the !SET-SELF routine, which has the line:

($!lastn := nqp::setelems(nqp::list, $!size = size)),

Which attempts to assign size, which in our BOOM case is '1', to $!size. But $!size is declared as:

has int $!size;

Bingo.


Or is it? I don't know if I really have correctly tracked the problem down. I'm only spelunking the code in the github repo, not actually running an instrumented version of the compiler and tracing its execution, as discussed as the sensible step #3 for trying to figure out the problem you've encountered.

Worse, when I'm running a compiler it's an old one whereas the code I'm spelunking is the master...


Why does this work?

(*,*).tail('1') # OK

The code path for this will presumably be this method. The parameter $n isn't a Callable so the code path will run thru the path that uses the $n in the lines:

              nqp::unless(
                nqp::istype($n,Whatever) || $n == Inf,
                $iterator.skip-at-least(nqp::elems($!reified) - $n.Int)

The $n == Inf shouldn't be a problem. The == will coerce its operands to numerics and that should take care of $n being '1'.

The nqp::elems($!reified) - $n.Int shouldn't be a problem either.

The nqp ops doc shows that nqp::elems always returns an int. So this boils down to an int - Int which should work.

Hmm.

A blame of these lines shows that the .Int in the last line was only added 3 months ago.

So, clutching at straws, what happens if one tries:

(my int $foo = 1) - '1' # OK

Nope, that's not the problem.

It seems the trail has grown cold or rather I've wandered off the actual execution path.

I'll publish what I've got. Maybe someone else can pick it up from here or I'll have another go in a day or three...

Sessile answered 22/7, 2019 at 1:57 Comment(1)
Thank you very much raiph !!! You have always been so detailed in your explanations. I see that the answer lies deeper than I thought, and I will try to dig into App::MoarVM::Debug and the Rakudo source. It will be a hefty adventure in itself. Thanks again raiph !!!Ferroconcrete

© 2022 - 2024 — McMap. All rights reserved.