diff options
-rw-r--r-- | txr.1 | 145 | ||||
-rw-r--r-- | unwind.c | 54 | ||||
-rw-r--r-- | unwind.h | 5 |
3 files changed, 161 insertions, 43 deletions
@@ -41031,14 +41031,20 @@ which must be an integer. An .I exception in \*(TX is a special event in the execution of the program which -results in transfer of control. An exception is identified by a symbol, -known as the +potentially results in a transfer of control. An exception is identified by a +symbol, known as the .IR "exception type" , and it carries zero or more arguments, called the .IR "exception arguments" . When an exception is initiated, it is said to be .IR thrown . +This action is initiated by the following functions: +.codn throw , +.code throwf +and +.codn error , +and possibly other functions which invoke these. When an exception is thrown, \*(TX enters into exception processing mode. Exception processing mode terminates in one of several ways: .IP - @@ -41054,22 +41060,48 @@ Handlers are defined by the .code handler-bind operator or .code handle -macro. -.IP - -If no catch or accepting handler is found, control is transferred -to the function stored in the -.code *unhandled-hook* -variable. If that function returns, then unwinding is performed -after which the process terminates (unless the unwinding actions -intercept the control to prevent that). +macro. If a handler returns, then by so doing it declines to +handle the exception. .IP - -If no catch or accepting handler is found and +If no catch or accepting handler is found for an exception derived from +.code error +exception and .code *unhandled-hook* is .codn nil , then a built-in strategy for handling the exception is invoked, consisting of unwinding, and then printing some informational messages and terminating. +If the +.code *unhandled-hook* +variable contains a value that isn't +.codn nil , +then control is transferred to the function stored in the +that variable first; only if that function returns is the above +built-in strategy invoked. +.IP - +If no catch or accepting handler is found for an exception derived from +.codn warning , +then a warning diagnostic is issued on the +.code *stderr* +stream and a +.code continue +exception is thrown with no arguments. If no catch or handler is found +for that exception, then control returns normally to the site which +threw the warning exception. +.IP - +If no catch or accepting handler is found for an exception that is +neither derived from +.code error +nor from +.codn warning , +then no control transfer takes place; control returns to the +.code throw +or +.code throwf +function which returns normally, with a return value of +.codn nil . + .PP .NP* Catches and Handlers @@ -41403,6 +41435,26 @@ using the .code format string and additional arguments. +Because +.code error +throws an error exception, it does not return. If an error exception +is not handled, \*(TX will issue diagnostic messages and terminate. +Likewise, +.code throw +or +.code throwf +are used to generate an error exception, they do not return. + +If the +.code throw +and +.code throwf +functions are used to generate an exception not derived from +.codn error , +and no handler is found which accepts the exception, they return normally, with +a value of +.codn nil . + .coNP Macros @, catch @ catch* and @ catch** .synb .mets (catch < try-expression @@ -42335,18 +42387,25 @@ more information which is deduced. .code warning to identify certain situations of interest. Ordinary non-deferrable warnings have a structure identical to errors, except for the exception -symbol. \*(TX's built-in handling of warnings expects these exceptions -to be continuable. What this means is that a -.code catch -for the +symbol. \*(TX's provides built-in "auto continue" handling for warnings. If a warning +exception is not intercepted by a catch or an accepting handler, then a +diagnostic is issued on the +.code *stderr* +stream, after which a .code continue -exception is expected to be visible. The handler for a warning exception -issues a diagnostic which incorporates the warning message. Then the -handler throws a +exception is thrown with no arguments. If that .code continue +exception is not handled, then control returns normally to the point that exception to resume the computation which generated the warning. -The generation of a warning thus conforms to the following pattern: +Callers which invoke code that may generate warning exceptions are therefore +not required to handle them. However, callers which do handle warning +exceptions expect to be able to throw a +.code continue +exception in order to resume the computation that triggered the warning, +without allowing other handlers to see the exception. + +The generation of a warning should thus conform to the following pattern: .verb (catch @@ -42424,11 +42483,26 @@ The .code compile-warning function throws an exception of type .code warning -and internally provides the expected +and internally provides a .code catch for the .code continue -exception needed to resume after the warning. +exception which allow a warning handler to resume execution +after the warning. If a handler throws a +.code continue +exception which is caught by +.codn compile-warning , +then +.code compile-warning +returns +.codn nil . + +Because +.code compile-warning +throws a non-error exception, it returns +.code nil +in the event that no catch is found for the exception, and no handler which +accepts it. The argument conventions are the same for both functions. The @@ -42457,7 +42531,7 @@ The .code compile-defr-warning function throws an exception of type .code defr-warning -and internally provides the expected +and internally provides a .code catch for the .code continue @@ -42478,6 +42552,15 @@ argument of the exception. The .meta tag argument is taken as the second argument. +If the exception isn't intercepted by a catch or by +an accepting handler, +.code compile-defr-warning +returns +.codn nil . +In also returns nil if it catches a +.code continue +exception. + .coNP Function @ purge-deferred-warning .synb .mets (purge-deferred-warning << tag ) @@ -72921,6 +73004,24 @@ of these version values, the described behaviors are provided if is given an argument which is equal or lower. For instance .code "-C 103" selects the behaviors described below for version 105, but not those for 102. +.IP 234 +In \*(TX 234 and older versions, the exception throwing functions +.code throw +and +.code throwf +did not return, regardless of the exception type. All unhandled exceptions +triggered internal handling leading to unwinding and termination. +The current behavior is that only +.code error +exceptions lead to termination. When a non-error exception isn't intercepted +by a catch or handler, the +.code throw +or +.code throwf +returns normally, yielding the value +.codn nil . +If a compatibility value equal to or lower than 234 is requested, +the old behavior occurs: all unhandled exceptions terminate. .IP 227 In \*(TX 227 and older versions, the functions .codn carray-uint , @@ -658,7 +658,7 @@ static void invoke_handler(uw_frame_t *fr, struct args *args) uw_catch_end; } -val uw_throw(val sym, val args) +val uw_rthrow(val sym, val args) { uw_frame_t *ex; static int reentry_count = 0; @@ -705,18 +705,28 @@ val uw_throw(val sym, val args) } if (ex == 0) { - if (std_error == 0) { - fprintf(stderr, "txr: unhandled exception in early initialization\n"); - abort(); - } - if (uw_exception_subtype_p(sym, warning_s)) { --reentry_count; if (uw_exception_subtype_p(sym, defr_warning_s)) uw_defer_warning(args); - else + else if (std_error != 0) format(std_error, lit("warning: ~a\n"), car(args), nao); + if (!opt_compat || opt_compat >= 234) { + uw_rthrow(continue_s, nil); + return nil; + } uw_throw(continue_s, nil); + } + + if (!opt_compat || opt_compat >= 234) { + if (!uw_exception_subtype_p(sym, error_s)) { + --reentry_count; + return nil; + } + } + + if (std_error == 0) { + fprintf(stderr, "txr: unhandled exception in early initialization\n"); abort(); } @@ -746,9 +756,23 @@ val uw_throw(val sym, val args) abort(); } -val uw_throwv(val sym, struct args *arglist) +val uw_rthrowv(val sym, struct args *arglist) { - uw_throw(sym, args_get_list(arglist)); + return uw_rthrow(sym, args_get_list(arglist)); +} + +val uw_rthrowfv(val sym, val fmt, struct args *args) +{ + val stream = make_string_output_stream(); + (void) formatv(stream, fmt, args); + return uw_rthrow(sym, get_string_from_stream(stream)); + abort(); +} + +val uw_throw(val sym, val args) +{ + uw_rthrow(sym, args); + abort(); } val uw_throwf(val sym, val fmt, ...) @@ -764,14 +788,6 @@ val uw_throwf(val sym, val fmt, ...) abort(); } -val uw_throwfv(val sym, val fmt, struct args *args) -{ - val stream = make_string_output_stream(); - (void) formatv(stream, fmt, args); - uw_throw(sym, get_string_from_stream(stream)); - abort(); -} - val uw_errorf(val fmt, ...) { va_list vl; @@ -1280,8 +1296,8 @@ void uw_late_init(void) reg_mac(intern(lit("defex"), user_package), func_n2(me_defex)); reg_var(unhandled_hook_s = intern(lit("*unhandled-hook*"), user_package), nil); - reg_fun(throw_s, func_n1v(uw_throwv)); - reg_fun(intern(lit("throwf"), user_package), func_n2v(uw_throwfv)); + reg_fun(throw_s, func_n1v(uw_rthrowv)); + reg_fun(intern(lit("throwf"), user_package), func_n2v(uw_rthrowfv)); reg_fun(error_s, func_n1v(uw_errorfv)); reg_fun(intern(lit("purge-deferred-warning"), user_package), func_n1(uw_purge_deferred_warning)); @@ -324,10 +324,11 @@ void uw_push_fcall(uw_frame_t *, val fun, struct args *args); void uw_push_eval(uw_frame_t *, val form, val env); void uw_push_expand(uw_frame_t *, val form, val env); #endif +val uw_rthrow(val sym, val exception); +val uw_rthrowv(val sym, struct args *); +val uw_rthrowfv(val sym, val fmt, struct args *); noreturn val uw_throw(val sym, val exception); -noreturn val uw_throwv(val sym, struct args *); noreturn val uw_throwf(val sym, val fmt, ...); -noreturn val uw_throwfv(val sym, val fmt, struct args *); noreturn val uw_errorf(val fmt, ...); noreturn val uw_errorfv(val fmt, struct args *args); val uw_warningf(val fmt, ...); |