This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 29119 - [XP31] xs:error always raises a type error
Summary: [XP31] xs:error always raises a type error
Status: RESOLVED FIXED
Alias: None
Product: XPath / XQuery / XSLT
Classification: Unclassified
Component: XPath 3.1 (show other bugs)
Version: Proposed Recommendation
Hardware: PC Windows NT
: P2 normal
Target Milestone: ---
Assignee: Jonathan Robie
QA Contact: Mailing list for public feedback on specs from XSL and XML Query WGs
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-09-10 16:07 UTC by Abel Braaksma
Modified: 2015-10-20 21:01 UTC (History)
2 users (show)

See Also:


Attachments

Description Abel Braaksma 2015-09-10 16:07:16 UTC
We currently say:

<quote>
A variable binding with a type declaration xs:error always
raises a type error
</quote>

I believe this is only partially correct, as it would make the following always fail:

let $e := xs:error()
return 12

Instead I think we want to say: evaluation of a variable of type xs:error... or something along those lines.
Comment 1 Jonathan Robie 2015-09-10 19:10:31 UTC
Yeah, I'll do that.
Comment 2 Tim Mills 2015-09-22 15:31:09 UTC
Note that in

let $e := xs:error()
return 12

$e is not a variable binding with a type declaration.  That would be:

let $e as xs:error := xs:error()
return 12

which is XQuery, not XPath.
Comment 3 Abel Braaksma 2015-09-23 04:27:46 UTC
> let $e as xs:error := xs:error()
> return 12

> which is XQuery, not XPath.

Indeed. Is the text from comment#0 agnostic? I.e. is it supposed to apply to XSLT or other host languages as well that support typed variable bindings?

I believe I rose this bug report to signify that a binding (with or without a type declaration) may exist, though evaluation will always throw an error. Of course, early evaluation is allowed, but still, that shouldn't mean that constructs like the one above fail, or this:

let $e as xs:error := xs:error('assertion failed')
return if(@assert) then 'success' else $e

I believe it should only fail when the if-statement is false, otherwise this should not raise an error. Interestingly, currently this snippet won't compile on Saxon (not even without the type specification), stating that a "Cast to xs:error always fails" (9.6.0.5).

I don't think it should raise an error, and surely not statically. In fact, if I replace the above xs:error with xs:int('assertion failed') it behaves as expected (only raises if the if-statement fails).

Section 18.4 in FO31 seems to support this reading (in the Note), saying that the error is dynamic:

    "Because xs:error has no member types, and therefore has an empty value 
    space, casting will always fail with a dynamic error except in the case 
    where the supplied argument is an empty sequence, in which case the 
    result is also an empty sequence."

This seems to make sense.
Comment 4 Jonathan Robie 2015-09-28 20:21:30 UTC
(In reply to Abel Braaksma from comment #3)
> > let $e as xs:error := xs:error()
> > return 12
 

This sentence is part of a paragraph that addresses more than just dynamic values:

<quote>
xs:error is a subtype of all simple types, and a supertype only of itself. xs:error? and xs:error* are identical to empty-sequence(). A variable binding with a type declaration xs:error always raises a type error.
</quote>

The same text occurs in XQuery 3.0:

http://www.w3.org/TR/xquery-30/#id-xs-error

It's been in versions of the spec since June 2013. Changing this now would affect implementations of XQuery 3.0, so I would oppose changing this in XQuery.

> > which is XQuery, not XPath.
> 
> Indeed. Is the text from comment#0 agnostic? I.e. is it supposed to apply to
> XSLT or other host languages as well that support typed variable bindings?

XPath describes only the XPath syntax. I suspect this sentence should not appear in the XPath specification.
Comment 5 Jonathan Robie 2015-10-05 23:55:13 UTC
See also the discussion that starts here:

https://lists.w3.org/Archives/Public/public-xsl-query/2015Sep/0109.html
Comment 6 Jonathan Robie 2015-10-05 23:56:13 UTC
My own view, after much discussion: The simplest fix really might be to say that xs:error, used as a SequenceType, is always a type error.  That significantly simplifies specification, test cases, and implementation.
Comment 7 Michael Kay 2015-10-06 08:10:51 UTC
"...to say that xs:error, used as a SequenceType, is always a type error"

That would make

12 instance of xs:error

a type error, which can't possibly be right: it's a perfectly valid expression that returns false.

The essence of xs:error is that it's a type with no instances. That should be enough to answer all questions about it. It shouldn't get any special treatment. From this simple fact, everything else follows without any special rules:

$x instance of xs:error --> always returns false

$x cast as xs:error --> fails dynamically with FORG0001 for all possible values of $x

$x cast as xs:error? --> succeeds if empty($x), fails FORG0001 for all other values of $x

xs:error($x) --> same as ($x cast as xs:error?)

$x castable as xs:error --> always returns false

$x treat as xs:error --> fails with dynamic error XPDY0050 if evaluated. Never fails statically. 

declare variable $x as xs:error --> fails with type error XPTY0004 (the processor has the option to raise the error statically or dynamically, or to avoid raising the error if $x is not used)

let $x as xs:error := $y return EXPR --> ditto.

declare function f($arg as xs:error) {...} --> a legitimate function declaration; any call to the function (but see §1) fails with a type error XPTY0004, which may be raised statically or dynamically. If there is no function call, there is no error.

§1 - other than a call with an argument whose static type is xs:error, e.g. f($x treat as xs:error), which fails XPDY0050 if and when the argument is evaluated.

Historically, it's my belief that 2.5.7 was intended to explain this, and failed to do so adequately. 2.5.7 should be treated as non-normative - it doesn't say anything that you couldn't work out for yourself.
Comment 8 Jonathan Robie 2015-10-06 09:34:10 UTC
In Comment #7, Mike Kay wrote:

> The essence of xs:error is that it's a type with no instances. That should be enough to answer all questions about it. It shouldn't get any special treatment. From this simple fact, everything else follows without any special rules:

At the very least, we need test cases in the test suite for this, because implementations are not getting this right.  To me, the fact that implementations are not getting this right is one indication that this is not "just falling out".

But I think the fact that it's a type with no instances also tells us that it is never useful in a SequenceType production.  Why would anyone ever write "12 instance of xs:error" instead of "false()", or write functions that can only fail if called? It's hard to imagine a query where using xs:error as a SequenceType is useful, and it's easy to imagine it being a mistake. Don't we help the user by pointing this out?

As a SequenceType, xs:error is always an exception. A SequenceType is used to describe the type of an XQuery 3.1 value, and xs:error can never be the type of an XQuery 3.1 value.
Comment 9 Tim Mills 2015-10-06 09:48:10 UTC
(In reply to Michael Kay from comment #7)

> $x instance of xs:error --> always returns false

I presume that

xs:error(1) instance of xs:error 

can either raise FORG0001 or return false because sequence type matching requires a value, and there exists no value of type xs:error, even though the static type of xs:error(1) is xs:error.  Similarly for typeswitch.

I'd argue that 

declare function local:my-error($qname) as xs:error
{
  fn:error($qname, "boo")
};

might either raise the from fn:error() or XPTY0004.

Similarly, 

declare variable $x as xs:error := fn:error()
$x

and

let $x as xs:error := fn:error()
return $x

might raise FOER0000 or XPTY0004
Comment 10 Michael Kay 2015-10-06 09:55:43 UTC
When we added support for xs:error, we didn't justify it because it was useful, we justified it because it was there: there wasn't a good reason that we should exclude support for one of the types defined in XSD.

We do have test cases (test set xs-error - some 49 tests). Some of them might be incorrect, and there might be tests that could be usefully added, but tests exist and in some cases there has been a lot of discussion. 

You seem to be trying to push for a complete change in the way xs:error support was introduced in XQuery 3.0. There is absolutely no need for that.
Comment 11 Michael Kay 2015-10-06 10:06:27 UTC
In response to comment #9:

(a) xs:error(1) instance of xs:error can either raise FORG0001 or return false

Yes, I would agree. It can return false on the theory that "If X can be evaluated without error then (X instance of xs:error) is false, so there is no need to evaluate X to know the answer", and it can return FORG0001 as a consequence of the fact that evaluating xs:error(1) returns FORG0001.

(b) declare function local:my-error($qname) as xs:error {fn:error($qname, "boo")};

"might either raise the [error?] from fn:error() or XPTY0004."

I don't think the function declaration on its own is in any way erroneous. I agree that a call on this function could either fail with $qname or with XPTY0004.

(c) declare variable $x as xs:error := fn:error();$x

and

let $x as xs:error := fn:error() return $x

might raise FOER0000 or XPTY0004

I agree.
Comment 12 Michael Kay 2015-10-06 10:22:48 UTC
In response to the last part of comment #8:

As a SequenceType, xs:error is always an exception. A SequenceType is used to describe the type of an XQuery 3.1 value, and xs:error can never be the type of an XQuery 3.1 value.

This simply isn't true. xs:error is a type. Saying that "a type is an exception" doesn't make sense. We don't even have a concept of "an exception". A SequenceType does not "describe the type of a value", it describes the type of a set of XDM values, and empty sets are perfectly legitimate beasts. 

It's true that xs:error can never be the type of an XQuery 3.1 value. But it's a legal type, just as () is a legal value. It's a singularity in the type system, just as "item()*" is, so it has some special implications: just as "X instance of item()*" is always true, so "X instance of xs:error" is always false. That doesn't make either expression useless, any more than the integer 0 or the string "" is useless.
Comment 13 Jonathan Robie 2015-10-06 10:28:09 UTC
(In reply to Michael Kay from comment #12)

Can you give me an example of an expression where using xs:error as a SequenceType is useful?
Comment 14 Jonathan Robie 2015-10-06 10:37:27 UTC
(In reply to Michael Kay from comment #12)
> A SequenceType does not "describe the type of a value", it
> describes the type of a set of XDM values, and empty sets are perfectly
> legitimate beasts. 

xs:error does not describe the type of an empty set. It does not describe a type that can occur in an XDM value.  It describes the type of something that a query can never encounter.

> That doesn't make either expression useless, any more than the integer 0 or the string "" is useless.

The integer 0 and the string "" are types of XDM values that can occur.  xs:error is not.

The purpose of SequenceType is to describe the types of sets of XDM values.  When is it useful to have a SequenceType that describes the type of something that can never exist?  Can you give me an example?
Comment 15 Michael Kay 2015-10-06 11:03:21 UTC
At present types aren't first-class values so they are always hard-coded. But in XSLT, we now have the ability to manipulate the stylesheet at compile time through static expressions so you can write something like

<xsl:param name="x" _as="$input-type-x"/>
<xsl:param name="y" _as="$input-type-y"/>

where $input-type-x and $input-type-y are static (compile-time) variables containing the name of a type. You can do something similar in XQuery if you generate XQuery code. Once you have this capability, you very quickly run up against the requirement to say "in these circumstances, $y is not relevant and must not be supplied". That is, the set of permitted values of $y is empty; it's convenient to have a name for this, and xs:error serves the purpose.

Of course, if xs:error didn't exist, then there are plenty of other ways the user could devise a type that doesn't have any valid instances. For example, you can restrict xs:string with the facets length=4 and pattern="z". But XSD chose to give us a named built-in type for this purpose, so it's silly to make users invent their own. But a user-defined type with no instances would behave exactly like a built-in type with no instances: we're not doing anything strange here.

(We had all these discussions when we introduced xs:error to XPath 3.0. I'm really not sure why we are having them again now).
Comment 16 Jonathan Robie 2015-10-06 22:17:17 UTC
I have applied today's changes (see https://www.w3.org/XML/Group/qtspecs/specifications/xquery-31/html/xquery-31.html#id-xs-error), but I'm not sure whether I captured the correct error behavior in this bullet point:

> declare function f($arg as xs:error) {...}; is a valid function declaration, 
> but it always raises a type error [err:XPTY0004] or a dynamic error > [err:XPDY0050] if the function is called.
Comment 17 Michael Kay 2015-10-06 22:40:55 UTC
The constructor function xs:error($x) is actually equivalent to "cast $x as xs:error?", not to "cast $x as xs:error".

For function calls, I would say:

declare function local:f($arg as xs:error) {...} is a valid function declaration, but a function call local:f($x) will always fail (statically or dynamically) with a type error XPTY0004.

Note: in all the above examples, it is assumed that evaluation of $x does not fail. In some cases, the "Errors and Optimization" rules ensure that $x does not need to be evaluated (because the result does not depend on its value), which means that errors in evaluating $x may be masked.

Note to editor: some of these examples apply to both XPath and XQuery, some are XQuery-only.

During the call Tim suggested (to me) an interesting use case. Suppose we have a function

process-invalid-chars($x as xs:string, $f as function($s as xs:string) as xs:error)) as xs:string

which processes the supplied string $x by calling the supplied function $f to handle any character that is deemed invalid.

$f acts here as a kind of error handler. It cannot return a result, because no result will ever satisfy the required return type xs:error; so it is constrained to throw a dynamic error. But it can choose what dynamic error to throw.
Comment 18 Jonathan Robie 2015-10-08 20:18:43 UTC
(In reply to Michael Kay from comment #17)
> The constructor function xs:error($x) is actually equivalent to "cast $x as
> xs:error?", not to "cast $x as xs:error".

Then the text is wrong too, isn't it?  Shouldn't cast( () ) return the empty sequence, without raising an error?

> For function calls, I would say:
> 
> declare function local:f($arg as xs:error) {...} is a valid function
> declaration, but a function call local:f($x) will always fail (statically or
> dynamically) with a type error XPTY0004.
> 
> Note: in all the above examples, it is assumed that evaluation of $x does
> not fail. In some cases, the "Errors and Optimization" rules ensure that $x
> does not need to be evaluated (because the result does not depend on its
> value), which means that errors in evaluating $x may be masked.

Nice.

> Note to editor: some of these examples apply to both XPath and XQuery, some
> are XQuery-only.

Thanks.
 
> During the call Tim suggested (to me) an interesting use case. Suppose we
> have a function
> 
> process-invalid-chars($x as xs:string, $f as function($s as xs:string) as
> xs:error)) as xs:string
> 
> which processes the supplied string $x by calling the supplied function $f
> to handle any character that is deemed invalid.
> 
> $f acts here as a kind of error handler. It cannot return a result, because
> no result will ever satisfy the required return type xs:error; so it is
> constrained to throw a dynamic error. But it can choose what dynamic error
> to throw.

Yes, that's the best use case I've heard.
Comment 19 Jonathan Robie 2015-10-08 21:21:06 UTC
I just modified it as follows. I'm leaving this open until our next meeting.

Note:

Even though it cannot occur in an instance, xs:error is a valid type name in a sequence type. Although the practical uses of xs:error as a sequence type are limited, but they do exist. For instance, an error handling function that always raises a dynamic error never returns a value, so xs:error is a good choice for the return type of the function.

The semantics of xs:error are well-defined as a consequence of the fact that xs:error is defined as a union type with no member types. For example:

    $x instance of xs:error always returns false, regardless of the value of $x.

    $x cast as xs:error fails dynamically with error FORG0001, regardless of the value of $x.

    $x cast as xs:error? raises a dynamic error FORG0001 if exists($x), evaluates to the empty sequence if empty($x).

    xs:error($x) has the same semantics as $x cast as xs:error? (see the previous bullet point)

    $x castable as xs:error evaluates to false, regardless of the value of $x.

    $x treat as xs:error raises a dynamic error [err:XPDY0050] if evaluated, regardless of the value of $x. It never fails statically.

    let $x as xs:error := 1 return 2 raises a type error [err:XPTY0004], which can be raised statically or dynamically, and need not be raised if the variable $x is never evaluated by the query processor.

    declare function ns:f($arg as xs:error) {...}; is a valid function declaration, but it always raises a type error [err:XPTY0004] if the function is called.

All of the above examples assume that $x is actually evaluated. If the result of the query does not depend on the value of $x. the rules specified in 2.3.4 Errors and Optimization permit an implementation to avoid evaluating $x and thus to avoid raising an error.