Bug #5000

LoopLifter NPE after updating to Saxon 10.5 EE

Added by Martin Latterner 5 months ago. Updated about 1 month ago.

Start date:
Due date:
% Done:


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


We have recently upgraded from EE to 10.5 EE. We're experiencing now a new NullPointerException:

        at net.sf.saxon.expr.parser.LoopLifter.markDependencies(

The problem goes away when disabling the loop lifting optimization, via -opt:-l.

The origin of the exception is with some very old legacy code. Please see the attach sample, I have been running:

/usr/bin/java -jar /path/to/10.5/saxon-ee-10.5.jar -xsl:stylesheet.xsl -s:in.xml

The problem is with legacy.xsl which is only included. Note, when I comment lns 26-32 and 211-219, then the problem also goes away. I don't understand why.

Since this is very old code, I reckon the NPE is caused by some defect there, however I just can't figure it out. I think we should be able to run v10.5 without disabling the looplift optimization. (3.42 KB) Martin Latterner, 2021-05-25 17:39


#1 Updated by Michael Kay 5 months ago

An exception at this point in the LoopLifter always indicates a corruption in the expression tree, specifically where an expression P has a child expression C whose parent is not P. The difficulty is tracing back to where the corruption occurred. On this occasion the relevant expression is from line 26 of legacy.xml. P is the expression (data($space-count)) eq 0, C is the subexpression data($space-count), and the parent expression of C is convertUntyped(data($space-count)).

This suggests that we stripped off the "convertUntyped" because we discovered during type-checking that it wasn't needed (presumably when we established that $space-count was an untyped document node), and failed to reset the parent pointers when doing so. Alternatively it can happen that a subexpression is incorrectly referenced as a child of more than one parent, in which case an error is reported when it is reached by one of those routes. This could happen as a result of extraction of common subexpressions, because data($space-count) is computed rather frequently.

The UntypedSequenceConverter, it appears, is created while attempt to turn the xsl:choose into a switch expression, and the line of code that does this is marked as having been added in response to bug 4738 (therefore, quite recently).

The problem here is that the xsl:choose turns out not to satisfy all the conditions for conversion to a switch expression, and so the attempt is abandoned, but by then we have made anticipatory changes. We should be checking that all the conditions are satisfied before we do anything, because rolling back changes is always difficult and error-prone.

#2 Updated by Michael Kay 5 months ago

The problem appears to be solved by copying the data before changing it, that is change the code at OptimizerEE.trySwitch() (line 1669) from

        if (((ComparisonExpression) condition).getAtomicComparer() instanceof UntypedNumericComparer) {
            // Bug 4738
            lhs = new UntypedSequenceConverter(lhs, BuiltInAtomicType.DOUBLE);


        if (((ComparisonExpression) condition).getAtomicComparer() instanceof UntypedNumericComparer) {
            // Bug 4738, 5000
            lhs = new UntypedSequenceConverter(lhs.copy(new RebindingMap()), BuiltInAtomicType.DOUBLE);

#3 Updated by Michael Kay 5 months ago

  • Category set to Internals
  • Status changed from New to Resolved
  • Assignee set to Michael Kay
  • Priority changed from Low to Normal
  • Applies to branch trunk added
  • Fix Committed on Branch 10, trunk added

#4 Updated by O'Neil Delpratt about 1 month ago

  • Status changed from Resolved to Closed
  • % Done changed from 0 to 100
  • Fixed in Maintenance Release 10.6 added

Bug fix applied in the Saxon 10.6 maintenance release

Please register to edit this issue

Also available in: Atom PDF