raku

CATCH and throw in custom exception


Should 'CATCH' be called strictly after 'throw'?

Example 1:

say 'Hello World!';

class E is Exception { method message() { "Just stop already!" } }

CATCH {
    when E {
            .resume;
    }
}

E.new.throw;

Error:

Cannot find method 'sink': no method cache and no .^find_method in block at /tmp/739536251/main.pl6 line 11

Example 2:

say 'Hello World!';

class E is Exception { method message() { "Just stop already!" } }

E.new.throw;

CATCH {
    when E {
            .resume;
    }
}

No error


Solution

  • It's an already filed .resume bug.


    The error message isn't the most awesome P6 has ever produced but it isn't technically LTA because it is descriptive and related to the error (caused by the bug).


    CATCH and throw in custom exception

    I think it's just a .resume bug rather than being about custom exceptions.

    Should 'CATCH' be called strictly after 'throw'?

    No, that's not the issue. (That said, putting it after the .throw just so happens to avoid this bug; I'll return to that later.)


    In the code that goes boom, you throw an exception, then .resume in response to it. Per the doc .resume:

    Resumes control flow where .throw left it

    Which in this case means where the arrow points:

    E.new.throw ;
               🡅
    

    Now, consider this program:

    42;
    

    If you run that program you'll see:

    Useless use of constant integer 42 in sink context (line 1)
    

    That's because Raku applies "sink context" rules when deciding what to do at the end of a statement. Applying sink context entails calling .sink on the value produced by the statement. And for 42 the .sink method generates the "useless" warning.

    But what's the value of a .resumed thrown exception?

    class E is Exception {}
    CATCH { when E { .resume } }
    say E.new.throw.^name; # BOOTException
    E.new.throw.sink;      # Cannot find method 'sink':
                           # no method cache and no .^find_method
    

    It turns out it's a BOOTException object which isn't a high level Raku object but instead a low level VM object, one that doesn't have a .sink method (and also stymies P6's fallback methods for finding a method, hence the "I tried everything" error message).


    So why does putting the CATCH block after the throw make a difference?

    It seems the bug only occurs if the throw statement is the last statement. This works fine, just displaying 42:

    class E is Exception {}
    CATCH { when E { .resume } }
    E.new.throw;
    say 42;
    

    As you presumably know, Raku treats the last statement of a block specially. Perhaps this bug is related to that.