Jump to page: 1 2 3
Thread overview
3 days ago

Yesterday (or today, depending upon who you ask), Dennis asked a wonderful question in the monthly meeting.

Paraphrased, why should we use a DFA engine instead of DIP1000?

And you know what, this is a great question, especially since DIP1000 is a subset of a specialised DFA engine. But with some serious ceiling limitations.

Afterwards, I came up with an example, where DIP1000 will error:

int* ptr;

void func(bool b, scope int* p) @safe {
  assert(!b);

  if (b) {
    ptr = p; // Error: scope variable `p` assigned to global variable `ptr`
  }
}

This is clearly a false positive; that branch could never run!

One of the hallmarks of a real data flow analysis engine is called dead code elimination; all optimising backends implement it. In fact, it's one of the first that ever gets implemented and DIP1000 can't do it!

But what if we did have the ability to model truthiness? Well then we could cull that dead branch, and not do the report. But that is slow right? NOPE! We can actually do this fast!

Here is a modified example from my fast DFA engine, which I've been working on for around six months for both truthiness and nullability:

void branchKill(bool b)
{
    assert(!b);

    if (b)
    {
        int* ptr;
        int val = *ptr; // no error branch dead
    }
}

My hope, should it succeed, is to be on by default; this requires it to be fast and not have false positives like the above code.

I hope this is enlightening for those who don't know what data flow analysis is all about!

2 days ago

Good stuff, Rikki. Thanks for working on this.

2 days ago

On Saturday, 13 September 2025 at 02:39:40 UTC, Richard (Rikki) Andrew Cattermole wrote:

>

This is clearly a false positive; that branch could never run!

It could run if asserts were off. Obviously asserts are never meant to fail when the program is in a correct state though.

>

One of the hallmarks of a real data flow analysis engine is called dead code elimination; all optimising backends implement it. In fact, it's one of the first that ever gets implemented and DIP1000 can't do it!

This example doesn't seem very realistic. A branch that never runs shouldn't really exist to begin with, right? Can you think of any more realistic examples where DFA allows you to infer safety where DIP1000 is currently limited?

Also I'm curious what other problems DFA could be applied to? For instance, detecting what types a function throws and allowing you to catch only those types instead of always having to catch(Exception) to satisfy the compiler. Or doing more complex type inference.

2 days ago

On Saturday, 13 September 2025 at 02:39:40 UTC, Richard (Rikki) Andrew Cattermole wrote:

>

[...]
Afterwards, I came up with an example, where DIP1000 will error:

int* ptr;

void func(bool b, scope int* p) @safe {
  assert(!b);

  if (b) {
    ptr = p; // Error: scope variable `p` assigned to global variable `ptr`
  }
}

This is clearly a false positive; that branch could never run!

What about "release" mode? And what about that code:

void main ()
{
   assert (0);

   string s = "x";
   s [0] = 'y';
}

Is "Error: cannot modify immutable expression s[0]" a false positive, too?

2 days ago

On Saturday, 13 September 2025 at 02:39:40 UTC, Richard (Rikki) Andrew Cattermole wrote:

>

Paraphrased, why should we use a DFA engine instead of DIP1000?

(...)

Afterwards, I came up with an example, where DIP1000 will error:

Thanks for providing the example, it's a great discussion starter.

Like I said yesterday, it's not that I don't "know what data flow analysis is all about" - when you have a solution, you can come up with problems it solves, sure.

I was mostly curious what real world code made you believe that more advanced DFA is necessary for useful scope pointer analysis. There's many problems with DIP1000, but no issue was ever opened requesting scope checks to be disabled in dead code. As far as I'm aware, users expect dead code to be type checked by the frontend as usual.

2 days ago

On Saturday, 13 September 2025 at 10:19:45 UTC, kdevel wrote:

>

On Saturday, 13 September 2025 at 02:39:40 UTC, Richard (Rikki) Andrew Cattermole wrote:

>

This is clearly a false positive; that branch could never run!

What about "release" mode? And what about that code:

void main ()
{
   assert (0);

   string s = "x";
   s [0] = 'y';
}

The program has entered an invalid state and the behaviour of the continuing execution of the program is undefined.
https://dlang.org/spec/expression.html#AssertExpression
That said, I don't know if we should be suppressing these errors. Doesn't seem useful to me?

>

Is "Error: cannot modify immutable expression s[0]" a false positive, too?

It's not a 'false positive' because it's not related to DIP1000's stack escape rules at all.

2 days ago

On Saturday, 13 September 2025 at 11:08:29 UTC, IchorDev wrote:

>

[...]

>

What about "release" mode? And what about that code:

void main ()
{
   assert (0);

   string s = "x";
   s [0] = 'y';
}

The program has entered an invalid state and the behaviour of the continuing execution of the program is undefined.

Please note that the program is not run but just compiled.

>

[...]

>

Is "Error: cannot modify immutable expression s[0]" a false positive, too?

It's not a 'false positive' because it's not related to DIP1000's stack escape rules at all.

I thought that the intent of the original code

int* ptr;

void func(bool b, scope int* p) @safe {
  assert(!b);

  if (b) {
    ptr = p; // Error: scope variable `p` assigned to global variable `ptr`
  }
}

was to show that there is some code-block

    ptr = p; // Error: scope variable `p` assigned to global variable `ptr`

which is never executed regardless of the value of b.

2 days ago

On Saturday, 13 September 2025 at 13:06:06 UTC, kdevel wrote:

>

Please note that the program is not run but just compiled.

What does that even mean?

>

I thought that the intent of the original code

void func(bool b, scope int* p) @safe {
  assert(!b);

  if (b) {
    ptr = p; // Error: scope variable `p` assigned to global variable `ptr`
  }
}

was to show that there is some code-block

    ptr = p; // Error: scope variable `p` assigned to global variable `ptr`

which is never executed regardless of the value of b.

Then you clearly didn't read the function beforehand.

2 days ago
On 13/09/2025 9:42 PM, Dejan Lekic wrote:
> Good stuff, Rikki. Thanks for working on this.

Thanks!
2 days ago
On 13/09/2025 10:13 PM, IchorDev wrote:
> On Saturday, 13 September 2025 at 02:39:40 UTC, Richard (Rikki) Andrew Cattermole wrote:
>> This is clearly a false positive; that branch could never run!
> 
> It could run if asserts were off. Obviously asserts are never meant to fail when the program is in a correct state though.
> 
>> One of the hallmarks of a real data flow analysis engine is called dead code elimination; all optimising backends implement it. In fact, it's one of the first that ever gets implemented and DIP1000 can't do it!
> 
> This example doesn't seem very realistic. A branch that never runs shouldn't really exist to begin with, right? Can you think of any more realistic examples where DFA allows you to infer safety where DIP1000 is currently limited?

Its not meant to be too realistic, what it is meant to do is have all the primitives in place to show that the compiler has ability to track state and what happens should X happen.

Everything from loops, assignments, if statements, to function calls mess with this, and its not a great example. The logs from stuff like this get very big. Small font size + multi-gigabyte logs. Not fun and certainly not educational.

> Also I'm curious what other problems DFA could be applied to? For instance, detecting what types a function throws and allowing you to catch only those types instead of always having to `catch(Exception)` to satisfy the compiler. Or doing more complex type inference.

Exceptions need a set in the compiler rather than just "is nothrow flag set". The compiler can (and should) do this without DFA.

« First   ‹ Prev
1 2 3