[m-dev.] require_tail_recursion(name/arity, [none])

Zoltan Somogyi zoltan.somogyi at runbox.com
Sat Jun 6 16:33:23 AEST 2026



On Fri, 05 Jun 2026 16:21:34 +1000 (AEST), "Zoltan Somogyi" <zoltan.somogyi at runbox.com> wrote:
> The existing require_tail_recursion pragma controls diagnostics in one of two ways:
> 
> - in the usual case where --warn-non-tail-recursion is not set, it ENABLES diagnostics
>   about non-tail recursion for a pred or func, and
>
> - in the case where --warn-non-tail-recursion IS set, it can DISABLE those very same
>   diagnostics for a pred or func.
> 
> People can now do the latter by specifying the option "none". This option is completely
> different from all the other options, and is also incompatable with all of them.
> Instead of trying to describe this clusterfck in the manual, I would prefer to
> simply describe a simple pragma that disables these diagnostics for a pred or func
> in the presence of --warn-non-tail-recursion.

After working on this more, I find that the above is what the documentation said
the pragmas and options would do, but is not what they ACTUALLY do. The reality
is that to print a report about non-tail recursion, you need both a require_tail_recursion
pragma AND the --warn-non-tail-recursion option. In other words, not specifying
--warn-non-tail-recursion shuts up warnings about recursive calls that are not tail recursive
even in predicates that have require_tail_recursion pragmas. (It does let through diagnostics
about require_tail_recursion pragmas for predicates that contain no recursive calls, which
seems a bit strange.)

I would prefer to change this to something like what I described in the quoted email,
meaning that

- if a predicate has a require_tail_recursion pragma, then it gets both reports about recursive calls
  that are not TAIL recursive, and about the predicate containing no recursive calls at all
  (which makes the pragma useless), while

- if a predicate does not have a require_tail_recursion pragma, but --warn-non-tail-recursion
  is set (to either self or self-and-mutual), then it also gets reports about non-tail recursion
  (of the requested types), but does not get reports about no recursive calls at all.
  And any reports it does get could be disabled by a disable_non_tail_recursion_report pragma.

Would anyone object to this? And would this scheme require a new option that users
can specify to shut up all the reports that the above can generate? (I don't think so,
but I am open to arguments in favor of such an option.)

On a separate but related note: mark_tail_calls.m does two jobs: it generates reports about
non-tail recursion, and it marks tail calls. Diagnostics generation is backend-agnostic, but
the MLDS backend needs tail calls marked much, much more than the LLDS backend.
In the MLDS backend, tail call markings are essential for enabling tail recursion. In the LLDS
backend, they are not needed at all, with one minor exception: when --exec-trace-tail-rec
is enabled, which is rare in my experience.

Despite this, it is the LLDS backend that *always* invokes mark_tail_calls.m, while the MLDS
backend does so only if --enable-tailcalls is set. (That option is used only by the MLDS backend,
so this name is somewhat misleading.) Since --enable-tailcalls is enabled at optimization level 1,
and the default 2, this is functionally equivalent to the LLDS case, with the complication of that
things behave differently with -O0. I would like to avoid this complication (to avoid the need
to document it) by making the MLDS backend also invoke mark_tail_calls.m without consulting
that option. Would anyone object to that?

I am in two minds about whether users should have access to --no-enable-tailcalls at all.
The only circumstance in which it would be useful to them is when the implementation
of tail-recursive-SCCs in the MLDS code generator has a bug, but the rest of the MLDS
code generator is ok. Other than that situation, which most users would not be able to recognize,
--enable-tailcalls is clearly better for them.

For us developers, such an option can help debug changes to the code generator, and it can
help with benchmarking, but for that, a private, developer-only option is sufficient, and we can
tweak the operation of a private option more freely. We could also give it a less misleading
and more descriptive name. What do you guys think?

Zoltan.


More information about the developers mailing list