www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - return scope ref outlives the scope of the argument

reply Eugene Wissner <belka caraus.de> writes:
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?
Jun 25 2019
next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
Jun 25 2019
parent Eugene Wissner <belka caraus.de> writes:
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.
Jun 25 2019
prev sibling next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
Jun 25 2019
parent reply Eugene Wissner <belka caraus.de> writes:
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.
Jun 25 2019
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
Jun 25 2019
prev sibling parent Eugene Wissner <belka caraus.de> writes:
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
Jun 25 2019