May 25, 2023
On Thursday, 25 May 2023 at 10:35:45 UTC, Quirin Schroll wrote:
> On Wednesday, 24 May 2023 at 23:04:53 UTC, Cecil Ward wrote:
>> [...]
>
> I had this almost happen in my C++ code. It never ran because I got a compile error. The compiler is MSVC.
>
> [...]

I like your ‘quick sketch’. Now how do we recruit a volunteer? Since unfortunately, I am not up to the task having zero experience and various other problems.
May 25, 2023
On Thursday, 25 May 2023 at 13:46:32 UTC, Cecil Ward wrote:
> On Thursday, 25 May 2023 at 10:35:45 UTC, Quirin Schroll wrote:
>> On Wednesday, 24 May 2023 at 23:04:53 UTC, Cecil Ward wrote:
>>> [...]
>>
>> I had this almost happen in my C++ code. It never ran because I got a compile error. The compiler is MSVC.
>>
>> [...]
>
> My apologies, when I wrote ‘straightforward’ I should meant to say, ‘without body, with no content’. You mentioned without side-effects, which is more general and very useful.

We would have to add something in your quick sketch to mention exceptions. So if the basic block is not null, a type of check that I would settle for as a first step, in addition to your conditions all calls would have to be nothrow if there is an exception handler.

I don’t know what the D call is to terminate the current process, or even begin the shutdown sequence for the system if we are building an embedded system with (other) no o/s. But I should think that a routine having an attribute of ‘terminate the process’, when the reason isn’t  because we mean ‘panic’, core_dump, ud_n, etc because of a bug, but because we simply want to terminate the process because we’re all done, with success or failure, means that it is not pure.

In GCC there is a noreturn attribute iirc - is that correct? but I think that there ought to be subtypes of that - to say whether it’s debug code or not, whether it’s a straight ‘end if process’ rather than a panic / bug / core_dump etc. (Does GCC or LDC generate a ud2 when you wish to panic? Presumably without cleanup?)

Merely having an exception handler present above or around your non-trivial infinite loop might be a sign that you have something equivalent to a break inside the loop. But I don’t think we should consider this, as there may actually be no exception throwing at all, and that could be a bug - that the way out has been omitted.

Anyway, it would be easier to do the "L1: jmp L1" thing first as then there are no worries, and we would get some benefit much sooner.
May 25, 2023
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:

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.

Ali

May 25, 2023
On Wednesday, 24 May 2023 at 23:04:53 UTC, Cecil Ward wrote:
> Due to some stupidity of mine involving misuse of templates I ended up with a routine that simply called itself infinitely. Directly, that is, and in this case the routine was optimised down into a simple infinite loop with no loop body content.
>
> Is it possible for our compilers to detect direct infinite recursion ? I’m ignoring the indirect call case, and I’m also ignoring anything involving function pointers, so given those restrictions, we will be able to see that the generated code of a routine is calling its own address, because that address is a literal and we know what the current function is that we’re currently in.
>
> And while we’re at it, is it possible to detect all kinds of straightforward infinite loops (not coming from recursion)? That is where the generated code, perhaps as a result of optimisation, is a straight loop with no body. Can we implement that?
>
> It ought to be possible because the backend can spot a pattern like "L1:  jmp  L1".
>
> I promise I’m not trying to solve the Halting Problem :)
>
> Cecil Ward.

Is it a nightmare to get template generation to detect a call-to-self, one line function (‘direct’ recursion bar the mangling)? My stupidity was trying to ‘forward’ a call to a particular template specialisation on to the routine for the general case, and getting it all wrong. I wouldn’t want to make more work compared to the L1: jmp L1 case, but if anyone did feel like doing a check at template instantiation-related code emission time then a very nice specific and meaningful message could be output. Indeed there are some very worthwhile diagnostic longwinded template-related error messages already when someone has an error relating to multiple possible template expansion possibilities. (What is that called, that I’m thinking of?)
May 25, 2023
On Thursday, 25 May 2023 at 15:56:43 UTC, Cecil Ward wrote:
> [...]

Typo: s/template generation/template expansion/
May 25, 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:

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

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.

May 25, 2023

On Thursday, 25 May 2023 at 15:51:15 UTC, Ali Çehreli wrote:

>

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".

It still does, though it requires the -w flag to enable warnings.

1 2
Next ›   Last »