[m-users.] Weird compilation error
Volker Wysk
post at volker-wysk.de
Sat Oct 3 14:41:06 AEST 2026
Am Freitag, dem 02.10.2026 um 17:50 +1000 schrieb Zoltan Somogyi:
>
>
> On Thu, 01 Oct 2026 06:23:39 +0200, Volker Wysk <post at volker-wysk.de>
> wrote:
> > > It seems some compiler pass is "optimizing away" or otherwise hiding
> > > the recursive call.
> > >
> > > Without a complete compilable module, and its exact compilation
> > > command,
> > > finding out why this happens would be a futile guessing game.
> >
> > You can clone it from here:
>
> With the ability to reproduce the error, finding the issue was not hard.
>
> The reason for the missing recursive call is that the loop invariant
> optimization finds two small unifications (neither of which appears
> in the source code, with both added by the compiler itself, as part of its
> usual housekeeping) that compute the same result on each iteration.
> It therefore replaces the last (and only) recursive call to
> master_loop_normal
> with a call to its loop-invariant-optimized version, which takes the
> two variables constructed by these unifications as additional arguments.
>
> This is not so much as a bug as an unfinished design, because we have not
> really considered until now what the relationship should be between the
> require tailrec pragma and optimizations like this that can redirect
> recursive
> calls.
>
> I see four obvious ways forward.
>
> One: disable optimizations that can redirect recursive calls for
> predicates
> and functions that have require tailrec pragmas. I don't like this option,
> because people use such pragmas to help ensure efficiency, and this
> course of action works by deliberately foregoing possible optimizations.
>
> Two: when we find that a predicate with a require tailrec pragma
> has no recursive calls at all, check whether it contains calls to
> predicates
> that were derived from itself. (Such derivations are recorded as part of
> the "origin story" of each predicate.) If it does, then print an
> informational
> message about being unable to verify tail recursion. This does not forego
> any optimization, but leaves the pragma useless.
>
> Three: have the loop invariant transformation effectively *transfer*
> the pragma to the optimized predicate. This is the most useful option,
> but it is also the one that takes the most work to implement.
>
> Four: check require tailrec pragmas before we perform optimizations
> that can redirect recursive calls. This should be as effective as option
> three,
> and is a bit simpler to implement, but it is not as future proof. This is
> because
> there is the possibility that some compiler pass that comes later in the
> compilation process, either a pass that exists now or one that will be
> added
> in the future, takes tail recursive code and makes it non-tail recursive.
> I am pretty sure that we will never add any such pass *intentionally*,
> but bugs happen, and therefore we may add them *unintentionally*.
>
> Note that the fourth alternative requires detecting tail recursion twice.
> The first time, after semantic checks are complete and optimizations
> have not yet begun, has the task of checking for violations of require
> tailrec pragmas, while the second time is after all program
> transformations
> are complete, and has the task of marking tail recursive calls for the
> code generator.
Regarding way number four: Maybe the compiler could remember how many tail
recursive calls there are in a predicate/function, the first time they are
detected. When there are less of them in the second time, you'd know that
something didn't go as expected...
Volker
More information about the users
mailing list