Thread overview
Problem with dmd-2.104.0 -dip1000 & @safe
Jun 09, 2023
An Pham
Jun 10, 2023
Dennis
Jun 11, 2023
Timon Gehr
June 09, 2023

Getting with below error for following codes. Look like bug?
onlineapp.d(61): Error: scope variable a assigned to non-scope parameter a calling foo

@safe:

struct A(S = string)
{
@safe:
    S s;
    void delegate() c;
}

struct B(S = string)
{
@safe:
    @disable this();

    this(C!S c, A!S a)
    {
        this.c = c;
        this.a = a;
    }

    C!S foo()
    {
        return c;
    }

    A!S a;
    C!S c;
}

class C(S = string)
{
@safe:
    C!S foo(A!S a)
    {
        auto o = new Object();
        return foo2(o, a);
    }

    C!S foo2(Object n, A!S a)
    {
        auto b = B!S(this, a);
     	return b.foo();
    }
}

unittest
{
    static struct X
    {
    @safe:
    	void foo3()
		{
    	}
    }

    X x;
    A!string a;
    a.s = "foo";
    a.c = &x.foo3;
    auto c = new C!string();
    c.foo(a);
}

void main()
{
}

June 10, 2023

On Friday, 9 June 2023 at 04:05:27 UTC, An Pham wrote:

>

Getting with below error for following codes. Look like bug?

Filed as https://issues.dlang.org/show_bug.cgi?id=23985

You can work around it by marking parameter a as return scope

June 11, 2023
On 6/9/23 06:05, An Pham wrote:
> Getting with below error for following codes. Look like bug?
> onlineapp.d(61): Error: scope variable `a` assigned to non-scope parameter `a` calling `foo`
> 
>      @safe:
> 
>      struct A(S = string)
>      {
>      @safe:
>          S s;
>          void delegate() c;
>      }
> 
>      struct B(S = string)
>      {
>      @safe:
>          @disable this();
> 
>          this(C!S c, A!S a)
>          {
>              this.c = c;
>              this.a = a;
>          }
> 
>          C!S foo()
>          {
>              return c;
>          }
> 
>          A!S a;
>          C!S c;
>      }
> 
>      class C(S = string)
>      {
>      @safe:
>          C!S foo(A!S a)
>          {
>              auto o = new Object();
>              return foo2(o, a);
>          }
> 
>          C!S foo2(Object n, A!S a)
>          {
>              auto b = B!S(this, a);
>               return b.foo();
>          }
>      }
> 
>      unittest
>      {
>          static struct X
>          {
>          @safe:
>              void foo3()
>              {
>              }
>          }
> 
>          X x;
>          A!string a;
>          a.s = "foo";
>          a.c = &x.foo3;
>          auto c = new C!string();
>          c.foo(a);
>      }
> 
> void main()
> {
> }
> 

I think the behavior you are seeing here is by design. There are two things happening:

- There is no `scope` inference for virtual methods, as it is impossible to get the inference right without knowing all overriding methods in advance.

- You cannot mark the parameter `a` `scope`, because the lifetimes of `this` and `a` become conflated within `b` in the body of `foo2` when calling the constructor of `B!S`, and `this` is subsequently escaped.

As Dennis points out, a workaround is to mark the parameter `a` `return scope`. However, this may lead to other problems down the line, as `a` is never actually escaped.