Bug 18877 - [XQ30] try/catch
[XQ30] try/catch
Status: RESOLVED FIXED
Product: XPath / XQuery / XSLT
Classification: Unclassified
Component: XQuery 3.0
Working drafts
PC Windows NT
: P2 normal
: ---
Assigned To: Jonathan Robie
Mailing list for public feedback on specs from XSL and XML Query WGs
:
: 21221 (view as bug list)
Depends on:
Blocks: 21221
  Show dependency treegraph
 
Reported: 2012-09-14 11:02 UTC by Tim Mills
Modified: 2013-06-11 16:34 UTC (History)
4 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Tim Mills 2012-09-14 11:02:05 UTC
The specification states:

"A try/catch expression catches dynamic errors and type errors raised during dynamic evaluation for expressions that are lexically contained within the try clause."

Consider the contrived example:

declare function local:func()
{
  try
  {
    return function($arg)
           { 
             10 div $arg
           }
  }
  catch *
  {
    0
  }
};

local:func()(0)

10 div 0 will raise a dynamic error.  The expression 10 div $arg appears to be lexically contained within the try clause.  While it is surely not the intent of the specification that such a dynamic error would be caught, I can't point to anything in the specification to be sure.
Comment 1 Michael Kay 2012-09-14 11:34:52 UTC
Yes, I've always felt the spec was too informal in this area. And I also don't think it's too hard to get right.

Basically, we should say that the evaluation of an expression E may directly cause the evaluation of another expression F. This relationship has a transitive closure, "directly or indirectly causes", and we can say that an error in the evaluation of an expression Z is caught by a try/catch expression T if evaluation of the "try" block of T directly or indirectly causes the evaluation of Z. We can then enumerate how one expression may directly cause the evaluation of others:

(a) evaluation of an expression directly causes the evaluation of its operand expressions

(b) evaluation of a function call directly causes the evaluation of the corresponding function body 

[(c) evaluation of a variable reference does NOT directly cause the evaluation of the variable initializer]

and that's about it. We can use this rhetoric to more formally explain the case where an exception is not caught by an outer try/catch because it is caught by an inner try/catch.

Putting this machinery into place would help greatly in the XSLT spec, where we need to explain for example whether an error in evaluating a match pattern is caught by the apply-templates instruction that caused the pattern to be evaluated.
Comment 2 Jonathan Robie 2012-09-14 13:13:47 UTC
I like the precision of the approach Mike Kay suggests in comment #1. 

I'm confused about one thing: Mike seems to be agreeing with Tim's understanding of the example in comment #0, but I think the "try" block of in that example "directly or indirectly causes the evaluation of" 10 div 0, so it would be caught. Tim seems to believe this is "surely not the intent". What am I missing?
Comment 3 Tim Mills 2012-09-14 13:27:11 UTC
local:func() directly causes the evaluation of the corresponding function body, which returns a (yet to be evaluated) function.

local:func()(0) directly causes the evaluation of the function body 10 div $arg.

The function invocation is outside the lexical scope of the try clause.

I don't think that Mike means that the local:func() indirectly evaluates 10 div $arg.
Comment 4 Michael Kay 2012-09-14 14:22:02 UTC
Indeed. When we explain that evaluating an expression causes evaluation of its operand expressions, we need to do this in a way that makes it clear that the enclosed expression making up the body of an Inline Function Expression is not an "operand" of the inline function expression in this sense, and evaluating the IFE does not cause evaluation of its body.
Comment 5 Jonathan Robie 2012-10-02 17:08:06 UTC
This does not change the behavior of implementations. Reclassifying to minor, since this does not change the semantics of the language. We will address this during CR.

Comment #1 is a good start, it defines the transitive cases of "cause the evaluation of" well. It does not precisely define the simple case yet - need to find the right wording for that.
Comment 6 Tim Mills 2013-03-17 08:49:48 UTC
*** Bug 21221 has been marked as a duplicate of this bug. ***
Comment 7 Michael Kay 2013-03-17 14:20:57 UTC
Reclassifying to "normal". This is a technical problem not an editorial one.
Comment 8 Jonathan Robie 2013-06-04 15:10:11 UTC
I propose the following wording.

A try/catch expression catches dynamic errors and type errors raised during dynamic evaluation of the target expression E, found in the try clause T, if E raises an error, or if E directly causes or indirectly causes the evaluation of expression Z and Z raises an error. If the target expression does not raise a dynamic error or a type error, the result of the try/catch expression is the result of the target expression.

[Definition: An expression E directly causes evaluation of expression Z if Z is an operand expression of E, or if E is a function call and Z is the corresponding function body.]

Note:

Evaluation of a variable reference does not directly cause the evaluation of the variable initializer.

Note:

Evaluation of an inline function expression does not directly cause evaluation of the body of the inline function expression.

[Definition: An expression E indirectly causes evaluation of expression Z if there is a sequence of expressions beginning at E and ending at Z such that each expression directly causes evaluation of the next. ]
Comment 9 Jonathan Robie 2013-06-04 16:27:32 UTC
In comment #8, I missed this case, which should be added:

If an expression E indirectly evaluates a try/catch expression T, errors caught by T are not raised beyond the scope of T
Comment 10 Jonathan Robie 2013-06-04 16:30:21 UTC
Instead of "Evaluation of an inline function expression does not directly cause evaluation of the body of the inline function expression", I prefer "Evaluation of an inline function expression does not evaluate its body."
Comment 11 Jonathan Robie 2013-06-04 16:33:29 UTC
(In reply to comment #0)

Actually, 10 div $arg is not evaluated - it's lexically contained, but not evaluated. There may be no bug here.

It might be clearer if the spec said:

"A try/catch expression catches dynamic errors and type errors raised during
dynamic evaluation OF expressions that are lexically contained within the
try clause."
Comment 12 Michael Kay 2013-06-04 17:59:31 UTC
(In reply to comment #11)
> (In reply to comment #0)
> 
> Actually, 10 div $arg is not evaluated - it's lexically contained, but not
> evaluated. There may be no bug here.
> 
> It might be clearer if the spec said:
> 
> "A try/catch expression catches dynamic errors and type errors raised during
> dynamic evaluation OF expressions that are lexically contained within the
> try clause."

I think that completely misses the point of the bug report. Eventually, at some point, (10 div $arg) will be evaluated, and an error will occur during dynamic evaluation, and the try/catch should not catch that error. The proposed text says that the try/catch will catch the error even though the dynamic evaluation does not occur during the evaluation of the try/catch expression.
Comment 13 Jonathan Robie 2013-06-04 19:28:41 UTC
(In reply to comment #12)

> I think that completely misses the point of the bug report.

Possibly, but I think the bug report may be based on a misunderstanding. Let's talk this through.

> Eventually, at
> some point, (10 div $arg) will be evaluated, and an error will occur during
> dynamic evaluation, and the try/catch should not catch that error. 

Agreed.

> The proposed text says that the try/catch will catch the error even though the
> dynamic evaluation does not occur during the evaluation of the try/catch
> expression.

I don't think it does. Let's take it one step at a time.

local:func() returns this function:

    function($arg)
    { 
        10 div $arg
    }

local:func()(0) calls the above function with $arg := 0.  There is no try/catch in the function, so no try/catch is available to catch the error.  By the time (10 div $arg) is evaluated, the try/catch is irrelevant.
Comment 14 Jonathan Robie 2013-06-04 19:44:10 UTC
If what I said in comment #13 is correct, the easiest thing to do right now is to close the bug as invalid, and that also results in a more stable spec.

If we want the formality of comment #8, #9, #10, we can easily do that in XQuery 3.1. If we make these changes now and there are any errors in the new text, that could keep us from going to PR.
Comment 15 Michael Kay 2013-06-04 20:08:07 UTC
(In reply to comment #13)

> local:func()(0) calls the above function with $arg := 0.  There is no
> try/catch in the function, so no try/catch is available to catch the error. 
> By the time (10 div $arg) is evaluated, the try/catch is irrelevant.

I think we are all in agreement that the try/catch should be irrelevant. The problem is that the spec, in both its current form and after the proposed change, says otherwise.

Specifically, the sentence "A try/catch expression catches dynamic errors and type errors raised during dynamic evaluation of expressions that are lexically contained within the try clause." implies that in this case, the try/catch expression is capable of 'acting at a distance' by catching dynamic errors that occur not during the evaluation of the try/catch expression itself. We know it doesn't intend to say that, but that's still what it says.

A better formulation would be "A try/catch expression catches dynamic errors and type errors raised during dynamic evaluation of expressions that are lexically contained within the try clause and that are evaluated during the evaluation of the try clause."
Comment 16 Michael Dyck 2013-06-05 06:14:51 UTC
(In reply to comment #15)
>  
> A better formulation would be "A try/catch expression catches dynamic errors
> and type errors raised during dynamic evaluation of expressions that are
> lexically contained within the try clause and that are evaluated during the
> evaluation of the try clause."

Except that seems to be excluding (errors raised during dynamic evaluation of) expressions *not* lexically contained within the try clause that are nevertheless evaluated during the evaluation of the try clause (i.e., via function calls). And we shouldn't exclude those, unless they're caught by a deeper try/catch.

My guess is, nothing good will come of the phrase "expressions lexically contained within the try clause".

In fact, Bug 11547 indicates that over a year ago we agreed to wording that didn't use that phrase. As far as I can tell, that resolution was never applied to the spec.
Comment 17 Michael Dyck 2013-06-05 06:37:03 UTC
In addition, note that other sentences in the same section have the same bug as raised in comment #0...

"If a function call occurs within a try clause, errors raised by evaluating
the corresponding function are caught by the try/catch expression."

But consider:
    declare function local:f() { 1 div 0 };
    try { function() { local:f() } }
    catch * { 42 }
The function call local:f() occurs with the try clause, and so the spec indicates that the error it raises is caught by the try/catch expr.
However, it shouldn't be, because the eval of that function call is not
caused by the eval of the target expression.

----

"If a variable reference is used in a try clause, errors raised by binding
a value to the variable are not caught unless the binding expression occurs
within the try clause."

But consider:
    try { function() { let $x := 1 div 0 return $x } }
    catch * { 42 }
The binding expression "1 div 0" occurs within the try clause, and so the
spec (maybe) says that the dynamic error raised by evaluating that expression
is caught by the try/catch expr. However, it shouldn't be.

(Actually, this dynamic error isn't raised by "binding a value to the
variable", but by attempting to come up with a value in the first place.
But you get the idea.)
Comment 18 Andrew Eisenberg 2013-06-10 19:38:40 UTC
I agree with Michael Dyck in comment #16, when he said:

"My guess is, nothing good will come of the phrase "expressions lexically contained within the try clause"."

Is it possible for us to simplify the paragraph that contains it, saying instead:

"A try/catch expression catches dynamic errors and type errors raised by the evaluation of the target expression of the try clause. If the target expression does not raise a dynamic error or a type error, the result of the try/catch expression is the result of the target expression."
Comment 19 Ghislain Fourny 2013-06-11 14:58:26 UTC
I like Andrew's comment a lot. I also think we could push recursion down in the wording, i.e., just say:

"A try/catch expression catches dynamic errors and type errors raised or forwarded by the evaluation of the target expression of the try clause."

With the understanding that:

- An expression _raises_ an error if the error is "freshly created" according to the semantics of the expression. Example: "foo" + 1 raises an error because it has directly to do with the semantics of addition.

- An expression _forwards_ an error that has been already raised or forwarded (the recursion is here) by an operand expression. When an error is forwarded and when not is left to the (already existing) semantics of each expression, for example if the condition is true, then a conditional expression does not forward an error raised or forwarded by the else clause, but it does forward an error raised or forwarded by its then clause. Likewise, a try-catch expression does not forward an error raised or forwarded by its try expression if it has been caught.

That way, we can remove complexity from the semantics of try catch and push it back to the semantics of each individual expression.

Andrew's previous formulation, which is simpler, can be obtained by removing the fine distinction between raising and forwarding an error.

I hope this makes sense?
Comment 20 Jonathan Robie 2013-06-11 16:34:17 UTC
The Working Group has adopted the following wording as the resolution of this bug.


(In reply to comment #18)

> "A try/catch expression catches dynamic errors and type errors raised by the
> evaluation of the target expression of the try clause. If the target
> expression does not raise a dynamic error or a type error, the result of the
> try/catch expression is the result of the target expression."