Project

Profile

Help

Bug #6518

open

Different handling of "xs:boolean" result from function and local "false()"?

Added by Uwe Lagler about 1 month ago. Updated about 1 month ago.

Status:
New
Priority:
Normal
Assignee:
Category:
XSLT conformance
Sprint/Milestone:
-
Start date:
2024-08-25
Due date:
% Done:

0%

Estimated time:
Legacy ID:
Applies to branch:
Fix Committed on Branch:
Fixed in Maintenance Release:
Platforms:
Java

Description

I am not sure what causes the following problem: item types of a function's params are validated even if the function is not invoked. But this only happens if the conditional test is using the xs:boolean result of a function.

Situation A) The function "my:process()" will cause the error "XPTY0004 The required item type of the first argument of my:handle-map() is map(xs:string, item())", but why is the function my:handle-map() invoked at all?

<xsl:function name="my:is-map" visibility="public" as="xs:boolean">
    <xsl:param name="item"/>
    <xsl:sequence select="$item instance of map(xs:string, item())"/>
</xsl:function>

<xsl:function name="my:handle-map" visibility="public">
    <xsl:param name="map" as="map(xs:string, item())"/>
    <map>
        <size><xsl:value-of select="map:size($map)"/></size>
    </map>
</xsl:function>

<xsl:function name="my:do-something" visibility="public">
    <xsl:param name="item"/>
    <no-map>Do something with other value than map.</no-map>
</xsl:function>

<xsl:function name="my:process" visibility="public">
    <xsl:sequence select="

        let $test1 := map{'a': 'b'},
            $test2 := true(),
            $value := $test2
        return

            if(my:is-map($value)) then(
                my:handle-map($value)
            ) else(
                my:do-something($value)
            )

    "/>
</xsl:function>

Situation B) The same conditional test is working as expected if it is done directly (without a function call), the function my:handle-map() is not invoked and no error is thrown:

<xsl:function name="my:process" visibility="public">
    <xsl:sequence select="

        let $test1 := map{'a': 'b'},
            $test2 := true(),
            $value := $test2
        return

            if($value instance of map(xs:string, item())) then(
                my:handle-map($value)
            ) else(
                my:do-something($value)
            )

    "/>
</xsl:function>

Is there a difference between "false()" and the xs:boolean result from a function? I could be wrong but I would expect the xs:boolean result of a function can be used as it is.


Files

test.xsl (2.05 KB) test.xsl Uwe Lagler, 2024-08-25 16:23
Actions #1

Updated by Uwe Lagler about 1 month ago

stylesheet file attached

Actions #2

Updated by Michael Kay about 1 month ago

  • Project changed from Non-Conformances to Saxon
  • Category set to XSLT conformance
  • Assignee set to Michael Kay
  • Priority changed from Low to Normal
  • Found in version deleted (Saxon EE 10.3)

It's a good idea to start here with the spec. XSLT section 2.14 explains it like this:

[Definition: Certain errors are classified as type errors. A type error occurs when the value supplied as input to an operation is of the wrong type for that operation, for example when an integer is supplied to an operation that expects a node.] If a type error occurs in an instruction that is actually evaluated, then it must be signaled in the same way as a dynamic error. Alternatively, an implementation may signal a type error during the analysis phase in the same way as a static error, even if it occurs in part of the stylesheet that is never evaluated, provided it can establish that execution of a particular construct would never succeed.

I often refer to this as "optimistic static typing" - (a) do as much type checking statically as you can, (b) if you find a construct that is bound to fail if executed, report it immediately, (c) if you find a construct that might or might not succeed, generate code to do the type checking at run-time. As the spec says, this may result in type errors being reported in code that is never executed (just as it would be in a strongly typed language). The principle, of course, is that it's much better to know about errors in your code from the compiler than to only discover them when you run tests with suitable test data, or worse still, in production.

The static type checking that is done isn't defined by the spec, processors are allowed to be as smart as they like in the inferencing. There are some cases where Saxon let's you get away with something that's technically a type error, for example when an xsl:choose has no xsl:otherwise clause, on the basis that it's quite common to write an xsl:choose in which the xsl:when clauses cover all the possibilities. But it can sometimes happen that the error is in a path that genuinely and by design will never be executed, in which case the static type error can be a bit irritating.

Let's look at how these principles apply in your case.

In the first example, Saxon has made a static type inference that the type of $test2 is xs:boolean, and from that it has worked out that the type of $value is xs:boolean, so it has worked out statically that you're supplying a boolean to the function my:handle-map which expects a map, That function call can never succeed, so it's legitimate to report a static type error even if the function call is never executed. It would also be legitimate, incidentally, to execute the function call at compile time, since its result has no dependencies on any run-time information. For example, if you wrote abs(-2) then Saxon would reduce that to the number 2 during compilation (a process that compiler-writers call "constant folding").

In the second example, Saxon could have legitimately raised a type error here, but it has chosen not to. Instead it has noticed that the expression $value instance of map(xs:string, item()) can be evaluated statically to false, and since the if condition is false, the then branch can be discarded and analysed no further.

Going back to the first example, Saxon could have done the same thing: that is, evaluated my:is-my-map() to false statically. In fact, I think that if it hadn't found the type error first, it would probably have done so. So there's an element of unpredictability here: the effect depends on the order in which different expressions are type-checked and optimized.

The bottom line is that "optimistic static type checking" is something that is permitted but not required by the spec. Saxon tries to do it as extensively as possible, both to give early diagnostics for problems and to eliminate the cost of unnecessary run-time type checking. But there's no non-conformance if it isn't done under any particular circumstances.

Actions #3

Updated by Uwe Lagler about 1 month ago

Dear Michael,

thank you very much for your quick and helpful support! Thanks to your insights, I now better understand some cases that I previously classified as "anomalies". I am now also able to deal with "optimistic static typing" specifically, keeping me from spending hours to find errors where there are no errors :-)

Best regards from Vienna, Uwe

P.S.: I really appreciate the work of you and your team, also because you are driving the further development of XSLT. Thanks!

Please register to edit this issue

Also available in: Atom PDF