9.6.1.2275 THROW EXCEPTION

( 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).

See:

Rationale:

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 )
   KEY DUP [CHAR] Q = IF 1 THROW THEN ;

: 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
   THEN
;

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

Implementation:

This is the counter part to E.9.6.1.0875 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
    THEN
;

Testing:

DECIMAL

: 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

ContributeContributions

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

This contribution has been moved to the proposal section.

LeonWagneravatar of LeonWagner

This reply has been moved to the proposal section.

AntonErtlavatar of AntonErtl

This reply has been moved to the proposal section.

AlexDyachenkoavatar of AlexDyachenko

This reply has been moved to the proposal section.

AntonErtlavatar of AntonErtl

This reply has been moved to the proposal section.

StephenPelcavatar of StephenPelc

This reply has been moved to the proposal section.

JimPetersonavatar of JimPeterson

This reply has been moved to the proposal section.

ruvavatar of ruv

This reply has been moved to the proposal section.

UlrichHoffmannavatar of UlrichHoffmann

This reply has been moved to the proposal section.
Considered
Reply New Version

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

This contribution has been moved to the proposal section.

AntonErtlavatar of AntonErtl

This reply has been moved to the proposal section.

AndrewReadavatar of AndrewRead

This reply has been moved to the proposal section.
Retracted
Reply New Version

MitraArdronavatar of MitraArdron [170] THROW: text doesn't match implementation exampleComment2020-12-06 08:05:30

I think the text needs updating - it is not clear what happens when the value is zero, though the implementation example is.

I think this is particularly imporant as this differs from other implementations of THROW, for example in eForth where it is always triggered , i.e. there is no "?DUP IF"

AntonErtlavatar of AntonErtl

The text starts with

If any bits of n are non-zero,

and later it says

If the top of the stack is non zero [...]

Admittedly it does not say anything about what happens if n=0, but that generally means that it does nothing. However, what it does in that case is to pop n; this is reflected in the stack effect ( k * x n -- k * x ) for that case, but the specification does not say that this stack effect refers to this case, so I have proposed to make it clear that n is popped.

Closed
Reply New Version

JimPetersonavatar of JimPeterson [191] Throwing past DO/LOOPComment2021-04-19 19:06:28

I didn't implement my DO/LOOP to store limits/counters on the return stack (limited system, no room there, the standard does not require it). If I were to THROW up through a DO/LOOP (particularly a nested one), using the above specification for THROW as my bare-bones attempt at implementing it, I know that "an ambiguous condition" would then most certainly exist.

This is my own fault, but shouldn't there at least be a note in the standard along the lines of "The system should implement an appropriate number of UNROLL operations when throwing an exception up through DO/LOOP constructs."? For most systems, this is implied by adjusting the return stack, and systems implementors would just think "easy", but at least it would be explicitly stated.

Certainly, any similar user-made constructs have identical issues that are entirely the user's responsibility. Maybe this is an argument for making CATCH/THROW compatible with DEFER!? How else would user code that acquires global resources properly interoperate in the presence of THROW?

ruvavatar of ruv

I didn't implement my DO/LOOP to store limits/counters on the return stack

In the case of your do-loop, a system-specific solution is to keep in the exception frame the pointer of the loop control stack (or the number of loop control parameters that are updated by do-loop run-time), and restore the pointer (or unloop the parameters) in THROW.

Certainly, any similar user-made constructs have identical issues [...] How else would user code that acquires global resources properly interoperate in the presence of THROW?

A proper way for programs to free resources is to use CATCH (see an example in A.9.6.1.2275).

Actually, even your do-loop can be implemented in a portable way using quotations:

: do c{ [: do }c ; immediate
: ?do c{ [: ?do }c ; immediate
variable no-exit \ it should be a user variable in case of multitasking
: execute-loop-safely ( xt -- flag-to-exit ) catch no-exit @ no-exit off swap dup if (unloop) then throw 0= ;
: (finalize-loop) c{ no-exit on ;] execute-loop-safely if exit then }c ; immediate
: loop c{ loop (finalize-loop) }c ; immediate
: +loop c{ +loop (finalize-loop) }c ; immediate

Here I use c{ ... }c notation (construct) to compile code fragments, and in this case it's equal to ]] ... [[ notation, that is to apply postpone to each contained word.

The variable no-exit is used to properly handle unloop exit phrase. So, a word

  : test 10 0 do i .  i 4 > if unloop exit then  loop ."  fail " ;

is compiled as

  : test 10 0 [: _do i .  i 4 > if unloop exit then  _loop  no-exit on ;] execute-loop-safely if exit then ."  fail " ;

Where _do and _loop are the original versions of these words.

JimPetersonavatar of JimPeterson

That's quite an interesting technique. Also, I've noticed I was mostly wrong about most of my concerns.

Is there a place, online, to discuss many Forth-related things without distracting the spec. experts with my amateurish ramblings?

ruvavatar of ruv

Is there a place, online, to discuss many Forth-related things

  1. ForthHub on GitHub platform:

  2. Discussions in the dedicated discussion repository

  3. Discussions in the issues of the in the dedicated discussion repository

  4. Discussions in the forthhub team

  5. Forth related questions at StackOverflow

  6. Forth subreddit at Reedit

  7. The old comp.lang.forth news group, that is available via NNTP and Google Groups web-interface.

  8. Various forums, chats in IRC, Telegram etc.

MitraArdronavatar of MitraArdron

Is there a place, online, to discuss many Forth-related things

The most active places I've found seem to be the Facebook groups: ForthArduino Early Forth Forth Programming Forth 2020

UlrichHoffmannavatar of UlrichHoffmann

The committee endorses ruv's answer. closed.

Closed
Reply New Version

ruvavatar of ruv [283] Input source after THROWComment2023-02-15 22:50:52

In presence of the save-input and restore-input words, it could appear that throw should restore the state of the input source as it was before execution of the corresponding catch, due to similar wording.

But actually the specification only requires that if a throw code is non zero, the input source is the same before and after execution of catch. But the state of this input source may be changed.

Namely, the specification for THROW says:

  • (if n <> 0) "restore the input source specification in use before the corresponding CATCH" (emphasis added)

While the specification for RESTORE-INPUT says:

  • "restore the input source specification to the state described by..." (emphasis added)

If the state of the input source were to be restored, the following test would show the same line "source: test" twice:

: test [: cr ." source: " source type cr ." next line: " refill cr . cr abort ;] catch drop ." source: " source type cr  postpone \ ;
test
\ some other line

NB: the state of the input source can be only changed via >in (directly or indirectly), refill, and restore-input. No other way to change this state for a standard program.

Please correct me if I'm wrong somewhere. Otherwise, the wording should reflect this nuance more explicit, I think.

ruvavatar of ruv

See also some thoughts on this subject in the comment in 2019 by Anton.

AntonErtlavatar of AntonErtl

In the long run we should probably specify a (logical) input stack that physically typically (or requiredly?) resides on the return stack, and that THROW restores, like the other stacks, with appropriate changes in words like INCLUDE-FILE, EVALUATE, and LOAD. The restoration of course has to restore the input source in the same way as regularly leaving INCLUDE-FILE, EVALUATE, and LOAD would. But if the code controlled by catch just advances the position in the current input stream, no input stack item is pushed and throw does not restore it, and therefore does not restore the position.

I leave this open so we don't forget about wanting to do this change.

ruvavatar of ruv

As I can see, "nesting of input sources (i.e., with LOAD, EVALUATE, or INCLUDE-FILE)" is mentioned, and un-nesting input source specifications is required by the section 9.3.5 Exception handling.

And A.13 also says that the standard "does require INCLUDE-FILE to unnest the input source specification if an exception is THROWn".

I used to think that it's an omission that THROW restores the input source specification, while CATCH does not save it. But actually it's by intention. The Idea is that THROW restores what INCLUDE-FILE (or EVALUATE, etc) saves. If nothing was saved (between an instance of THROW and the nearest instance of CATCH), then the input source specification is already the same as before CATCH, and no need to restore it.


In the long run we should probably specify a (logical) input stack

It's a possible way to describe this behavior more clear/ thoroughly. Another possible way is to formally use the exception stack (and exception frames) for that, taking into account that the Exception word set is mandatory.

Although, it seems this formalism is not worth introducing, since there is no questions on this regard. The questions were only about the state of the input source (whether this state shall be restored or not). So it's enough to clarify only this.

Reply New Version

MitraArdronavatar of MitraArdron [304] Imlementation seems wrong for the "-2" caseSuggested reference implementation2023-08-03 10:30:03

If the top of the stack is -2 , and there is no handler, then THROW is supposed to display a message

I can't see how the reference implementation does that. It deals with the case of being called from inside a CATCH correctly as far as I can tell, but does not handle the case of HANDLER being empty - in fact if HANDLER=0 I think this would crash.

Note - I picked this up when the Hayes test suite failed its exception test set.

ruvavatar of ruv

If the top of the stack is -2 , and there is no handler, then THROW is supposed to display a message.
I can't see how the reference implementation does that.

A reference implementation is just an example, it is not required to cover all the cases.

The reference implementation for THROW is preceded by the note: "This is the counter part to E.9.6.1.0875 CATCH", if we follow the link, we in turn can read in the note: "This sample implementation does not explicitly handle the case in which CATCH has never been called (i.e., the ABORT behavior)".

So, the implementation explicitly says that the case of no handler is not covered.

Reply New Version