Skip to content

Commit

Permalink
[FIX] Recursive function items: Typing. Closes #2314
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristianGruen committed Feb 6, 2025
1 parent 762ca71 commit 5434b76
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 38 deletions.
34 changes: 2 additions & 32 deletions basex-core/src/main/java/org/basex/query/QueryContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,9 @@ public final class QueryContext extends Job implements Closeable {
public boolean tco;

/** Function for the next tail call. */
private XQFunction tcFunc;
public XQFunction tcFunc;
/** Arguments for the next tail call. */
private Value[] tcArgs;
public Value[] tcArgs;
/** Counter for variable IDs. */
public int varIDs;

Expand Down Expand Up @@ -544,36 +544,6 @@ public void set(final Var var, final Value value) throws QueryException {
stack.set(var, value, this);
}

/**
* Registers a tail-called function and its arguments to this query context.
* @param fn function to call
* @param args arguments to pass to {@code fn}
*/
public void registerTailCall(final XQFunction fn, final Value[] args) {
tcFunc = fn;
tcArgs = args;
}

/**
* Returns and clears the currently registered tail-call function.
* @return function to call if present, {@code null} otherwise
*/
public XQFunction pollTailCall() {
final XQFunction fn = tcFunc;
tcFunc = null;
return fn;
}

/**
* Returns and clears registered arguments of a tail-called function.
* @return argument values if a tail call was registered, {@code null} otherwise
*/
public Value[] pollTailArgs() {
final Value[] args = tcArgs;
tcArgs = null;
return args;
}

/**
* Initializes the static date and time context of a query if not done yet.
* @return self reference
Expand Down
3 changes: 2 additions & 1 deletion basex-core/src/main/java/org/basex/query/expr/TypeCheck.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ public Value value(final QueryContext qc) throws QueryException {
if(!st.occ.check(value.size())) throw typeError(value, st, info);
return value;
}
return st.coerce(value, null, qc, null, info);
// ignore type of result returned by tail call function
return qc.tcFunc != null ? value : st.coerce(value, null, qc, null, info);
}

@Override
Expand Down
14 changes: 9 additions & 5 deletions basex-core/src/main/java/org/basex/query/func/XQFunction.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@ default Value invoke(final QueryContext qc, final InputInfo info, final Value...
while(true) {
qc.checkStop();
final Value value = fn.invokeInternal(qc, info, values);
fn = qc.pollTailCall();
if(fn == null) return value;
values = qc.pollTailArgs();
if(qc.tcFunc == null) return value;
// call registered tail call function
fn = qc.tcFunc;
qc.tcFunc = null;
values = qc.tcArgs;
qc.tcArgs = null;
qc.stack.reuseFrame(fn.stackFrameSize());
}
} catch(final QueryException ex) {
Expand All @@ -58,8 +61,9 @@ default Value invokeTail(final QueryContext qc, final InputInfo info, final Valu
qc.checkStop();
final int size = stackFrameSize();
if(qc.tco && qc.stack.tco(size)) {
// too many tail calls on the stack, eliminate them
qc.registerTailCall(this, args);
// stack is full: register tail call function
qc.tcFunc = this;
qc.tcArgs = args;
return Empty.VALUE;
}

Expand Down
12 changes: 12 additions & 0 deletions basex-core/src/test/java/org/basex/query/ast/RewritingsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3341,4 +3341,16 @@ private static String gh1852(final String query) {
execute(new Add("4.xml", "<d/>"));
query("db:get('" + NAME + "1'), db:get('" + NAME + "2')", "<a/>\n<b/>\n<c/>\n<d/>");
}

/** Recursive function items: Typing. */
@Test public void gh2314() {
query("declare function local:f($p) as xs:integer {"
+ " if($p < 100000) then local:f($p + 1) else $p"
+ "};"
+ "local:f(1)", 100000);
query("let $f := fn($p, $self) as xs:integer {"
+ " if($p < 100000) then $self($p + 1, $self) else $p"
+ "}"
+ "return $f(1, $f)", 100000);
}
}

0 comments on commit 5434b76

Please sign in to comment.