Project

Profile

Help

Bug #5193

closed

$nodes => fold-left((), function($all, $this) {$all, $this except $all}) call gives error "Supplied value element <a> does not match required type xs:error in dynamic call of anonymous function (arity 2)"

Added by Martin Honnen 6 months ago. Updated about 1 month ago.

Status:
Closed
Priority:
Normal
Assignee:
Category:
XPath Conformance
Sprint/Milestone:
Start date:
2021-12-29
Due date:
% Done:

100%

Estimated time:
Applies to JS Branch:
2
Fix Committed on JS Branch:
2
Fixed in JS Release:
SEF Generated with:
Platforms:
Company:
-
Contact person:
-
Additional contact persons:
-

Description

In the "aftermath" of a lively discussion of node identity on the XSL list I tried to run some code through Saxon-JS 2.3 and found an odd error message when using the arrow operator and fold-left, when I run

xslt3 -xp:"let $xml := parse-xml('<t><a/><b/><c/></t>'), $nodes := ($xml/*/a, $xml/*/c, $xml/*/b, $xml/*/b) return $nodes => fold-left((), function($all, $this) {$all, $this except $all})"

I get an error

Error XPTY0004
    Supplied value element <a> does not match required type xs:error in dynamic call of anonymous function (arity 2)

The plain use of fold-left without the arrow operator, as in

xslt3 -xp:"let $xml := parse-xml('<t><a/><b/><c/></t>'), $nodes := ($xml/*/a, $xml/*/c, $xml/*/b, $xml/*/b) return fold-left($nodes, (), function($all, $this) {$all, $this except $all})"

works fine.

Actions #1

Updated by Michael Kay 4 months ago

Looking at the SEF trees resulting from the XPath parsing of the two expressions, the only significant difference is that with explicit function call, the inferred type of the second argument of the anonymous inline function is 1E (a single element node, element(*)) whereas with the arrow operator the inferred type is 1NE u[NE u[NE u[NE nQ{}a,NE nQ{}c],NE nQ{}b],NE nQ{}b], that is a union of elements a, c, b, and b.

Although this type inference appears to be correct, it's surprisingly ambitious. At some subsequent stage it seems to have been replaced by xs:empty (the type with no instances), and after this incorrect substitution, the call supplying element a is inevitably rejected.

It's rather suprising (a) that the type inference should be so ambitious, and (b) that it should differ between the two ways of writing the function call. It's less surprising (although still of concern) that things then go wrong subsequently.

Actions #2

Updated by Michael Kay 4 months ago

Looking at the parseFast / parseFunctionCall() code, which handles the lexical parsing of both conventional function calls and arrow expressions, mixed in with it is some special-case type inferencing logic for specific higher order functions (fold-left, fold-right, filter, for-each). I've no idea why this is needed and this certainly doesn't seem to be the right place for it.

But apart from that, it contains a simple bug: the loop at line 3129 is only processing the explicit arguments (within the parentheses), it's not processing the implicit argument that precedes the arrow operator

Actions #3

Updated by Michael Kay 4 months ago

The code in parseFunctionCall (which treats well-known higher-order functions specially) is setting the expected types of the inline function argument (if there is one!) in an array inlineFunctionArgTypes which is used while parsing the inline function to supply defaults for the parameter types. But this is hopelessly flawed because inlineFunctionArgTypes is a parser-level variable and there's no provision for inline functions being nested.

My suspicion is that the whole mechanism could come out without problems; after all, we have to handle dynamic calls on these functions where nothing is known in advance.

I've experimented with disabling the mechanism, and it seems to have no adverse effect on QT3 results.

If we do want to be smarter about the static type-checking of these cases I think there are two proper ways it could be done. (a) we could do the type refinement as a separate pass over the expression tree, as SaxonJ does, or (b) we could pass expectedType as an additional argument through the recursive parseXXX() function calls. But the benefits are questionable.

I think I'm going to take this code out.

Actions #4

Updated by Michael Kay 4 months ago

  • Status changed from New to Resolved
  • Fix Committed on JS Branch Trunk added

Fix committed to parseFast.parseFunctionCall().

Actions #5

Updated by Debbie Lockett 3 months ago

  • Assignee set to Michael Kay
  • Fix Committed on JS Branch 2 added
  • Fix Committed on JS Branch deleted (Trunk)
Actions #6

Updated by Debbie Lockett about 1 month ago

  • Status changed from Resolved to Closed
  • % Done changed from 0 to 100
  • Fixed in JS Release set to SaxonJS 2.4

Bug fix applied in the SaxonJS 2.4 maintenance release.

Please register to edit this issue

Also available in: Atom PDF Tracking page