Did you know ... Search Documentation:
Title for pldoc(default)
Branch: development (switch to stable),
version to version

SWI-Prolog Changelog from version 10.1.19 to 10.1.20

[Oct 30 2026]

  • ENHANCED: the text report of profile/2 lists XPCE methods and keeps its columns aligned The report lists all nodes, as profile_data/1 does. The new multifile hook profile_node_label/2 names a node that is not a predicate. A label too wide for its column is elided in the middle, so the module or class and the name remain visible. The columns of the counts are as wide as their widest count among the rows shown, taking the extra width from the labels, so the report keeps its width.
  • FIXED: profiler: a call still running gave 2^64-1 redos A call that has not exited or failed when the profile is collected, such as profiler/2 itself, made exits+fails-calls negative. With ports enabled, profile_data/1 and the text report showed this as a huge redo count.
  • FIXED: profile_data/1 omitted the nodes of XPCE methods So the profiler GUI could show a method only as a callee: neither its own callees nor its node. $prof_procedure_data/8 raised a type error on such a node rather than leaving it to the XPCE profile type.
  • CLEANUP: Access cell tags only through pl-data.h Code outside pl-data.h no longer uses LMASK_BITS, the STG_* storage bits or storage(). pl-data.h provides named tagex() values (TAGEX_*), including the private markers of the compiler, read/1, tries and variant/2, constructors consCompound(), consAttVar(), consIndirect(), consTagged()/valTagged() for index cells, consAddr()/valAddr()/ addrDelta() for address cells and storage tests isGlobalPtr(), isIndirectHdr(), isLocalPtr(), isLocalRef() and isInlineInt(). No change to the encoding. This is step 2 of doc/tagging/top-tags.md.
  • FIXED: concurrent trie_symbol() could give a trie two atoms Each atom destroys the trie when reclaimed, so the second destroyed it again, typically at halt. The thread that loses the race to set the symbol detaches its atom from the trie.
  • FIXED: discarding the nodes of a compiled answer trie left T_DELAY pointing to a freed answer Simplification can make a conditional answer unconditional without recompiling the trie, so the trie becomes discardable while its clause still refers to the answer node. trie_discard_nodes() now keeps the nodes if the clause has a T_DELAY that refers to one.
  • FIXED: abolishing an imported predicate created a definition with share count 0 If another module imported the new definition, halting freed it while it was still referenced. lookupProcedure() and abolishProcedure() now use newDefinition(), which also clears the argument info.
  • FIXED: race destroying a variant table that another thread enumerates If a thread enumerated the variant table of another thread (e.g., using trie_gen/3 in an engine, as the IDG viewer does) while the owner abolished its tables, both threads could see the reference count drop to zero and empty the table, causing a crash. clear_variant_table() now holds a reference while deciding, so exactly one thread empties the table.
  • TEST: Skip thread and profiler tests in single-threaded builds Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
  • ENHANCED: Check that pack name and repository name match. Currently, the demanded match is case-insensitive.

[Oct 29 2026]

  • ENHANCED: profile/2 uses exact timing by default if this is cheap The new Prolog flag profile_exact (default auto) provides the default for the exact option of profile/2. With auto, exact timing is used if reading the clock is cheap and, for CPU time, the thread CPU clock is precise. In virtual machines that trap reading the TSC, exact profiling can be more than 20 times slower than sampling.
  • ADDED: profile/2 option exact(Bool) for exact wall and CPU time Rather than sampling, the profiler reads a clock each time execution moves to another node of the call tree. Time spent updating the call tree is measured and reported as accounting time. For CPU time, the thread's CPU time is read once per sample period and after each long interval, where the thread may have been off the CPU. The summary of profile_data/1 has the new keys clock and exact.
  • FIXED: profile/2 in one thread stopped the profiler of another thread reset_profiler/0 and profile/2 stopped the profiler of whatever thread was being profiled. Starting the profiler also re-armed the process' interval timer before checking that no other thread is being profiled. profile/2 now raises a permission error and leaves the other thread alone.
  • FIXED: show_profile/1 raised zero_divisor if no sample was taken A sampled profile of a goal that runs shorter than the sample period has no ticks. The textual report now shows 0% rather than raising an error.
  • FIXED: stop the profiler timer when the profiled thread exits If a thread exited while being profiled, the profiler was deactivated, but the interval timer kept firing and the signal handler stayed installed until the next profiler start.
  • FIXED: wall-time profiling crashed if SIGALRM arrived in a foreign thread SIGPROF and SIGALRM are process signals that may be delivered to any thread. The profiler signal handler now forwards them to the profiled thread. Before, a sample taken in a non-Prolog thread crashed in WallTime() and a sample taken in another Prolog thread updated the call tree of the profiled thread concurrently.
  • ENHANCED: profiler records time in nanoseconds rather than truncated ms Profile ticks are now nanoseconds of CPU or wall time. Each sample was truncated to whole milliseconds, losing on average 0.5ms per 5ms sample, and samples below 1ms were lost completely. The time of the last tick is kept as nanoseconds since activation, so the ticks add up exactly to the profiled time.
  • BUILD: silence maybe-uninitialized warning in set_stream/2 newline handling newline_domain_error() returned the result of PL_error(), so GCC could not see that atom_to_newline_mode() never succeeds without setting the mode.
  • FIXED: write_term/2 with attributes(write) crashed on cyclic attributes E.g., put_attr(X, a, f(X)) caused unbounded recursion. An attributed variable met while writing its own attributes is now written as just its name. Also close the foreign frame if writing the attributes fails. Reported by Shivaji Gadde.
  • FIXED: location of syntax errors for unterminated quoted text and block comments For string input the context held only the first byte of the input, cutting multibyte characters in half. It now holds the text read with the offset of the opening quote or comment. For other streams the error now points at the opening quote or comment rather than (one character after) the start of the term. Reported by Shivaji Gadde.
  • FIXED: retract/1 could not remove SSU (=>) clauses retract((Head => Body)) rebuilt the clause using :-/2 and thus never matched. Reported by Shivaji Gadde.
  • FIXED: current_op/3 with given priority found operators hidden by a local definition E.g., after op(250, yfx, rem), current_op(400, T, rem) returned the system definition. Reported by Shivaji Gadde.
  • FIXED: clause/2 left the body unbound if all moved unifications became head unifications E.g., for p(X,Y) :- Y = f(X), clause(p(A,B), Body) succeeded with Body unbound. Reported by Shivaji Gadde.
  • FIXED: bagof/3 missed free variables of a qualified goal bound through a variable E.g., G = member(X-Y,[1-a,2-b]), bagof(X, user:G, L) called as a term did not see Y as free variable. Reported by Shivaji Gadde.
  • FIXED: powm/3 continued evaluation after raising a type error Reported by Shivaji Gadde.
  • FIXED: crash on an invalid newline(Mode) stream option open/4 and set_stream/2 passed the atom rather than a term reference as culprit of the domain error, causing a crash or instantiation error. Reported by Shivaji Gadde.

[Oct 28 2026]

  • ENHANCED: reduce atom-table contention when many threads create blobs Tabling creates a trie blob per table, so a multi-threaded tabling workload creates millions of atoms concurrently.
  • FIXED: crash when a thread writes to an inherited with_output_to/2 capture Threads inherit the standard streams of their creator. If the creator is inside with_output_to/2,3, these are the mutex-less memory stream used for the capture. Concurrent access corrupted the lock count and the buffer, causing a Sunlock() assertion failure or invalid code points. This notably affected plunit tests that create threads. copyStandardStreams() now adds a mutex to such streams.

[Oct 27 2026]

  • FIXED: prune answers deleted by incremental reevaluation Answers that do not reappear when re-evaluating an incremental table were deleted without pruning their branch, leaving dead nodes in the answer trie and its compiled clause. reeval_complete() now prunes the trie after the answer walk if the answer set changed and nobody is enumerating the trie.
  • ENHANCED: discard the answer nodes of compiled incremental tables Incremental tables kept both the nodes of their answer trie and the compiled clause, as reevaluation works on the nodes. Now they discard the nodes like other tables and prepare_reeval() restores them from the clause. For a 10M answer incremental table this reduces the memory after completion from 1,064 MB to 409 MB (after trim_heap/0).
  • FIXED: prepare_reeval() clears TRIE_DISCARDABLE, so a table that becomes conditional on reevaluation does not lose its delay lists. Tables with an IDG are only discardable if they have no conditional answers and are never dropped as intermediate tables.
  • ENHANCED: compile a trie directly into its clause compile_trie() used to compile into a growing temporary buffer that was copied into the clause, so compiling a large answer trie needed about twice the memory of the clause. This is significant for incremental tables, which are always compiled on completion. Now a first pass computes the exact code size, after which the second pass compiles into a clause of that size. For a 10M answer incremental table this reduces the peak memory from 1,651 MB to 1,270 MB without slowing down.
  • DOC: cyc150: smaller trie, trie node and IDG node structs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
  • ENHANCED: shrink struct trie from 168 to 152 bytes The release hook pointer is replaced by the flags TRIE_ISTABLE and the new TRIE_ISVARIANT, which also avoids a call per released answer node without delay info. The lookup and gen_call statistics are 32-bit.
  • ENHANCED: shrink trie nodes from 48 to 40 bytes The delay info of conditional answers moves from every trie node to a table, flagged by TN_DELAYINFO. Few answers are conditional, while every node of every trie paid for the pointer. On ErgoAI's cyc150 the table space shrinks by 400 MB.
  • ENHANCED: shrink the IDG node from 120 to 80 bytes The fields for monotonic dependencies move to struct idg_mono, which only nodes with monotonic dependencies allocate, and the counters are 32-bit. On ErgoAI's cyc150 the 9.4M nodes use 376 MB less.
  • ENHANCED: private tables get an atom only when Prolog needs a handle The variant table of a thread now refers to its answer tries directly rather than through their atom. A trie gets its atom when Prolog asks for a handle and is destroyed directly if it never got one. The pseudo tables that record calls to incremental dynamic predicates never need an atom. On ErgoAI's cyc150 this avoids 3M of 9.5M atoms (450 MB) and about 4% of the solve time.
  • ENHANCED: the atom of a trie no longer copies a reference to the trie The trie blob is now PL_BLOB_NOCOPY with the trie as its data. Creating a trie symbol no longer allocates and symbol_trie() follows one pointer less. A duplicate shared table is now destroyed by atom-GC rather than explicitly, as releasing the blob can no longer happen twice.
  • DOC: cyc150: ergoAIp now declares its \opposes stores abstract(0) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
  • DOC: cyc150: measure abstract(0) dependencies for ergoAIp's rule metadata Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
  • FIXED: calling a complete table of an SSU predicate tabled after its rules If table/1 was applied to a => predicate that was already defined, the S_SSU_DET supervisor runs before S_TABLED. If the fast path of S_TABLED failed from the complete table, this raised "No rule matches".
  • ENHANCED: abolish all private tables in bulk Clearing a thread's variant table (abolish_all_tables/0, thread exit) now resets the tables in creation order, frees the IDG edge sets of private nodes without updating the other end and releases the IDG registry at once. On ErgoAI's cyc150 this reduces abolishing 13M tables from 29s to 4.4s and frees the registry slots that remained.
  • ENHANCED: do not compile incremental tables with a single answer Incremental tables were always compiled on completion because the answer trie may change while it is enumerated. Generating a single answer is deterministic, so S_TABLED and S_TRIE_GEN now unify such an answer directly from the trie. On ErgoAI's cyc150 this avoids 2.3M of 2.4M compiled clauses and their clause-reference atoms (-520 MB RSS).
  • DOC: doc/tabling: where ErgoAI's cyc150 programs spend memory and time Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

[Oct 26 2026]

  • ENHANCED: allocate the IDG from the table space The nodes of the incremental dependency graph, their edge sets and the registry of a thread are now allocated from the table pool of their tables. They are thus included in the statistics table_space_used and shared_table_space_used and subject to the table_space and shared_table_space limits. Exceeding the limit while adding an IDG node or edge raises a resource error rather than halting.

[Oct 25 2026]

[Oct 24 2026]

  • DOC: the SDG viewer and the thread monitor graphs of incomplete tables Document prolog_ide(sdg_viewer), the graphs of incomplete tables of the thread monitor and, in doc/tabling, the SDG viewer and the SDG counters.

[Oct 26 2026]

  • FIXED: reevaluation leaked the IDG nodes of incremental dynamic calls Reevaluating an incremental table drops all its IDG edges and adds only those of the calls it makes this time. Dynamic call variants it no longer makes kept their pseudo tables, so a table whose calls change with the data grows the variant table without bound. The same applies to tables destroyed on completion and dropped intermediate tables. Such nodes are now reclaimed by a sweep over the thread's variant table after completing a root component, amortized over the removed edges.
  • FIXED: exception with two attributed catchers whose wakeup fails aborted findCatcher() rewound the wakeup-state foreign frame after a failed catcher, freeing the term reference for the wakeup exception that was allocated only once, as well as the saved wakeup list. Each catcher is now tried in a foreign frame of its own that is discarded on failure. Reported by Shivaji Gadde.
  • FIXED: '$attv_unify'(X,X) on an attributed variable created a self-reference Binding the attvar cell to a reference to itself made the next dereference loop forever. Succeed without binding. Also compute the new value before fetching the attvar address as linkValG() may shift the stacks. Reported by Shivaji Gadde.
  • FIXED: '$current_prolog_flag'/5 crashed enumerating local scope without local flags If the thread has no thread-local flag table, fail rather than create an enumerator over a NULL table. Reported by Shivaji Gadde.
  • FIXED: '$source_file_property'/3 crashed asking loaded for an unknown file The loaded property dereferenced the source file without checking that lookupSourceFile() found one. Reported by Shivaji Gadde.

[Oct 24 2026]

  • ADDED: tripwires max_incomplete_subgoals and max_scc_subgoals The Prolog flags max_incomplete_subgoals and max_scc_subgoals, with their _action flags, limit the number of incomplete tables of a thread and the number of tables in an SCC. '$tbl_incomplete'/3 reads the number of incomplete tables and active SCCs of any thread without stopping it.
  • ADDED: '$tbl_sdg'/3 describes the subgoal dependency graph of a thread The incomplete tables of the calling thread with their components and the positive and negative suspensions between them, the base for a viewer of "why does this not complete". Another thread reads it with call_in_thread/2. save_tabling_status() now links the saved state into the thread, so the components hidden by break/0 (a tripwire suspend) and answer completion are found too.

[Oct 25 2026]

  • FIXED: WFS simplification could loop forever on a removed delay set Removing a delay set from an answer (propagate_to_answer()) leaves its elements in the answer's delay buffer. answer_delays_on() counted them, so the answer stayed registered in the delays of a table it no longer depends on. If that table ended up without answers, simplify_component() propagated to the answer in every pass without changing anything and never terminated. answer_delays_on() now only considers the elements of live delay sets.
  • MODIFIED: macOS: system_thread_id is the id of pthread_threadid_np() It used to be the pthread_t, which macOS reuses for a new thread, so it did not identify a thread over time. The new id is unique across the system and is what tools such as lldb and Instruments show.
  • TEST: idg_reader: skip the tests that need threads in a single-threaded build Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kakt381LQVidSMrMYqTh8W
  • FIXED: single-threaded build: statistics key shared_table_space_used There are no shared tables without threads, so the key is 0.
  • FIXED: clang: avoid setjmp() for each arithmetic evaluation Clang builds guarded valueExpression() using setjmp(). On macOS this implies two system calls, slowing down arithmetic that is not compiled (no -O) by up to 4 times. The guard was added because clang crashed on a longjmp() into PL_next_solution(), which no longer applies since setjmp() was moved out of the VM main function.
  • FIXED: abolishing tables now reclaims the IDG nodes of incremental dynamic calls A call from a table to an incremental dynamic predicate adds a dynamic pseudo table to the variant trie that records the depending tables. abolish_table() ignored these, so abolish_module_tables/1 and abolish_table_subgoals/1 never reclaimed them; only abolish_all_tables/0 did. A process that repeatedly loads and abolishes KBs kept growing (6.5M nodes, about 5 GB, after one ErgoAI benchmark).
  • FIXED: XSB dialect: add message for ignored table options; accept if_not_tabled library(dialect/xsb/source) printed xsb(table_option_ignored(Option)) without a message definition. if_not_tabled only skips the declaration if the predicate is already tabled, so it is accepted silently, like opaque.

[Oct 23 2026]

[Oct 24 2026]

  • DOC: doc/tabling: tools for debugging tabled programs in XSB and ErgoAI Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

[Oct 23 2026]

  • ADDED: '$idg_predicate_edges'/3 summarizes the IDG per predicate Counts the IDG nodes per predicate (and how many are invalid) and the edges between predicates of any thread, as an external reader. On Pharos this takes 0.064 seconds for 7.3M edges, where the same in Prolog took 9.3 seconds. This is the data for an IDG viewer.
  • ADDED: reading the incremental dependency graph from another thread A thread may now inspect the IDG of another thread. Readers bracket their access with idg_reader_enter() and idg_reader_exit(); while there are readers, freed IDG nodes, edge-set blocks and answer trie structs are kept until the last reader leaves. The IDG nodes of a thread are kept in a registry that a reader finds through acquire_ldata(). '$idg_thread_summary'/3 counts the nodes and edges of a thread this way.
  • ENHANCED: compact edge sets for the incremental dependency graph The edges of an IDG node used generic lock-free hash tables. Most nodes have no or one edge, so this was mostly overhead: on Pharos the IDG used 654 MB. The new idg_set (src/pl-idgset.c) is a single word for none or one edge and an append-only block, hashed above 8 entries, otherwise. The sets hold keys only; monotonic dependency chains moved to idg_node->mono_deps. This reduces the IDG to 403 MB and the RSS from 1.46 GB to 1.09 GB at the same speed.
  • FIXED: table_statistics/2,3 counted IDG nodes of dynamic predicates as tables The variant table also holds, for incremental dynamic predicates, the variants incremental tables depend on. These have no answers and were counted as answer tables, e.g., 387,426 tables with 0 answers. They are now reported as dependency_nodes and dependency_space.

[Oct 24 2026]

  • FIXED: #1553 GCC musttail check passed on ppc64le and s390x The configure probe was too simple: GCC resolved its call through the static table to a direct call. The probe now mirrors the VM: five arguments, the last modified, and an indirect call through the code. ppc64le cannot do indirect sibling calls and s390x passes the fifth argument in the callee-saved r6, so both now use the default VM.

[Oct 23 2026]

  • FIXED: trie_property/2 size: only check counts of our own private tries The size property compared the counted nodes and values with the administration and printed an OOPS message if they differ. Another thread may add to the trie while we walk it, e.g., when inspecting the tables of another thread, so the check is now limited to the private tries of the calling thread.
  • ENHANCED: dark theme: colour for the thread monitor's tables graph Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

[Oct 22 2026]

  • DOC: prolog_ide(memory_viewer) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
  • ADDED: '$thread_local_predicate_size'/3 for the copy in another thread The memory used by the copy of a thread-local predicate in some thread, the counterpart of '$thread_local_clause_count'/3. Used by the memory viewer of the IDE.
  • ADDED: erased clauses not yet reclaimed: predicate properties and statistics predicate_property/2 properties erased_clauses(Count) and erased_clause_space(Bytes), and statistics/2 keys of the same name, tell how many erased clauses clause garbage collection did not reclaim yet and how much memory they use.
  • ADDED: statistics/2 key shared_table_space_used The bytes in use by shared answer tables, the counterpart of table_space_used for the private tables of a thread.
  • ADDED: Prolog flag validate_api to disable the foreign interface checks The checks on term_t, atom_t and functor_t arguments cost a few nanoseconds per call. The flag is a bit in the flag mask, tested inline before calling the check. It is true by default and false if Prolog is started with -O.
  • ADDED: PL_get_list_slice() to call a C function for a slice of a list The callback receives a reused term reference for each element, its index and a closure. The list is walked once, detecting cycles using Brent's algorithm after 1,000 elements. The callback may cause GC. With CVT_EXCEPTION, partial, improper and cyclic lists raise an error.
  • ADDED: PL_get_arg_values() to read a range of arguments of a compound Fills an array of pl_value_t, each holding the type and value of an argument. This avoids a term_t and API call per argument for C code that reads many small terms. Text is converted following the CVT_*, REP_* and BUF_* flags of PL_get_nchars(). PL_AS_FLOAT (an alias for CVT_FLOAT) stores all numbers as double.

Package bdb

[Oct 21 2026]

  • CLEANUP: remove unused atoms from bdb4pl.c The server atoms are only needed for the RPC client of Berkeley DB before 5.1. Reported by clang's -Wunused-but-set-global.

Package clib

[Oct 21 2026]

  • CLEANUP: remove unused atoms, functors and variables from time.c, process.c and socket.c Some are only needed outside Windows or with AF_UNIX. Reported by clang's -Wunused-but-set-global.

Package cpp

[Oct 22 2026]

  • TEST: PL_get_list_slice() Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
  • TEST: PL_get_arg_values() Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Package http

[Oct 24 2026]

  • FIXED: #176 http_stop_server/2 could hang if the stop signal arrives in http:schedule_workers/1 The catch around the hook caught any exception, including the http_stop(_) signal used to stop the accept thread. Only catch error(_,_) exceptions. Fixes #176.

Package jpl

[Oct 21 2026]

  • FIXED: JPL turned atoms with characters above U+00FF into garbage Strings Converting such an atom to a Java String repeated its first character ("ab€d" became "aaaa"). This affected all systems where wchar_t is 32 bits, i.e., all but Windows. Converting a Java String ending in a lone UTF-16 lead surrogate read beyond the string. Also removes unused atoms and functors and compiles the UTF-16 helpers only when needed.

Package ltx2htm

[Oct 21 2026]

  • CLEANUP: remove unused ATOM_nbsp from tex.c Reported by clang's -Wunused-but-set-global.

Package odbc

[Oct 21 2026]

  • CLEANUP: remove unused atoms and functors from odbc.c Reported by clang's -Wunused-but-set-global.

Package pldoc

[Oct 21 2026]

  • FIXED: LaTeX backend: Text links to a section whose id holds an _ section_label//1 removes underscores from the \label, but the \secref for a link to it kept them, so the reference was unresolved.

Package semweb

[Oct 21 2026]

  • CLEANUP: remove unused atoms and functor from rdf_db.c and error.c Reported by clang's -Wunused-but-set-global.

Package ssl

[Oct 21 2026]

  • CLEANUP: remove unused atoms and functors from ssl4pl.c and crypto4pl.c ATOM_warning is only used outside Windows and ATOM_sslv23 in crypto4pl.c only with OpenSSL before 3.0. Reported by clang's -Wunused-but-set-global.

Package sweep

[Oct 21 2026]

  • PORT: declare emacs_module_init() exported before emacs-module.h Adding dllexport on a redeclaration makes clang warn for Windows targets (-Wdll-attribute-on-redeclaration).

Package xpce

[Oct 30 2026]

  • ENHANCED: the text report of profile/2 names XPCE methods Defines profile_node_label/2, so the report shows a method as, e.g., frame <-keep_alive.
  • FIXED: profiler: Edit of a method; its popup opened the manpce window The popup of a method in the details opened, or raised, the window of manpce/0 to find out whether the manual documents it. It now uses the HTML manual directly, as Documentation does. Edit uses edit/1, which also finds the C source of a built-in method, and edit/1 no longer offers the source of class send_method or get_method beside it.
  • FIXED: profiler: a method in the flat profile broke sorting by name profile_data/1 now returns the nodes of XPCE methods, so the profiler lists them and the details of a method show what it calls.
  • FIXED: swipl-win: Control-X 1 in an editor could halt Prolog Control-X 1 silently closed an idle Prolog console beside the editor. In swipl-win the editor left does not keep the application alive, so Prolog halted. If closing the other panes ends the application, the panes that keep it alive are now moved to a new tab instead.
  • TEST: Skip pce_date tests that Windows cannot run rather than failing Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
  • TEST: tool_panes axis labelling test independent of the font height Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
  • TEST: Skip video tests on Wine's Media Foundation encoder stub Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
  • TEST: Skip tests that need threads in single-threaded builds Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
  • FIXED: xdot: run graphviz without threads in single-threaded builds Without threads, the graph source and dot's standard error go through temporary files instead of a thread that feeds dot's input and one that reads its errors.

[Oct 29 2026]

  • ENHANCED: profiler call graph edges have a tooltip with calls and time The label of an edge only shows the number of calls. The tooltip also tells the caller, the callee and the time, which sets the width of the edge.
  • ENHANCED: xdot: edge labels on top of a translucent background Graphviz places an edge label beside the centre line of the edge, regardless of the width of the line and its arrows, which could then hide part of the label. Labels are now drawn after the arrows, on a rounded, translucent box of the background of the graph or window. The class variable xdot.edge_label_alpha (default 160) sets its opacity; 0 draws no background.
  • ENHANCED: profiler status line tells exact profiles from sampled ones For exact(true) profiles, the status line reports the time in the predicates and the profiler overhead, which is excluded from the times, rather than samples and distortion. Sampled profiles show whether CPU or wall time was sampled.
  • FIXED: PceEmacs: edit/1 from a window holding only a tool failed When the arrangements put the new editor elsewhere, e.g., in a window of its own, emacs_buffer<-open answered the current pane of the window it was asked from, typically the navigator, raising "No implementation for: <-editor". `@emacs<-show_buffer` now answers the view it shows the buffer in.

[Oct 27 2026]

  • TEST: memory viewer: the largest tables may share their size top_variants assumed that only the tables for 2400 and 2500 answers use the same space. With smaller trie nodes, more tables share the largest size.
  • FIXED: IDG viewer, profiler, class variable editor: no lambda expressions The lambdas of the IDG viewer used variables of the clause without declaring them as {Free}. When compiled, these became fresh variables, so the patterns of predicates outside the focus graph and the groups expanded for the previous predicate were not filtered. Uncompiled lambdas are slow, so all are replaced by helper predicates.
  • FIXED: IDG viewer: showing a huge table or predicate exceeded the stack The worker collected all variants of the predicate or call pattern and all answers of the selected variant before showing the first page. Engines now produce the pages of matching variants and answers using findnsols/4 with count(N), so the "Next 10/100/1000" buttons get the next N.
  • ENHANCED: IDG viewer: show tabled dynamic predicates as tables in italics A predicate that is both tabled and dynamic, as used by ErgoAI, was shown as a dynamic predicate (a box). It is now an ellipse, as other tables, with its name in italics to show it is also dynamic. The browser keeps showing it in italics and the tooltip says "incremental table, dynamic".

[Oct 26 2026]

  • ENHANCED: memory viewer: show the call trie and IDG; report slow refreshes The tables of a pool now have a `call trie` node, the variant table that maps calls to their tables, and an IDG node, the incremental dependency graph. While computing the chart takes more than a quarter of a second, the chart shows the progress cursor and the status line tells what is being described.

[Oct 25 2026]

  • FIXED: memory viewer: a thread with millions of tables vanished from the chart Describing the private tables of a thread collected every table with its variant before grouping them by predicate. With 12.9M tables this exceeded the stack of the thread computing the chart, and the error made the whole thread disappear from the threads and tables views.
  • ADDED: thread monitor: graphs of the incomplete tables and SCCs per table The diagram of a thread shows the number of its incomplete tables and its active SCCs per incomplete table, read with '$tbl_incomplete'/3 without stopping the thread. Few SCCs per table hint at badly structured rules, many and growing at recursion over growing terms.

[Oct 24 2026]

  • ADDED: library(swi/pce_sdg_viewer): show the incomplete tables of a thread The class prolog_sdg_viewer is a pane of the IDE that shows the subgoal dependency graph of a thread from '$tbl_sdg'/3: the incomplete tables in their components (SCCs), nested as the components are, and which tables wait for which. Negative (tnot/1) edges are red and dashed, tables grow with their answers, leaders have a double outline. Large graphs are shown per predicate; clicking a predicate shows its tables.

[Oct 25 2026]

  • TEST: memory viewer: thread_tables_cached removes the cache entry it fakes Thread ids and, on macOS, system thread ids are reused, so the next test's thread found the faked description.
  • FIXED: rings chart: child labels raised a type error with a fractional font height Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
  • FIXED: @display<-animation_save_formats crashed on a format it cannot save IMG_CreateAnimationEncoder_IO() with closeio true closes the stream when it fails, after which we closed it again.
  • FIXED: dependency viewer: the count column fits the largest count The column of node counts in the browser was as wide as 000,000, so counts of a million nodes or more did not fit.

[Oct 24 2026]

  • ENHANCED: dependency viewer: resizing it resizes the graph, not the browser The column of the predicate browser neither stretches nor shrinks, so the graph takes a resize of the viewer. It relied on the freeze that setting the width of a tile causes, which does not hold before the predicates arrive.
  • FIXED: the outline of a drop on a tool with nested windows showed on its tabs A drop on a tool whose window holds windows itself, such as the views of the dependency viewer, only outlined the label row of those views, which looked like a drop that adds a tab there. The outline now goes on the windows all the way down.
  • FIXED: xdot_window ->fit of an empty graph divided by zero Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kakt381LQVidSMrMYqTh8W
  • ENHANCED: IDE: breakpoints, exceptions and debug messages moved to a Debug menu Every window of the IDE has a Debug menu holding Edit breakpoints, Edit exceptions, Debug messages and a Tabling submenu with View incremental dependencies (IDG), which was View dependencies on the Tools menu. In a terminal they follow the terminal's own Debug items.
  • ENHANCED: xdot_window refits the graph when resized Unless the user moved or zoomed it, a resized xdot_window fits the graph again, up to its natural_zoom, as the call graph of the profiler did. The dependency viewer now gets this as well.

[Oct 23 2026]

  • ADDED: IDE dependency viewer: the incremental dependency graph of a thread Tools/View dependencies shows the IDG of a thread using library(xdot): which incremental dynamic predicates and tables affect which tables. As the profiler, it has a browser of the predicates involved with a filter and shows the graph around the current predicate, marked with a star: what affects it on the left and what it affects on the right. Relatives that are not shown are summarised by "... N more" nodes, whose menu shows them all. Clicking a predicate shows its call patterns as a cluster. An overview shows the IDG by module and, for the non-modular code of user, by file, where expanded groups are graphviz clusters; it follows the current predicate. A pane browses the variants of a predicate or call pattern and their answers, paged and filtered.
  • FIXED: memory viewer: IDG nodes of dynamic predicates are not tables The variants of an incremental dynamic predicate in the variant table are nodes of the dependency graph. They are now described as such rather than as tables with 0 answers.
  • ADDED: ->tooltip and <-tooltip as shorthand for help_message: tag Defined on visual and dict_item by library(help_message). Tooltips may be markup, which the balloon renders.
  • ENHANCED: thread monitor: tables graph, opens on the busiest thread A graph shows the private tables of a thread once it has them. A new monitor shows the first console thread and switches to the running thread using most CPU. A low window labels fewer decades and the graph window is a plain window, so it has no scrollbars.
  • FIXED: plot_axis: grid lines follow a change of the other axis's range The dotted support lines of an axis run across the pixel range of the other axis, but were only redrawn when their own axis changed. Making the Y axis of the thread monitor shorter left the X grid lines at their old height, so the diagram was centred too low in a low window.

[Oct 22 2026]

  • ADDED: IDE memory viewer: how Prolog uses its memory as rings or a treemap Tools/View memory opens a pane with views on the process, the application (static program and dynamic data, with user split by file), the program per module and predicate (indexes, clauses and erased clauses), the dynamic data, the threads (stacks, thread-local data and private tables), the tables per thread and predicate, and the objects of xpce per class. The tree is computed in a thread of its own, optionally refreshed every so many seconds; the popup edits what is below the pointer. memory_tree/2,3 provide the data without the GUI.
  • ENHANCED: disk usage demo: popup to open, scan a folder or its parent The desktop's folder dialog selects the folder you came from when you go up, so it cannot easily be used to scan the parent.
  • ADDED: profiler: Call tree tab shows where the time below a predicate goes The call tree of the current predicate is shown as rings or a treemap using library(plot/hierarchy_chart). It is built from the call graph by expanding the callees, sharing the time below a callee over its own callees in proportion; recursion and small calls are not expanded. Clicking makes a predicate the current one; the popup edits it.
  • ADDED: demo Disk usage: rings and treemap of the disk space of a folder Scans a folder in a thread of its own and shows the result using library(plot/hierarchy_chart), in the style of GNOME's baobab. It starts in the working directory and skips symbolic links, also those that loop.
  • ADDED: library(open_document): open a file in the editor or the desktop Epilog's opening of file links, moved to a library of its own so that other tools can use it: folders and binary files are opened using desktop_open/1, text files using edit/1.
  • ADDED: library(plot/hierarchy_chart) shows a hierarchical distribution Shows how a quantity such as disk space, time or memory is distributed over a tree of hierarchy_node objects, as rings around the focus or as a squarified treemap, in the style of GNOME's baobab. Hovering a node highlights it and shows its value; clicking zooms in or out. Class hierarchy_window shows a chart filling a picture and switches views.
  • FIXED: help balloon swallowed pointer moves over its owner While a balloon showed, moving the pointer over its owner updated the balloon but the move never reached the owner, so a graphical that highlights what is below the pointer stopped doing so. The move is now processed as usual after updating the balloon.
  • ADDED: path<->event_area selects how a path is hit by the mouse painted accepts events exactly where the path is painted, so filled shapes that touch, such as the segments of a chart, no longer accept events just outside their edge. area and outline hit only the interior or only the line. The default, auto, now also hits the interior of a filled path that is not closed.
  • ADDED: plotter ->hover_values shows the values of the graphs below the pointer When enabled, hovering a plotter shows a vertical line at the pointer and a box in the colour of each graph holding its value there. The thread monitor uses this to show the exact stack sizes and CPU usage of a sample.
  • ADDED: demo Ripples: stir the water of a ripple tank A wave equation in Prolog moves a grid of heights. An sdl_mesh shows the floor of the pool refracted by the slope of the water, and a second mesh adds glints using additive blending. Drag the mouse to stir, click to drop a stone that stays on the floor, toggle rain, run at maximum speed and clear the floor. Using a pen, the pressure sets the depth of the waves. Resizing the window enlarges the pool; the number of triangles grows with its area.
  • ENHANCED: pce_sdl_mesh_set/3 accepts v(X,Y,Colour) for untextured vertices Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
  • FIXED: converting a cyclic or very long Prolog list to an xpce object crashed The conversion collected the elements in an alloca() array that grew without limit, overflowing the C stack. It now uses PL_get_list_slice(), which detects cycles, and moves to malloc()ed memory for long lists. Invalid lists now raise type_error(list, L) or an instantiation error rather than type_error(pce(proper_list), L).
  • ENHANCED: pce_sdl_mesh_set/3 is 3 times faster and accepts integer colours The vertex colour may be an integer 0xRRGGBBAA as well as rgba(R,G,B,A). Vertices are read using PL_get_arg_values() and both lists are walked once using PL_get_list_slice(). A partial list now raises an instantiation error. The particles demo uses integer colours. pce_sdl_mesh_set/3 and pce_open_terminal_image/4 are now documented for help/1.
  • FIXED: video and camera frames in BT.601 could not be converted on SDL < 3.4 SDL before 3.4 only converts YUV to RGB if both use the same primaries, so frames in BT.601 could not become sRGB images. Keep the primaries of the frame for the conversion.
  • TEST: test_theme_editor: class_variable_link needs the manual Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
  • TEST: test_sdl_input: pinch events require SDL 3.4 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
  • FIXED: sdl_mesh without texture ignored <-blend on SDL 3.2 SDL 3.2 draws two triangles that form a rectangle as a filled rectangle with alpha blending if the texture coordinates have no size, as for the white pixel we use if the layer has no texture.
  • TEST: test_image_save: SDL_image before 3.4 cannot write GIF, TGA and ICO Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
  • ADDED: @display<-animation_save_formats Formats animation->save can write. It is empty with SDL_image before 3.4, which cannot write animations. test_animation uses it to skip tests that save animations or load APNG on such systems.
  • FIXED: date->set (and new(date(...))) was off by an hour across DST setDate() started from the localtime() of the old date and kept its tm_isdst, so mktime() applied the DST state of the old date (typically now) to the new one. Let mktime() determine DST. Reported by Mike Elston (test_pce_date failed in New Zealand).

[Oct 21 2026]

  • ENHANCED: terminal_image ->save_lines resizes the scroll-back buffer Previously the buffer size was fixed when the terminal was created, so changing the preference only affected new terminals. Growing keeps all text, shrinking drops the oldest lines. The caret, selection, view, incremental search and command blocks follow their text.
  • FIXED: terminal output was slow: cat of a large file took 25 sec, now 0.4 Each pty event processed at most 4K of output, after which the terminal was redrawn. Now process all buffered output, for at most 20ms. The per-character consistency check walked the whole scroll-back (release builds keep assert()); it now only does so in debug builds.
  • ADDED: terminal_image <->auto_links: turn text matching a regex into links terminal_image->auto_link: Regex, [Target] makes text that matches Regex a hyperlink to Target, where \N is replaced by what group N matched. Epilog uses this to link http(s) URLs and File:Line[:Column] locations; add patterns using the multifile hook auto_link/2.
  • TEST: rename xpce tests with "fail" in their name Such names get in the way when searching test output for failed tests. xpce:failed_initialise becomes xpce:initialise_free.
  • TEST: sdl_layer tests: allow rounding differences between renderer and Cairo Metal draws a blue YUV frame at 0.5 alpha as 127 where Cairo draws 128, failing sdl_layer:alpha_no_alpha_channel on macOS.
  • TEST: run the video test units in parallel The video tests wait for playback in real time, 40 seconds in one process. Each unit now runs as a ctest of its own, the stalling HTTP handler is released when the server stops rather than sleeping 5 seconds, and the input format tests play shorter clips: 7.5 seconds. With INSTALL_TESTS, test_video/0 is still recorded for the installed tree; its ctest is disabled as the units run separately.
  • ENHANCED: epilog rounds the terminal width to whole pixels The scroll_bar width need not be a whole number of pixels, so the terminal, scroll_bar and report bar are placed using round(TW-SBW).
  • ADDED: xpce expressions round(X), floor(X) and X//Y xpce numbers need not be integers, so X/Y is 3.5 for 7/2. These make an integer where one is needed, with the semantics of Prolog's round/1, floor/1 and //. Integer division by zero raises division_by_zero.
  • FIXED: scroll_bar width of 4mm made the terminal one column too narrow The default width of a scroll_bar is 4mm, which is not a whole number of pixels. Now that expressions yield non-integral numbers, epilog gave the terminal a fractional width just short of 80 columns. The width is now rounded to whole pixels.
  • FIXED: class date on Windows: 32-bit times, crashes before 1970, no zone offset Dates, date->convert, date->advance and date<-difference used C long, which is 32 bits on Windows, so times past 2038 and intervals beyond 68 years were wrong. date->advance tested for overflow after it happened. Getters crashed on dates the C library cannot represent, on Windows any date before 1970; they now fail. Windows has no tm_gmtoff, so date<-time_zone gave no offset and date<-rfc_string had no zone; the offset is now computed with timegm(), which is _mkgmtime() on Windows. The fallback timegm() used local time if TZ was not set, so date<-convert of XML dates was off by the local offset on Windows.
  • FIXED: xpce integer add and subtract detected overflow after the fact The checks inspected the result of a signed overflow, which is undefined behaviour that compilers may optimise away. They now test the operands before computing.
  • FIXED: xpce integer arithmetic was 32-bit on Windows and crashed on x/0 Expressions and number objects used C long, which is 32 bits on Windows, truncating integers above 2^31. They now use intptr_t. Multiplication falls back to floats when the product does not fit an xpce integer rather than checking the operands with labs(), and an integer division by zero no longer raises SIGFPE but yields inf.
  • FIXED: compile xpce on Windows with clang (llvm-mingw, Windows on ARM) Clang accepts the visibility attribute for MinGW targets, so SO_LOCAL made _rewindAnswerStack() hidden while interface.h declares it dllexport, which clang rejects. SO_LOCAL is now empty on Windows, where DLLs only export what is declared dllexport.
  • FIXED: compile with SDL 3.2 and SDL_image 3.2 (Debian trixie) With SDL_image before 3.4, animations load through IMG_LoadAnimation_IO() without metadata, saving animations raises an error and image->save writes PNG, JPEG, AVIF and BMP only. With SDL before 3.4, an animated cursor shows its first frame.