Bug #6040
closedInvalid warning displayed when map:contains() is used
100%
Description
If I transform the following XML with the XSL I obtain an warning. I think that the warning that should not be presented because in the template I match the "element" from the "relevant" parent, and in the XML I have an "irrelevant" parent. Probably an evaluation is made in this case for optimization, but the warning should not be displayed.
I tested with Saxon 11.4
An error occurred matching pattern {element(Q{}element)[Q{http://www.w3.org/2005/xpath-functions/map}contains(exactly-one(($some-map) treat as map(*)), atomizeSingleton(attribute::attribute(Q{}relevant-attribute)))]}: An empty sequence is not allowed as the second argument of map:contains()
<root>
<irrelevant>
<element/>
</irrelevant>
</root>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0" xmlns:map="http://www.w3.org/2005/xpath-functions/map">
<xsl:param name="some-map" select="map {}"/>
<xsl:template match="/*/relevant/element[map:contains($some-map, @relevant-attribute)]">
</xsl:template>
</xsl:stylesheet>
Updated by Michael Kay over 1 year ago
When we find an element
, then to see if the pattern matches we have to test (a) that its parent is named relevant
, and (b) that the predicate matches. There's nothing in the spec that says which of these tests should be done first, and we try to make a decision about which approach is likely to perform better.
(When I say there's nothing in the spec, that's not actually correct. The notes in 5.5.3 and 5.5.4 of the XSLT 3.0 explicitly say that bottom-up evaluation of patterns is allowed, and might result in spurious errors.)
There's been discussion about this in the QT4 working group, and the consensus is that the processor should behave as if the predicates are only evaluated when the base expression selects something (moreover, it should be "as if" multiple predicates are evaluated from left to right). The "as if" means we can continue to optimise order of execution if we wish, but we should not raise an error if the "unoptimised" execution path wouldn't raise an error.
We haven't yet implemented these changes in all cases, especially not for pattern matching.
Updated by Michael Kay over 1 year ago
Probably as a consequence of the fix to bug #5867, I had to write a more tortuous pattern to trigger the warning:
<xsl:param name="some-map" select="map {}"/>
<xsl:template match="/*/irrelevant[f:expensive(.)]/element[map:contains($some-map, @relevant-attribute)]">
</xsl:template>
<xsl:function name="f:expensive">
<xsl:param name="n"/>
<xsl:sequence select="some $x in 1 to 10000000 satisfies count($n/@attribute) eq $x"/>
</xsl:function>
In this case the map:contains()
predicate is quite correctly evaluated before testing whether the f:expensive()
predicate is satisfied.
I'm going to look at whether we can suppress the warning in this situation.
Updated by Michael Kay over 1 year ago
First attempt failed. It's not that simple because AncestorQualifiedPattern basically reduces to (pattern0.matches() and pattern1.matches())
and the logic to output a warning message is in the operand patterns, recursively. If we want to output the message only when one operand pattern matches and the other throws an error, then we need to move the logic for deciding whether to output the warning to the upper level.
Updated by Michael Kay over 1 year ago
We're catching the error and issuing the warning at the level of BasePatternWithPredicate
when evaluation of the predicate fails; I think we need to let it bubble up and issue it only at the level of Mode.ruleMatches() - except that we then need to apply the same logic to other places that invoke pattern matching, such as xsl:number and xsl:for-each-group.
Pattern is an abstract class rather than an interface, so the logic can probably go into a new final method in this class, one that invokes the current matches() and does the error handling.
This appears to do the trick, at least for this test case. (But I haven't yet worked through all the changes needed.)
Updated by Michael Kay over 1 year ago
- Category set to Diagnostics
- Status changed from New to Resolved
- Assignee set to Michael Kay
- Priority changed from Low to Normal
- Applies to branch 12, trunk added
- Fix Committed on Branch 12, trunk added
- Platforms .NET, Java added
OK, this seems to work. However, I'm a bit wary that it's the kind of change that could have unforeseen side-effects, so I'm not going to retrofit it to earlier branches: it's 12.x and main branches only, to limit any damage.
Updated by O'Neil Delpratt over 1 year ago
- Status changed from Resolved to Closed
- % Done changed from 0 to 100
- Fixed in Maintenance Release 12.3 added
Bug fix applied in the Saxon 12.3 maintenance release.
Please register to edit this issue