digitalmars.D - Understanding DIP 1000 semantics -- Where's the bug?
- Mike Franklin (55/55) Sep 21 2019 @safe:
- Suleyman (3/5) Sep 21 2019 This looks like the right answer. I don't see any local reference
- Max Haughton (3/9) Sep 21 2019 This would involve inferring scope on non templates, which I
- Sebastiaan Koppe (5/11) Sep 22 2019 When calling a function the compiler only looks at the function
- Sebastiaan Koppe (6/7) Sep 22 2019 I don't think there is.
- Mike Franklin (18/22) Sep 22 2019 If that's the case then shouldn't Exhibit B (copied below for
- Sebastiaan Koppe (22/32) Sep 22 2019 scope only works on pointers.
- Mike Franklin (8/32) Sep 22 2019 I wouldn't say that `scope` only works for pointers, but rather
- Sebastiaan Koppe (7/13) Sep 22 2019 I know that an argument passed to a `ref int i` parameter is
- ag0aep6g (32/75) Sep 22 2019 As far as I understand, converting a `ref` to a pointer is still allowed...
- Mike Franklin (5/34) Sep 22 2019 Interestingly, taking the address of a `ref` in `@safe` code is
- Jacob Carlborg (8/49) Sep 22 2019 With DIP1000 enabled the compiler can figure out that it is safe to take...
- ag0aep6g (9/13) Sep 22 2019 That's just the normal pre-DIP-1000 error you get when taking the
- Olivier FAURE (18/23) Sep 23 2019 It's allowed as long as you don't escape... wait, no, it's always
- Sebastiaan Koppe (3/19) Sep 23 2019 Well, dip1000 doesn't do data-flow analyses. Which means the
- Meta (7/31) Sep 23 2019 AFAICT, according to dip1000 this code should not allow the
- Meta (25/30) Sep 23 2019 Specifically, this breaks rule 1 from DIP1000:
- Sebastiaan Koppe (3/10) Sep 23 2019 It does all those things when you replace the body of foo with
- ag0aep6g (24/35) Sep 23 2019 Not really. When you do that, DMD rejects the `return` statement,
- Sebastiaan Koppe (21/52) Sep 24 2019 But that is what you want right? You want it to error out.
- ag0aep6g (39/63) Sep 24 2019 Not at that point. Unless `ref` implies restrictions that pointers don't...
- Olivier FAURE (25/42) Sep 24 2019 That's a bad comparison. Refs aren't equivalent to unqualified
- ag0aep6g (8/36) Sep 24 2019 I think we're almost on the same page now. There are two ways to attack
- ag0aep6g (4/6) Sep 24 2019 And now I notice that you're not Sebastiaan Koppe, Olivier. I thought
- Sebastiaan Koppe (4/25) Sep 24 2019 Where does it say that? Would be good though.
- Walter Bright (2/11) Sep 25 2019 That's a bug. Not sure how I missed that. `a` should be inferred as `sco...
- Walter Bright (2/3) Sep 25 2019 https://issues.dlang.org/show_bug.cgi?id=20245
- Sebastiaan Koppe (3/15) Sep 26 2019 That is interesting. Thanks for the clear up.
- Jacob Carlborg (10/72) Sep 22 2019 I think this is working as intended.
- Mike Franklin (6/8) Sep 22 2019 Ok, that is an interesting observation. If that's the case, then
- Jacob Carlborg (4/8) Sep 22 2019 Yes.
- Mike Franklin (6/12) Sep 22 2019 According to DIP1000
- Mike Franklin (30/44) Sep 22 2019 Yes, `scope ref` is definitely a thing. Compare the following.
- Olivier FAURE (4/8) Sep 23 2019 Huh.
- Walter Bright (8/9) Sep 25 2019 int* foo(scope ref int* p, ref int* q)
- Mike Franklin (6/8) Sep 22 2019 Actually, ag0aep6g's demonstration at
- Sebastiaan Koppe (3/12) Sep 22 2019 Yes. The compiler only looks at the signature when it is checking
- Jacob Carlborg (7/11) Sep 22 2019 That might be a bug in the compiler. Adding `scope` to the reference
- Walter Bright (4/6) Sep 25 2019 `ref` semantics are set up so a ref cannot escape the function it is pas...
safe: // Exhibit A //-------------------------- int getValue1(int* i) { return *i; } int* foo1() { int value; int[] y; y ~= getValue1(&value); //Error: reference to local variable `value` assigned to non-scope parameter `i` calling `onlineapp.getValue1` return &y[0]; } // Exhibit B //-------------------------- int getValue2(ref int i) { return i; } int* foo2() { int value; int[] y; y ~= getValue2(value); // No error return &y[0]; } // Exhibit C (Same as Exhibit A, but with `scope` attribute on `i`) //-------------------------- int getValue3(scope int* i) { return *i; } int* foo3() { int value; int[] y; y ~= getValue3(&value); // No error return &y[0]; } Compile with `-preview=dip1000`. See it live at https://run.dlang.io/is/0zODCr So, there's an inconsistency. Possibility 1: Exhibit A should not emit an error Possibility 2: Exhibit B should require the `scope` attribute on `i` or emit an error if `foo2`. Which is it? Where's the bug? Shouldn't the compiler treat `int* i` and `ref i` consistently? Thanks for the help. Mike
Sep 21 2019
On Sunday, 22 September 2019 at 02:29:49 UTC, Mike Franklin wrote:Possibility 1: Exhibit A should not emit an errorThis looks like the right answer. I don't see any local reference escaping in the entire program.
Sep 21 2019
On Sunday, 22 September 2019 at 02:52:17 UTC, Suleyman wrote:On Sunday, 22 September 2019 at 02:29:49 UTC, Mike Franklin wrote:This would involve inferring scope on non templates, which I believe would require modifying the specification.Possibility 1: Exhibit A should not emit an errorThis looks like the right answer. I don't see any local reference escaping in the entire program.
Sep 21 2019
On Sunday, 22 September 2019 at 02:52:17 UTC, Suleyman wrote:On Sunday, 22 September 2019 at 02:29:49 UTC, Mike Franklin wrote:When calling a function the compiler only looks at the function signature. You can never pass a scope int* to a function accepting int*, regardless of whether the body of the function escapes it.Possibility 1: Exhibit A should not emit an errorThis looks like the right answer. I don't see any local reference escaping in the entire program.
Sep 22 2019
On Sunday, 22 September 2019 at 02:29:49 UTC, Mike Franklin wrote:So, there's an inconsistency.I don't think there is. I think that taking a reference of a local variable should result in a scope T*. In that case `&value` in `y ~= getValue1(&value);` should result in a scope int*.
Sep 22 2019
On Sunday, 22 September 2019 at 07:01:42 UTC, Sebastiaan Koppe wrote:I think that taking a reference of a local variable should result in a scope T*. In that case `&value` in `y ~= getValue1(&value);` should result in a scope int*.If that's the case then shouldn't Exhibit B (copied below for convenience) require getValue2` to be `int getValue2(scope ref int i)`? If not, what's the justification? // Exhibit B //-------------------------- int getValue2(ref int i) { return i; } int* foo2() { int value; int[] y; y ~= getValue2(value); // No error return &y[0]; }
Sep 22 2019
On Sunday, 22 September 2019 at 07:54:12 UTC, Mike Franklin wrote:On Sunday, 22 September 2019 at 07:01:42 UTC, Sebastiaan Koppe wrote:scope only works on pointers. --- safe: // Exhibit B //-------------------------- struct P(T) { T t; } alias Value1 = P!(int); alias Value2 = P!(int*); Value1 getValue1(ref Value1 v) { return v; } Value2 getValue2(ref Value2 v) { return v; } void foo2() { scope Value1 value1; scope Value2 value2; getValue1(value1); // No error getValue2(value2); // Error: scope variable value2 assigned to non-scope parameter v calling onlineapp.getValue2 } ---I think that taking a reference of a local variable should result in a scope T*. In that case `&value` in `y ~= getValue1(&value);` should result in a scope int*.If that's the case then shouldn't Exhibit B (copied below for convenience) require getValue2` to be `int getValue2(scope ref int i)`? If not, what's the justification?
Sep 22 2019
On Sunday, 22 September 2019 at 10:09:14 UTC, Sebastiaan Koppe wrote:I wouldn't say that `scope` only works for pointers, but rather `scope` only works for references (i.e. pointers and `ref` types). The issue is that `ref int i` behaves differently than `int* i`. I understand that scope doesn't make any sense for something like `int i` because it's not a "reference type" (for lack of a better term).If that's the case then shouldn't Exhibit B (copied below for convenience) require getValue2` to be `int getValue2(scope ref int i)`? If not, what's the justification?scope only works on pointers. --- safe: // Exhibit B //-------------------------- struct P(T) { T t; } alias Value1 = P!(int); alias Value2 = P!(int*); Value1 getValue1(ref Value1 v) { return v; } Value2 getValue2(ref Value2 v) { return v; } void foo2() { scope Value1 value1; scope Value2 value2; getValue1(value1); // No error getValue2(value2); // Error: scope variable value2 assigned to non-scope parameter v calling onlineapp.getValue2 } ---
Sep 22 2019
On Sunday, 22 September 2019 at 10:24:39 UTC, Mike Franklin wrote:I wouldn't say that `scope` only works for pointers, but rather `scope` only works for references (i.e. pointers and `ref` types). The issue is that `ref int i` behaves differently than `int* i`. I understand that scope doesn't make any sense for something like `int i` because it's not a "reference type" (for lack of a better term).I know that an argument passed to a `ref int i` parameter is implemented with a pointer. And you would expect dip1000 to apply there as well, but the underlying type is still `int`, and scope only works on pointers. Don't get me wrong, I rather see this work with value types as well, but this is how it currently works.
Sep 22 2019
On 22.09.19 04:29, Mike Franklin wrote:safe: // Exhibit A //-------------------------- int getValue1(int* i) { return *i; } int* foo1() { int value; int[] y; y ~= getValue1(&value); //Error: reference to local variable `value` assigned to non-scope parameter `i` calling `onlineapp.getValue1` return &y[0]; } // Exhibit B //-------------------------- int getValue2(ref int i) { return i; } int* foo2() { int value; int[] y; y ~= getValue2(value); // No error return &y[0]; }[...]So, there's an inconsistency. Possibility 1: Exhibit A should not emit an error Possibility 2: Exhibit B should require the `scope` attribute on `i` or emit an error if `foo2`. Which is it? Where's the bug? Shouldn't the compiler treat `int* i` and `ref i` consistently?As far as I understand, converting a `ref` to a pointer is still allowed with DIP 1000. So they should be handled the same. Then it's possibility 2: `foo2` should error. Your code can easily be expanded to demonstrate memory corruption: ---- safe: immutable(int)* ip; // Exhibit B //-------------------------- int getValue2(ref immutable int i) { immutable int* my_ip = &i; ip = my_ip; return i; } int* foo2() { immutable int value = 42; int[] y; y ~= getValue2(value); // No error return &y[0]; } void main() { auto p = foo2(); import std.stdio; writeln(*ip); /* Prints "42". */ writeln(*ip); /* Prints garbage. An immutable value has changed. */ } ----
Sep 22 2019
On Sunday, 22 September 2019 at 08:49:14 UTC, ag0aep6g wrote:Then it's possibility 2: `foo2` should error. Your code can easily be expanded to demonstrate memory corruption: ---- safe: immutable(int)* ip; // Exhibit B //-------------------------- int getValue2(ref immutable int i) { immutable int* my_ip = &i; ip = my_ip; return i; } int* foo2() { immutable int value = 42; int[] y; y ~= getValue2(value); // No error return &y[0]; } void main() { auto p = foo2(); import std.stdio; writeln(*ip); /* Prints "42". */ writeln(*ip); /* Prints garbage. An immutable value has changed. */ } ----Interestingly, taking the address of a `ref` in ` safe` code is prevented, but only when compiling *without* -preview=dip1000. See https://run.dlang.io/is/GArFzk That's seems like a bug to me.
Sep 22 2019
On 2019-09-22 19:26, Mike Franklin wrote:On Sunday, 22 September 2019 at 08:49:14 UTC, ag0aep6g wrote:With DIP1000 enabled the compiler can figure out that it is safe to take the address of `i` and assign it to `my_ip`. It seems to fail to recognize that assigning to `ip` is unsafe. Perhaps that requires data flow analysis. If you assign directly to `ip` it fails to compile. -- /Jacob CarlborgThen it's possibility 2: `foo2` should error. Your code can easily be expanded to demonstrate memory corruption: ---- safe: immutable(int)* ip; // Exhibit B //-------------------------- int getValue2(ref immutable int i) { immutable int* my_ip = &i; ip = my_ip; return i; } int* foo2() { immutable int value = 42; int[] y; y ~= getValue2(value); // No error return &y[0]; } void main() { auto p = foo2(); import std.stdio; writeln(*ip); /* Prints "42". */ writeln(*ip); /* Prints garbage. An immutable value has changed. */ } ----Interestingly, taking the address of a `ref` in ` safe` code is prevented, but only when compiling *without* -preview=dip1000. See https://run.dlang.io/is/GArFzk That's seems like a bug to me.
Sep 22 2019
On Sunday, 22 September 2019 at 17:26:20 UTC, Mike Franklin wrote:Interestingly, taking the address of a `ref` in ` safe` code is prevented, but only when compiling *without* -preview=dip1000. See https://run.dlang.io/is/GArFzk That's seems like a bug to me.That's just the normal pre-DIP-1000 error you get when taking the address of any local (`ref` or not). Allowing `&some_local` is pretty much the point of DIP 1000, so it's expected that the old error doesn't appear anymore. There is a bug for sure, though. If `ref` implies `scope`, there should be a new error ("cannot take address of scope parameter"). If `scope` is not implied, it's ok that the function compiles, but then it shouldn't be possible to call it on a local.
Sep 22 2019
On Sunday, 22 September 2019 at 17:54:48 UTC, ag0aep6g wrote:There is a bug for sure, though. If `ref` implies `scope`, there should be a new error ("cannot take address of scope parameter"). If `scope` is not implied, it's ok that the function compiles, but then it shouldn't be possible to call it on a local.It's allowed as long as you don't escape... wait, no, it's always allowed. Whoops. The following code compiles with -dip1000. safe: int* foo(ref int x) { int* a = &x; return a; } void main() { int* p; { int x; p = foo(x); } *p = 1; // Memory corruption } That's a bug.
Sep 23 2019
On Monday, 23 September 2019 at 08:46:18 UTC, Olivier FAURE wrote:Whoops. The following code compiles with -dip1000. safe: int* foo(ref int x) { int* a = &x; return a; } void main() { int* p; { int x; p = foo(x); } *p = 1; // Memory corruption } That's a bug.Well, dip1000 doesn't do data-flow analyses. Which means the compiler doesn't see that `x` escapes through `a`.
Sep 23 2019
On Monday, 23 September 2019 at 18:39:03 UTC, Sebastiaan Koppe wrote:On Monday, 23 September 2019 at 08:46:18 UTC, Olivier FAURE wrote:AFAICT, according to dip1000 this code should not allow the result of `foo(x)` to be assigned to p, as it has a longer lifetime. Likewise, it should not allow foo to return &x without the parameter being annotated with `return`. This looks like a bug in the implementation.Whoops. The following code compiles with -dip1000. safe: int* foo(ref int x) { int* a = &x; return a; } void main() { int* p; { int x; p = foo(x); } *p = 1; // Memory corruption } That's a bug.Well, dip1000 doesn't do data-flow analyses. Which means the compiler doesn't see that `x` escapes through `a`.
Sep 23 2019
On Monday, 23 September 2019 at 18:46:40 UTC, Meta wrote:AFAICT, according to dip1000 this code should not allow the result of `foo(x)` to be assigned to p, as it has a longer lifetime. Likewise, it should not allow foo to return &x without the parameter being annotated with `return`. This looks like a bug in the implementation.Specifically, this breaks rule 1 from DIP1000: A scope variable can only be initialized and assigned from values that have lifetimes longer than the variable's lifetime. (As a consequence a scope variable can only be assigned to scope variables that have shorter lifetime.) This rule is broken by `p = foo(x)`, since p has a longer lifetime than x. Marking the `ref int x` parameter of foo `scope`, `return scope` or `scope return` did not help, but marking it as `return` *did* cause the compiler to correctly reject this code. Also changing foo to: int* foo(ref int x) { return &x; } OR int* foo(int* x) { return x; } ... p = foo(&x); Causes the compiler to correctly reject this code, so the problem seems to specifically be with returning the address of a ref parameter stored in a local variable.
Sep 23 2019
On Monday, 23 September 2019 at 18:46:40 UTC, Meta wrote:On Monday, 23 September 2019 at 18:39:03 UTC, Sebastiaan KoppeIt does all those things when you replace the body of foo with just `return &x;`.Well, dip1000 doesn't do data-flow analyses. Which means the compiler doesn't see that `x` escapes through `a`.AFAICT, according to dip1000 this code should not allow the result of `foo(x)` to be assigned to p, as it has a longer lifetime. Likewise, it should not allow foo to return &x without the parameter being annotated with `return`.
Sep 23 2019
On Monday, 23 September 2019 at 19:11:46 UTC, Sebastiaan Koppe wrote:On Monday, 23 September 2019 at 18:46:40 UTC, Meta wrote:Not really. When you do that, DMD rejects the `return` statement, not the assignment `p = foo(x);`. It works as expected when you mark the parameter with `return`. This has nothing to do with missing flow analysis. It's just a bug. Maybe there is some clever idea regarding `ref` parameters and the implementation is just buggy, or maybe the bug is that they're not treated just like pointers. I'd suggest treating them just like pointers. Seems to me that's the most straight-forward approach. Meaning: ---- safe: int* fp(int* p) { return p; } /* This works. */ int* fr(ref int r) { return &r; } /* So this should work too. */ void main() { int x; fp(&x); /* This doesn't work. */ fr(x); /* So this shouldn't work either. */ } ----On Monday, 23 September 2019 at 18:39:03 UTC, Sebastiaan KoppeIt does all those things when you replace the body of foo with just `return &x;`.Well, dip1000 doesn't do data-flow analyses. Which means the compiler doesn't see that `x` escapes through `a`.AFAICT, according to dip1000 this code should not allow the result of `foo(x)` to be assigned to p, as it has a longer lifetime. Likewise, it should not allow foo to return &x without the parameter being annotated with `return`.
Sep 23 2019
On Monday, 23 September 2019 at 20:50:30 UTC, ag0aep6g wrote:On Monday, 23 September 2019 at 19:11:46 UTC, Sebastiaan Koppe wrote:But that is what you want right? You want it to error out. My reasoning is as follows, because dataflow analyses is missing, you need to simplify the body of foo to `return &x`. Then the compilers errors out saying you are escaping a reference and maybe you need to add the return annotation. After you fix that the compiler errors out saying "Error: address of variable x assigned to p with longer lifetime". Which is exactly what we want. Ergo, missing dataflow analyses.On Monday, 23 September 2019 at 18:46:40 UTC, Meta wrote:Not really. When you do that, DMD rejects the `return` statement, not the assignment `p = foo(x);`.On Monday, 23 September 2019 at 18:39:03 UTC, Sebastiaan KoppeIt does all those things when you replace the body of foo with just `return &x;`.Well, dip1000 doesn't do data-flow analyses. Which means the compiler doesn't see that `x` escapes through `a`.AFAICT, according to dip1000 this code should not allow the result of `foo(x)` to be assigned to p, as it has a longer lifetime. Likewise, it should not allow foo to return &x without the parameter being annotated with `return`.It works as expected when you mark the parameter with `return`. This has nothing to do with missing flow analysis. It's just a bug.I agree that it will be better to get the 2 errors the first time, but is that a bug?---- safe: int* fp(int* p) { return p; } /* This works. */ int* fr(ref int r) { return &r; } /* So this should work too. */ void main() { int x; fp(&x); /* This doesn't work. */ fr(x); /* So this shouldn't work either. */ } ----1) fp isn't `scope int*` so the function body compiles without errors; 2) you cannot return a reference to a local, so the function `fr` errors out correctly; 3) you cannot pass a reference to a local to a non-scope argument, so fp(&x) errors out correctly; 4) it is perfectly valid to pass a value to a ref parameter; Still, I agree that it will be better to get the 2 errors the first time.
Sep 24 2019
On 24.09.19 10:05, Sebastiaan Koppe wrote:On Monday, 23 September 2019 at 20:50:30 UTC, ag0aep6g wrote:[...]Not at that point. Unless `ref` implies restrictions that pointers don't have, `foo` should compile.Not really. When you do that, DMD rejects the `return` statement, not the assignment `p = foo(x);`.But that is what you want right? You want it to error out.My reasoning is as follows, because dataflow analyses is missing, you need to simplify the body of foo to `return &x`. Then the compilers errors out saying you are escaping a reference and maybe you need to add the return annotation. After you fix that the compiler errors out saying "Error: address of variable x assigned to p with longer lifetime". Which is exactly what we want. Ergo, missing dataflow analyses.Aside: Your reasoning is circular. You start and end with "dataflow analysis is missing". When using a pointer instead of `ref`, the code is rejected as expected even with the more complex body: ---- safe: int* foo(int* x) { int* a = x; return x; } void main() { int* p; { int x; p = foo(&x); /* error here */ } } ---- So it works just fine with pointers. But maybe `ref`s are intentionally treated differently from pointers. And maybe that means that they need more advanced dataflow analysis which isn't currently implemented. If that's so, it's a hole in -dip1000. So I'm saying: Just treat `ref`s like pointers and the trouble disappears. I suspect that it's actually intended to work like that, and we're just looking at a good old bug.Memory corruption in safe code is always a bug. [...]It works as expected when you mark the parameter with `return`. This has nothing to do with missing flow analysis. It's just a bug.I agree that it will be better to get the 2 errors the first time, but is that a bug?1) fp isn't `scope int*` so the function body compiles without errors;Yup.2) you cannot return a reference to a local, so the function `fr` errors out correctly;A `ref` parameter is significantly different from a true local. In the context of DIP 1000, it's more straight-forward to treat it like a pointer.3) you cannot pass a reference to a local to a non-scope argument, so fp(&x) errors out correctly;Yup.4) it is perfectly valid to pass a value to a ref parameter;Not when we treat `ref` like the pointer that it really is. Pretending that `ref` is anything but a pointer wearing a fancy hat just leads to headaches like the one we're experiencing right now.
Sep 24 2019
On Tuesday, 24 September 2019 at 13:35:04 UTC, ag0aep6g wrote:When using a pointer instead of `ref`, the code is rejected as expected even with the more complex body: ---- safe: int* foo(int* x) { int* a = x; return x; } void main() { int* p; { int x; p = foo(&x); /* error here */ } } ----That's a bad comparison. Refs aren't equivalent to unqualified pointers, they're equivalent to a scope pointers. By the way, with the following code: safe: int* foo(scope int* x) { int* a = x; return a; // Compile error: scope variable *a* may not be returned } void main() { int* p; { int x; p = foo(&x); } *p = 1; // Memory corruption } the compiler correctly identifies that the problem isn't "foo(&x)", but "return a", and gives an appropriate error message. So the problem isn't a lack of flow analysis or wrong ref semantics, it's that ref isn't implemented the same way as scope. (Anyone feel like submitting a bug report? I don't have an account)
Sep 24 2019
On 24.09.19 19:01, Olivier FAURE wrote:Refs aren't equivalent to unqualified pointers, they're equivalent to a scope pointers.Whether `ref` does or should imply `scope` is topic of the discussion.By the way, with the following code: safe: int* foo(scope int* x) { int* a = x; return a; // Compile error: scope variable *a* may not be returned } void main() { int* p; { int x; p = foo(&x); } *p = 1; // Memory corruption } the compiler correctly identifies that the problem isn't "foo(&x)", but "return a", and gives an appropriate error message. So the problem isn't a lack of flow analysis or wrong ref semantics, it's that ref isn't implemented the same way as scope.I think we're almost on the same page now. There are two ways to attack the issue: 1) Make `ref int` more like `scope int*`. I.e., disallow `return a;`. 2) Make `ref int` more like (unqualified) `int*`. I.e., disallow `foo(&x);`.
Sep 24 2019
On 24.09.19 19:49, ag0aep6g wrote:On 24.09.19 19:01, Olivier FAURE wrote:[...]I think we're almost on the same page now.And now I notice that you're not Sebastiaan Koppe, Olivier. I thought your post was by him. Sorry for the additional confusion.
Sep 24 2019
On Tuesday, 24 September 2019 at 17:01:56 UTC, Olivier FAURE wrote:That's a bad comparison. Refs aren't equivalent to unqualified pointers, they're equivalent to a scope pointers.Where does it say that? Would be good though.By the way, with the following code: safe: int* foo(scope int* x) { int* a = x; return a; // Compile error: scope variable *a* may not be returned } void main() { int* p; { int x; p = foo(&x); } *p = 1; // Memory corruption } the compiler correctly identifies that the problem isn't "foo(&x)", but "return a", and gives an appropriate error message.That is because scope is inferred on `int* a`.
Sep 24 2019
On 9/23/2019 1:46 AM, Olivier FAURE wrote:The following code compiles with -dip1000. safe: int* foo(ref int x) { int* a = &x; return a; }That's a bug. Not sure how I missed that. `a` should be inferred as `scope`.
Sep 25 2019
On 9/25/2019 10:36 PM, Walter Bright wrote:That's a bug. Not sure how I missed that. `a` should be inferred as `scope`.https://issues.dlang.org/show_bug.cgi?id=20245
Sep 25 2019
On Thursday, 26 September 2019 at 05:36:39 UTC, Walter Bright wrote:On 9/23/2019 1:46 AM, Olivier FAURE wrote:That is interesting. Thanks for the clear up.The following code compiles with -dip1000. safe: int* foo(ref int x) { int* a = &x; return a; }That's a bug. Not sure how I missed that. `a` should be inferred as `scope`.
Sep 26 2019
On 2019-09-22 04:29, Mike Franklin wrote:safe: // Exhibit A //-------------------------- int getValue1(int* i) { return *i; } int* foo1() { int value; int[] y; y ~= getValue1(&value); //Error: reference to local variable `value` assigned to non-scope parameter `i` calling `onlineapp.getValue1` return &y[0]; } // Exhibit B //-------------------------- int getValue2(ref int i) { return i; } int* foo2() { int value; int[] y; y ~= getValue2(value); // No error return &y[0]; } // Exhibit C (Same as Exhibit A, but with `scope` attribute on `i`) //-------------------------- int getValue3(scope int* i) { return *i; } int* foo3() { int value; int[] y; y ~= getValue3(&value); // No error return &y[0]; } Compile with `-preview=dip1000`. See it live at https://run.dlang.io/is/0zODCr So, there's an inconsistency. Possibility 1: Exhibit A should not emit an error Possibility 2: Exhibit B should require the `scope` attribute on `i` or emit an error if `foo2`. Which is it? Where's the bug? Shouldn't the compiler treat `int* i` and `ref i` consistently?I think this is working as intended. In Exhibit A, `i` is a pointer and may escape the function `getValue1`. Therefore it's not allowed to pass the address of a local variable to it. In Exhibit B, `i` is a reference and cannot escape the function `getValue2`. In Exhibit C, the `scope` annotation of `i` makes sure that `i` cannot escape the function `getValue3`. Therefore it's ok to pass the address of a local variable to it. -- /Jacob Carlborg
Sep 22 2019
On Sunday, 22 September 2019 at 11:03:29 UTC, Jacob Carlborg wrote:In Exhibit B, `i` is a reference and cannot escape the function `getValue2`.Ok, that is an interesting observation. If that's the case, then I don't see any reason to ever annotate a `ref` parameter with `scope`. In other words, for `ref` parameters `scope` is inferred (or implied). Do you agree with that?
Sep 22 2019
On 2019-09-22 13:22, Mike Franklin wrote:Ok, that is an interesting observation. If that's the case, then I don't see any reason to ever annotate a `ref` parameter with `scope`. In other words, for `ref` parameters `scope` is inferred (or implied). Do you agree with that?Yes. -- /Jacob Carlborg
Sep 22 2019
On Sunday, 22 September 2019 at 16:32:02 UTC, Jacob Carlborg wrote:On 2019-09-22 13:22, Mike Franklin wrote:According to DIP1000 (https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1000.m #identity-function) `scope ref` is a thing, so I guess `scope` can't be inferred or implied for `ref`. I don't know. I think Walter is the only one that can clear this up.Ok, that is an interesting observation. If that's the case, then I don't see any reason to ever annotate a `ref` parameter with `scope`. In other words, for `ref` parameters `scope` is inferred (or implied). Do you agree with that?Yes.
Sep 22 2019
On Sunday, 22 September 2019 at 17:01:59 UTC, Mike Franklin wrote:On Sunday, 22 September 2019 at 16:32:02 UTC, Jacob Carlborg wrote:Yes, `scope ref` is definitely a thing. Compare the following. // Exhibit C safe: struct A { int* ptr; } int* gptr; void foo(ref A a) { gptr = a.ptr; // error, can't leak borrowed a.ptr into global context } https://run.dlang.io/is/ZU780c // Exhibit D safe: struct A { int* ptr; } int* gptr; void foo(scope ref A a) { gptr = a.ptr; // error, can't leak borrowed a.ptr into global context } https://run.dlang.io/is/ZU780c This was taken from the discussion at https://forum.dlang.org/post/twgsrfcolypurgylhizh forum.dlang.orgOn 2019-09-22 13:22, Mike Franklin wrote:According to DIP1000 (https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1000.m #identity-function) `scope ref` is a thing, so I guess `scope` can't be inferred or implied for `ref`. I don't know. I think Walter is the only one that can clear this up.Ok, that is an interesting observation. If that's the case, then I don't see any reason to ever annotate a `ref` parameter with `scope`. In other words, for `ref` parameters `scope` is inferred (or implied). Do you agree with that?Yes.
Sep 22 2019
On Sunday, 22 September 2019 at 17:17:40 UTC, Mike Franklin wrote:Yes, `scope ref` is definitely a thing. Compare the following. [...] This was taken from the discussion at https://forum.dlang.org/post/twgsrfcolypurgylhizh forum.dlang.orgHuh. I knew that was a thing, but I assumed the semantics were emergent. I didn't realize they might be intentional.
Sep 23 2019
On 9/22/2019 10:01 AM, Mike Franklin wrote:I don't know. I think Walter is the only one that can clear this up.int* foo(scope ref int* p, ref int* q) { return p; // error return q; // ok } I.e. with `ref int*` there are two pointers here - the ref pointer and the * pointer. The scope in `scope ref int*` refers to the * pointer, not the ref pointer.
Sep 25 2019
On Sunday, 22 September 2019 at 11:03:29 UTC, Jacob Carlborg wrote:In Exhibit B, `i` is a reference and cannot escape the function `getValue2`.Actually, ag0aep6g's demonstration at https://forum.dlang.org/post/qm7cib$2n07$1 digitalmars.com proves that statement false. Unless it is a bug in the compiler that one can convert a `ref` to a pointer in ` safe` code.
Sep 22 2019
On Sunday, 22 September 2019 at 11:42:44 UTC, Mike Franklin wrote:On Sunday, 22 September 2019 at 11:03:29 UTC, Jacob Carlborg wrote:Yes. The compiler only looks at the signature when it is checking the call site of said function.In Exhibit B, `i` is a reference and cannot escape the function `getValue2`.Actually, ag0aep6g's demonstration at https://forum.dlang.org/post/qm7cib$2n07$1 digitalmars.com proves that statement false. Unless it is a bug in the compiler that one can convert a `ref` to a pointer in ` safe` code.
Sep 22 2019
On 2019-09-22 13:42, Mike Franklin wrote:Actually, ag0aep6g's demonstration at https://forum.dlang.org/post/qm7cib$2n07$1 digitalmars.com proves that statement false. Unless it is a bug in the compiler that one can convert a `ref` to a pointer in ` safe` code.That might be a bug in the compiler. Adding `scope` to the reference doesn't help. But with DIP1000, there's some documentation missing and I think only Walter knows how it should behave. -- /Jacob Carlborg
Sep 22 2019
On 9/21/2019 7:29 PM, Mike Franklin wrote:Which is it? Where's the bug? Shouldn't the compiler treat `int* i` and `ref i` consistently?`ref` semantics are set up so a ref cannot escape the function it is passed to, hence there's no error. Pointers have no such restrictions, and so require an extra annotation `scope` to acquire the restriction.
Sep 25 2019