Attached are 3 versions of naive reverse that give different results with and without wait/2, and also different results depending on whether they are compiled or dynamically loaded.
These work with SWI-Prolog (outputs included) but fail in three different ways on XSB:
- sometimes throwing an error (calling a variable)
- wrong answer when compiled without the mytrue/1 predicate
- a different wrong answer when loaded dynamically
The theory (which is mentioned at the end of constraintLib.P) is that the compiler's optimization is removing a "local" variable. However, this seems contradicted by the observation that load_dyn gives a different result.
Also note that not all the tracebacks are the same -- in some cases, the t_append and t_reverse predicates show up and in some they don't.
[Minor note: there seems to be a line missing from the error description in constraintLib]
Attached are the programs and a lightly edited output (removing some of the banner and trace output).
This comment from David Warren in a private email about a year ago seems related. The obvious question is how to make sure that this problem doesn't occur (adding the mytrue(_) call was insufficient, presumably because there are implicit calls to =/2 in the head). I'll post something more after I've tried a few things out.
[The problem I was discussing: in a unit test, an attvar was unified, followed by a ==/ test, and the unification results didn't show up; in this case, it was easy to add a mytrue(_) call after the unification.]
[actually, no need to even pass an arg; calling any predicate seems to work]
However, this little trick could easily be optimized away by the compiler (in fact, I'm surprised it isn't), so there should be some kind of builtin to force unification handlers to be called.
--- (from David) ---
The problem is how/when XSB calls the attributed-variable handler. Unification of attributed variables posts an interrupt which then gets handled (i.e. the handler is called) at the next predicate call (call xwam instruction) or return from the clause (proceed instruction). In this case, if you call the writeln after the equality, the handler is called then and the variables are processed and the == tests succeed. In the case in which the == immediately follows the =, the handler has not been called (it will be called at the return), and the = and == are in-lined and so there is no call instruction and the handler has not been called, and so the == fails.
It’s not immediately clear to me how to fix this bug, since it would slow down a lot of programs to check for interrupts after every =/2 (which would eliminate a number of optimizations.)
A work-around would be to add a call to, say, mytrue, with a definition of:
mytrue.
right after the =/2 predicate. (true doesn’t work since it is optimized away.)
(Or maybe you’d want to call it check_for_interrupts.)
We do have an xwam instruction, check_interrupts, that just checks for interrupts without actually making a call, but we don’t have a way to generate it explicitly. It would be only a minor improvement over the above suggestion, in any case.
Attached is a new version of test_with_mytrue.pl (except I've renamed mytrue(_) to call_attvar_handlers). It's not clear to me why this works with compiler option 'spec_repr',
Anyway, it works in compiled mode but fails with load_dyn. Any suggestions about how to make it work with load_dyn?
I looked at the generated code, and there is a significant difference when call_attvar_handlers is added.
One question occurs to me: are the attvar handler interrupts processed when an "execute" instruction occurs, or only when a "call" or "proceed" happens? Maybe changing XSB to call the handlers on an "execute" would fix this.
Perhaps there is something similar that needs to be added for executing dynamic predicates?
(I haven't looked into why I sometimes get a wrong result, but I'm guessing it's because a when/2 didn't get executed at all ... I thought that there's a predicate for finding/displaying left-over attvars, but I couldn't find it in the manual.)
Interrupts are currently also handled in the WAM instruction execute.
We could add a handle_interrupts/0 predicate (in machine or standard) which could currently be defined with a single fact. This might ease Peter's concern about a user-defined such thing being optimized away.
I don't see an issue with dynamic predicates. Every body subgoal is invoked by a call or execute. There are no "inlined" subgoals in dynamic code.