( k * x n -- k * x | i * x n )

If any bits of n are non-zero, pop the topmost exception frame from the exception stack, along with everything on the return stack above that frame. Then restore the input source specification in use before the corresponding CATCH and adjust the depths of all stacks defined by this standard so that they are the same as the depths saved in the exception frame (i is the same number as the i in the input arguments to the corresponding CATCH), put n on top of the data stack, and transfer control to a point just after the CATCH that pushed that exception frame.

If the top of the stack is non zero and there is no exception frame on the exception stack, the behavior is as follows:

If n is minus-one (-1), perform the function of 6.1.0670 ABORT (the version of ABORT in the Core word set), displaying no message.

If n is minus-two, perform the function of 6.1.0680 ABORT" (the version of ABORT" in the Core word set), displaying the characters ccc associated with the ABORT" that generated the THROW.

Otherwise, the system may display an implementation-dependent message giving information about the condition associated with the THROW code n. Subsequently, the system shall perform the function of 6.1.0670 ABORT (the version of ABORT in the Core word set).



If THROW is executed with a non zero argument, the effect is as if the corresponding CATCH had returned it. In that case, the stack depth is the same as it was just before CATCH began execution. The values of the i * x stack arguments could have been modified arbitrarily during the execution of xt. In general, nothing useful may be done with those stack items, but since their number is known (because the stack depth is deterministic), the application may DROP them to return to a predictable stack state.

Typical use:

: could-fail ( -- char )

: do-it ( a b -- c) 2DROP could-fail ;

: try-it ( --)
   1 2 ['] do-it CATCH IF
   ( x1 x2 ) 2DROP ." There was an exception" CR
   ELSE ." The character was " EMIT CR

; retry-it ( -- )
   BEGIN 1 2 ['] do-it CATCH WHILE
   ( x1 x2) 2DROP ." Exception, keep trying" CR
   REPEAT ( char )
   ." The character was " EMIT CR


This is the counter part to E. CATCH.

: THROW ( ??? exception# -- ??? exception# )
    ?DUP IF          ( exc# )     \ 0 THROW is no-op
      HANDLER @ RP!   ( exc# )     \ restore prev return stack
      R> HANDLER !    ( exc# )     \ restore prev handler
      R> SWAP >R      ( saved-sp ) \ exc# on return stack
      SP! DROP R>     ( exc# )     \ restore stack
      \ Return to the caller of CATCH because return
      \ stack is restored to the state that existed
       \ when CATCH began execution



: t1 9 ;
: c1 1 2 3 ['] t1 CATCH ;
T{ c1 -> 1 2 3 9 0 }T    \ No THROW executed

: t2 8 0 THROW ;
: c2 1 2 ['] t2 CATCH ;
T{ c2 -> 1 2 8 0 }T    \ 0 THROW does nothing

: t3 7 8 9 99 THROW ;
: c3 1 2 ['] t3 CATCH ;
T{ c3 -> 1 2 99 }T    \ Restores stack to CATCH depth

: t4 1- DUP 0> IF RECURSE ELSE 999 THROW -222 THEN ;
: c4 3 4 5 10 ['] t4 CATCH -111 ;
T{ c4 -> 3 4 5 0 999 -111 }T        \ Test return stack unwinding

: t5 2DROP 2DROP 9999 THROW ;
: c5 1 2 3 4 ['] t5 CATCH           \ Test depth restored correctly
   DEPTH >R DROP 2DROP 2DROP R> ;    \ after stack has been emptied
T{ c5 -> 5 }T


AlexDyachenkoavatar of AlexDyachenko The value of STATE should be restoredProposal2017-09-03 11:07:49

To meaningfully continue after a THROW/CATCH, the value of STATE should be captured during CATCH and restored during THROW.

LeonWagneravatar of LeonWagner 2017-09-08 09:07:15

What is your use case for this? Could you show us an example?

AntonErtlavatar of AntonErtl 2017-09-08 11:48:24

We discussed this at the standards meeting, but do not have an answer for this proposal yet. Anyway, here is my personal answer:

It is a common problem to have a global variable that you want to restore on exiting a word, whether the exit is regular, or through THROW. This problem exists not just for system variables like STATE or BASE, but also for application variables. The general approach that I recommend is to have wrappers around the code that changes the variables. The wrapper catches any exception coming through, restores the old value of the variable, and then THROWs the exception (if any) on. For STATE, this can be done as follows:

: state! ( f -- )
  if ] else postpone [ then ;
: state-wrapper ( xt -- )
  state @ >r catch r> state! throw ;

\ usage example
s" ] non-existent-word" ' evaluate ' state-wrapper catch .

AlexDyachenkoavatar of AlexDyachenko 2017-09-25 14:04:02

My main use case was trying to handle any errors that may occur when including another file, e.g.

S" somefile.4th" ' INCLUDED CATCH ...

This code could be interpreted or compiled, and of course inside somefile.4th the STATE can change arbitrarily prior to a THROW. So anything following CATCH would only work as intended if STATE had not changed. Your example is certainly one way to work around this, but it requires every use of CATCH to be wrapped. In my opinion, if CATCH does not restore STATE, it falls short of satisfying this from the THROW Rationale: "If THROW is executed with a non zero argument, the effect is as if the corresponding CATCH had returned it." The application variables are certainly the application's responsibility, but system variables should be the system's.

If an otherwise standard system chose to restore STATE and BASE as part of the CATCH semantics, would this still be a standard system?

AntonErtlavatar of AntonErtl 2017-10-31 12:26:22

Not every CATCH needs to be wrapped, only code that changes STATE. If you have a word that changes and has to restore STATE, use a STATE-wrapper. All other CATCHes are unaffected. Because the INCLUDED can change STATE, use a STATE-wrapper for the INCLUDED in your example.

The cited statement from the rationale is about what happens on the stack, and is unrelated to other state.

A system that saves and restores STATE on a CATCH would not be standard, because it would do programmer-visible things that are not specified in the standard.

StephenPelcavatar of StephenPelc 2017-11-01 14:08:50

The current definition of CATCH and THROW is deliberately minimal. I believe that it should stay minimal. An embedded system may use CATCH/THROW but not have STATE or BASE.

We (MPE) have attempted in the past to cope with customer wishes for extended facilities. 30 years later we regret all these concessions. The best that we could do now is to DEFER CATCH and THROW.

In the given use case (INCLUDED and friends), the most likely action of the final error handler is to leave compilation and perform some form of restart. We also have plenty of instances in which errors are rethrown because the CATCH/THROW mechanism itself is used to ensure state recovery at several levels. In this case, putting a CATCH inside INCLUDED and then throwing again permits STATE restoration, and no specification change for CATCH is required.


AndrewReadavatar of AndrewRead EXCEPTION LOCALsProposal2017-10-28 07:04:49

Disclaimer - my opinion is biased by the work I have already done on my hardware Forth machine

Conceptually I see BASE, and STATE (and some variables connected to the standard input / output) not as GLOBALs but as "EXCEPTION LOCALs". I.e. variables located on the exception stack. (My analogy here is with the more familiar "SUBROUTINE LOCALs" we are familiar with from the subroutine stack.)

When the Exception stack is PUSH_ed (by CATCH), then the current values of BASE etc. are pushed onto the Exception stack too.

When the Exception stack in POP_ed (by 1 THROW), then the current values of BASE etc. are discarded and the values from the exception layer above become current.

This is easy to implement if BASE etc. are located wherever the exception stack is located. I do it in hardware, but that's a detail.

AntonErtlavatar of AntonErtl 2018-08-15 10:12:05

There is a lot of STATE that can be changed in a word called through CATCH, and that we may want to restore when an exception is THROWn, not just BASE and STATE. Conversely, there are many words that are called through CATCH that do not change BASE and STATE, so always saving and restoring BASE and STATE would be wasteful. My solution for this is cleanup wrappers, such as

: base-execute ( xt u -- )
  base @ >r base ! catch r> base ! throw ;

And while the standard describes the input source specification as a kind of exception locals, at least Gforth uses a cleanup wrapper for restoring it. You can read more about cleanup in my EuroForth'08 paper.

Anyway, I don't think we need exception locals.

Another issue is that some people fail to use such wrappers (after all Thinking Forth does not advocate it), and then a THROW returns to a different CATCH or the Forth command line without restoring the state. For STATE, the standard specifies that it is set to interpreting when there is no user-supplied exception handler, for BASE the user has to fend for himself (as advocated in Thinking Forth). It is a good idea if the system warns of non-decimal BASE, e.g., by mentioning it after "ok".