August 26

On Sunday, 25 August 2024 at 13:10:22 UTC, Mike Parker wrote:

>

Second, we'd like to get a number of examples of problems people have had with using DIP1000 that haven't shown up in Bugzilla.

  1. scope on a struct variable makes all fields scope. This came up when making a tokenizer range: When parsing a stack-allocated string, the resulting tokens also become scope even when they don't refer to the input string.
class Token {}

struct Tokenizer
{
    char[] input;
    Token front;
}

Token getToken() @safe
{
    char[5] input = "1 + 2";
    auto range = Tokenizer(input[]); // range as a whole becomes `scope` here
    return range.front; // Unnecessary error that `range.front` is `scope`
}
  1. Scope inference on assignment doesn't remember the original lifetime, but shortens it to the assigned variable.
struct S
{
    string name;
    void doSomething() scope;
}

void f(scope S s)
{
    string prev = s.name; // prev simply becomes `scope`, doesn't remember it came from `s`
    s.doSomething();
    s.name = prev; // Unnecessary error: `prev` assigned to s with longer lifetime
}
  1. Lack of transitive scope

I.e. you can't have an array of stack allocated strings, or a hashmap using scope keys/values. There are workarounds. For example, in my regex match function, I return indices of matches instead of slices of the input string. This does make the API a lot less ergonomic though.

August 26

On Monday, 26 August 2024 at 09:31:18 UTC, Timon Gehr wrote:

>

On 8/26/24 10:17, Walter Bright wrote:

>

@trusted ref int ghi(ref int r) { return r; }

What's inconsistent here is your code. The first @trusted function is invalid.

Yes, though the compiler could actually error on the return statement for ghi (because a @trusted function must have a safe interface).

That would be consistent with erroring when returning a local variable as ref:

@trusted ref int h() {
    int i;
    return i; // Error: returning `i` escapes a reference to local variable `i`
}
August 26
Mike Parker kirjoitti 25.8.2024 klo 16.10:
> DIP1000 keeps rearing its head in discussions touching on safety in our monthly meetings. So we recently held a planning session focused exclusively on DIP1000. Two things came out of it.
> 
> First, it's pretty clear that inference by default is necessary to make DIP1000 easier to use. It's not clear at the moment how this can be achieved, but it's something that we need to work out.
> 
> Second, we'd like to get a number of examples of problems people have had with using DIP1000 that haven't shown up in Bugzilla. Walter wants to be as certain as he can whether such issues are fixable or if the design is fundamentally flawed.
> 
> If you've had problems using DIP1000, please post here with a description of what you encountered and any details about it you think we may find helpful. Please provide example code where possible.
> 
> As an example of the sort of thing we're looking for, someone in the meeting brought up the problems encountered with trying to add DIP1000 support to vibe.d.
> 
> **Please do not reply to posts in this thread unless you have additional relevant information regarding the problem described.** We want to collect examples, and any side discussions will just be noise.
> 
> Thanks!
> 
> 

That `scope` can get inferred to a local variable due to a LATER assignment to it leads to pretty confusing situations. I think only the initialisation should infer. I'm not sure if it's worth a change anymore though, as it'll lead to more breakage. Maybe over an edition switch.

I still think `scope`/`return scope` inference should only happen on a `@safe` function (whether explicitly safe or inferred as so). In `@trusted` / `@system` code it is critical you don't have those attributes added and removed under your nose or in an implementation-defined manner. Yet it [can happen](https://forum.dlang.org/post/edtbjavjzkwogvutxpho@forum.dlang.org).

Design by introspection also doesn't [fully work with](https://issues.dlang.org/show_bug.cgi?id=24003) `scope` / `return scope`, as I discovered when I tried to make a [Phobos function DIP1000 - compilant](https://issues.dlang.org/show_bug.cgi?id=23300). Dennis did make a pull request to enable this but it stalled.
August 26

On Sunday, 25 August 2024 at 13:10:22 UTC, Mike Parker wrote:

>

Second, we'd like to get a number of examples of problems people have had with using DIP1000 that haven't shown up in Bugzilla. Walter wants to be as certain as he can whether such issues are fixable or if the design is fundamentally flawed.

As stated in my previous reply, I dont have problems with DIP1000 as I just ignore it, because of bad ergonomics.
But I felt guilty for bashing something I haven't used in years, so here is first thing I tried.
All my shenanigans are catched without -dip1000.

(I over-use @safe just to be sure)

@safe:

    struct S {
        int x;
        S* s_ptr_0;
        S* s_ptr_1;
    }

S* g_ptr = null;
S* goo(return ref S sp) @safe => &sp;

    S foo(int v) @safe {

        S s0 = S(333);
        S s1 = S(v);

        void ez_escape() @safe {
            auto res = goo(s1);
            g_ptr = res;
        }
        ez_escape();

        () @safe {
            S* moo(return ref S sp)@safe  => &sp;
            s1.s_ptr_0 = moo(s0);
        }();

        () @safe {
            S* moo(S* sp)@safe  => sp;
            s1.s_ptr_1 = moo(&s0);
        }();


        return s1;
    }
August 26
On 8/26/2024 1:33 AM, Richard (Rikki) Andrew Cattermole wrote:
> This appears to be an interaction between DIP1000 and the fact that ``@trusted`` functions have an interface that is required to be ``@safe`` but not its body validated.

It not only appears to be, it is true that trusted functions are not validated.
August 26
That is correct, I was just thinking of using @trusted to avoid checks, rather than ensuring safe code.

The reason inference is not done automatically for all functions is because:

```
int* foo(int*);
```
```
int* foo(int* p) { return p; }
```

are different functions.

Inference affects the name mangling of D functions. So, for D functions, the program will fail to link with inferred attributes.

But with C and C++ linkage, the name mangling isn't reflected in the symbol name.

I suppose it's a problem we can live with.

Attribute inference isn't perfect, either, but it may work well enough to make dip1000 more palatable.
August 26

On Monday, 26 August 2024 at 18:54:26 UTC, Walter Bright wrote:

>

On 8/26/2024 1:33 AM, Richard (Rikki) Andrew Cattermole wrote:

>

This appears to be an interaction between DIP1000 and the fact that @trusted functions have an interface that is required to be @safe but not its body validated.

It not only appears to be, it is true that trusted functions are not validated.

But their API should be checked. That would actually make @trusted functions (not trusted blocks) much more useful.
That would make for a useful difference between @safe and @trusted - one is completely checked, for the other only the parameters and return values are checked.
These checks would make it much more plausible why @safe functions should be allowed to call @trusted functions but not @system functions.

Having a (guaranteed) safe interface makes @trusted functions a whole lot more trustworthy.

August 27

On Sunday, 25 August 2024 at 16:23:40 UTC, Richard (Rikki) Andrew Cattermole wrote:

>

[snip]

  • The abomination that is scope return + return scope (this is easier to resolve in terms of new UDA's, but does not solve communicability of behavior).

I think it would probably be useful to separate woes with DIP 1000 into woes with scope parameters and woes with return scope/ref return scope parameters.

When it comes to mental complexity, I see that as less of an issue for scope parameters than for return scope or ref return scope parameters

There might be some outstanding issues with scope, like what you and Dennis bring up, but ultimately I think what return scope is doing is kind of like Rust lifetimes-lite. It solves a particular use case, but isn't sophisticated enough for more complicated ones.

August 27

On Sunday, 25 August 2024 at 13:10:22 UTC, Mike Parker wrote:

>

If you've had problems using DIP1000, please post here with a description of what you encountered and any details about it you think we may find helpful. Please provide example code where possible.

Here's an issue I ran into when working on my proof-of-concept allocator/container library.

When writing exception-safe code, a common pattern is to save the original value of a variable before performing an operation that might throw, and then restore that value if the operation fails.

Unfortunately, it is impossible to do this in @safe code if the variable you want to save and restore is scope, because the compiler will treat the saved copy as having a shorter lifetime than the original.

For example, this code fails to compile when using -preview=dip1000:

@safe void example(ref scope int[] var)
{
    int[] savedValue = var;
    scope (failure) var = savedValue;

    // Do stuff with a that might throw...
}

...with the following error message:

onlineapp.d(4): Error: scope variable `savedValue` assigned to `ref` variable `var` with longer lifetime

Ideally, the compiler should be able to infer that savedValue has the exact same lifetime as var in this code, and allow the assignment.

August 27

On Sunday, 25 August 2024 at 13:10:22 UTC, Mike Parker wrote:

>

First, it's pretty clear that inference by default is necessary to make DIP1000 easier to use. It's not clear at the moment how this can be achieved, but it's something that we need to work out.

In addition to inference, it is vitally important to have detailed error messages showing exactly where and why inference failed.

Here's an illustrative example:

int* global;

void foo()(int* p)
{
    global = p;
}

void bar()()
{
    int n;
    foo(&n);
}

@safe void main()
{
    bar();
}

Currently, compiling this program with -preview=dip1000 gives the following error message:

example.d(16): Error: `@safe` function `D main` cannot call `@system` function `example.bar!().bar`
example.d(11):        which wasn't inferred `@safe` because of:
example.d(11):        reference to local variable `n` assigned to non-scope parameter `p` calling `foo`

The problem with this message is that it does not explain why p is a non-scope parameter. In order to get that information, we have to modify the definition of foo and add scope manually:

-void foo()(int* p)
+void foo()(scope int* p)

When we do, we get an error message that points to the real source of the problem:

example2.d(16): Error: `@safe` function `D main` cannot call `@system` function `example2.bar!().bar`
example2.d(3):        which calls `example2.foo!().foo`
example2.d(5):        which wasn't inferred `@safe` because of:
example2.d(5):        scope variable `p` assigned to global variable `global`

In order for normal D programmers to have any hope of using DIP 1000 successfully, all of the relevant information must be present in the original error message. Users must not be required to modify their code (or, worse, the code of libraries they depend on) in order to reveal this information.