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 30064 - [xslt30ts] Test case function-5006
Summary: [xslt30ts] Test case function-5006
Alias: None
Product: XPath / XQuery / XSLT
Classification: Unclassified
Component: XSLT 3.0 Test Suite (show other bugs)
Version: Candidate Recommendation
Hardware: PC All
: P2 normal
Target Milestone: ---
Assignee: Abel Braaksma
QA Contact: Mailing list for public feedback on specs from XSL and XML Query WGs
Depends on:
Reported: 2017-02-15 20:56 UTC by Michael Kay
Modified: 2017-08-18 19:52 UTC (History)
1 user (show)

See Also:


Description Michael Kay 2017-02-15 20:56:12 UTC
I'm struggling a bit with this test, which reads:

    <xsl:template match="/">
          <xsl:for-each select="outermost(doc/sf:deep-descent(section[1]))/text()" >
                <xsl:copy-of select="self::text()" />
    <xsl:function name="sf:deep-descent" streamability="deep-descent" as="node()*">
        <xsl:param name="node" as="node()"/>
        <xsl:sequence select="$node//section//p" />

First observation is that if we inline the function, we get 

    <xsl:for-each select="outermost(doc/section[1]//section//p)/text()" >
        <xsl:copy-of select="self::text()" />

and this isn't streamable (you can't use the descendant axis from crawling posture, unless you're covered by the exemption for scanning expressions, which doesn't apply here because doc/section[1]//section//p is not a motionless pattern, because of the numeric predicate).

It would seem a bit surprising if the function call should be streamable when the result of inlining it isn't.

The rules for deep-descent functions say that the function body must be crawling. That will only be the case if $node//section//p is a scanning expression, which requires it to be equivalent to a motionless pattern. But a pattern cannot be a motionless pattern if it contains a RootedPath, which this does (the variable reference at the start).

This is somewhat legalistic wriggling: I can't see why the function can't be implemented using the scanning approach. But I don't think it passes the current rules.

So I don't think this test passes the streamability rules.
Comment 1 Abel Braaksma 2017-03-02 16:46:30 UTC
I am not sure this should be considered a rooted expression when we assess it as a scanning expression (but see my own analysis).

I think this is an omission. Esp. since here $node has the role of . (context expr) and I see the same expression going wrong when written as ".//section//p" (with CP striding). I doubt that was the intent here, but I have to admit my memory is rather fuzzy.

What I basically don't get if you have A/B/C and A is an expression with striding posture, but is not a pattern, that A/B/C will fail even if only B/C is a scanning expression:

/A//B  -> fails, it is a rooted expr, even if context is root
outermost(X//Y)//Z -> fails, even though outermost(X//Y) is striding
.//B[name() = 'foo'] -> fails, "." is not allowed in a pattern

I think some of these also fail because we have to rewrite X//Y for the analysis, leading invariably to a roaming expression (child axis upon crawling CP).

I think we need something in the text that says "in a RelativePathExpr A/B where A has striding posture and B is a scanning expression, the result is crawling/consuming".



I tried from a different angle for analysis, but with the current text (and the previous draft!) I come to similar conclusions as you, but with some trouble near the end.

We say in, first item:

1) First, the provisional posture is determined as follows: The provisional posture of the expression is the posture of the right-hand operand, assessed with a context posture and type set to the posture and type of the left-hand operand; and the provisional sweep is the wider of the sweeps of the two operands. 

We start this analysis with "$node" from "$node//section//p":

Step 1: $node is striding and motionless for deep-descent (

Then we analyze "//section" with CP from step 1, i.e. like [striding/motionless]/descendant-or-self::node()/section

Step 2a) "[CP]/descendant-or-self::node()" with CP=striding, gives crawling/consuming (

Step 2b) [CP]/section, with CP=crawling, gives roaming free/ranging

At this moment, section kicks in, as this is the "provisional posture". 

Now the rules are unclear, because the text does not say that we need to assess the expression as a whole. If we don't then we can consider "section//p" separately, but we would still have trouble with "//section", because it is rooted (and not because it is preceded by $node).
Comment 2 Abel Braaksma 2017-03-09 14:20:04 UTC
Rereading my own comment, you could conclude there are two things here:

1) it is possible to interpret the current rules liberal enough to make path expressions like .//x streaming

2) it is not clear that .//x (or $a//x) is not streamable

3) suggested fix is to allow path expressions .//A/B or $x//A, I believe the intent of our text and rules is to allow such expressions anyway

Wouldn't another definition of scanning expressions be that (draft definition, needs tuning): 

If each step is a StepExprP starting from posture grounded/striding/crawling, and if the whole expression is roaming, then the result-posture is crawling.

Such a definition would have the benefit of applying to many common situations currently incorrectly forbidden (I think), without requiring the expression to also be a rootless path, and hopefully removing the present ambiguities.
Comment 3 Michael Kay 2017-03-09 17:00:27 UTC
Let's first check exactly what the spec says. says (for deep-descent functions):

Rules for the function body: For the function to be guaranteed-streamable, the type-adjusted posture of the function body with respect to the declared return type must be crawling, and the type-adjusted sweep of the function body with respect to the declared return type must be motionless or consuming.

The body of the function is effectively


This is analyzed as


So we start with XX = $node/descendant-or-self::node() which is a RelativePathExpr. tells us:

First, the provisional posture is determined as follows: The provisional posture of the expression is the posture of the right-hand operand, assessed with a context posture and type set to the posture and type of the left-hand operand; and the provisional sweep is the wider of the sweeps of the two operands. tells us that the LH operand is striding and motionless. then tells us that the RH operand is crawling and consuming.

Now we consider XX/child::section where XX is the expression we've just analyzed. tells us this has a provisional posture of roaming.

So we reassess $node/descendant-or-self::node()/child::section to see whether it is a scanning expression. It conforms to the syntax of a pattern, but the pattern is not motionless, because it contains a rooted path.

So the body of the function is roaming, so it is not guaranteed streamable.

Now, I'm sure it is possible to devise a change to the rules that make it streamable. But I don't think we should do so. There is too much risk of getting it wrong, and not enough benefit. Implementations can provide this as a streamability extension if they wish.

There's then a question what we should do with the test. We could simply mark it as expecting XTSE3430, or we could drop the test, or we could somehow label it to say that it might be streamable if the processor supports streaming extensions.
Comment 4 Abel Braaksma 2017-03-23 15:51:27 UTC
I fixed the test by rewriting the test as follows, which yields the same result and keeps the same premise (overlapping nodes returned from a function).

However, it should be reviewed, I am not 100% sure it is correct. It does run with Exselt currently, though ;).
Comment 5 Michael Kay 2017-08-15 11:49:31 UTC
Reopening because I'm not sure this is fixed.

In the meantime I fixed a trivial error, somehow the attribute 


had been dropped.
Comment 6 Michael Kay 2017-08-17 14:32:48 UTC
Closing again. I've come to the conclusion the test is now OK.
Comment 7 Abel Braaksma 2017-08-18 19:52:12 UTC