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 28833 - [XSLT30] Streamability of fn:current and varrefs should be roaming in higher-order operands
Summary: [XSLT30] Streamability of fn:current and varrefs should be roaming in higher-...
Status: CLOSED FIXED
Alias: None
Product: XPath / XQuery / XSLT
Classification: Unclassified
Component: XSLT 3.0 (show other bugs)
Version: Last Call drafts
Hardware: PC Windows NT
: P2 normal
Target Milestone: ---
Assignee: Michael Kay
QA Contact: Mailing list for public feedback on specs from XSL and XML Query WGs
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-06-21 14:32 UTC by Abel Braaksma
Modified: 2015-10-29 12:42 UTC (History)
0 users

See Also:


Attachments

Description Abel Braaksma 2015-06-21 14:32:54 UTC
Originally reported internally to the XSLWG here: https://lists.w3.org/Archives/Member/w3c-xsl-wg/2015Jun/0043.html (member only) 

We have, under GSR 2.d.i:

* If o is a higher-order operand of C, then roaming and free-ranging.

However, I know of at least two places where the position is higher-order, but the GSR do not apply, leading to a gap in the analysis:

1) Map expressions
2) Path expressions

Consider:

row/current()/cell

Here fn:current is in a higher-order position. It points to the current node which is disjoint from the <row> element. The expression requires free-ranging tree-walking to reach the <cell> element under fn:current, after <row> was visited.

Note that the issue does not arise in an inspection context:

row[current()]/cell

Perhaps a clearer example on how this can go horribly wrong is;

<xsl:apply-templates select="row/current()" />

At risk are, I think, the following:

1) variable references bound to a node
2) let $a := copy-of(b) return current()
3) function() { current() } (: or disallowed by function closure rules? :)
4) ForExpr, QuantifiedExpr are *not* at risk, they are described as having higher-order operands
Comment 1 Michael Kay 2015-06-25 17:06:32 UTC
I agree with the analysis; the question is how best to fix it.

Higher-order operands appear where any of the following applies:

(1) The context item for evaluation of O is different from the context item for evaluation of C.

(2) C is an instruction and O is a pattern (as with the from and count attributes of xsl:number, and the group-starting-with and group-ending-with attributes of xsl:for-each-group).

(3) C is an XPath for, some, or every expression and O is the expression in its return or satisfies clause.

(4) C is an inline function declaration and O is the expression in its body.

Case 1: For XPath expressions, the relevant cases are path expressions, bang expressions, and filter expressions.

For XSLT instructions, there are quite a few: analyze-string, copy[@select], for-each, for-each-group, iterate, merge, merge-key, xsl:sort, xsl:key(?)

Case 2: Applies only to for-each-group and xsl:number.

In for-each-group, neither of the two notes at the end of 19.8.4.19 discuss the patterns. They need to.

xsl:number uses the GSRs, so it is OK.

Case 3: The GSRs apply, so this case is OK.

Case 4: Section 9.8.8.15 deals with references to streaming parameters, but not with current().

There seem to be sufficiently many that a general statement is needed. Is it sufficient to say that current() and references to streaming parameters are roaming and free-ranging if used within a higher order operand of a construct whose focus-setting container is not grounded?
Comment 2 Abel Braaksma 2015-06-27 15:38:46 UTC
> Is it sufficient to say that current() and references to streaming 
> parameters are roaming and free-ranging if used within a higher order 
> operand of a construct whose focus-setting container is not grounded?

No, unfortunately not, consider a/copy-of(b)/current()/c with CP striding. The focus for the third segment can be a streaming node, but the expression was already consuming and moved on, so current()/c can no longer be evaluated.

I think that the main issue is with path and simple map expressions, where the StepExpr is not an AxisStep. I propose to solve this by adding rules for StepExpr as follows:

19.8.8.x Streamability of StepExpr

1) if the StepExpr is an AxisStep, the posture and sweep of the axis step, see 19.8.8.8.
2) if the StepExpr is a PostfixExpr E, the GSR applies, where the (fictive) operand is a higher-order operand (HOO) with usage transmission and the context posture is the posture of E.

With this rule added, the expression a/copy-of(b)/current()/c (assuming context posture Striding) is now assessed as follows:

1) it is a path expression, assess a/copy-of(b) first
2) result: consuming and grounded
3) assess (cons, gnd)/current()
4) the new rule says apply GSR for E with as HOO
5) E is current(), which is motionless and striding
6) apply GSR for motionless, striding with usage T
7) according to GSR this is potentially consuming
8) according to GSR 2.d.i (HOO rule) it is roaming and free-ranging

For the Simple Mapping Expressions, since the production exists of PathExpr, they will automatically come to the rules above, so nothing more to do there.

For Filter Expressions, these can only end up in being in HOO position inside the filter, which must be motionless, so there is no problem if fn:current is used inside the predicate as long as it is not consuming. If the P-part of the filter expression is a path expression, the new rules above kick in. Nothing to do there.

For variable references, they will now get to the GSR if part of a SimpleMapExpr or PathExpr and rule 2.d.i will prevent their usage in HOO positions.

For inline function declarations, I can't find it back in XP30, but I believe that the context item expression is disallowed inside it (unless bound to a variable), so I think the fn:current() function should be disallowed as well (similarly to it being disallowed in a stylesheet function). If it is bound to a variable, this is already disallowed, so it cannot happen.

For xsl:for-each-group, the GSR applies and the patterns are already considered higher-order operands, nothing to do there.

> There seem to be sufficiently many that a general statement is needed

We already have a general statement on higher-order operands and what they are. But we might expand on it and/or add some Notes or examples.
Comment 3 Abel Braaksma 2015-06-27 23:36:09 UTC
There may be an even simpler solution. Check the fn:current-group rules, which presently say, as a requirement to be non-roaming:

1.b. The path in the construct tree that connects C to the sequence constructor forming the body of F is such that no child construct is a higher-order operand of its parent

For fn:current we could rewrite this and say that:

Let the posture P be the  context posture for evaluation of the outermost containing XPath expression (that is, the context posture that would obtain if the entire XPath expression were replaced with ".")

Then, the first that applies:

1. If P is grounded then motionless and grounded;
2. If fn:current is used within a higher-order operand then roaming and free-ranging;
3. Otherwise, posture P and the sweep is motionless.

The same can be used for variable references.

We already have a statement on higher-order operands, but the part for path and simple mapping expressions is generic, it may be helpful to extend it with examples for "/" and "!" (see under 19 Streamability 13th para, 3rd bullet).
Comment 4 Michael Kay 2015-07-23 17:08:59 UTC
In email and telcon discussion we converged on consensus for the following proposal:

This suggests using posture=“climbing”, which doesn’t make much sense intuitively [1], but has the required behavior of allowing inspection but not allowing downwards selection. This suggests the following alternative rules:

1. Let E be the outermost containing XPath expression of the call to the current function.

2. If the context posture of E is grounded, then motionless and grounded.

3. If the path in the expression tree that connects the call on current to E (excluding E itself) contains an expression that is a higher-order operand of its parent expression, then motionless and climbing.

4. Otherwise, the sweep is motionless, and the posture is the context posture of E.

[1] There might be a rationale along the lines: if the stream position P at the time current() is evaluated is not the node C that current() returns, then it must be a descendant of C, in which case getting back to C is a climbing operation.
Comment 5 Michael Kay 2015-07-23 22:44:10 UTC
The changes for streamability of fn:current() have been applied.