Direct recursion detection possible?

Quirin Schroll qs.il.paperinik at gmail.com
Thu May 25 16:44:10 UTC 2023


On Thursday, 25 May 2023 at 15:51:15 UTC, Ali Çehreli wrote:
> On 5/24/23 16:04, Cecil Ward wrote:
>
> > Is it possible for our compilers to detect direct infinite
> recursion?
>
> I agree the compiler can detect some cases. However, a 
> diagnostic may not be what the programmer wants in all cases.
>
> I am writing on this topic because I've recently been 
> pleasantly surprised how dmd lets me inject assert(false) 
> expressions during development:
> ```d
> void foo() {
>   someCode();
>
>   assert(false, "some information");
>
>   moreCode();
> }
> ```
> I am a `printf`-style programmer, where I use such asserts as 
> well.
>
> I *think* the compiler used to complain about that in earlier 
> versions (or maybe I remember compilers of earlier languages 
> like C++?); the compiler would say "unreachable code".
>
> I am thankful that dmd allows me do it during development and 
> debugging.
>
> So, your case may fall into this category where although it 
> doesn't make sense, a programmer may have caused it 
> intentionally.

I just tried what DMD does with
```d
for (ubyte i = 0; i < 256u; ++i) { ... }
```

It doesn’t care that `i < 256` is trivially true (by type). GCC 
and Clang give me:
```
warning: comparison is always true due to limited range of data 
type (GCC)
warning: result of comparison of constant 256 with expression of 
type 'unsigned char' is always true (Clang)
```
(For the record, in C++, this is platform dependent; there’s no 
guarantee by the language that `unsigned char` is only 8-bit.)

An `assert(false)` leading to dead code is indeed similar, but 
also different from an infinite loop/recursion. This is 
opinionated: `assert(false)` is placed with intention; it’s a 
good question if (or under which circumstances) dead code should 
be an error; dead code is dubious and in a release build or 
otherwise optimized build, in non-template code, I’d probably 
like the compiler telling me about it. An infinite loop/recursion 
without observable effects is almost certainly not intentional 
and something is wrong: The condition is wrong or the body 
doesn’t affect the condition as intended. Even in a debug build, 
it would be valuable to have the info. There could be 3 degrees 
of diagnostic:
1. In a debug build, an infinite loop/recursion, if detected, 
produces a warning. One big reason for this is that in debug 
mode, `pure` functions can actually have observable effects.
2. In a regular (non-debug, non-release, non-optimized) build, an 
infinite loop/recursion by syntax (something like `while(1){}` or 
`int f(int x) => f(x)` and a little less basic examples) are 
errors (because they’re obviously wrong and should be detected by 
any compiler) and infinite loop/recursion by (deep) semantic 
analysis gets a warning.
3. In optimized/release, any detected infinite loop/recursion is 
an error.

Of course, by infinite loop/recursion, I mean those without 
observable effects, but “infinite loop/recursion” is wordy 
enough. A read–eval–print loop is perfectly fine. I might add 
that `pure` is not enough. Throwing an exception can break a 
loop, so `nothrow` is required as well for all the operations in 
question.


More information about the Digitalmars-d mailing list