Thread overview
return scope ref outlives the scope of the argument
Jun 25, 2019
Eugene Wissner
Jun 25, 2019
Jonathan M Davis
Jun 25, 2019
Eugene Wissner
Jun 25, 2019
Jonathan M Davis
Jun 25, 2019
Eugene Wissner
Jun 25, 2019
Jonathan M Davis
Jun 25, 2019
Eugene Wissner
June 25, 2019
struct Container
{
}

static struct Inserter
{
    private Container* container;

    private this(return scope ref Container container) @trusted
    {
        this.container = &container;
    }

}

auto func()()
{
    Container container;
    return Inserter(container);
}

void main()
{
    static assert(!is(typeof(func!())));
}

The code above compiles with dmd 2.085, but not 2.086 (with -preview=dip1000). What am I doing wrong?
June 25, 2019
On Tuesday, June 25, 2019 1:32:58 AM MDT Eugene Wissner via Digitalmars-d- learn wrote:
> struct Container
> {
> }
>
> static struct Inserter
> {
>      private Container* container;
>
>      private this(return scope ref Container container) @trusted
>      {
>          this.container = &container;
>      }
>
> }
>
> auto func()()
> {
>      Container container;
>      return Inserter(container);
> }
>
> void main()
> {
>      static assert(!is(typeof(func!())));
> }
>
> The code above compiles with dmd 2.085, but not 2.086 (with
> -preview=dip1000). What am I doing wrong?

You're storing a pointer to a scope variable. That's violating the entire point of scope. If something is scope, you can't store any kind of reference to it. And since container is a local variable in func, and Inserter tries to return from func with a pointer to container, you definitely have an @safety problem, because that pointer would be invalid once func returned.

- Jonathan M Davis



June 25, 2019
On Tuesday, 25 June 2019 at 11:16:47 UTC, Jonathan M Davis wrote:
> On Tuesday, June 25, 2019 1:32:58 AM MDT Eugene Wissner via Digitalmars-d- learn wrote:
>> struct Container
>> {
>> }
>>
>> static struct Inserter
>> {
>>      private Container* container;
>>
>>      private this(return scope ref Container container) @trusted
>>      {
>>          this.container = &container;
>>      }
>>
>> }
>>
>> auto func()()
>> {
>>      Container container;
>>      return Inserter(container);
>> }
>>
>> void main()
>> {
>>      static assert(!is(typeof(func!())));
>> }
>>
>> The code above compiles with dmd 2.085, but not 2.086 (with
>> -preview=dip1000). What am I doing wrong?
>
> You're storing a pointer to a scope variable. That's violating the entire point of scope. If something is scope, you can't store any kind of reference to it. And since container is a local variable in func, and Inserter tries to return from func with a pointer to container, you definitely have an @safety problem, because that pointer would be invalid once func returned.
>
> - Jonathan M Davis

So you're saying that func() shouldn't compile? And it is exactly what the assertion in the main function does: it asserts that the function cannot be instantiated. And it was true for 2.085 but it can be instantiated with 2.086.
June 25, 2019
On Tuesday, June 25, 2019 1:32:58 AM MDT Eugene Wissner via Digitalmars-d- learn wrote:
> struct Container
> {
> }
>
> static struct Inserter
> {
>      private Container* container;
>
>      private this(return scope ref Container container) @trusted
>      {
>          this.container = &container;
>      }
>
> }
>
> auto func()()
> {
>      Container container;
>      return Inserter(container);
> }
>
> void main()
> {
>      static assert(!is(typeof(func!())));
> }
>
> The code above compiles with dmd 2.085, but not 2.086 (with
> -preview=dip1000). What am I doing wrong?

Okay. I clearly looked over what you posted too quickly and assumed that the subject was the error that you were actually getting. The @trusted there is what's making the static asertion fail.

Inserter is able to compile with -dip1000 (or -preview=dip1000), because you marked it as @trusted, which throws away the scope checks. If you mark it @safe, it won't compile. Without -dip1000, I wouldn't have expected anything to be caught, but trying it on run.dlang.io, it looks like the return probably makes it fail, which I find surprising, since I didn't think that return had any effect without -dip25, but I haven't done much with return on parameters.

You'd have an easier time figuring out what's going on if you'd just not make func a template rather than use the static assertion, because then you'd see the compiler errors.

In any case, by using @trusted, you're getting around the scope compiler checks, which is why Inserter is able to compile with -dip1000. Without -dip1000, I'm not experience enough with return parameters to know what the compiler will or won't catch, but the code is an @safety probelm regardless. It does look like the behavior changed with 2.086 even without -dip1000 being used, which probably has something to do with how the compiler was changed for DIP 1000, though it probably wasn't on purpose, since in theory, the behavior shouldn't have changed without -dip1000, but I don't know.

- Jonathan M Davis



June 25, 2019
On Tuesday, 25 June 2019 at 12:04:27 UTC, Jonathan M Davis wrote:
> On Tuesday, June 25, 2019 1:32:58 AM MDT Eugene Wissner via Digitalmars-d- learn wrote:
>> struct Container
>> {
>> }
>>
>> static struct Inserter
>> {
>>      private Container* container;
>>
>>      private this(return scope ref Container container) @trusted
>>      {
>>          this.container = &container;
>>      }
>>
>> }
>>
>> auto func()()
>> {
>>      Container container;
>>      return Inserter(container);
>> }
>>
>> void main()
>> {
>>      static assert(!is(typeof(func!())));
>> }
>>
>> The code above compiles with dmd 2.085, but not 2.086 (with
>> -preview=dip1000). What am I doing wrong?
>
> Okay. I clearly looked over what you posted too quickly and assumed that the subject was the error that you were actually getting. The @trusted there is what's making the static asertion fail.
>
> Inserter is able to compile with -dip1000 (or -preview=dip1000), because you marked it as @trusted, which throws away the scope checks. If you mark it @safe, it won't compile. Without -dip1000, I wouldn't have expected anything to be caught, but trying it on run.dlang.io, it looks like the return probably makes it fail, which I find surprising, since I didn't think that return had any effect without -dip25, but I haven't done much with return on parameters.
>
> You'd have an easier time figuring out what's going on if you'd just not make func a template rather than use the static assertion, because then you'd see the compiler errors.
>
> In any case, by using @trusted, you're getting around the scope compiler checks, which is why Inserter is able to compile with -dip1000. Without -dip1000, I'm not experience enough with return parameters to know what the compiler will or won't catch, but the code is an @safety probelm regardless. It does look like the behavior changed with 2.086 even without -dip1000 being used, which probably has something to do with how the compiler was changed for DIP 1000, though it probably wasn't on purpose, since in theory, the behavior shouldn't have changed without -dip1000, but I don't know.
>
> - Jonathan M Davis

Yes, reduced code could be a bit better.

@trusted doesn't throw scope checks away (and it wouldn't make any sense since I don't see another way to make the code above safe). Try:

struct Container
{
}

private Container* stuff(return scope ref Container container) @trusted
{
    return &container;
}

auto func()
{
    Container container;
    return stuff(container);
}

It fails with -dip1000 and works without (as expected).

"return scope ref" parameter in the constructor means, that the constructed object has the same scope as the scope of the argument.

I just want to know whether the behaviour of 2.085 or 2.086 is correct and if it is an "improvement" in 2.086, what I'm doing wrong.
June 25, 2019
On Tuesday, June 25, 2019 6:32:35 AM MDT Eugene Wissner via Digitalmars-d- learn wrote:
> On Tuesday, 25 June 2019 at 12:04:27 UTC, Jonathan M Davis wrote:
> > On Tuesday, June 25, 2019 1:32:58 AM MDT Eugene Wissner via
> >
> > Digitalmars-d- learn wrote:
> >> struct Container
> >> {
> >> }
> >>
> >> static struct Inserter
> >> {
> >>
> >>      private Container* container;
> >>
> >>      private this(return scope ref Container container)
> >>
> >> @trusted
> >>
> >>      {
> >>
> >>          this.container = &container;
> >>
> >>      }
> >>
> >> }
> >>
> >> auto func()()
> >> {
> >>
> >>      Container container;
> >>      return Inserter(container);
> >>
> >> }
> >>
> >> void main()
> >> {
> >>
> >>      static assert(!is(typeof(func!())));
> >>
> >> }
> >>
> >> The code above compiles with dmd 2.085, but not 2.086 (with
> >> -preview=dip1000). What am I doing wrong?
> >
> > Okay. I clearly looked over what you posted too quickly and assumed that the subject was the error that you were actually getting. The @trusted there is what's making the static asertion fail.
> >
> > Inserter is able to compile with -dip1000 (or -preview=dip1000), because you marked it as @trusted, which throws away the scope checks. If you mark it @safe, it won't compile. Without -dip1000, I wouldn't have expected anything to be caught, but trying it on run.dlang.io, it looks like the return probably makes it fail, which I find surprising, since I didn't think that return had any effect without -dip25, but I haven't done much with return on parameters.
> >
> > You'd have an easier time figuring out what's going on if you'd just not make func a template rather than use the static assertion, because then you'd see the compiler errors.
> >
> > In any case, by using @trusted, you're getting around the scope compiler checks, which is why Inserter is able to compile with -dip1000. Without -dip1000, I'm not experience enough with return parameters to know what the compiler will or won't catch, but the code is an @safety probelm regardless. It does look like the behavior changed with 2.086 even without -dip1000 being used, which probably has something to do with how the compiler was changed for DIP 1000, though it probably wasn't on purpose, since in theory, the behavior shouldn't have changed without -dip1000, but I don't know.
> >
> > - Jonathan M Davis
>
> Yes, reduced code could be a bit better.
>
> @trusted doesn't throw scope checks away (and it wouldn't make any sense since I don't see another way to make the code above safe). Try:
>
> struct Container
> {
> }
>
> private Container* stuff(return scope ref Container container)
> @trusted
> {
>      return &container;
> }
>
> auto func()
> {
>      Container container;
>      return stuff(container);
> }
>
> It fails with -dip1000 and works without (as expected).
>
> "return scope ref" parameter in the constructor means, that the constructed object has the same scope as the scope of the argument.
>
> I just want to know whether the behaviour of 2.085 or 2.086 is correct and if it is an "improvement" in 2.086, what I'm doing wrong.

scope is only checked in @safe code. If you use @trusted, it's not checked. At that point, it's up to you to make sure that no references escape, and taking the address of the scope variable and storing it is definitely escaping a reference to it. As I understand it, what you're trying to do is not something that works with scope. scope objects can be passed around, but they can't be stored like this. Unfortunately, the primary source of documentation for DIP 1000 is the DIP itself, and it was "superceded," meaning that the actual implementation does not match what's in the DIP, and I don't know how it differs. Unfortunately, for the most part, with DIP 1000, you just have to see what works, but I am very sure that using @trusted or @system means that the scope checks are off, and if you have to use @trusted to make something work with scope, then what you're trying to do doesn't work with scope.

I can say however that if you can get the compiler to let you return a reference to a local variable like you're doing here with only @safe code (so, no @trusted), then it's a definitely a compiler bug.

- Jonathan M Davis



June 25, 2019
On Tuesday, 25 June 2019 at 07:32:58 UTC, Eugene Wissner wrote:
> struct Container
> {
> }
>
> static struct Inserter
> {
>     private Container* container;
>
>     private this(return scope ref Container container) @trusted
>     {
>         this.container = &container;
>     }
>
> }
>
> auto func()()
> {
>     Container container;
>     return Inserter(container);
> }
>
> void main()
> {
>     static assert(!is(typeof(func!())));
> }
>
> The code above compiles with dmd 2.085, but not 2.086 (with -preview=dip1000). What am I doing wrong?

Whatever. https://issues.dlang.org/show_bug.cgi?id=20006