throw

Definition: throw(object e, object user_data={})
Description: Transfer control immediately to some containing try/catch statement, even if it is several levels lower down the call stack, or terminate the program if no such handler exists.

e: typically an integer or string, but it can also be a complete exception in the re-throw case.
user_data: if not {} (or 0), then e must be an atom. It can contain anything that you might find helpful in the handler(/logfile, etc).
Comments: The throw routine builds an exception and passes it to an appropriate handler.

The short version is:
An exception is a sequence of run-time diagnostic information, with the offending instruction address converted to a human-readable source file/line number.
The long version is:
The following constants (automatically defined in psym.e/syminit) can be used to examine the contents of an exception:
1 E_CODE (atom) hardware and operating system exceptions usually have bit #800000000 set, user exceptions can be any atom value, even 0.
2 E_ADDR (atom) a machine address indicating where the exception ocurred.
3 E_RTN (integer, optional) equivalent to routine_id(), an index to the symbol table identifying the routine.
4 E_LINE (integer, optional) the source code line matching E_ADDR in E_RTN.
5 E_NAME (string|integer, optional) the human-readable name of E_RTN.
6 E_FILE (string|integer, optional) the source filename containing E_NAME.
7 E_PATH (string|integer, optional) the directory containing E_FILE.
8 E_USER (object, optional) user defined/application specific content in e[8..$].

if user_data!={} then e must be atom and throw(e,user_data) is equivalent to throw({e,-1,-1,-1,-1,-1,-1,user_data}), modified as below.
else if e is an atom, throw(e) is equivalent to throw({e,-1,-1,-1,-1,-1,-1}), modifed as below.
else if e is a string, throw(e) is equivalent to throw({0,-1,-1,-1,-1,-1,-1,e}), modified as below.
else e must be a sequence containing at least the first two elements, E_CODE and E_ADDR, type safe as above and any user-defined data in e[8..$],
       which is padded with -1 to be at least length 7 and then modified as below.
NB Failure to meet the type-safety requirements outlined above, eg throw({"junk"}), results in a non-catchable fatal error.

if e[E_ADDR] is -1 it is replaced with the return address from the throw() call.
if e[E_RTN] is -1 it is replaced with a routine number from the current call stack.
if e[E_LINE] is -1 and e[E_ADDR] was -1, then the (new) value of e[ADDR]-1 is mapped to a line number in e[E_RTN] and stored in e[E_LINE].
if e[E_NAME] is -1 the name is retrieved from the symbol table (the string "-1" for top-level subroutines).
Likewise e[E_FILE] and e[E_PATH] of -1 are replaced with the expected strings.
Traditional fatal runtime errors are mapped as follows (with E_ADDR..E_LINE also pre-populated, and E_NAME..E_PATH -1’d as above):
    throw(1,"type check error, %s is %s")
    throw(2,"attempt to divide by 0")
    ...
    throw(122,"invalid poke size")

See builtins\VM\pDiagN.e for the full/up-to-date list. In the catch clause, e[E_CODE] will be 1..122 and e[E_USER] will be a (post-sprintf) string.
Obviously 55/"unhandled exception" is not and should not be mapped, and if you get one and then try to write a handler to catch it, you might be slightly disappointed, though you should be able to catch the error actually thrown (which could technically be a fake 55/"unhandled exception") rather than get the (real/from pDiagN.e) error 55 being reported on the throw statement (and your application terminated). Likewise 12/"program aborted" (ie '!' keyed in the trace window) is not automatically re-thrown, and hence cannot be caught.

Sometimes you may need to re-throw an exception, and let some containing catch process it - which is as simple and straightforward as it could possibly ever be:
    try
        ...
    catch e
        if <recognised> then
            <handle it here>
        else
            throw(e)
        end if
    end try

All details are preserved, which also means that you can make up a completely fake exception, with made-up a routine name, line, source file, etc. I don’t particularly recommend that; while I can imagine cases where you specifically want to catch "that exception from line 851", it would almost certainly be better to use a unique error code when possible.

Compilation may issue "Error: without debug in force" messages, since that option suppresses the tables needed for machine address->source code line number mapping.

There is no absolute guarantee that the contents of an exception will be meaningful: any of e[E_RTN..E_PATH] may be left as -1, e[E_USER] may or may not exist, and length(e) may be greater than 8. The catch clause should be coded accordingly/defensively.

Obviously the application will terminate immediately, just like /0, should throw() be invoked when there is no active handler (try/catch construct) in the current call stack, and likewise in that case the above mapping to throw(1..122) does not occur.
Performance: Exception handling is designed to minimise the impact on code which does /not/ trigger it, and is intended for very infrequent events.
Should you compare the performance overhead of throw("failure") with return false then unfortunately you are in for a very big shock.
Building detailed run-time diagnostic information and converting a machine address into a human-readable line number, file, and routine name, is simply always going to take considerably longer than a single "mov eax,ebx" instruction!

Should you need to process 10,000 items, but 2 of them trigger an exception, the savings of simplified code (such as not checking for division by zero, whether a file of that name already exists, etc) on 9,998 of them may very well significantly outweigh the cost of the two exceptions that are triggered. It is in that sense, alone, that appropriate use of exceptions can make some programs noticeably faster.

However, the wilful and excessive use of throw() in quite unnecessary situtations will almost inevitably result in a slower program.
That said, any code not being iterated many thousands of times is better off being as clear and intuitive as you can make it.
Example:
try
    integer i = 1/0
--  throw(2,"attempt to divide by 0") -- equivalent/as triggered via pDiagN.e/diag().
catch e
    ?{"raw:",e}
    if length(e)>=E_USER
    and string(e[E_USER])
    and string(e[E_FILE])
    and e[E_LINE]!=-1 then
        printf(1,"%s (%s line %d)\n",{e[E_USER],e[E_FILE],e[E_LINE]})
    else
        ?{"oops... (test.exw line 12)",e}
    end if
end try
Output:
{"raw:",{2,8249772,2,21,"-1","test.exw","C:\\Program Files (x86)\\Phix\\","attempt to divide by 0"}}
attempt to divide by 0 (test.exw line 2)

While the above conditions/printf should give you the basic idea of what may be required, be advised that that using cut-and-paste may cause unforseen problems, since the precise content of an exception can vary quite dramatically. Although those four tests on 'e' cover everything I can think of right now, creative use of throw() is quite likely to find a way to make that catch clause as it stands do the completely wrong thing.
See Also: try/catch, crash_file, crash_message, crash_routine, era