REBOL3 tracker
  0.9.12 beta
Ticket #0001506 User: anonymous

Project:



rss
TypeBug Statusproblem Date28-Feb-2010 20:45
Versionalpha 97 CategoryError Handling Submitted bySunanda
PlatformAll Severityminor Prioritynormal

Summary ATTEMPT and TRY don't catch unhandled goto functions
Description BrianH has dismissed, with an explanation, a similar bug:

CC#583: neither R2 and R3 cannot recover from executing this bad code, even though it is wrapped in an ATTEMPT or TRY:

attempt [break]
** Throw error: No loop to break
try [break]
** Throw error: no loop to break

***

This is the new bug: R2 and R3 differ in their behavior with TRY [EXIT]; and R3 has the least useful behavior:

R2: traps the error nicely:
attempt [exit]
;; no message on console

R3: fails similar to BREAK:
attempt [exit]
** Throw error: return or exit not in function
Example code
try [exit]
attempt [exit]

Assigned ton/a Fixed in- Last Update17-Mar-2010 09:09


Comments
(0002050)
BrianH
1-Mar-2010 21:16

ATTEMPT in R2 was incorrectly catching RETURN and EXIT because the mezzannine was missing a [throw] attribute.
Fixed in 2.7.7:
>> attempt [exit]
** Throw Error: Return or exit not in function
** Where: attempt
** Near: exit

As for whether ATTEMPT should be catching that error, this points to an interesting detail about how the BREAK, CONTINUE, THROW, QUIT, HALT, RETURN and EXIT functions (all basically gotos) are implemented and where, exactly, the error is generated from.

The goto functions all throw to a handler, different for each of those functions, if one has been established. If no handler has been established then the fallback handler triggers the error you see. The goto functions don't actually trigger those errors at all: They don't know whether they will be handled, they just throw their respective stuff.

Now the tricky part is that ATTEMPT [EXIT] is not necessarily erroneous code, and as far as the ATTEMPT is concerned it isn't erroneous at all. The error you see is generated by the top-level code, outside of the ATTEMPT, not by the EXIT function itself. So what you are requesting is for ATTEMPT or TRY to handle an error before it is triggered or even known to be erroneous.

The only way to do that would be to trigger the error earlier, but that can have some problems. For one thing, triggering the error outside of the handler would depend on being able to tell from the outside whether the throw is being handled by the fallback handler or not, which might not even be possible (Carl?), and would certainly involve task-local management overhead. That overhead would need to be added to every call to EXIT, RETURN, THROW, BREAK and CONTINUE if those functions were modified to trigger an error.

The worse alternative would be for ATTEMPT and TRY to catch all throws from those functions, check the handlers, then rethrow if they are OK. If that could even work it would add huge overhead to all of the unstructured return code, so it's a bad idea.

Unless Carl can come up with a magic method to make all of the above problems go away, I'd have to recommend that this ticket be dismissed. Not as "not a bug", but as a subtlety in the nature of the error that you just need to consider.

Changed the summary to reflect what is going on here.
(0002054)
Sunanda
2-Mar-2010 07:11

Thanks for the detailed analysis, Brian.
But I am not sure we can yet agree on the issue involved.

I take a simplistic view: the code below should always print "still running" no matter what 'f is....
attempt [f]
print "still running"

....The only exceptions being if 'f executes a console-stopping word, eg:
f: func [] [halt]
f: func [] [quit]
f: func [] [q]
I fully accept that I need to defuse such console-stopping words before being able to execute arbitrary REBOL code and expect the console to still be running afterwards.

But there are other possible 'f that crash the console. The two Curecode reports cover two of them:
f: func [][return break]
f: func [][return exit]

Key questions are:
1. Are there more of them?
2. How could I get the complete list?
3. Will there be more of them in the future? (R3 has added the EXIT problem; R2 could handle that)
4. How can I protect against BREAK or EXIT being misused in arbitrary code while allowing them to be properly used in the same code?

The current behavior allows me to easily craft attacks against arbitrary code.
And so forces me to use CALL to launch another instance before executing arbitrary code.

Surely it would be better to (say) have a SECURE policy to set ATTEMPT and TRY as STRICT.
(0002057)
BrianH
3-Mar-2010 08:44

Sorry, there are more functions than just HALT and QUIT that are supposed to stop execution. The THROW, BREAK, CONTINUE, EXIT and RETURN functions also stop execution, though where execution resumes is bounded by a different scope.

Now ignoring the already reported issues (in high priority tickets, no less), and unanswerable questions 1, 2 and 3, we are left with question 4, and the answer is simple: There is no way to tell the difference without running or otherwise tracing the code. For instance:

>> a: does [break]
>> loop 2 [print 1 a print 2]
1
>> loop 2 [print 1 attempt [a] print 2]
1
>> a: does [attempt [break]]
>> loop 2 [print 1 a print 2]
1
>> loop 2 [print 1 attempt [a] print 2]
1
>> a
** Throw error: no loop to break

This is all correct code. There are no errors here. ATTEMPT [BREAK] is never itself an error, it can only be an error in context (real context, not a binding thing). ATTEMPT, TRY, EXIT, BREAK, RETURN, and so on are all functions, not syntax. A BREAK may be called from any number of nested function calls, and it doesn't matter that these look like syntax, they are still nested function calls. And ATTEMPT and TRY should *never* catch HALT, QUIT, EXIT, RETURN, BREAK or THROW, because doing that would be an error in almost all code. Those functions are innocent: It's their handlers that are at fault, not them.

So the question needs to be whether EXIT, RETURN, BREAK, CONTINUE or THROW should be provided with the information at runtime about whether they would be handled by the error-triggering handler, and if so then trigger the error themselves instead of doing their job (HALT and QUIT should never be caught by ATTEMPT or TRY under any circumstances). It is likely that this would add unacceptable overhead to the process of calling and returning from functions, loops, CATCH and DO (Carl?). If that is the case then we should give up on this.

"R3 has added the EXIT problem; R2 could handle that" That was an error in ATTEMPT, which was catching all RETURN and EXIT calls, especially the legitimate ones. Fixed in 2.7.7.
(0002058)
BrianH
3-Mar-2010 10:22

Another consideration is that what BREAK, CONTINUE, EXIT, RETURN, THROW, QUIT and HALT do *already* is trigger a kind of error (code: 0 for BREAK), and that the error message that you see above actually comes from that error already; see system/catalog/errors/throw for the relevant messages. These errors are specifically supposed to be ignored by TRY and ATTEMPT because they don't represent erroneous conditions, they represent legitimate behavior by definition.

So the proposal is not to have the functions trigger errors, it is to have them trigger *different* errors, in a different category with codes greater than 100, and probably full error objects inside them. And to do this when they can determine that they are not going to be handled by an official handler, but by the main handler instead. Information that they currently can't know.
(0002063)
BrianH
3-Mar-2010 22:26

See #1509 for the error? try [break] bug.
(0002069)
abolka
6-Mar-2010 00:39

> EXIT, RETURN, BREAK, CONTINUE, [and] THROW should be provided with the information at
> runtime about whether they would be handled by the error-triggering handler, and if so
> then trigger the error themselves

I think that improved error causation as described by Brian in above comment would be worthwhile for the sake of language consistency and should therefore be pursued.

After discussing this a bit on AltME, it seems that a fairly straightforward implementation adding only minimal overhead could be possible. But that's, of course, a question only Carl can answer definitively.
(0002071)
BrianH
7-Mar-2010 22:12

Based on the stuff said in http://www.rebol.com/r3/notes/errors.html it seems that BREAK, CONTINUE, EXIT, RETURN and THROW (and maybe QUIT and HALT) don't actually throw their pseudo-error values, they just return them and DO handles the propagation. This means that the solution to this would be simpler: The functions can just create and trigger real errors with the same error codes when the unwinds wouldn't be handled by their proper handlers, rather than returning and unwinding.
(0002072)
Ladislav
8-Mar-2010 14:50

"...it seems that BREAK, CONTINUE, EXIT, RETURN and THROW (and maybe QUIT and HALT) don't actually throw their pseudo-error values, they just return them..." - this terminology is unreadable as noted at #1491, at least IMO (Ceterum censeo, Carthaginem esse delendam).

To single out what I see as terminologically insane: "THROW does not throw"

In my preferred terminology, THROW is a function that throws, as opposed to other functions, e.g. the DO function, that does something else, which does not deserve to be described using the word "throw" in my opinion, not even in cases like:

do make error! "phew!"
(0002076)
BrianH
8-Mar-2010 22:08

Yes, it is ironic that THROW doesn't really throw in R3, while DO error! does throw, but not with THROW. When you get into language internals there tends to be a lot of ironic situations like this. The naming of the functions is based on their surface behavior though, not their internal implementation model. And the surface behavior of THROW is to be the counterpart of CATCH.
(0002079)
Ladislav
9-Mar-2010 10:22

As far as I am concerned, I do not want to explain people, that "THROW does not throw". That is why I proposed to use a different terminology as mentioned in #1491
(0002091)
Ladislav
12-Mar-2010 12:19

Rebol [
    Purpose: {
        A dynamic B-CATCH/B-THROW pair implemented to illustrate how
        a dynamic THROW-like construct can be implemented so
        that unhandled throws are detectable during the B-THROW call
    }
]

make object! [
    ; indicating B-CATCH presence:
    b-catch?: false

    set 'b-catch func [
        block [block!]
        /local result previous
    ] [
        ; remember the current state
        previous: b-catch?
        b-catch?: true

        set/any 'result catch block
        b-catch?: previous
        :result
    ]

    set 'b-throw func [value [any-type!]] [
        unless b-catch? [do make error! "unhandled B-THROW"]
        throw get/any 'value
    ]
]
(0002105)
Ladislav
17-Mar-2010 09:09

Definitional Return note: since the test of function context availability has already been implemented in R3, it can also be used to test whether a definitional Return will be caught.

Date User Field Action Change
31-Jan-2013 10:30 Ladislav Comment : 0002091 Modified -
30-Jan-2013 15:53 Ladislav Comment : 0002091 Modified -
17-Mar-2010 09:09 Ladislav Comment : 0002105 Added -
12-Mar-2010 12:19 Ladislav Comment : 0002091 Added -
9-Mar-2010 10:22 Ladislav Comment : 0002079 Added -
8-Mar-2010 22:09 BrianH Comment : 0002076 Modified -
8-Mar-2010 22:08 BrianH Comment : 0002076 Added -
8-Mar-2010 15:09 Ladislav Comment : 0002072 Modified -
8-Mar-2010 15:04 Ladislav Comment : 0002072 Modified -
8-Mar-2010 15:03 Ladislav Comment : 0002072 Modified -
8-Mar-2010 15:01 Ladislav Comment : 0002072 Modified -
8-Mar-2010 14:55 Ladislav Comment : 0002072 Modified -
8-Mar-2010 14:55 Ladislav Comment : 0002072 Modified -
8-Mar-2010 14:53 Ladislav Comment : 0002072 Modified -
8-Mar-2010 14:50 Ladislav Comment : 0002072 Added -
7-Mar-2010 22:12 BrianH Comment : 0002071 Added -
6-Mar-2010 00:39 abolka Comment : 0002069 Added -
3-Mar-2010 22:26 BrianH Comment : 0002063 Added -
3-Mar-2010 10:22 BrianH Comment : 0002058 Added -
3-Mar-2010 09:39 BrianH Comment : 0002057 Modified -
3-Mar-2010 09:37 BrianH Comment : 0002050 Modified -
3-Mar-2010 08:44 BrianH Comment : 0002057 Added -
2-Mar-2010 07:11 sunanda Comment : 0002054 Added -
1-Mar-2010 21:24 BrianH Comment : 0002050 Modified -
1-Mar-2010 21:17 BrianH Status Modified submitted => problem
1-Mar-2010 21:17 BrianH Category Modified Unspecified => Error Handling
1-Mar-2010 21:17 BrianH Code Modified -
1-Mar-2010 21:17 BrianH Description Modified -
1-Mar-2010 21:17 BrianH Summary Modified attempt returns error with bad code (part 2) => ATTEMPT and TRY don't catch unhandled goto functions
1-Mar-2010 21:16 BrianH Comment : 0002050 Added -
28-Feb-2010 20:45 sunanda Ticket Added -