Bug #4636


AssertionError compiling xsl:for-each-group with a higher-order-function

Added by Michael Kay almost 2 years ago. Updated over 1 year ago.

Start date:
Due date:
% Done:


Estimated time:
Legacy ID:
Applies to branch:
10, 9.9
Fix Committed on Branch:
10, 9.9
Fixed in Maintenance Release:


A compile-time assertion error occurs when compiling an xsl:for-each-group instruction with group-starting-with="*[$starts-function(.)], where $starts-function is an anonymous inline function.


test.xsl (2.94 KB) test.xsl Michael Kay, 2020-07-08 11:10
Actions #1

Updated by Michael Kay almost 2 years ago

A workaround is to change the group-starting-with attribute to


(The problem arises because Saxon doesn't know whether $starts.function() might return a numeric value, and it therefore has to cater for the possibility; patterns involving a predicate where the predicate value is potentially numeric are much more complex to deal with.)

Actions #2

Updated by Michael Kay almost 2 years ago

What's happing is that optimization of the function els:wrap-elements-starting-with-names attempts to inline the function els:wrap-elements-starting-with, and after inlining, the reoptimization of the xsl:for-each-group function now knows more about the supplied group-starting-with function, in particular it knows that it will return a boolean. So it tries to take advantage of this knowledge. In particular, this enables it to use a BasePatternWithPredicate rather than a GeneralPositionalPattern, which is a lot more efficient to evaluate because it doesn't need to know the sibling position of the node being tested.

Where this goes wrong is that the group-starting-with operand of xsl:for-each-group is labelled as a "constrained class" operand, which limits the ability to substitute an expression of a different class during optimization.

The intent of marking it as constrained is to guarantee that the output of a rewrite will always be a Pattern (which is implemented as a subclass of Expression; but it's not the case that any Expression can be used in this context). However, the constraint actually being enforced is that the expression produced by the optimizer must be the same class as the original - that is, the same kind of Pattern. The constraint is too restrictive.

Actions #3

Updated by Michael Kay almost 2 years ago

  • Status changed from New to Resolved
  • Fix Committed on Branch 10, 9.9 added

I have replaced the CONSTRAINED_CLASS property of OperandRole with a property that describes an explicit constraint on the expression participating in that role; for patterns the constraint is that the expression must be a pattern, it is no longer required to be the same kind of pattern as was used before the rewrite.

Actions #4

Updated by O'Neil Delpratt almost 2 years ago

  • % Done changed from 0 to 100
  • Fixed in Maintenance Release 10.2 added

Bug fix applied in the Saxon 10.2 maintenance release.

Actions #5

Updated by O'Neil Delpratt over 1 year ago

  • Status changed from Resolved to Closed
  • Fixed in Maintenance Release added

Bug fix applied on the Saxon maintenance release.

Please register to edit this issue

Also available in: Atom PDF