digitalmars.D - 'scope' confusion
- Andy (37/37) Apr 07 2022 In the following code, I can pass a non-`scope` argument to a
- Dukc (23/26) Apr 08 2022 I was already writing "these both should pass", but then I
- Andy (29/56) Apr 14 2022 Is there any way to do this safely?
- Paul Backus (25/48) Apr 14 2022 No, because `scope` isn't transitive. If the above example were
- Walter Bright (6/6) Apr 15 2022 The trouble here is the scope array is a static array. This means the sc...
- Salih Dincer (3/11) Apr 15 2022 A very good explanation. Thank you very much for my own name.
- Andy (36/36) Apr 30 2022 I have another puzzle related to scoping. In this case it's about
- Paul Backus (16/25) Apr 30 2022 Simplified example:
- Dennis (8/11) May 02 2022 True. I thought this wouldn't be a problem in my fix for [Issue
In the following code, I can pass a non-`scope` argument to a function expecting a `scope` parameter, but I can't pass a `scope` argument. That seems backwards? ``` safe nogc pure nothrow: void main() { string[2] notScope = ["one", "two"]; f(notScope); scope string[2] yesScope = ["one", "two"]; f(yesScope); } void f(scope string[] expectingScope) {} ``` Output (of `dmd a.d -preview=dip1000`): ``` a.d(8): Error: cannot take address of `scope` local `yesScope` in ` safe` function `main` a.d(8): Error: cannot cast expression `yesScope` of type `string[2]` to `string[]` ``` The error only happens in dmd 2.099.0 -- it didn't happen in 2.098.1 . ------ There is another even simpler case that doesn't work in either version: ``` safe nogc pure nothrow: void main() { scope string[2] xs = ["one", "two"]; foreach (scope immutable string x; xs) {} } ``` Output (of `dmd a.d -preview=dip1000`): ``` a.d(5): Error: cannot take address of `scope` local `xs` in ` safe` function `main` ```
Apr 07 2022
On Friday, 8 April 2022 at 05:52:09 UTC, Andy wrote:In the following code, I can pass a non-`scope` argument to a function expecting a `scope` parameter, but I can't pass a `scope` argument. That seems backwards?I was already writing "these both should pass", but then I noticed what's wrong. The scope at `f` means you cannot escape the outer array. But on `yesScope`, it means that you cannot escape the strings! In other words, this fails for the same reason that this would fail: ``` safe void main() { scope immutable(char)[] a = "one"; scope immutable(char)[] b = "two"; auto array = [a, b]; } ``` It fails, because D does not have a concept of an array holding scope variables (or a pointer pointing to a scope variable). Only the outernmost array or pointer can be `scope`. That's with dynamic arrays. However, with static arrays and structs, `scope` means that each member of them is `scope`. Nothing else would make sense, because these contain their data in the variable itself. Assign a static array or a struct to new one, and it's contents becomes and independent copies of the original. Data escaping them is no more issue than `5` escaping an `int`.
Apr 08 2022
On Friday, 8 April 2022 at 07:02:57 UTC, Dukc wrote:On Friday, 8 April 2022 at 05:52:09 UTC, Andy wrote:Is there any way to do this safely? ``` safe nogc pure nothrow: void f() { scope immutable Node a = Node(1, null); g(a); } void g(scope immutable Node a) { scope immutable Node b = Node(2, &a); } struct Node { immutable int head; immutable Node* tail; } ``` I would think that getting `&a` in that context ought to be safe because it's only used in a `scope` variable, but apparently not. I'm using `immnutable` here because I know `a.tail = b` would be wrong; they're both `scope` but `a` is a parameter and thus outlives `b`. To put it more simply, why doesn't this work? ``` safe nogc pure nothrow: void f() { int x = 0; scope int* y = &x; } ```In the following code, I can pass a non-`scope` argument to a function expecting a `scope` parameter, but I can't pass a `scope` argument. That seems backwards?I was already writing "these both should pass", but then I noticed what's wrong. The scope at `f` means you cannot escape the outer array. But on `yesScope`, it means that you cannot escape the strings! In other words, this fails for the same reason that this would fail: ``` safe void main() { scope immutable(char)[] a = "one"; scope immutable(char)[] b = "two"; auto array = [a, b]; } ``` It fails, because D does not have a concept of an array holding scope variables (or a pointer pointing to a scope variable). Only the outernmost array or pointer can be `scope`. That's with dynamic arrays. However, with static arrays and structs, `scope` means that each member of them is `scope`. Nothing else would make sense, because these contain their data in the variable itself. Assign a static array or a struct to new one, and it's contents becomes and independent copies of the original. Data escaping them is no more issue than `5` escaping an `int`.
Apr 14 2022
On Friday, 15 April 2022 at 05:42:42 UTC, Andy wrote:Is there any way to do this safely? ``` safe nogc pure nothrow: void f() { scope immutable Node a = Node(1, null); g(a); } void g(scope immutable Node a) { scope immutable Node b = Node(2, &a); } struct Node { immutable int head; immutable Node* tail; } ```No, because `scope` isn't transitive. If the above example were allowed, then `b.tail.tail` would not be `scope`. Here's a simplified version, which makes the levels of indirection more obvious: ```d safe nogc pure nothrow: void f() { scope immutable int* a = null; g(a); } void g(scope immutable int* a) { scope immutable int** b = &a; // error } ```To put it more simply, why doesn't this work? ```d safe nogc pure nothrow: void f() { int x = 0; scope int* y = &x; } ```That example does work. It's only when you get to two levels of indirection that it fails; for example: ```d safe nogc pure nothrow: void f() { int x = 0; scope int* y = &x; scope int** z = &y; // error } ```
Apr 14 2022
On Friday, 15 April 2022 at 06:12:06 UTC, Paul Backus wrote:No, because `scope` isn't transitive. [...] That example does work. It's only when you get to two levels of indirection that it fails; for example: ```d safe nogc pure nothrow: void f() { int x = 0; scope int* y = &x; scope int** z = &y; // error } ```If it's not transitive, perhaps we need something to make it explicit: ```d void f() { int x = 0; scope int* y = &x; scope int* scope* z = &y; // like const* in C++ } ```
Apr 15 2022
On Friday, 15 April 2022 at 10:31:31 UTC, Dom DiSc wrote:On Friday, 15 April 2022 at 06:12:06 UTC, Paul Backus wrote:Actually the documentation is a bit confusing about `scope` storage class and which operations are admitted for scoped variables/function parameters. I think the role of `scope` inside D is not well defined at this moment (and the original DIP1000 was written even worse) so until this feature will be fixed we should avoid use it in released cose (and use `const` instead of `in`)No, because `scope` isn't transitive. [...] That example does work. It's only when you get to two levels of indirection that it fails; for example: ```d safe nogc pure nothrow: void f() { int x = 0; scope int* y = &x; scope int** z = &y; // error } ```If it's not transitive, perhaps we need something to make it explicit: ```d void f() { int x = 0; scope int* y = &x; scope int* scope* z = &y; // like const* in C++ } ```
Apr 15 2022
The trouble here is the scope array is a static array. This means the scope applies to the elements of the static array. But when passing it to the function, the static array is converted to a dynamic array. A dynamic array is a pointer to the array's elements. The scope protections are not transitive, so a scope dynamic array does not protect its elements as scope, hence the errors you're seeing.
Apr 15 2022
On Friday, 15 April 2022 at 20:43:19 UTC, Walter Bright wrote:The trouble here is the scope array is a static array. This means the scope applies to the elements of the static array. But when passing it to the function, the static array is converted to a dynamic array. A dynamic array is a pointer to the array's elements. The scope protections are not transitive, so a scope dynamic array does not protect its elements as scope, hence the errors you're seeing.A very good explanation. Thank you very much for my own name. SDB 79
Apr 15 2022
I have another puzzle related to scoping. In this case it's about defining data structures that will be compatible with scope. ``` safe nogc pure nothrow: extern(C) void main() {} struct ArrayWrapper(T) { private T[] inner; ref inout(T) opIndex(immutable size_t index) inout { return inner[index]; } } struct SomeValue { int* a; } int* f0(int[] xs) { return &xs[0]; } SomeValue* f1(SomeValue[] xs) { return &xs[0]; } int* g0(ArrayWrapper!int xs) { return &xs[0]; } SomeValue* g1(ArrayWrapper!SomeValue xs) { return &xs[0]; } ``` Compiling this code results in an error in `g1` only: ``` a.d(30): Error: cannot take address of `ref return` of `xs.opIndex()` in ` safe` function `g1` ``` Writing `return &xs.inner[0];` would work .. which is exactly what `opIndex` is supposed to do. The question is how to get the compiler to understand this. How can I write `ArrayWrapper` so that it behaves the same way an array does?
Apr 30 2022
On Sunday, 1 May 2022 at 04:04:04 UTC, Andy wrote:Compiling this code results in an error in `g1` only: ``` a.d(30): Error: cannot take address of `ref return` of `xs.opIndex()` in ` safe` function `g1` ``` Writing `return &xs.inner[0];` would work .. which is exactly what `opIndex` is supposed to do. The question is how to get the compiler to understand this. How can I write `ArrayWrapper` so that it behaves the same way an array does?Simplified example: ```d int** p; ref int* get() safe { return *p; } int** g1() safe { return &get(); // error } ``` Looks like the compiler has a blanket rule against taking the address of a `ref` return value if the value's type contains indirections.
Apr 30 2022
On Sunday, 1 May 2022 at 05:05:47 UTC, Paul Backus wrote:Looks like the compiler has a blanket rule against taking the address of a `ref` return value if the value's type contains indirections.True. I thought this wouldn't be a problem in my fix for [Issue 22519 - [dip1000] cannot take address of `ref return`](https://issues.dlang.org/show_bug.cgi?id=22519), but the `opIndex` of a dynamic array is a compelling case, so I'm working on it. https://issues.dlang.org/show_bug.cgi?id=23079 https://github.com/dlang/dmd/pull/14062
May 02 2022